/* $NetBSD: machdep.c,v 1.75.22.1 2018/08/28 16:24:24 snj Exp $ */ /* * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.75.22.1 2018/08/28 16:24:24 snj Exp $"); #include "opt_compat_netbsd.h" #include "opt_openpic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MULTIPROCESSOR #include #endif #include #include "com.h" #if (NCOM > 0) #include #include #include #endif #include "opt_interrupt.h" void initppc(u_long, u_long, u_int, void *); void dumpsys(void); static void prep_init(void); static void init_intr(void); char bootinfo[BOOTINFO_MAXSIZE]; char bootpath[256]; vaddr_t prep_intr_reg; /* PReP interrupt vector register */ uint32_t prep_intr_reg_off; /* IVR offset within the mapped page */ #define OFMEMREGIONS 32 struct mem_region physmemr[OFMEMREGIONS], availmemr[OFMEMREGIONS]; paddr_t avail_end; /* XXX temporary */ struct pic_ops *isa_pic; int isa_pcmciamask = 0x8b28; uint32_t busfreq; extern int primary_pic; extern struct platform_quirkdata platform_quirks[]; RESIDUAL *res; RESIDUAL resdata; void initppc(u_long startkernel, u_long endkernel, u_int args, void *btinfo) { /* * copy bootinfo */ memcpy(bootinfo, btinfo, sizeof(bootinfo)); /* * copy residual data */ { struct btinfo_residual *resinfo; resinfo = (struct btinfo_residual *)lookup_bootinfo(BTINFO_RESIDUAL); if (!resinfo) panic("not found residual information in bootinfo"); if (((RESIDUAL *)resinfo->addr != 0) && ((RESIDUAL *)resinfo->addr)->ResidualLength != 0) { memcpy(&resdata, resinfo->addr, sizeof(resdata)); res = &resdata; } else panic("No residual data."); } aprint_normal("got residual data\n"); /* * Set memory region */ { u_long memsize = res->TotalMemory; physmemr[0].start = 0; physmemr[0].size = memsize & ~PGOFSET; availmemr[0].start = (endkernel + PGOFSET) & ~PGOFSET; availmemr[0].size = memsize - availmemr[0].start; } avail_end = physmemr[0].start + physmemr[0].size; /* XXX temporary */ /* * Set CPU clock */ { struct btinfo_clock *clockinfo; extern u_long ticks_per_sec, ns_per_tick; VPD *vpd; clockinfo = (struct btinfo_clock *)lookup_bootinfo(BTINFO_CLOCK); if (!clockinfo) panic("not found clock information in bootinfo"); ticks_per_sec = clockinfo->ticks_per_sec; ns_per_tick = 1000000000 / ticks_per_sec; vpd = &res->VitalProductData; busfreq = be32toh(vpd->ProcessorBusHz); } prep_initppc(startkernel, endkernel, args); } /* * Machine dependent startup code. */ void cpu_startup(void) { /* * Do common startup. */ oea_startup(res->VitalProductData.PrintableModel); /* * General prep setup using pnp residual. Also provides for * external interrupt handler install */ prep_init(); /* * Now allow hardware interrupts. */ { int msr; splraise(-1); __asm volatile ("mfmsr %0; ori %0,%0,%1; mtmsr %0" : "=r"(msr) : "K"(PSL_EE)); } /* * Now safe for bus space allocation to use malloc. */ bus_space_mallocok(); } /* * lookup_bootinfo: * Look up information in bootinfo of boot loader. */ void * lookup_bootinfo(int type) { struct btinfo_common *bt; struct btinfo_common *help = (struct btinfo_common *)bootinfo; do { bt = help; if (bt->type == type) return (help); help = (struct btinfo_common *)((char*)help + bt->next); } while (bt->next && (size_t)help < (size_t)bootinfo + sizeof (bootinfo)); return (NULL); } /* * Halt or reboot the machine after syncing/dumping according to howto. */ void cpu_reboot(int howto, char *what) { static int syncing; if (cold) { howto |= RB_HALT; goto halt_sys; } boothowto = howto; if ((howto & RB_NOSYNC) == 0 && syncing == 0) { syncing = 1; vfs_shutdown(); /* sync */ resettodr(); /* set wall clock */ } /* Disable intr */ splhigh(); #ifdef MULTIPROCESSOR /* Halt other CPUs */ ppc_send_ipi(IPI_T_NOTME, PPC_IPI_HALT); delay(100000); /* XXX */ #endif /* Do dump if requested */ if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP) oea_dumpsys(); halt_sys: doshutdownhooks(); pmf_system_shutdown(boothowto); if (howto & RB_HALT) { printf("\n"); printf("The operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); cnpollc(1); /* for proper keyboard command handling */ cngetc(); cnpollc(0); } printf("rebooting...\n\n"); reset_prep(); for (;;) continue; /* NOTREACHED */ } struct powerpc_bus_space prep_eisa_io_space_tag = { .pbs_flags = _BUS_SPACE_LITTLE_ENDIAN|_BUS_SPACE_IO_TYPE, .pbs_offset = 0x80000000, .pbs_base = 0x00000000, .pbs_limit = 0x0000f000, }; struct powerpc_bus_space prep_eisa_mem_space_tag = { .pbs_flags = _BUS_SPACE_LITTLE_ENDIAN|_BUS_SPACE_MEM_TYPE, .pbs_offset = 0xC0000000, .pbs_base = 0x00000000, .pbs_limit = 0x3f000000, }; #if defined(PIC_OPENPIC) static int prep_setup_openpic(PPC_DEVICE *dev) { uint32_t l; uint8_t *p; int tag, size, item, i; unsigned char *baseaddr = NULL; l = be32toh(dev->AllocatedOffset); p = res->DevicePnPHeap + l; i = find_platform_quirk(res->VitalProductData.PrintableModel); /* look for the large vendor item that describes the MPIC's memory * range */ for (; p[0] != END_TAG; p += size) { struct _L4_Pack *pack = (void *)p; struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack; tag = *p; if (tag_type(p[0]) == PNP_SMALL) { size = tag_small_count(tag) + 1; continue; } size = (p[1] | (p[2] << 8)) + 3 /* tag + length */; item = tag_large_item_name(tag); if (item != LargeVendorItem || pa->Type != LV_GenericAddress) continue; /* otherwise, we have a memory packet */ if (pa->PPCData[0] == 1) baseaddr = (unsigned char *)mapiodev( le64dec(&pa->PPCData[4]) | PREP_BUS_SPACE_IO, le64dec(&pa->PPCData[12]), false); else if (pa->PPCData[0] == 2) baseaddr = (unsigned char *)mapiodev( le64dec(&pa->PPCData[4]) | PREP_BUS_SPACE_MEM, le64dec(&pa->PPCData[12]), false); if (baseaddr == NULL) return 0; pic_init(); if (i != -1 && (platform_quirks[i].quirk & PLAT_QUIRK_ISA_HANDLER && platform_quirks[i].isa_intr_handler == EXT_INTR_I8259)) { isa_pic = setup_prepivr(PIC_IVR_MOT); } else isa_pic = setup_prepivr(PIC_IVR_IBM); (void)setup_openpic(baseaddr, 0); /* set the timebase frequency to 1/8th busfreq */ openpic_write(OPENPIC_TIMER_FREQ, busfreq/8); primary_pic = 1; /* set up the IVR as a cascade on openpic 0 */ intr_establish(16, IST_LEVEL, IPL_HIGH, pic_handle_intr, isa_pic); oea_install_extint(pic_ext_intr); #ifdef MULTIPROCESSOR setup_openpic_ipi(); #endif return 1; } return 0; } #endif /* PIC_OPENPIC */ /* * Locate and setup the isa_ivr. */ static void setup_ivr(PPC_DEVICE *dev) { uint32_t l, addr; uint8_t *p; int tag, size, item; l = be32toh(dev->AllocatedOffset); p = res->DevicePnPHeap + l; /* Find the IVR vector's Generic Address in a LVI */ for (; p[0] != END_TAG; p += size) { struct _L4_Pack *pack = (void *)p; struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack; tag = *p; if (tag_type(p[0]) == PNP_SMALL) { size = tag_small_count(tag) + 1; continue; } size = (p[1] | (p[2] << 8)) + 3 /* tag + length */; item = tag_large_item_name(tag); if (item != LargeVendorItem || pa->Type != LV_GenericAddress) continue; /* otherwise we have a memory packet */ addr = le64dec(&pa->PPCData[4]) & ~(PAGE_SIZE-1); prep_intr_reg_off = le64dec(&pa->PPCData[4]) & (PAGE_SIZE-1); prep_intr_reg = (vaddr_t)mapiodev(addr, PAGE_SIZE, false); if (!prep_intr_reg) panic("startup: no room for interrupt register"); return; } } /* * There are a few things that need setting up early on in the prep * architecture. Foremost of these is the MPIC (if present) and the * l2 cache controller. This is a cut-down version of pnpbus_search() * that looks for specific devices, and sets them up accordingly. * This should also look for and wire up the interrupt vector. */ static void prep_init(void) { PPC_DEVICE *ppc_dev; int i, foundmpic; uint32_t ndev; ndev = be32toh(res->ActualNumDevices); ppc_dev = res->Devices; foundmpic = 0; prep_intr_reg = 0; for (i = 0; i < ((ndev > MAX_DEVICES) ? MAX_DEVICES : ndev); i++) { if (ppc_dev[i].DeviceId.DevId == 0x41d00000) /* ISA_PIC */ setup_ivr(&ppc_dev[i]); #if defined(PIC_OPENPIC) if (ppc_dev[i].DeviceId.DevId == 0x244d000d) { /* MPIC */ foundmpic = prep_setup_openpic(&ppc_dev[i]); } #endif } if (!prep_intr_reg) { /* * For some reason we never found one, this is known to * occur on certain motorola VME boards. Instead we need * to just hardcode it. */ prep_intr_reg = (vaddr_t) mapiodev(PREP_INTR_REG, PAGE_SIZE, false); if (!prep_intr_reg) panic("startup: no room for interrupt register"); prep_intr_reg_off = INTR_VECTOR_REG; } if (!foundmpic) init_intr(); } static void init_intr(void) { int i; #if defined(PIC_OPENPIC) openpic_base = 0; #endif pic_init(); i = find_platform_quirk(res->VitalProductData.PrintableModel); if (i != -1) if (platform_quirks[i].quirk & PLAT_QUIRK_ISA_HANDLER && platform_quirks[i].isa_intr_handler == EXT_INTR_I8259) { isa_pic = setup_prepivr(PIC_IVR_MOT); return; } isa_pic = setup_prepivr(PIC_IVR_IBM); oea_install_extint(pic_ext_intr); }