/* $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 <sys/param.h>
#include <powerpc/psl.h>
#include <powerpc/oea/spr.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include <lib/libkern/libkern.h>
#include <machine/bootinfo.h>
#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<NUL>, 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 */