#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.121 2024/12/16 11:52:43 martin Exp $");
#include "opt_ofwoea.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/boot_flag.h>
#include <sys/mount.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <uvm/uvm_extern.h>
#include <dev/ofw/openfirm.h>
#include <dev/cons.h>
#include <machine/autoconf.h>
#include <machine/pmap.h>
#include <machine/powerpc.h>
#include <machine/trap.h>
#include <sys/bus.h>
#include <machine/isa_machdep.h>
#include <powerpc/spr.h>
#include <powerpc/oea/spr.h>
#include <powerpc/oea/bat.h>
#include <powerpc/ofw_cons.h>
#include <powerpc/rtas.h>
#include "com.h"
#if (NCOM > 0)
#include <sys/termios.h>
#include <dev/ic/comreg.h>
#include <dev/ic/comvar.h>
#endif
#include "rtas.h"
extern struct pmap ofw_pmap;
extern char bootpath[256];
extern u_int l2cr_config;
#if (NRTAS > 0)
extern int machine_has_rtas;
#endif
struct model_data modeldata;
static void model_init(void);
#ifdef OFWOEA_DEBUG
#define DPRINTF printf
#else
#define DPRINTF while (0) printf
#endif
static uint16_t
ranges_bitmap(int node, uint16_t bitmap)
{
int child, mlen, acells, scells, reclen, i, j;
uint32_t addr, len, map[160];
for (child = OF_child(node); child; child = OF_peer(child)) {
mlen = OF_getprop(child, "ranges", map, sizeof(map));
if (mlen == -1)
goto noranges;
j = OF_getprop(child, "#address-cells", &acells,
sizeof(acells));
if (j == -1)
goto noranges;
j = OF_getprop(child, "#size-cells", &scells,
sizeof(scells));
if (j == -1)
goto noranges;
reclen = acells + modeldata.ranges_offset + scells;
for (i = 0; i < (mlen / 4) / reclen; i++) {
addr = map[reclen * i + acells];
len = map[reclen * i + reclen - 1];
for (j = 0; j < len / 0x10000000; j++)
bitmap |= 1 << ((addr+j*0x10000000) >> 28);
bitmap |= 1 << (addr >> 28);
}
noranges:
bitmap |= ranges_bitmap(child, bitmap);
continue;
}
return bitmap;
}
void
initppc(u_int startkernel, u_int endkernel, char *args)
{
int node, i;
uint16_t bitmap;
node = OF_finddevice("/");
if (node != -1) {
i = OF_getprop(node, "model", model_name, sizeof(model_name));
if (i == -1) {
OF_getprop(node, "name", model_name,
sizeof(model_name));
}
}
model_init();
if ((oeacpufeat & OEACPU_NOBAT) == 0) {
node = OF_finddevice("/");
bitmap = ranges_bitmap(node, 0);
oea_batinit(0);
for (i = 1; i < 0x10; i++) {
if (i == USER_SR || i == KERNEL_SR || i == KERNEL2_SR) {
continue;
}
if (bitmap & (1 << i)) {
oea_iobat_add(0x10000000 * i, BAT_BL_256M);
DPRINTF("Batmapped 256M at 0x%x\n",
0x10000000 * i);
}
}
}
#if defined(MULTIPROCESSOR)
int l;
char cpupath[32];
extern void cpu_spinstart(u_int);
extern volatile u_int cpu_spinstart_ack;
for (i = 1; i < CPU_MAXNUM; i++) {
snprintf(cpupath, sizeof(cpupath), "/cpus/@%x", i);
node = OF_finddevice(cpupath);
if (node <= 0)
continue;
aprint_verbose("Starting up CPU %d %s\n", i, cpupath);
OF_start_cpu(node, (u_int)cpu_spinstart, i);
for (l = 0; l < 100000000; l++) {
if (cpu_spinstart_ack == i) {
aprint_verbose("CPU %d spun up.\n", i);
break;
}
__asm volatile ("sync");
}
}
#endif
ofwoea_initppc(startkernel, endkernel, args);
}
static void
model_init(void)
{
int qhandle, phandle, j;
memset(&modeldata, 0, sizeof(struct model_data));
for (j=0; j < MAX_PCI_BUSSES; j++) {
modeldata.pciiodata[j].start = 0x00008000;
modeldata.pciiodata[j].limit = 0x0000ffff;
}
modeldata.ranges_offset = 1;
if (strncmp(model_name, "FirePower,", 10) == 0) {
modeldata.ranges_offset = 0;
}
if (strcmp(model_name, "MOT,PowerStack_II_Pro4000") == 0) {
modeldata.ranges_offset = 0;
}
if (strncmp(model_name, "IBM,7044", 8) == 0) {
for (j=0; j < MAX_PCI_BUSSES; j++) {
modeldata.pciiodata[j].start = 0x00fff000;
modeldata.pciiodata[j].limit = 0x00ffffff;
}
}
if (strncmp(model_name, "Pegasos", 7) == 0) {
static uint16_t modew[] = { 640, 800, 1024, 1280, 0 };
static uint16_t modeh[] = { 480, 600, 768, 1024, 0 };
uint32_t width, height, mode, fbaddr;
char buf[32];
int i;
modeldata.pciiodata[0].start = 0x00001400;
modeldata.pciiodata[0].limit = 0x0000ffff;
l2cr_config = L2CR_L2PE;
for (qhandle = OF_peer(0); qhandle; qhandle = phandle) {
if (OF_getprop(qhandle, "name", buf, sizeof buf) > 0
&& strncmp(buf, "display", 7) == 0) {
OF_setprop(qhandle, "device_type", "display", 8);
break;
}
if ((phandle = OF_child(qhandle)))
continue;
while (qhandle) {
if ((phandle = OF_peer(qhandle)))
break;
qhandle = OF_parent(qhandle);
}
}
OF_interpret("screen-width", 0, 1, &width);
if (width == 0)
width = 800;
OF_interpret("screen-height", 0, 1, &height);
if (height == 0)
height = 600;
for (i = 0, mode = 0; modew[i] != 0; i++) {
if (modew[i] == width && modeh[i] == height) {
mode = 0x101 + 2 * i;
break;
}
}
if (!mode) {
mode = 0x103;
width = 800;
height = 600;
}
snprintf(buf, sizeof(buf), "%x vesa-set-mode", mode);
OF_interpret(buf, 0, 0);
snprintf(buf, sizeof(buf), "%x to screen-width", width);
OF_interpret(buf, 0, 0);
snprintf(buf, sizeof(buf), "%x to screen-height", height);
OF_interpret(buf, 0, 0);
OF_interpret("vesa-frame-buffer-adr", 0, 1, &fbaddr);
if (fbaddr != 0) {
snprintf(buf, sizeof(buf), "%x to frame-buffer-adr", fbaddr);
OF_interpret(buf, 0, 0);
}
}
}
void
cpu_startup(void)
{
oea_startup(model_name[0] ? model_name : NULL);
bus_space_mallocok();
}
void
consinit(void)
{
ofwoea_consinit();
}
void
dumpsys(void)
{
aprint_normal("dumpsys: TBD\n");
}
void
cpu_reboot(int howto, char *what)
{
static int syncing;
static char str[256];
char *ap = str, *ap1 = ap;
#if (NRTAS > 0)
int junk;
#endif
boothowto = howto;
if (!cold && !(howto & RB_NOSYNC) && !syncing) {
syncing = 1;
vfs_shutdown();
}
splhigh();
if (howto & RB_HALT) {
doshutdownhooks();
pmf_system_shutdown(boothowto);
aprint_normal("halted\n\n");
#if (NRTAS > 0)
if ((howto & 0x800) && machine_has_rtas &&
rtas_has_func(RTAS_FUNC_POWER_OFF))
rtas_call(RTAS_FUNC_POWER_OFF, 2, 1, 0, 0, &junk);
#endif
ppc_exit();
}
if (!cold && (howto & RB_DUMP))
oea_dumpsys();
doshutdownhooks();
pmf_system_shutdown(boothowto);
aprint_normal("rebooting\n\n");
#if (NRTAS > 0)
if (machine_has_rtas && rtas_has_func(RTAS_FUNC_SYSTEM_REBOOT)) {
rtas_call(RTAS_FUNC_SYSTEM_REBOOT, 0, 1, &junk);
for(;;);
}
#endif
if (what && *what) {
if (strlen(what) > sizeof str - 5)
aprint_normal("boot string too large, ignored\n");
else {
strcpy(str, what);
ap1 = ap = str + strlen(str);
*ap++ = ' ';
}
}
*ap++ = '-';
if (howto & RB_SINGLE)
*ap++ = 's';
if (howto & RB_KDB)
*ap++ = 'd';
*ap++ = 0;
if (ap[-2] == '-')
*ap1 = 0;
ppc_boot(str);
}
#define divrnd(n, q) (((n)*2/(q)+1)/2)
void
ofppc_init_comcons(int isa_node)
{
#if (NCOM > 0)
char name[64];
uint32_t reg[2], comfreq;
uint8_t dll, dlm;
int speed, rate, err, com_node, child;
bus_space_handle_t comh;
memset(name, 0, sizeof(name));
OF_getprop(console_node, "device_type", name, sizeof(name));
if (strcmp(name, "serial") != 0)
return;
com_node = -1;
for (child = OF_child(isa_node); child; child = OF_peer(child)) {
memset(name, 0, sizeof(name));
OF_getprop(child, "device_type", name, sizeof(name));
if (strcmp(name, "serial") == 0) {
if (child == console_node) {
com_node = child;
break;
}
if (com_node == -1)
com_node = child;
}
}
if (com_node == -1)
return;
if (OF_getprop(com_node, "reg", reg, sizeof(reg)) == -1)
return;
if (OF_getprop(com_node, "clock-frequency", &comfreq, 4) == -1)
comfreq = 0;
if (comfreq == 0)
comfreq = COM_FREQ;
if (bus_space_map(&genppc_isa_io_space_tag, reg[1], 8, 0, &comh) != 0)
panic("Can't map isa serial\n");
bus_space_write_1(&genppc_isa_io_space_tag, comh, com_cfcr, LCR_DLAB);
dll = bus_space_read_1(&genppc_isa_io_space_tag, comh, com_dlbl);
dlm = bus_space_read_1(&genppc_isa_io_space_tag, comh, com_dlbh);
rate = dll | (dlm << 8);
bus_space_write_1(&genppc_isa_io_space_tag, comh, com_cfcr, LCR_8BITS);
speed = divrnd((comfreq / 16), rate);
err = speed - (speed + 150)/300 * 300;
speed -= err;
if (err < 0)
err = -err;
if (err > 50)
speed = 9600;
bus_space_unmap(&genppc_isa_io_space_tag, comh, 8);
aprint_verbose("Switching to COM console at speed %d", speed);
if (comcnattach(&genppc_isa_io_space_tag, reg[1],
speed, comfreq, COM_TYPE_NORMAL,
((TTYDEF_CFLAG & ~(CSIZE | CSTOPB | PARENB)) | CS8)))
panic("Can't init serial console");
aprint_verbose("\n");
#endif
}
void
copy_disp_props(device_t dev, int node, prop_dictionary_t dict)
{
uint32_t temp;
char typestr[32];
memset(typestr, 0, sizeof(typestr));
OF_getprop(console_node, "device_type", typestr, sizeof(typestr));
if (strcmp(typestr, "serial") != 0) {
prop_dictionary_set_bool(dict, "is_console", 1);
}
if (!of_to_uint32_prop(dict, node, "width", "width")) {
OF_interpret("screen-width", 0, 1, &temp);
prop_dictionary_set_uint32(dict, "width", temp);
}
if (!of_to_uint32_prop(dict, node, "height", "height")) {
OF_interpret("screen-height", 0, 1, &temp);
prop_dictionary_set_uint32(dict, "height", temp);
}
of_to_uint32_prop(dict, node, "linebytes", "linebytes");
if (!of_to_uint32_prop(dict, node, "depth", "depth")) {
prop_dictionary_set_uint32(dict, "depth", 8);
}
if (!of_to_uint32_prop(dict, node, "address", "address")) {
uint32_t fbaddr = 0;
OF_interpret("frame-buffer-adr", 0, 1, &fbaddr);
if (fbaddr != 0)
prop_dictionary_set_uint32(dict, "address", fbaddr);
}
}