/* $NetBSD: brdsetup.c,v 1.1 2008/05/30 14:54:16 nisimura Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Tohru Nishimura. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``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 THE FOUNDATION OR CONTRIBUTORS * 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 <sys/param.h> #include <lib/libsa/stand.h> #include <lib/libkern/libkern.h> #include "globals.h" const unsigned dcache_line_size = 32; /* 32B linesize */ const unsigned dcache_range_size = 4 * 1024; /* 16KB / 4-way */ unsigned mpc107memsize(void); void setup_82C686B(void); void setup_83C553F(void); unsigned uartbase; #define THR 0 #define DLB 0 #define DMB 1 #define IER 1 #define FCR 2 #define LCR 3 #define MCR 4 #define LSR 5 #define DCR 11 #define LSR_THRE 0x20 #define UART_READ(r) *(volatile char *)(uartbase + (r)) #define UART_WRITE(r, v) *(volatile char *)(uartbase + (r)) = (v) extern int brdtype; void brdsetup() { unsigned pchb, pcib, div; /* BAT to arrange address space */ /* EUMBBAR */ pchb = pcimaketag(0, 0, 0); pcicfgwrite(pchb, 0x78, 0xfc000000); brdtype = BRD_UNKNOWN; if (pcifinddev(0x10ad, 0x0565, &pcib) == 0) { brdtype = BRD_SANDPOINTX3; setup_83C553F(); } else if (pcifinddev(0x1106, 0x0686, &pcib) == 0) { brdtype = BRD_ENCOREPP1; setup_82C686B(); } /* now prepare serial console */ if (strcmp(CONSNAME, "eumb") != 0) uartbase = 0xfe000000 + CONSPORT; /* 0x3f8, 0x2f8 */ else { uartbase = 0xfc000000 + CONSPORT; /* 0x4500, 0x4600 */ div = (TICKS_PER_SEC * 4) / CONSSPEED / 16; UART_WRITE(DCR, 0x01); /* 2 independent UART */ UART_WRITE(LCR, 0x80); /* turn on DLAB bit */ UART_WRITE(FCR, 0x00); UART_WRITE(DMB, div >> 8); UART_WRITE(DLB, div & 0xff); /* 0x36 when 115200bps@100MHz */ UART_WRITE(LCR, 0x03); /* 8 N 1 */ UART_WRITE(MCR, 0x03); /* RTS DTR */ UART_WRITE(FCR, 0x07); /* FIFO_EN | RXSR | TXSR */ UART_WRITE(IER, 0x00); /* make sure INT disabled */ } } void putchar(c) int c; { unsigned timo, lsr; if (c == '\n') putchar('\r'); timo = 0x00100000; do { lsr = UART_READ(LSR); } while (timo-- > 0 && (lsr & LSR_THRE) == 0); if (timo > 0) UART_WRITE(THR, c); } void _rtt() { run(0, 0, 0, 0, (void *)0xFFF00100); /* reset entry */ /* NOTREACHED */ } static inline u_quad_t mftb() { u_long scratch; u_quad_t tb; asm ("1: mftbu %0; mftb %0+1; mftbu %1; cmpw %0,%1; bne 1b" : "=r"(tb), "=r"(scratch)); return (tb); } time_t getsecs() { u_quad_t tb = mftb(); return (tb / TICKS_PER_SEC); } /* * Wait for about n microseconds (at least!). */ void delay(n) u_int n; { u_quad_t tb; u_long tbh, tbl, scratch; tb = mftb(); tb += (n * 1000 + NS_PER_TICK - 1) / NS_PER_TICK; tbh = tb >> 32; tbl = tb; asm volatile ("1: mftbu %0; cmpw %0,%1; blt 1b; bgt 2f; mftb %0; cmpw 0, %0,%2; blt 1b; 2:" : "=&r"(scratch) : "r"(tbh), "r"(tbl)); } void _wb(adr, siz) uint32_t adr, siz; { uint32_t bnd; asm volatile("eieio"); for (bnd = adr + siz; adr < bnd; adr += dcache_line_size) asm volatile ("dcbst 0,%0" :: "r"(adr)); asm volatile ("sync"); } void _wbinv(adr, siz) uint32_t adr, siz; { uint32_t bnd; asm volatile("eieio"); for (bnd = adr + siz; adr < bnd; adr += dcache_line_size) asm volatile ("dcbf 0,%0" :: "r"(adr)); asm volatile ("sync"); } void _inv(adr, siz) uint32_t adr, siz; { uint32_t off, bnd; off = adr & (dcache_line_size - 1); adr -= off; siz += off; asm volatile ("eieio"); if (off != 0) { /* wbinv() leading unaligned dcache line */ asm volatile ("dcbf 0,%0" :: "r"(adr)); if (siz < dcache_line_size) goto done; adr += dcache_line_size; siz -= dcache_line_size; } bnd = adr + siz; off = bnd & (dcache_line_size - 1); if (off != 0) { /* wbinv() trailing unaligned dcache line */ asm volatile ("dcbf 0,%0" :: "r"(bnd)); /* it's OK */ if (siz < dcache_line_size) goto done; siz -= off; } for (bnd = adr + siz; adr < bnd; adr += dcache_line_size) { /* inv() intermediate dcache lines if ever */ asm volatile ("dcbi 0,%0" :: "r"(adr)); } done: asm volatile ("sync"); } unsigned mpc107memsize() { unsigned tag, val, n, bankn, end; tag = pcimaketag(0, 0, 0); if (brdtype == BRD_ENCOREPP1) { /* the brd's PPCBOOT looks to have erroneous values */ unsigned tbl[] = { #define MPC106_MEMSTARTADDR1 0x80 #define MPC106_EXTMEMSTARTADDR1 0x88 #define MPC106_MEMENDADDR1 0x90 #define MPC106_EXTMEMENDADDR1 0x98 #define MPC106_MEMEN 0xa0 #define BK0_S 0x00000000 #define BK0_E (128 << 20) - 1 #define BK1_S 0x3ff00000 #define BK1_E 0x3fffffff #define BK2_S 0x3ff00000 #define BK2_E 0x3fffffff #define BK3_S 0x3ff00000 #define BK3_E 0x3fffffff #define AR(v, s) ((((v) & SAR_MASK) >> SAR_SHIFT) << (s)) #define XR(v, s) ((((v) & EAR_MASK) >> EAR_SHIFT) << (s)) #define SAR_MASK 0x0ff00000 #define SAR_SHIFT 20 #define EAR_MASK 0x30000000 #define EAR_SHIFT 28 AR(BK0_S, 0) | AR(BK1_S, 8) | AR(BK2_S, 16) | AR(BK3_S, 24), XR(BK0_S, 0) | XR(BK1_S, 8) | XR(BK2_S, 16) | XR(BK3_S, 24), AR(BK0_E, 0) | AR(BK1_E, 8) | AR(BK2_E, 16) | AR(BK3_E, 24), XR(BK0_E, 0) | XR(BK1_E, 8) | XR(BK2_E, 16) | XR(BK3_E, 24), }; tag = pcimaketag(0, 0, 0); pcicfgwrite(tag, MPC106_MEMSTARTADDR1, tbl[0]); pcicfgwrite(tag, MPC106_EXTMEMSTARTADDR1, tbl[1]); pcicfgwrite(tag, MPC106_MEMENDADDR1, tbl[2]); pcicfgwrite(tag, MPC106_EXTMEMENDADDR1, tbl[3]); pcicfgwrite(tag, MPC106_MEMEN, 1); } bankn = 0; val = pcicfgread(tag, MPC106_MEMEN); for (n = 0; n < 4; n++) { if ((val & (1U << n)) == 0) break; bankn = n; } bankn = bankn * 8; val = pcicfgread(tag, MPC106_EXTMEMENDADDR1); end = ((val >> bankn) & 0x03) << 28; val = pcicfgread(tag, MPC106_MEMENDADDR1); end |= ((val >> bankn) & 0xff) << 20; end |= 0xfffff; return (end + 1); /* size of bankN SDRAM */ } /* * VIA82C686B Southbridge * 0.22.0 1106.0686 PCI-ISA bridge * 0.22.1 1106.0571 IDE (viaide) * 0.22.2 1106.3038 USB 0/1 (uhci) * 0.22.3 1106.3038 USB 2/3 (uhci) * 0.22.4 1106.3057 power management * 0.22.5 1106.3058 AC97 (auvia) */ void setup_82C686B() { unsigned pcib, ide, usb12, usb34, ac97, pmgt, val; pcib = pcimaketag(0, 22, 0); ide = pcimaketag(0, 22, 1); usb12 = pcimaketag(0, 22, 2); usb34 = pcimaketag(0, 22, 3); pmgt = pcimaketag(0, 22, 4); ac97 = pcimaketag(0, 22, 5); #define CFG(i,v) do { \ *(volatile unsigned char *)(0xfe000000 + 0x3f0) = (i); \ *(volatile unsigned char *)(0xfe000000 + 0x3f1) = (v); \ } while (0) val = pcicfgread(pcib, 0x84); val |= (02 << 8); pcicfgwrite(pcib, 0x84, val); CFG(0xe2, 0x0f); /* use COM1/2, don't use FDC/LPT */ val = pcicfgread(pcib, 0x84); val &= ~(02 << 8); pcicfgwrite(pcib, 0x84, val); /* route pin C to i8259 IRQ 5, pin D to 11 */ val = pcicfgread(pcib, 0x54); val = (val & 0xff) | 0xb0500000; /* Dx CB Ax xS */ pcicfgwrite(pcib, 0x54, val); /* enable EISA ELCR1 (0x4d0) and ELCR2 (0x4d1) */ val = pcicfgread(pcib, 0x44); val = val | 0x20000000; pcicfgwrite(pcib, 0x44, val); /* select level trigger for IRQ 5/11 at ELCR1/2 */ *(volatile uint8_t *)0xfe0004d0 = 0x20; /* bit 5 */ *(volatile uint8_t *)0xfe0004d1 = 0x08; /* bit 11 */ /* USB and AC97 are hardwired with pin D and C */ val = pcicfgread(usb12, 0x3c) &~ 0xff; val |= 11; pcicfgwrite(usb12, 0x3c, val); val = pcicfgread(usb34, 0x3c) &~ 0xff; val |= 11; pcicfgwrite(usb34, 0x3c, val); val = pcicfgread(ac97, 0x3c) &~ 0xff; val |= 5; pcicfgwrite(ac97, 0x3c, val); } /* * WinBond/Symphony Lab 83C553 with PC87308 "SuperIO" * * 0.11.0 10ad.0565 PCI-ISA bridge * 0.11.1 10ad.0105 IDE (slide) */ void setup_83C553F() { #if 0 unsigned pcib, ide, val; pcib = pcimaketag(0, 11, 0); ide = pcimaketag(0, 11, 1); #endif } void pcifixup() { unsigned pcib, ide, nic, val, steer, irq; int line; switch (brdtype) { case BRD_SANDPOINTX3: pcib = pcimaketag(0, 11, 0); ide = pcimaketag(0, 11, 1); nic = pcimaketag(0, 15, 0); /* * //// WinBond PIRQ //// * 0x40 - bit 5 (0x20) indicates PIRQ presense * 0x60 - PIRQ interrupt routing steer */ if (pcicfgread(pcib, 0x40) & 0x20) { steer = pcicfgread(pcib, 0x60); if ((steer & 0x80808080) == 0x80808080) printf("PIRQ[0-3] disabled\n"); else { unsigned i, v = steer; for (i = 0; i < 4; i++, v >>= 8) { if ((v & 0x80) != 0 || (v & 0xf) == 0) continue; printf("PIRQ[%d]=%d\n", i, v & 0xf); } } } #if 1 /* * //// IDE fixup -- case A //// * - "native PCI mode" (ide 0x09) * - don't use ISA IRQ14/15 (pcib 0x43) * - native IDE for both channels (ide 0x40) * - LEGIRQ bit 11 steers interrupt to pin C (ide 0x40) * - sign as PCI pin C line 11 (ide 0x3d/3c) */ /* ide: 0x09 - programming interface; 1000'SsPp */ val = pcicfgread(ide, 0x08); val &= 0xffff00ff; pcicfgwrite(ide, 0x08, val | (0x8f << 8)); /* pcib: 0x43 - IDE interrupt routing */ val = pcicfgread(pcib, 0x40) & 0x00ffffff; pcicfgwrite(pcib, 0x40, val); /* pcib: 0x45/44 - PCI interrupt routing */ val = pcicfgread(pcib, 0x44) & 0xffff0000; pcicfgwrite(pcib, 0x44, val); /* ide: 0x41/40 - IDE channel */ val = pcicfgread(ide, 0x40) & 0xffff0000; val |= (1 << 11) | 0x33; /* LEGIRQ turns on PCI interrupt */ pcicfgwrite(ide, 0x40, val); /* ide: 0x3d/3c - use PCI pin C/line 11 */ val = pcicfgread(ide, 0x3c) & 0xffffff00; val |= 11; /* pin designation is hardwired to pin A */ pcicfgwrite(ide, 0x3c, val); #else /* * //// IDE fixup -- case B //// * - "compatiblity mode" (ide 0x09) * - IDE primary/secondary interrupt routing (pcib 0x43) * - PCI interrupt routing (pcib 0x45/44) * - no PCI pin/line assignment (ide 0x3d/3c) */ /* ide: 0x09 - programming interface; 1000'SsPp */ val = pcicfgread(ide, 0x08); val &= 0xffff00ff; pcicfgwrite(ide, 0x08, val | (0x8a << 8)); /* pcib: 0x43 - IDE interrupt routing */ val = pcicfgread(pcib, 0x40) & 0x00ffffff; pcicfgwrite(pcib, 0x40, val | (0xee << 24)); /* ide: 0x45/44 - PCI interrupt routing */ val = pcicfgread(ide, 0x44) & 0xffff0000; pcicfgwrite(ide, 0x44, val); /* ide: 0x3d/3c - turn off PCI pin/line */ val = pcicfgread(ide, 0x3c) & 0xffff0000; pcicfgwrite(ide, 0x3c, val); #endif /* * //// fxp fixup //// * - use PCI pin A line 15 (fxp 0x3d/3c) */ val = pcicfgread(nic, 0x3c) & 0xffff0000; pcidecomposetag(nic, NULL, &line, NULL); val |= (('A' - '@') << 8) | line; pcicfgwrite(nic, 0x3c, val); break; case BRD_ENCOREPP1: #define STEER(v, b) (((v) & (b)) ? "edge" : "level") pcib = pcimaketag(0, 22, 0); ide = pcimaketag(0, 22, 1); nic = pcimaketag(0, 25, 0); /* * //// VIA PIRQ //// * 0x57/56/55/54 - Dx CB Ax xS */ val = pcicfgread(pcib, 0x54); /* Dx CB Ax xs */ steer = val & 0xf; irq = (val >> 12) & 0xf; /* 15:12 */ if (irq) { printf("pin A -> irq %d, %s\n", irq, STEER(steer, 0x1)); } irq = (val >> 16) & 0xf; /* 19:16 */ if (irq) { printf("pin B -> irq %d, %s\n", irq, STEER(steer, 0x2)); } irq = (val >> 20) & 0xf; /* 23:20 */ if (irq) { printf("pin C -> irq %d, %s\n", irq, STEER(steer, 0x4)); } irq = (val >> 28); /* 31:28 */ if (irq) { printf("pin D -> irq %d, %s\n", irq, STEER(steer, 0x8)); } #if 0 /* * //// IDE fixup //// * - "native mode" (ide 0x09) * - use primary only (ide 0x40) */ /* ide: 0x09 - programming interface; 1000'SsPp */ val = pcicfgread(ide, 0x08) & 0xffff00ff; pcicfgwrite(ide, 0x08, val | (0x8f << 8)); /* ide: 0x10-20 - leave them PCI memory space assigned */ /* ide: 0x40 - use primary only */ val = pcicfgread(ide, 0x40) &~ 03; val |= 02; pcicfgwrite(ide, 0x40, val); #else /* * //// IDE fixup //// * - "compatiblity mode" (ide 0x09) * - use primary only (ide 0x40) * - remove PCI pin assignment (ide 0x3d) */ /* ide: 0x09 - programming interface; 1000'SsPp */ val = pcicfgread(ide, 0x08) & 0xffff00ff; val |= (0x8a << 8); pcicfgwrite(ide, 0x08, val); /* ide: 0x10-20 */ /* experiment shows writing ide: 0x09 changes these register behaviour. The pcicfgwrite() above writes 0x8a at ide: 0x09 to make sure legacy IDE. Then reading BAR0-3 is to return value 0s even though pcisetup() has written range assignments. Value overwrite makes no effect. Having 0x8f for native PCIIDE doesn't change register values and brings no weirdness. */ /* ide: 0x40 - use primary only */ val = pcicfgread(ide, 0x40) &~ 03; val |= 02; pcicfgwrite(ide, 0x40, val); /* ide: 0x3d/3c - turn off PCI pin */ val = pcicfgread(ide, 0x3c) & 0xffff00ff; pcicfgwrite(ide, 0x3c, val); #endif /* * //// USBx2, audio, and modem fixup //// * - disable USB #0 and #1 (pcib 0x48 and 0x85) * - disable AC97 audio and MC97 modem (pcib 0x85) */ /* pcib: 0x48 - disable USB #0 at function 2 */ val = pcicfgread(pcib, 0x48); pcicfgwrite(pcib, 0x48, val | 04); /* pcib: 0x85 - disable USB #1 at function 3 */ /* pcib: 0x85 - disable AC97/MC97 at function 5/6 */ val = pcicfgread(pcib, 0x84); pcicfgwrite(pcib, 0x84, val | 0x1c00); /* * //// fxp fixup //// * - use PCI pin A line 25 (fxp 0x3d/3c) */ /* 0x3d/3c - PCI pin/line */ val = pcicfgread(nic, 0x3c) & 0xffff0000; val |= (('A' - '@') << 8) | 25; pcicfgwrite(nic, 0x3c, val); break; } }