/* $NetBSD: brdsetup.c,v 1.39 2017/08/03 19:22:15 phx 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 #include #include #include #include #include #include #include "globals.h" #define BRD_DECL(xxx) \ void xxx ## setup(struct brdprop *); \ void xxx ## brdfix(struct brdprop *); \ void xxx ## pcifix(struct brdprop *); \ void xxx ## launch(struct brdprop *); \ void xxx ## reset(void) BRD_DECL(mot); BRD_DECL(enc); BRD_DECL(kuro); BRD_DECL(syno); BRD_DECL(qnap); BRD_DECL(iomega); BRD_DECL(dlink); BRD_DECL(nhnas); BRD_DECL(kurot4); static void brdfixup(void); static void setup(void); static void send_iomega(int, int, int, int, int, int); static inline uint32_t mfmsr(void); static inline void mtmsr(uint32_t); static inline uint32_t cputype(void); static inline uint64_t mftb(void); static void init_uart(unsigned, unsigned, uint8_t); static void send_sat(char *); static unsigned mpc107memsize(void); /* UART registers */ #define RBR 0 #define THR 0 #define DLB 0 #define DMB 1 #define IER 1 #define FCR 2 #define LCR 3 #define LCR_DLAB 0x80 #define LCR_PEVEN 0x18 #define LCR_PNONE 0x00 #define LCR_8BITS 0x03 #define MCR 4 #define MCR_RTS 0x02 #define MCR_DTR 0x01 #define LSR 5 #define LSR_THRE 0x20 #define LSR_DRDY 0x01 #define DCR 0x11 #define UART_READ(base, r) in8(base + (r)) #define UART_WRITE(base, r, v) out8(base + (r), (v)) /* MPC106 and MPC824x PCI bridge memory configuration */ #define MPC106_MEMSTARTADDR1 0x80 #define MPC106_EXTMEMSTARTADDR1 0x88 #define MPC106_MEMENDADDR1 0x90 #define MPC106_EXTMEMENDADDR1 0x98 #define MPC106_MEMEN 0xa0 /* Iomega StorCenter MC68HC908 microcontroller data packet */ #define IOMEGA_POWER 0 #define IOMEGA_LED 1 #define IOMEGA_FLASH_RATE 2 #define IOMEGA_FAN 3 #define IOMEGA_HIGH_TEMP 4 #define IOMEGA_LOW_TEMP 5 #define IOMEGA_ID 6 #define IOMEGA_CHECKSUM 7 #define IOMEGA_PACKETSIZE 8 /* NH230/231 GPIO */ #define NHGPIO_WRITE(x) *((volatile uint8_t *)0x70000000) = (x) /* Synology CPLD (2007 and newer models) */ #define SYNOCPLD_READ(r) *((volatile uint8_t *)0xff000000 + (r)) #define SYNOCPLD_WRITE(r,x) do { \ *((volatile uint8_t *)0xff000000 + (r)) = (x); \ delay(10); \ } while(0) static struct brdprop brdlist[] = { { "sandpoint", "Sandpoint X3", BRD_SANDPOINTX3, 0, "com", 0x3f8, 115200, motsetup, motbrdfix, motpcifix, NULL, NULL }, { "encpp1", "EnCore PP1", BRD_ENCOREPP1, 0, "com", 0x3f8, 115200, encsetup, encbrdfix, encpcifix, NULL, NULL }, { "kurobox", "KuroBox", BRD_KUROBOX, 0, "eumb", 0x4600, 57600, kurosetup, kurobrdfix, NULL, NULL, kuroreset }, { "synology", "Synology CS/DS/RS", BRD_SYNOLOGY, 0, "eumb", 0x4500, 115200, synosetup, synobrdfix, synopcifix, synolaunch, synoreset }, { "qnap", "QNAP TS", BRD_QNAPTS, 33164691, /* Linux source says 33000000, but the Synology */ /* clock value delivers a much better precision. */ "eumb", 0x4500, 115200, NULL, qnapbrdfix, NULL, NULL, qnapreset }, { "iomega", "IOMEGA StorCenter G2", BRD_STORCENTER, 0, "eumb", 0x4500, 115200, NULL, iomegabrdfix, NULL, NULL, iomegareset }, { "dlink", "D-Link DSM-G600", BRD_DLINKDSM, 33000000, "eumb", 0x4500, 9600, NULL, dlinkbrdfix, NULL, NULL, NULL }, { "nhnas", "Netronix NH-230/231", BRD_NH230NAS, 33000000, "eumb", 0x4500, 9600, NULL, nhnasbrdfix, NULL, NULL, nhnasreset }, { "kurot4", "KuroBox/T4", BRD_KUROBOXT4, 32768000, "eumb", 0x4600, 57600, NULL, kurot4brdfix, NULL, NULL, NULL }, { "unknown", "Unknown board", BRD_UNKNOWN, 0, "eumb", 0x4500, 115200, NULL, NULL, NULL, NULL, NULL }, /* must be the last */ }; static struct brdprop *brdprop; static uint32_t ticks_per_sec, ns_per_tick; const unsigned dcache_line_size = 32; /* 32B linesize */ const unsigned dcache_range_size = 4 * 1024; /* 16KB / 4-way */ unsigned uart1base; /* console */ unsigned uart2base; /* optional satellite processor */ void brdsetup(void); /* called by entry.S */ void brdsetup(void) { static uint8_t pci_to_memclk[] = { 30, 30, 10, 10, 20, 10, 10, 10, 10, 20, 20, 15, 20, 15, 20, 30, 30, 40, 15, 40, 20, 25, 20, 40, 25, 20, 10, 20, 15, 15, 20, 00 }; static uint8_t mem_to_cpuclk[] = { 25, 30, 45, 20, 20, 00, 10, 30, 30, 20, 45, 30, 25, 35, 30, 35, 20, 25, 20, 30, 35, 40, 40, 20, 30, 25, 40, 30, 30, 25, 35, 00 }; char *consname; int consport; uint32_t extclk; unsigned pchb, pcib, dev11, dev12, dev13, dev15, dev16, val; extern struct btinfo_memory bi_mem; extern struct btinfo_console bi_cons; extern struct btinfo_clock bi_clk; extern struct btinfo_prodfamily bi_fam; /* * CHRP specification "Map-B" BAT012 layout * BAT0 0000-0000 (256MB) SDRAM * BAT1 8000-0000 (256MB) PCI mem space * BAT2 fc00-0000 (64MB) EUMB, PCI I/O space, misc devs, flash * * EUMBBAR is at fc00-0000. */ pchb = pcimaketag(0, 0, 0); pcicfgwrite(pchb, 0x78, 0xfc000000); brdtype = BRD_UNKNOWN; extclk = EXT_CLK_FREQ; /* usually 33MHz */ busclock = 0; dev11 = pcimaketag(0, 11, 0); dev12 = pcimaketag(0, 12, 0); dev13 = pcimaketag(0, 13, 0); dev15 = pcimaketag(0, 15, 0); dev16 = pcimaketag(0, 16, 0); if (pcifinddev(0x10ad, 0x0565, &pcib) == 0) { /* WinBond 553 southbridge at dev 11 */ brdtype = BRD_SANDPOINTX3; } else if (pcifinddev(0x1106, 0x0686, &pcib) == 0) { /* VIA 686B southbridge at dev 22 */ brdtype = BRD_ENCOREPP1; } else if (PCI_CLASS(pcicfgread(dev11, PCI_CLASS_REG)) == PCI_CLASS_ETH) { /* ADMtek AN985 (tlp) or RealTek 8169S (re) at dev 11 */ if (PCI_VENDOR(pcicfgread(dev11, PCI_ID_REG)) == 0x1317) brdtype = BRD_KUROBOX; else if (PCI_VENDOR(pcicfgread(dev11, PCI_ID_REG)) == 0x10ec) { if (PCI_PRODUCT(pcicfgread(dev12,PCI_ID_REG)) != 0x3512) brdtype = BRD_KUROBOX; else brdtype = BRD_KUROBOXT4; } } else if (PCI_VENDOR(pcicfgread(dev15, PCI_ID_REG)) == 0x1148 || PCI_VENDOR(pcicfgread(dev15, PCI_ID_REG)) == 0x11ab) { /* SKnet/Marvell (sk) at dev 15 */ brdtype = BRD_SYNOLOGY; } else if (PCI_VENDOR(pcicfgread(dev13, PCI_ID_REG)) == 0x1106) { /* VIA 6410 (viaide) at dev 13 */ brdtype = BRD_STORCENTER; } else if (PCI_VENDOR(pcicfgread(dev16, PCI_ID_REG)) == 0x1191) { /* ACARD ATP865 (acardide) at dev 16 */ brdtype = BRD_DLINKDSM; } else if (PCI_VENDOR(pcicfgread(dev16, PCI_ID_REG)) == 0x1283 || PCI_VENDOR(pcicfgread(dev16, PCI_ID_REG)) == 0x1095) { /* ITE (iteide) or SiI (satalink) at dev 16 */ brdtype = BRD_NH230NAS; } else if (PCI_VENDOR(pcicfgread(dev15, PCI_ID_REG)) == 0x8086 || PCI_VENDOR(pcicfgread(dev15, PCI_ID_REG)) == 0x10ec) { /* Intel (wm) or RealTek (re) at dev 15 */ brdtype = BRD_QNAPTS; } brdprop = brd_lookup(brdtype); /* brd dependent adjustments */ setup(); /* determine clock frequencies */ if (brdprop->extclk != 0) extclk = brdprop->extclk; if (busclock == 0) { if (cputype() == MPC8245) { /* PLL_CFG from PCI host bridge register 0xe2 */ val = pcicfgread(pchb, 0xe0); busclock = (extclk * pci_to_memclk[(val >> 19) & 0x1f] + 10) / 10; /* PLLRATIO from HID1 */ asm volatile ("mfspr %0,1009" : "=r"(val)); cpuclock = ((uint64_t)busclock * mem_to_cpuclk[val >> 27] + 10) / 10; } else busclock = 100000000; /* 100MHz bus clock default */ } ticks_per_sec = busclock >> 2; ns_per_tick = 1000000000 / ticks_per_sec; /* now prepare serial console */ consname = brdprop->consname; consport = brdprop->consport; if (strcmp(consname, "eumb") == 0) { uart1base = 0xfc000000 + consport; /* 0x4500, 0x4600 */ UART_WRITE(uart1base, DCR, 0x01); /* enable DUART mode */ uart2base = uart1base ^ 0x0300; } else uart1base = 0xfe000000 + consport; /* 0x3f8, 0x2f8 */ /* more brd adjustments */ brdfixup(); bi_mem.memsize = mpc107memsize(); snprintf(bi_cons.devname, sizeof(bi_cons.devname), "%s", consname); bi_cons.addr = consport; bi_cons.speed = brdprop->consspeed; bi_clk.ticks_per_sec = ticks_per_sec; snprintf(bi_fam.name, sizeof(bi_fam.name), "%s", brdprop->family); } struct brdprop * brd_lookup(int brd) { u_int i; for (i = 0; i < sizeof(brdlist)/sizeof(brdlist[0]); i++) { if (brdlist[i].brdtype == brd) return &brdlist[i]; } return &brdlist[i - 1]; } static void setup() { if (brdprop->setup == NULL) return; (*brdprop->setup)(brdprop); } static void brdfixup() { if (brdprop->brdfix == NULL) return; (*brdprop->brdfix)(brdprop); } void pcifixup() { if (brdprop->pcifix == NULL) return; (*brdprop->pcifix)(brdprop); } void launchfixup() { if (brdprop->launch == NULL) return; (*brdprop->launch)(brdprop); } void encsetup(struct brdprop *brd) { #ifdef COSNAME brd->consname = CONSNAME; #endif #ifdef CONSPORT brd->consport = CONSPORT; #endif #ifdef CONSSPEED brd->consspeed = CONSSPEED; #endif } void encbrdfix(struct brdprop *brd) { unsigned ac97, ide, pcib, pmgt, usb12, usb34, val; /* * 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) */ 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); (void) pcicfgread(ide, 0x08); (void) pcicfgread(pmgt, 0x08); } void encpcifix(struct brdprop *brd) { unsigned ide, irq, net, pcib, steer, val; #define STEER(v, b) (((v) & (b)) ? "edge" : "level") pcib = pcimaketag(0, 22, 0); ide = pcimaketag(0, 22, 1); net = 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) */ /* 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 */ #else /* * //// IDE fixup //// * - "compatiblity mode" (ide 0x09) * - 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: 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(net, 0x3c) & 0xffff0000; val |= (('A' - '@') << 8) | 25; pcicfgwrite(net, 0x3c, val); } void motsetup(struct brdprop *brd) { #ifdef COSNAME brd->consname = CONSNAME; #endif #ifdef CONSPORT brd->consport = CONSPORT; #endif #ifdef CONSSPEED brd->consspeed = CONSSPEED; #endif } void motbrdfix(struct brdprop *brd) { /* * WinBond/Symphony Lab 83C553 with PC87308 "SuperIO" * * 0.11.0 10ad.0565 PCI-ISA bridge * 0.11.1 10ad.0105 IDE (slide) */ } void motpcifix(struct brdprop *brd) { unsigned ide, net, pcib, steer, val; int line; pcib = pcimaketag(0, 11, 0); ide = pcimaketag(0, 11, 1); net = 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(net, 0x3c) & 0xffff0000; pcidecomposetag(net, NULL, &line, NULL); val |= (('A' - '@') << 8) | line; pcicfgwrite(net, 0x3c, val); } void kurosetup(struct brdprop *brd) { if (PCI_VENDOR(pcicfgread(pcimaketag(0, 11, 0), PCI_ID_REG)) == 0x10ec) brd->extclk = 32768000; /* decr 2457600Hz */ else brd->extclk = 32521333; /* decr 2439100Hz */ } void kurobrdfix(struct brdprop *brd) { init_uart(uart2base, 9600, LCR_8BITS | LCR_PEVEN); /* Stop Watchdog */ send_sat("AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK"); } void kuroreset() { send_sat("CCGG"); /*NOTREACHED*/ } void synosetup(struct brdprop *brd) { if (1) /* 200 and 266MHz models */ brd->extclk = 33164691; /* from Synology/Linux source */ else /* 400MHz models XXX how to check? */ brd->extclk = 33165343; } void synobrdfix(struct brdprop *brd) { init_uart(uart2base, 9600, LCR_8BITS | LCR_PNONE); /* beep, power LED on, status LED off */ send_sat("247"); } #define SYNO_FAN_TIMEOUT 500 /* 500ms to turn the fan off */ #define SYNO_DISK_DELAY 30 /* 30 seconds to power up 2nd disk */ void synopcifix(struct brdprop *brd) { static const char models207[4][7] = { "???", "DS107e", "DS107", "DS207" }; static const char models209[2][7] = { "DS109j", "DS209j" }; static const char models406[3][7] = { "CS406e", "CS406", "RS406" }; static const char models407[4][7] = { "???", "CS407e", "CS407", "RS407" }; extern struct btinfo_model bi_model; const char *model_name; unsigned cpld, version, flags; uint8_t v, status; int i; /* * Determine if a CPLD is present and whether is has 4-bit * (models 107, 207, 209) or 8-bit (models 406, 407) registers. * The register set repeats every 16 bytes. */ cpld = 0; flags = 0; version = 0; model_name = NULL; SYNOCPLD_WRITE(0, 0x00); /* LEDs blinking yellow (default) */ v = SYNOCPLD_READ(0); if (v != 0x00) { v &= 0xf0; if (v != 0x00 || (SYNOCPLD_READ(16 + 0) & 0xf0) != v) goto cpld_done; cpld4bits: /* 4-bit registers assumed, make LEDs solid yellow */ SYNOCPLD_WRITE(0, 0x50); v = SYNOCPLD_READ(0) & 0xf0; if (v != 0x50 || (SYNOCPLD_READ(32 + 0) & 0xf0) != v) goto cpld_done; v = SYNOCPLD_READ(2) & 0xf0; if ((SYNOCPLD_READ(48 + 2) & 0xf0) != v) goto cpld_done; version = (v >> 4) & 7; /* * Try to determine whether it is a 207-style or 209-style * CPLD register set, by turning the fan off and check if * either bit 5 or bit 4 changes from 0 to 1 to indicate * the fan is stopped. */ status = SYNOCPLD_READ(3) & 0xf0; SYNOCPLD_WRITE(3, 0x00); /* fan off */ for (i = 0; i < SYNO_FAN_TIMEOUT * 100; i++) { delay(10); v = SYNOCPLD_READ(3) & 0xf0; if ((status & 0x20) == 0 && (v & 0x20) != 0) { /* set x07 model */ v = SYNOCPLD_READ(1) >> 6; model_name = models207[v]; cpld = BI_MODEL_CPLD207; /* XXXX DS107v2/v3 have no thermal sensor */ flags |= BI_MODEL_THERMAL; break; } if ((status & 0x10) == 0 && (v & 0x10) != 0) { /* set x09 model */ v = SYNOCPLD_READ(1) >> 7; model_name = models209[v]; cpld = BI_MODEL_CPLD209; if (v == 1) /* DS209j */ flags |= BI_MODEL_THERMAL; break; } /* XXX What about DS108j? Does it have a CPLD? */ } /* turn the fan on again */ SYNOCPLD_WRITE(3, status); if (i >= SYNO_FAN_TIMEOUT * 100) goto cpld_done; /* timeout: no valid CPLD */ } else { if (SYNOCPLD_READ(16 + 0) != v) goto cpld4bits; /* 8-bit registers assumed, make LEDs solid yellow */ SYNOCPLD_WRITE(0, 0x55); v = SYNOCPLD_READ(0); if (v != 0x55) goto cpld4bits; /* try 4 bits instead */ if (SYNOCPLD_READ(32 + 0) != v) goto cpld_done; v = SYNOCPLD_READ(2); if (SYNOCPLD_READ(48 + 2) != v) goto cpld_done; version = v & 3; if ((v & 0x0c) != 0x0c) { /* set 406 model */ model_name = models406[(v >> 2) & 3]; cpld = BI_MODEL_CPLD406; } else { /* set 407 model */ model_name = models407[v >> 6]; cpld = BI_MODEL_CPLD407; flags |= BI_MODEL_THERMAL; } } printf("CPLD V%s%u detected for model %s\n", cpld < BI_MODEL_CPLD406 ? "" : "1.", version, model_name); if (cpld == BI_MODEL_CPLD406 || cpld == BI_MODEL_CPLD407) { /* * CS/RS stations power-up their disks one after another. * We have to watch over the current power state in a CPLD * register, until all disks become available. */ do { delay(1000 * 1000); v = SYNOCPLD_READ(1); printf("Power state: %02x\r", v); } while (v != 0xff); putchar('\n'); } else if (model_name != NULL && model_name[2] == '2') { /* * DS207 and DS209 have a second SATA disk, which is started * with several seconds delay, but no CPLD register to * monitor the power state. So all we can do is to * wait some more seconds during SATA-init. * Also wait some seconds now, to make sure the first * disk is ready after a cold start. */ sata_delay[1] = SYNO_DISK_DELAY; delay(10 * 1024 * 1024); } cpld_done: if (model_name != NULL) { snprintf(bi_model.name, sizeof(bi_model.name), "%s", model_name); bi_model.flags = cpld | version | flags; } else printf("No CPLD found. DS101/DS106.\n"); } void synolaunch(struct brdprop *brd) { extern struct btinfo_model bi_model; struct dkdev_ata *sata1, *sata2; unsigned cpld; cpld = bi_model.flags & BI_MODEL_CPLD_MASK; if (cpld == BI_MODEL_CPLD406 || cpld == BI_MODEL_CPLD407) { /* set drive LEDs for active disk drives on CS/RS models */ sata1 = lata[0].drv; sata2 = lata[1].drv; SYNOCPLD_WRITE(0, (sata1->presense[0] ? 0x80 : 0xc0) | (sata1->presense[1] ? 0x20 : 0x30) | (sata2->presense[0] ? 0x08 : 0x0c) | (sata2->presense[1] ? 0x02 : 0x03)); } else if (cpld == BI_MODEL_CPLD207 || cpld == BI_MODEL_CPLD209) { /* set drive LEDs for DS207 and DS209 models */ sata1 = lata[0].drv; SYNOCPLD_WRITE(0, (sata1->presense[0] ? 0x80 : 0xc0) | (sata1->presense[1] ? 0x20 : 0x30)); } } void synoreset() { send_sat("C"); /*NOTREACHED*/ } void qnapbrdfix(struct brdprop *brd) { init_uart(uart2base, 19200, LCR_8BITS | LCR_PNONE); /* beep, status LED red */ send_sat("PW"); } void qnapreset() { send_sat("f"); /*NOTREACHED*/ } void iomegabrdfix(struct brdprop *brd) { init_uart(uart2base, 9600, LCR_8BITS | LCR_PNONE); /* LED flashing blue, fan auto, turn on at 50C, turn off at 45C */ send_iomega('b', 'd', 2, 'a', 50, 45); } void iomegareset() { send_iomega('g', 0, 0, 0, 0, 0); /*NOTREACHED*/ } void dlinkbrdfix(struct brdprop *brd) { init_uart(uart2base, 9600, LCR_8BITS | LCR_PNONE); send_sat("SYN\n"); send_sat("ZWO\n"); /* power LED solid on */ } void nhnasbrdfix(struct brdprop *brd) { /* status LED off, USB-LEDs on, low-speed fan */ NHGPIO_WRITE(0x04); } void nhnasreset() { /* status LED on, assert system-reset to all devices */ NHGPIO_WRITE(0x02); delay(100000); /*NOTREACHED*/ } void kurot4brdfix(struct brdprop *brd) { init_uart(uart2base, 38400, LCR_8BITS | LCR_PEVEN); } void _rtt(void) { uint32_t msr; netif_shutdown_all(); if (brdprop->reset != NULL) (*brdprop->reset)(); else { msr = mfmsr(); msr &= ~PSL_EE; mtmsr(msr); asm volatile ("sync; isync"); asm volatile("mtspr %0,%1" : : "K"(81), "r"(0)); msr &= ~(PSL_ME | PSL_DR | PSL_IR); mtmsr(msr); asm volatile ("sync; isync"); run(0, 0, 0, 0, (void *)0xFFF00100); /* reset entry */ } __unreachable(); } satime_t getsecs(void) { uint64_t tb = mftb(); return (tb / ticks_per_sec); } /* * Wait for about n microseconds (at least!). */ void delay(unsigned n) { uint64_t tb; uint32_t scratch, tbh, tbl; tb = mftb(); tb += ((uint64_t)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(uint32_t adr, uint32_t 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(uint32_t adr, uint32_t 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(uint32_t adr, uint32_t siz) { uint32_t bnd, off; 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"); } static inline uint32_t mfmsr(void) { uint32_t msr; asm volatile ("mfmsr %0" : "=r"(msr)); return msr; } static inline void mtmsr(uint32_t msr) { asm volatile ("mtmsr %0" : : "r"(msr)); } static inline uint32_t cputype(void) { uint32_t pvr; asm volatile ("mfpvr %0" : "=r"(pvr)); return pvr >> 16; } static inline uint64_t mftb(void) { uint32_t scratch; uint64_t tb; asm ("1: mftbu %0; mftb %0+1; mftbu %1; cmpw %0,%1; bne 1b" : "=r"(tb), "=r"(scratch)); return tb; } static void init_uart(unsigned base, unsigned speed, uint8_t lcr) { unsigned div; div = busclock / speed / 16; UART_WRITE(base, LCR, 0x80); /* turn on DLAB bit */ UART_WRITE(base, FCR, 0x00); UART_WRITE(base, DMB, div >> 8); /* set speed */ UART_WRITE(base, DLB, div & 0xff); UART_WRITE(base, LCR, lcr); UART_WRITE(base, FCR, 0x07); /* FIFO on, TXRX FIFO reset */ UART_WRITE(base, IER, 0x00); /* make sure INT disabled */ } /* talk to satellite processor */ static void send_sat(char *msg) { unsigned savedbase; savedbase = uart1base; uart1base = uart2base; while (*msg) putchar(*msg++); uart1base = savedbase; } #ifdef DEBUG static void iomega_debug(const char *txt, uint8_t buf[]) { int i; printf("%s:", txt); for (i = 0; i < IOMEGA_PACKETSIZE; i++) printf(" %02x", buf[i]); putchar('\n'); } #endif /* DEBUG */ static void send_iomega(int power, int led, int rate, int fan, int high, int low) { uint8_t buf[IOMEGA_PACKETSIZE]; unsigned i, savedbase; savedbase = uart1base; uart1base = uart2base; /* first flush the receive buffer */ again: while (tstchar()) (void)getchar(); delay(20000); if (tstchar()) goto again; /* * Now synchronize the transmitter by sending 0x00 * until we receive a status reply. */ do { putchar(0); delay(50000); } while (!tstchar()); for (i = 0; i < IOMEGA_PACKETSIZE; i++) buf[i] = getchar(); #ifdef DEBUG uart1base = savedbase; iomega_debug("68HC908 status", buf); uart1base = uart2base; #endif /* send command */ buf[IOMEGA_POWER] = power; buf[IOMEGA_LED] = led; buf[IOMEGA_FLASH_RATE] = rate; buf[IOMEGA_FAN] = fan; buf[IOMEGA_HIGH_TEMP] = high; buf[IOMEGA_LOW_TEMP] = low; buf[IOMEGA_ID] = 7; /* host id */ buf[IOMEGA_CHECKSUM] = (buf[IOMEGA_POWER] + buf[IOMEGA_LED] + buf[IOMEGA_FLASH_RATE] + buf[IOMEGA_FAN] + buf[IOMEGA_HIGH_TEMP] + buf[IOMEGA_LOW_TEMP] + buf[IOMEGA_ID]) & 0x7f; #ifdef DEBUG uart1base = savedbase; iomega_debug("G2 sending", buf); uart1base = uart2base; #endif for (i = 0; i < IOMEGA_PACKETSIZE; i++) putchar(buf[i]); /* receive the reply */ for (i = 0; i < IOMEGA_PACKETSIZE; i++) buf[i] = getchar(); #ifdef DEBUG uart1base = savedbase; iomega_debug("68HC908 reply", buf); uart1base = uart2base; #endif if (buf[0] == '#') goto again; /* try again on error */ uart1base = savedbase; } void putchar(int c) { unsigned timo, lsr; if (c == '\n') putchar('\r'); timo = 0x00100000; do { lsr = UART_READ(uart1base, LSR); } while (timo-- > 0 && (lsr & LSR_THRE) == 0); if (timo > 0) UART_WRITE(uart1base, THR, c); } int getchar(void) { unsigned lsr; do { lsr = UART_READ(uart1base, LSR); } while ((lsr & LSR_DRDY) == 0); return UART_READ(uart1base, RBR); } int tstchar(void) { return (UART_READ(uart1base, LSR) & LSR_DRDY) != 0; } #define SAR_MASK 0x0ff00000 #define SAR_SHIFT 20 #define EAR_MASK 0x30000000 #define EAR_SHIFT 28 #define AR(v, s) ((((v) & SAR_MASK) >> SAR_SHIFT) << (s)) #define XR(v, s) ((((v) & EAR_MASK) >> EAR_SHIFT) << (s)) static void set_mem_bounds(unsigned tag, unsigned bk_en, ...) { unsigned mbst, mbxst, mben, mbxen; unsigned start, end; va_list ap; int i, sh; va_start(ap, bk_en); mbst = mbxst = mben = mbxen = 0; for (i = 0; i < 4; i++) { if ((bk_en & (1U << i)) != 0) { start = va_arg(ap, unsigned); end = va_arg(ap, unsigned); } else { start = 0x3ff00000; end = 0x3fffffff; } sh = i << 3; mbst |= AR(start, sh); mbxst |= XR(start, sh); mben |= AR(end, sh); mbxen |= XR(end, sh); } va_end(ap); pcicfgwrite(tag, MPC106_MEMSTARTADDR1, mbst); pcicfgwrite(tag, MPC106_EXTMEMSTARTADDR1, mbxst); pcicfgwrite(tag, MPC106_MEMENDADDR1, mben); pcicfgwrite(tag, MPC106_EXTMEMENDADDR1, mbxen); pcicfgwrite(tag, MPC106_MEMEN, (pcicfgread(tag, MPC106_MEMEN) & ~0xff) | (bk_en & 0xff)); } static unsigned mpc107memsize(void) { unsigned bankn, end, n, tag, val; tag = pcimaketag(0, 0, 0); if (brdtype == BRD_ENCOREPP1) { /* the brd's PPCBOOT looks to have erroneous values */ set_mem_bounds(tag, 1, 0x00000000, (128 << 20) - 1); } else if (brdtype == BRD_NH230NAS) { /* * PPCBoot sets the end address to 0x7ffffff, although the * board has just 64MB (0x3ffffff). */ set_mem_bounds(tag, 1, 0x00000000, 0x03ffffff); } bankn = 0; val = pcicfgread(tag, MPC106_MEMEN); for (n = 0; n < 4; n++) { if ((val & (1U << n)) == 0) break; bankn = n; } bankn <<= 3; 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); /* assume the end address matches total amount */ } struct fis_dir_entry { char name[16]; uint32_t startaddr; uint32_t loadaddr; uint32_t flashsize; uint32_t entryaddr; uint32_t filesize; char pad[256 - (16 + 5 * sizeof(uint32_t))]; }; #define FIS_LOWER_LIMIT 0xfff00000 /* * Look for a Redboot-style Flash Image System FIS-directory and * return a pointer to the start address of the requested file. */ static void * redboot_fis_lookup(const char *filename) { static const char FISdirname[16] = { 'F', 'I', 'S', ' ', 'd', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', 0, 0, 0 }; struct fis_dir_entry *dir; /* * The FIS directory is usually in the last sector of the flash. * But we do not know the sector size (erase size), so start * at 0xffffff00 and scan backwards in steps of the FIS directory * entry size (0x100). */ for (dir = (struct fis_dir_entry *)0xffffff00; (uint32_t)dir >= FIS_LOWER_LIMIT; dir--) if (memcmp(dir->name, FISdirname, sizeof(FISdirname)) == 0) break; if ((uint32_t)dir < FIS_LOWER_LIMIT) { printf("No FIS directory found!\n"); return NULL; } /* Now find filename by scanning the directory from beginning. */ dir = (struct fis_dir_entry *)dir->startaddr; while (dir->name[0] != 0xff && (uint32_t)dir < 0xffffff00) { if (strcmp(dir->name, filename) == 0) return (void *)dir->startaddr; /* found */ dir++; } printf("\"%s\" not found in FIS directory!\n", filename); return NULL; } static void read_mac_string(uint8_t *mac, char *p) { int i; for (i = 0; i < 6; i++, p += 3) *mac++ = read_hex(p); } /* * Scan through the Flash memory and look for a string starting at 512 bytes * block boundaries, matching the format: xx:xx:xx:xx:xx:xx, where "x" * are hexadecimal digits. * Read the first match as our MAC address. * The start address of the search, p, *must* be dividable by 512! * Return false when no suitable MAC string was found. */ static int find_mac_string(uint8_t *mac, char *p) { int i; for (;;) { for (i = 0; i < 3 * 6; i += 3) { if (!isxdigit((unsigned)p[i]) || !isxdigit((unsigned)p[i + 1])) break; if ((i < 5 && p[i + 2] != ':') || (i >= 5 && p[i + 2] != '\0')) break; } if (i >= 6) { /* found a valid MAC address */ read_mac_string(mac, p); return 1; } if (p >= (char *)0xfffffe00) break; p += 0x200; } return 0; } /* * For cost saving reasons some NAS boxes lack SEEPROM for NIC's * ethernet address and keep it in their Flash memory instead. */ void read_mac_from_flash(uint8_t *mac) { uint8_t *p; switch (brdtype) { case BRD_SYNOLOGY: p = redboot_fis_lookup("vendor"); if (p == NULL) break; memcpy(mac, p, 6); return; case BRD_DLINKDSM: read_mac_string(mac, (char *)0xfff0ff80); return; case BRD_QNAPTS: if (find_mac_string(mac, (char *)0xfff00000)) return; break; default: printf("Warning: This board has no known method defined " "to determine its MAC address!\n"); break; } /* set to 00:00:00:00:00:00 in case of error */ memset(mac, 0, 6); } #ifdef DEBUG void sat_write(char *p, int len) { unsigned savedbase; savedbase = uart1base; uart1base = uart2base; while (len--) putchar(*p++); uart1base = savedbase; } int sat_getch(void) { unsigned lsr; do { lsr = UART_READ(uart2base, LSR); } while ((lsr & LSR_DRDY) == 0); return UART_READ(uart2base, RBR); } int sat_tstch(void) { return (UART_READ(uart2base, LSR) & LSR_DRDY) != 0; } #endif /* DEBUG */