From 04d76b937bdf60a8c9ac34e222e3ca977ab9ddc8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 1 Dec 2006 22:53:48 +0100 Subject: [POWERPC] Linkstation / kurobox support Support for the Kurobox(HG)/LinkStation-I NAS systems by Buffalo Technology, should be also applicable to the PPC TeraStation family. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Kumar Gala Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/embedded6xx/Kconfig | 18 +- arch/powerpc/platforms/embedded6xx/Makefile | 1 + arch/powerpc/platforms/embedded6xx/linkstation.c | 211 +++++++++++++++++++++++ arch/powerpc/platforms/embedded6xx/ls_uart.c | 131 ++++++++++++++ 4 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 arch/powerpc/platforms/embedded6xx/linkstation.c create mode 100644 arch/powerpc/platforms/embedded6xx/ls_uart.c (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig index 910d50a8333a..ddbe398fbd48 100644 --- a/arch/powerpc/platforms/embedded6xx/Kconfig +++ b/arch/powerpc/platforms/embedded6xx/Kconfig @@ -74,6 +74,18 @@ config SANDPOINT Select SANDPOINT if configuring for a Motorola Sandpoint X3 (any flavor). +config LINKSTATION + bool "Linkstation / Kurobox(HG) from Buffalo" + select MPIC + select FSL_SOC + select PPC_UDBG_16550 if SERIAL_8250 + help + Select LINKSTATION if configuring for one of PPC- (MPC8241) + based NAS systems from Buffalo Technology. So far only + KuroboxHG has been tested. In the future classical Kurobox, + Linkstation-I HD-HLAN and HD-HGLAN versions, and PPC-based + Terastation systems should be supported too. + config MPC7448HPC2 bool "Freescale MPC7448HPC2(Taiga)" select TSI108_BRIDGE @@ -196,7 +208,7 @@ config PPC_GEN550 depends on SANDPOINT || SPRUCE || PPLUS || \ PRPMC750 || PRPMC800 || LOPEC || \ (EV64260 && !SERIAL_MPSC) || CHESTNUT || RADSTONE_PPC7D || \ - 83xx + 83xx || LINKSTATION default y config FORCE @@ -270,13 +282,13 @@ config EPIC_SERIAL_MODE config MPC10X_BRIDGE bool - depends on POWERPMC250 || LOPEC || SANDPOINT + depends on POWERPMC250 || LOPEC || SANDPOINT || LINKSTATION select PPC_INDIRECT_PCI default y config MPC10X_OPENPIC bool - depends on POWERPMC250 || LOPEC || SANDPOINT + depends on POWERPMC250 || LOPEC || SANDPOINT || LINKSTATION default y config MPC10X_STORE_GATHERING diff --git a/arch/powerpc/platforms/embedded6xx/Makefile b/arch/powerpc/platforms/embedded6xx/Makefile index fa499fe59291..d3d11a3cd656 100644 --- a/arch/powerpc/platforms/embedded6xx/Makefile +++ b/arch/powerpc/platforms/embedded6xx/Makefile @@ -2,3 +2,4 @@ # Makefile for the 6xx/7xx/7xxxx linux kernel. # obj-$(CONFIG_MPC7448HPC2) += mpc7448_hpc2.o +obj-$(CONFIG_LINKSTATION) += linkstation.o ls_uart.o diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c new file mode 100644 index 000000000000..61599d919ea8 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/linkstation.c @@ -0,0 +1,211 @@ +/* + * Board setup routines for the Buffalo Linkstation / Kurobox Platform. + * + * Copyright (C) 2006 G. Liakhovetski (g.liakhovetski@gmx.de) + * + * Based on sandpoint.c by Mark A. Greer + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of + * any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct mtd_partition linkstation_physmap_partitions[] = { + { + .name = "mtd_firmimg", + .offset = 0x000000, + .size = 0x300000, + }, + { + .name = "mtd_bootcode", + .offset = 0x300000, + .size = 0x070000, + }, + { + .name = "mtd_status", + .offset = 0x370000, + .size = 0x010000, + }, + { + .name = "mtd_conf", + .offset = 0x380000, + .size = 0x080000, + }, + { + .name = "mtd_allflash", + .offset = 0x000000, + .size = 0x400000, + }, + { + .name = "mtd_data", + .offset = 0x310000, + .size = 0x0f0000, + }, +}; + +static int __init add_bridge(struct device_node *dev) +{ + int len; + struct pci_controller *hose; + int *bus_range; + + printk("Adding PCI host bridge %s\n", dev->full_name); + + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) + printk(KERN_WARNING "Can't get bus-range for %s, assume" + " bus 0\n", dev->full_name); + + hose = pcibios_alloc_controller(); + if (hose == NULL) + return -ENOMEM; + hose->first_busno = bus_range ? bus_range[0] : 0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + hose->arch_data = dev; + setup_indirect_pci(hose, 0xfec00000, 0xfee00000); + + /* Interpret the "ranges" property */ + /* This also maps the I/O region and sets isa_io/mem_base */ + pci_process_bridge_OF_ranges(hose, dev, 1); + + return 0; +} + +static void __init linkstation_setup_arch(void) +{ + struct device_node *np; +#ifdef CONFIG_MTD_PHYSMAP + physmap_set_partitions(linkstation_physmap_partitions, + ARRAY_SIZE(linkstation_physmap_partitions)); +#endif + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif + + /* Lookup PCI host bridges */ + for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) + add_bridge(np); + + printk(KERN_INFO "BUFFALO Network Attached Storage Series\n"); + printk(KERN_INFO "(C) 2002-2005 BUFFALO INC.\n"); +} + +/* + * Interrupt setup and service. Interrrupts on the linkstation come + * from the four PCI slots plus onboard 8241 devices: I2C, DUART. + */ +static void __init linkstation_init_IRQ(void) +{ + struct mpic *mpic; + struct device_node *dnp; + void *prop; + int size; + phys_addr_t paddr; + + dnp = of_find_node_by_type(NULL, "open-pic"); + if (dnp == NULL) + return; + + prop = (struct device_node *)get_property(dnp, "reg", &size); + paddr = (phys_addr_t)of_translate_address(dnp, prop); + + mpic = mpic_alloc(dnp, paddr, MPIC_PRIMARY | MPIC_WANTS_RESET, 4, 32, " EPIC "); + BUG_ON(mpic == NULL); + + /* PCI IRQs */ + mpic_assign_isu(mpic, 0, paddr + 0x10200); + + /* I2C */ + mpic_assign_isu(mpic, 1, paddr + 0x11000); + + /* ttyS0, ttyS1 */ + mpic_assign_isu(mpic, 2, paddr + 0x11100); + + mpic_init(mpic); +} + +extern void avr_uart_configure(void); +extern void avr_uart_send(const char); + +static void linkstation_restart(char *cmd) +{ + local_irq_disable(); + + /* Reset system via AVR */ + avr_uart_configure(); + /* Send reboot command */ + avr_uart_send('C'); + + for(;;) /* Spin until reset happens */ + avr_uart_send('G'); /* "kick" */ +} + +static void linkstation_power_off(void) +{ + local_irq_disable(); + + /* Power down system via AVR */ + avr_uart_configure(); + /* send shutdown command */ + avr_uart_send('E'); + + for(;;) /* Spin until power-off happens */ + avr_uart_send('G'); /* "kick" */ + /* NOTREACHED */ +} + +static void linkstation_halt(void) +{ + linkstation_power_off(); + /* NOTREACHED */ +} + +static void linkstation_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Buffalo Technology\n"); + seq_printf(m, "machine\t\t: Linkstation I/Kurobox(HG)\n"); +} + +static int __init linkstation_probe(void) +{ + unsigned long root; + + root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "linkstation")) + return 0; + return 1; +} + +define_machine(linkstation){ + .name = "Buffalo Linkstation", + .probe = linkstation_probe, + .setup_arch = linkstation_setup_arch, + .init_IRQ = linkstation_init_IRQ, + .show_cpuinfo = linkstation_show_cpuinfo, + .get_irq = mpic_get_irq, + .restart = linkstation_restart, + .power_off = linkstation_power_off, + .halt = linkstation_halt, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/embedded6xx/ls_uart.c b/arch/powerpc/platforms/embedded6xx/ls_uart.c new file mode 100644 index 000000000000..31bcdae84823 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/ls_uart.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __iomem *avr_addr; +static unsigned long avr_clock; + +static struct work_struct wd_work; + +static void wd_stop(void *unused) +{ + const char string[] = "AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK"; + int i = 0, rescue = 8; + int len = strlen(string); + + while (rescue--) { + int j; + char lsr = in_8(avr_addr + UART_LSR); + + if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) { + for (j = 0; j < 16 && i < len; j++, i++) + out_8(avr_addr + UART_TX, string[i]); + if (i == len) { + /* Read "OK" back: 4ms for the last "KKKK" + plus a couple bytes back */ + msleep(7); + printk("linkstation: disarming the AVR watchdog: "); + while (in_8(avr_addr + UART_LSR) & UART_LSR_DR) + printk("%c", in_8(avr_addr + UART_RX)); + break; + } + } + msleep(17); + } + printk("\n"); +} + +#define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600) + +void avr_uart_configure(void) +{ + unsigned char cval = UART_LCR_WLEN8; + unsigned int quot = AVR_QUOT(avr_clock); + + if (!avr_addr || !avr_clock) + return; + + out_8(avr_addr + UART_LCR, cval); /* initialise UART */ + out_8(avr_addr + UART_MCR, 0); + out_8(avr_addr + UART_IER, 0); + + cval |= UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR; + + out_8(avr_addr + UART_LCR, cval); /* Set character format */ + + out_8(avr_addr + UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ + out_8(avr_addr + UART_DLL, quot & 0xff); /* LS of divisor */ + out_8(avr_addr + UART_DLM, quot >> 8); /* MS of divisor */ + out_8(avr_addr + UART_LCR, cval); /* reset DLAB */ + out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */ +} + +void avr_uart_send(const char c) +{ + if (!avr_addr || !avr_clock) + return; + + out_8(avr_addr + UART_TX, c); + out_8(avr_addr + UART_TX, c); + out_8(avr_addr + UART_TX, c); + out_8(avr_addr + UART_TX, c); +} + +static void __init ls_uart_init(void) +{ + local_irq_disable(); + +#ifndef CONFIG_SERIAL_8250 + out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */ + out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); /* clear FIFOs */ + out_8(avr_addr + UART_FCR, 0); + out_8(avr_addr + UART_IER, 0); + + /* Clear up interrupts */ + (void) in_8(avr_addr + UART_LSR); + (void) in_8(avr_addr + UART_RX); + (void) in_8(avr_addr + UART_IIR); + (void) in_8(avr_addr + UART_MSR); +#endif + avr_uart_configure(); + + local_irq_enable(); +} + +static int __init ls_uarts_init(void) +{ + struct device_node *avr; + phys_addr_t phys_addr; + int len; + + avr = of_find_node_by_path("/soc10x/serial@80004500"); + if (!avr) + return -EINVAL; + + avr_clock = *(u32*)get_property(avr, "clock-frequency", &len); + phys_addr = ((u32*)get_property(avr, "reg", &len))[0]; + + if (!avr_clock || !phys_addr) + return -EINVAL; + + avr_addr = ioremap(phys_addr, 32); + if (!avr_addr) + return -EFAULT; + + ls_uart_init(); + + INIT_WORK(&wd_work, wd_stop, NULL); + schedule_work(&wd_work); + + return 0; +} + +late_initcall(ls_uarts_init); -- cgit v1.2.3-59-g8ed1b