diff options
author | Florian Fainelli <florian@openwrt.org> | 2014-12-07 21:59:25 +0000 |
---|---|---|
committer | Florian Fainelli <florian@openwrt.org> | 2014-12-07 21:59:25 +0000 |
commit | 06152c8abf7e428c29e14330f366db516cd426a4 (patch) | |
tree | 735e7f768c4a31163afabf7c11eca331011c7615 /target/linux/adm8668/files-3.14 | |
parent | 356447e6c257cae1613f2237c1d5ca2fbaa8f7c9 (diff) | |
download | master-31e0f0ae-06152c8abf7e428c29e14330f366db516cd426a4.tar.gz master-31e0f0ae-06152c8abf7e428c29e14330f366db516cd426a4.tar.bz2 master-31e0f0ae-06152c8abf7e428c29e14330f366db516cd426a4.zip |
adm8668: add 3.14 kernel support
Signed-off-by: Florian Fainelli <florian@openwrt.org>
SVN-Revision: 43551
Diffstat (limited to 'target/linux/adm8668/files-3.14')
19 files changed, 1465 insertions, 0 deletions
diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/Kconfig b/target/linux/adm8668/files-3.14/arch/mips/adm8668/Kconfig new file mode 100644 index 0000000000..2e7281ff0a --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/Kconfig @@ -0,0 +1,2 @@ +config ARM_AMBA + def_bool y diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/Makefile b/target/linux/adm8668/files-3.14/arch/mips/adm8668/Makefile new file mode 100644 index 0000000000..515c3a4993 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/Makefile @@ -0,0 +1,6 @@ +# +# something witty --neutronscott +# + +obj-y := irq.o prom.o platform.o gpio.o \ + setup.o clock.o time.o early_printk.o \ diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/Platform b/target/linux/adm8668/files-3.14/arch/mips/adm8668/Platform new file mode 100644 index 0000000000..c70cd270be --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/Platform @@ -0,0 +1,6 @@ +# +# Infineon ADM8668 WildPass +# +platform-$(CONFIG_ADM8668) += adm8668/ +cflags-$(CONFIG_ADM8668) += -I$(srctree)/arch/mips/include/asm/mach-adm8668 +load-$(CONFIG_ADM8668) += 0xffffffff80002000 diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/clock.c b/target/linux/adm8668/files-3.14/arch/mips/adm8668/clock.c new file mode 100644 index 0000000000..1e010fcc8f --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/clock.c @@ -0,0 +1,76 @@ +/* + * ADM8668 minimal clock support + * + * Copyright (C) 2012, Florian Fainelli <florian@openwrt.org> + * + * Licensed under the terms of the GPLv2 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/clk.h> + +#include <adm8668.h> + +struct clk { + unsigned long rate; +}; + +static struct clk uart_clk = { + .rate = 62500000, +}; + +static struct clk sys_clk; + +struct clk *clk_get(struct device *dev, const char *id) +{ + const char *lookup = id; + + if (dev) + lookup = dev_name(dev); + + if (!strcmp(lookup, "apb:uart0")) + return &uart_clk; + if (!strcmp(lookup, "sys")) + return &sys_clk; + + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(clk_get); + +int clk_enable(struct clk *clk) +{ + return 0; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} +EXPORT_SYMBOL(clk_get_rate); + +void clk_put(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_put); + +void __init adm8668_init_clocks(void) +{ + u32 adj; + + /* adjustable clock selection + * CR3 bit 14~11, 0000 -> 175MHz, 0001 -> 180MHz, etc... + */ + adj = (ADM8668_CONFIG_REG(ADM8668_CR3) >> 11) & 0xf; + sys_clk.rate = 175000000 + (adj * 5000000); + + pr_info("ADM8668 CPU clock: %lu MHz\n", sys_clk.rate / 1000000); +} diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/early_printk.c b/target/linux/adm8668/files-3.14/arch/mips/adm8668/early_printk.c new file mode 100644 index 0000000000..03dd72aa8b --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/early_printk.c @@ -0,0 +1,16 @@ +#include <linux/io.h> +#include <linux/amba/serial.h> +#include <adm8668.h> + +#define UART_READ(r) \ + __raw_readl((void __iomem *)(KSEG1ADDR(ADM8668_UART0_BASE) + (r))) + +#define UART_WRITE(v, r) \ + __raw_writel((v), (void __iomem *)(KSEG1ADDR(ADM8668_UART0_BASE) + (r))) + +void prom_putchar(char c) +{ + UART_WRITE(c, UART01x_DR); + while ((UART_READ(UART01x_FR) & UART01x_FR_TXFF) != 0) + ; +} diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/gpio.c b/target/linux/adm8668/files-3.14/arch/mips/adm8668/gpio.c new file mode 100644 index 0000000000..fb39f7f588 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/gpio.c @@ -0,0 +1,123 @@ +/* + * Infineon/ADMTek ADM8668 WildPass GPIO support + * + * Copyright (C) 2012 Florian Fainelli <florian@openwrt.org> + * + * Licensed under the terms of GPLv2. + * + */ +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/io.h> + +#include <adm8668.h> + +#define GPIO_MASK 0x3f + +#define GPIO_IN_OFS 0 +#define GPIO_OUT_OFS 6 +#define GPIO_OE_OFS 12 + +struct adm8668_gpio_chip { + void __iomem *base; + struct gpio_chip chip; +}; + +static int adm8668_gpio_dir_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct adm8668_gpio_chip *c = + container_of(chip, struct adm8668_gpio_chip, chip); + u32 mask; + + /* clear input, set output enable and output value */ + mask = __raw_readl(c->base); + mask &= ~(1 << offset); + mask |= (1 << (offset + GPIO_OE_OFS)); + if (value) + mask |= (1 << (offset + GPIO_OUT_OFS)); + else + mask &= ~(1 << (offset + GPIO_OUT_OFS)); + __raw_writel(mask, c->base); + + return 0; +} + +static int adm8668_gpio_dir_in(struct gpio_chip *chip, + unsigned offset) +{ + struct adm8668_gpio_chip *c = + container_of(chip, struct adm8668_gpio_chip, chip); + u32 mask; + + mask = __raw_readl(c->base); + mask &= ~(((1 << (offset + GPIO_OE_OFS)) | (1 << (offset + GPIO_OUT_OFS)))); + mask |= (1 << offset); + __raw_writel(mask, c->base); + + return 0; +} + +static void adm8668_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct adm8668_gpio_chip *c = + container_of(chip, struct adm8668_gpio_chip, chip); + u32 mask; + + mask = __raw_readl(c->base); + if (value) + mask |= (1 << (offset + GPIO_OUT_OFS)); + else + mask &= ~(1 << (offset + GPIO_OUT_OFS)); + __raw_writel(mask, c->base); +} + +static int adm8668_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + struct adm8668_gpio_chip *c = + container_of(chip, struct adm8668_gpio_chip, chip); + u32 value; + + value = __raw_readl(c->base) & GPIO_MASK; + + return value & (1 << offset); +} + +static struct adm8668_gpio_chip adm8668_gpio_cpu = { + .base = (void __iomem *)KSEG1ADDR(ADM8668_CONFIG_BASE + CRGPIO_REG), + .chip = { + .label = "adm8668-cpu-gpio", + .direction_output = adm8668_gpio_dir_out, + .direction_input = adm8668_gpio_dir_in, + .set = adm8668_gpio_set, + .get = adm8668_gpio_get, + .ngpio = 6, + }, +}; + +static struct adm8668_gpio_chip adm8668_gpio_wlan = { + .base = (void __iomem *)KSEG1ADDR(ADM8668_WLAN_BASE + GPIO_REG), + .chip = { + .label = "adm8668-wlan-gpio", + .direction_output = adm8668_gpio_dir_out, + .direction_input = adm8668_gpio_dir_in, + .set = adm8668_gpio_set, + .get = adm8668_gpio_get, + .ngpio = 6, + .base = 6, + }, +}; + +static int __init adm8668_gpio_init(void) +{ + int ret; + + ret = gpiochip_add(&adm8668_gpio_cpu.chip); + if (ret) + return ret; + + return gpiochip_add(&adm8668_gpio_wlan.chip); +} +arch_initcall(adm8668_gpio_init); diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/irq.c b/target/linux/adm8668/files-3.14/arch/mips/adm8668/irq.c new file mode 100644 index 0000000000..9d3b2b9b83 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/irq.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * Copyright (C) 2012 Florian Fainelli <florian@openwrt.org> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/pm.h> +#include <linux/irq.h> +#include <asm/mipsregs.h> +#include <asm/irq_cpu.h> +#include <asm/irq.h> +#include <adm8668.h> + +/* interrupt controller */ +#define IRQ_STATUS_REG 0x00 /* Read */ +#define IRQ_ENABLE_REG 0x08 /* Read/Write */ +#define IRQ_DISABLE_REG 0x0C /* Write */ + +#define IRQ_MASK 0xffff + +static inline void intc_write_reg(u32 val, unsigned int reg) +{ + void __iomem *base = (void __iomem *)KSEG1ADDR(ADM8668_INTC_BASE); + + __raw_writel(val, base + reg); +} + +static inline u32 intc_read_reg(unsigned int reg) +{ + void __iomem *base = (void __iomem *)KSEG1ADDR(ADM8668_INTC_BASE); + + return __raw_readl(base + reg); +} + +static void adm8668_irq_cascade(void) +{ + int irq; + u32 intsrc; + + intsrc = intc_read_reg(IRQ_STATUS_REG) & IRQ_MASK; + if (intsrc) { + irq = fls(intsrc) - 1; + do_IRQ(irq); + } else + spurious_interrupt(); +} + +/* + * System irq dispatch + */ +void plat_irq_dispatch(void) +{ + unsigned int pending; + + pending = read_c0_cause() & read_c0_status() & ST0_IM; + + /* timer interrupt, that we renumbered */ + if (pending & STATUSF_IP7) + do_IRQ(MIPS_CPU_IRQ_BASE + 7); + else if (pending & STATUSF_IP2) + adm8668_irq_cascade(); + else + spurious_interrupt(); +} + +/* + * enable 8668 irq + */ +static void enable_adm8668_irq(struct irq_data *d) +{ + intc_write_reg((1 << d->irq), IRQ_ENABLE_REG); +} + + +static void ack_adm8668_irq(struct irq_data *d) +{ + intc_write_reg((1 << d->irq), IRQ_DISABLE_REG); +} + +/* + * system irq type + */ + +static struct irq_chip adm8668_irq_type = { + .name = "adm8668", + .irq_ack = ack_adm8668_irq, + .irq_mask = ack_adm8668_irq, + .irq_unmask = enable_adm8668_irq +}; + +/* + * irq init + */ +static void __init init_adm8668_irqs(void) +{ + int i; + + /* disable all interrupts for the moment */ + intc_write_reg(IRQ_MASK, IRQ_DISABLE_REG); + + for (i = 0; i <= ADM8668_IRQ_MAX; i++) + irq_set_chip_and_handler(i, &adm8668_irq_type, + handle_level_irq); + + /* hw0 is where our interrupts are uh.. interrupted at. */ + set_c0_status(IE_IRQ0); +} + +/* + * system init + */ +void __init arch_init_irq(void) +{ + mips_cpu_irq_init(); + init_adm8668_irqs(); +} diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/platform.c b/target/linux/adm8668/files-3.14/arch/mips/adm8668/platform.c new file mode 100644 index 0000000000..9e40691f92 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/platform.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * Copyright (C) 2012 Florian Fainelli <florian@openwrt.org> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/platform_data/tulip.h> +#include <linux/usb/ehci_pdriver.h> +#include <linux/mtd/physmap.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/amba/bus.h> +#include <linux/amba/serial.h> + +#include <asm/reboot.h> +#include <asm/time.h> +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <adm8668.h> + +#define ADM8868_UBOOT_ENV 0x20000 +#define ADM8868_UBOOT_WAN_MAC 0x5ac +#define ADM8868_UBOOT_LAN_MAC 0x404 + +static void adm8668_uart_set_mctrl(struct amba_device *dev, + void __iomem *base, + unsigned int mcrtl) +{ +} + +static struct amba_pl010_data adm8668_uart0_data = { + .set_mctrl = adm8668_uart_set_mctrl, +}; + +static struct amba_device adm8668_uart0_device = { + .dev = { + .init_name = "apb:uart0", + .platform_data = &adm8668_uart0_data, + }, + .res = { + .start = ADM8668_UART0_BASE, + .end = ADM8668_UART0_BASE + 0xF, + .flags = IORESOURCE_MEM, + }, + .irq = { + ADM8668_UART0_IRQ, + -1 + }, + .periphid = 0x0041010, +}; + +static struct resource eth0_resources[] = { + { + .start = ADM8668_LAN_BASE, + .end = ADM8668_LAN_BASE + 256, + .flags = IORESOURCE_MEM, + }, + { + .start = ADM8668_LAN_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct tulip_platform_data eth0_pdata = { + .chip_id = ADM8668, +}; + +static struct platform_device adm8668_eth0_device = { + .name = "tulip", + .id = 0, + .resource = eth0_resources, + .num_resources = ARRAY_SIZE(eth0_resources), + .dev.platform_data = ð0_pdata, +}; + +static struct resource eth1_resources[] = { + { + .start = ADM8668_WAN_BASE, + .end = ADM8668_WAN_BASE + 256, + .flags = IORESOURCE_MEM, + }, + { + .start = ADM8668_WAN_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct tulip_platform_data eth1_pdata = { + .chip_id = ADM8668, +}; + +static struct platform_device adm8668_eth1_device = { + .name = "tulip", + .id = 1, + .resource = eth1_resources, + .num_resources = ARRAY_SIZE(eth1_resources), + .dev.platform_data = ð1_pdata, +}; + +static struct resource usb_resources[] = { + { + .start = ADM8668_USB_BASE, + .end = ADM8668_USB_BASE + 0x1FFFFF, + .flags = IORESOURCE_MEM, + }, + { + .start = ADM8668_USB_IRQ, + .end = ADM8668_USB_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct usb_ehci_pdata usb_pdata = { + .caps_offset = 0x100, + .has_tt = 1, +}; + +static struct platform_device adm8668_usb_device = { + .name = "ehci-platform", + .id = -1, + .resource = usb_resources, + .num_resources = ARRAY_SIZE(usb_resources), + .dev.platform_data = &usb_pdata, +}; + +static struct platform_device *adm8668_devs[] = { + &adm8668_eth0_device, + &adm8668_eth1_device, + &adm8668_usb_device, +}; + +static void adm8668_fetch_mac(int unit) +{ + u8 *mac; + u32 offset; + struct tulip_platform_data *pdata; + + switch (unit) { + case -1: + case 0: + offset = ADM8868_UBOOT_LAN_MAC; + pdata = ð0_pdata; + break; + case 1: + offset = ADM8868_UBOOT_WAN_MAC; + pdata = ð1_pdata; + break; + default: + pr_err("unsupported ethernet unit: %d\n", unit); + return; + } + + mac = (u8 *)(KSEG1ADDR(ADM8668_SMEM1_BASE) + ADM8868_UBOOT_ENV + offset); + + memcpy(pdata->mac, mac, sizeof(pdata->mac)); +} + +static void adm8668_ehci_workaround(void) +{ + u32 chipid; + + chipid = ADM8668_CONFIG_REG(ADM8668_CR0); + ADM8668_CONFIG_REG(ADM8668_CR66) = 0x0C1600D9; + + if (chipid == 0x86880001) + return; + + ADM8668_CONFIG_REG(ADM8668_CR66) &= ~(3 << 20); + ADM8668_CONFIG_REG(ADM8668_CR66) |= (1 << 20); + pr_info("ADM8668: applied USB workaround\n"); +} + + +int __init adm8668_devs_register(void) +{ + int ret; + + ret = amba_device_register(&adm8668_uart0_device, &iomem_resource); + if (ret) + panic("failed to register AMBA UART"); + + adm8668_fetch_mac(0); + adm8668_fetch_mac(1); + adm8668_ehci_workaround(); + + return platform_add_devices(adm8668_devs, ARRAY_SIZE(adm8668_devs)); +} +arch_initcall(adm8668_devs_register); diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/prom.c b/target/linux/adm8668/files-3.14/arch/mips/adm8668/prom.c new file mode 100644 index 0000000000..24b77f8b9d --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/prom.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * + * based on work of rb532 prom.c + * Copyright (C) 2003, Peter Sadik <peter.sadik@idt.com> + * Copyright (C) 2005-2006, P.Christeas <p_christ@hol.gr> + * Copyright (C) 2007, Gabor Juhos <juhosg@openwrt.org> + * Felix Fietkau <nbd@openwrt.org> + * Florian Fainelli <florian@openwrt.org> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/console.h> +#include <linux/string.h> +#include <linux/serial_core.h> +#include <asm/bootinfo.h> +#include <adm8668.h> +#include "u-boot.h" + +register volatile struct global_data *gd asm ("k0"); + +void __init prom_free_prom_memory(void) +{ + /* No prom memory to free */ +} + +static inline int match_tag(char *arg, const char *tag) +{ + return strncmp(arg, tag, strlen(tag)) == 0; +} + +static inline unsigned long tag2ul(char *arg, const char *tag) +{ + char *num; + + num = arg + strlen(tag); + return simple_strtoul(num, 0, 10); +} + +void __init prom_setup_cmdline(void) +{ + char *cp; + int prom_argc; + char **prom_argv; + int i; + + prom_argc = fw_arg0; + prom_argv = (char **)KSEG0ADDR(fw_arg1); + + cp = &(arcs_cmdline[0]); + for (i = 1; i < prom_argc; i++) { + prom_argv[i] = (char *)KSEG0ADDR(prom_argv[i]); + + /* default bootargs has "console=/dev/ttyS0" yet console won't + * show up at all if you include the '/dev/' nowadays ... */ + if (match_tag(prom_argv[i], "console=/dev/")) { + char *ptr = prom_argv[i] + strlen("console=/dev/"); + + strcpy(cp, "console="); + cp += strlen("console="); + strcpy(cp, ptr); + cp += strlen(ptr); + *cp++ = ' '; + continue; + } + strcpy(cp, prom_argv[i]); + cp += strlen(prom_argv[i]); + *cp++ = ' '; + } + if (prom_argc > 1) + --cp; /* trailing space */ + + *cp = '\0'; +} + +void __init prom_init(void) +{ + bd_t *bd = gd->bd; + int memsize; + + memsize = bd->bi_memsize; + printk("Board info:\n"); + printk(" RAM size: %d MB\n", (int)memsize/(1024*1024)); + printk(" NOR start: %#lx\n", bd->bi_flashstart); + printk(" NOR size: %#lx\n", bd->bi_flashsize); + + prom_setup_cmdline(); + add_memory_region(0, memsize, BOOT_MEM_RAM); +} diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/setup.c b/target/linux/adm8668/files-3.14/arch/mips/adm8668/setup.c new file mode 100644 index 0000000000..b33c483d24 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/setup.c @@ -0,0 +1,36 @@ +#include <linux/init.h> +#include <linux/reboot.h> + +#include <asm/reboot.h> +#include <adm8668.h> + +static void adm8668_restart(char *cmd) +{ + int i; + + /* the real deal */ + for (i = 0; i < 1000; i++) + ; + ADM8668_CONFIG_REG(ADM8668_CR1) = 1; +} + +void __init plat_mem_setup(void) +{ + _machine_restart = adm8668_restart; +} + +const char *get_system_type(void) +{ + unsigned long chipid = ADM8668_CONFIG_REG(ADM8668_CR0); + int product, revision; + static char ret[32]; + + product = chipid >> 16; + revision = chipid & 0xffff; + + /* i getting fancy :\ */ + snprintf(ret, sizeof(ret), "ADM%xr%x", product, revision); + + return ret; +} + diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/time.c b/target/linux/adm8668/files-3.14/arch/mips/adm8668/time.c new file mode 100644 index 0000000000..87bdd66965 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/time.c @@ -0,0 +1,20 @@ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/clk.h> + +#include <asm/time.h> +#include <adm8668.h> + +void __init plat_time_init(void) +{ + struct clk *sys_clk; + + adm8668_init_clocks(); + + sys_clk = clk_get(NULL, "sys"); + if (IS_ERR(sys_clk)) + panic("unable to get system clock\n"); + + mips_hpt_frequency = clk_get_rate(sys_clk) / 2; +} + diff --git a/target/linux/adm8668/files-3.14/arch/mips/adm8668/u-boot.h b/target/linux/adm8668/files-3.14/arch/mips/adm8668/u-boot.h new file mode 100644 index 0000000000..d9d226889b --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/adm8668/u-boot.h @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, <wd@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _U_BOOT_H_ +#define _U_BOOT_H_ 1 + +typedef struct bd_info { + int bi_baudrate; /* serial console baudrate */ + unsigned long bi_ip_addr; /* IP Address */ + unsigned char bi_enetaddr[6]; /* Ethernet adress */ + unsigned long bi_arch_number; /* unique id for this board */ + unsigned long bi_boot_params; /* where this board expects params */ + unsigned long bi_memstart; /* start of DRAM memory */ + unsigned long bi_memsize; /* size of DRAM memory in bytes */ + unsigned long bi_flashstart; /* start of FLASH memory */ + unsigned long bi_flashsize; /* size of FLASH memory */ + unsigned long bi_flashoffset; /* reserved area for startup monitor */ +} bd_t; + +struct global_data { + bd_t *bd; /* board data... */ + unsigned long flags; + unsigned long baudrate; + unsigned long have_console; /* serial_init() was called */ + unsigned long ram_size; /* RAM size */ + unsigned long reloc_off; /* Relocation Offset */ + unsigned long env_addr; /* Address of Environment struct */ + unsigned long env_valid; /* Checksum of Environment valid? */ + void **jt; /* jump table */ +}; + +#endif /* _U_BOOT_H_ */ diff --git a/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/adm8668.h b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/adm8668.h new file mode 100644 index 0000000000..8a16863c08 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/adm8668.h @@ -0,0 +1,69 @@ +/************************************************************************ + * + * Copyright (c) 2005 + * Infineon Technologies AG + * St. Martin Strasse 53; 81669 Muenchen; Germany + * + ************************************************************************/ + +#ifndef __ADM8668_H__ +#define __ADM8668_H__ + +/*======================= Physical Memory Map ============================*/ +#define ADM8668_SDRAM_BASE 0 +#define ADM8668_SMEM1_BASE 0x10000000 +#define ADM8668_MPMC_BASE 0x11000000 +#define ADM8668_USB_BASE 0x11200000 +#define ADM8668_CONFIG_BASE 0x11400000 +#define ADM8668_WAN_BASE 0x11600000 +#define ADM8668_WLAN_BASE 0x11800000 +#define ADM8668_LAN_BASE 0x11A00000 +#define ADM8668_INTC_BASE 0x1E000000 +#define ADM8668_TMR_BASE 0x1E200000 +#define ADM8668_UART0_BASE 0x1E400000 +#define ADM8668_SMEM0_BASE 0x1FC00000 +#define ADM8668_NAND_BASE 0x1FFFFF00 + +#define ADM8668_PCICFG_BASE 0x12200000 +#define ADM8668_PCIDAT_BASE 0x12400000 + +/* interrupt levels */ +#define ADM8668_SWI_IRQ 1 +#define ADM8668_COMMS_RX_IRQ 2 +#define ADM8668_COMMS_TX_IRQ 3 +#define ADM8668_TIMER0_IRQ 4 +#define ADM8668_TIMER1_IRQ 5 +#define ADM8668_UART0_IRQ 6 +#define ADM8668_LAN_IRQ 7 +#define ADM8668_WAN_IRQ 8 +#define ADM8668_WLAN_IRQ 9 +#define ADM8668_GPIO_IRQ 10 +#define ADM8668_IDE_IRQ 11 +#define ADM8668_PCI2_IRQ 12 +#define ADM8668_PCI1_IRQ 13 +#define ADM8668_PCI0_IRQ 14 +#define ADM8668_USB_IRQ 15 +#define ADM8668_IRQ_MAX ADM8668_USB_IRQ + +/* register access macros */ +#define ADM8668_CONFIG_REG(_reg) \ + (*((volatile unsigned int *)(KSEG1ADDR(ADM8668_CONFIG_BASE + (_reg))))) + +/* lan registers */ +#define NETCSR6 0x30 +#define NETCSR7 0x38 +#define NETCSR37 0xF8 + +/* known/used CPU configuration registers */ +#define ADM8668_CR0 0x00 +#define ADM8668_CR1 0x04 +#define ADM8668_CR3 0x0C +#define ADM8668_CR66 0x108 + +/** For GPIO control **/ +#define GPIO_REG 0x5C /* on WLAN */ +#define CRGPIO_REG 0x20 /* on CPU */ + +void adm8668_init_clocks(void); + +#endif /* __ADM8668_H__ */ diff --git a/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/asm/sizes.h b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/asm/sizes.h new file mode 100644 index 0000000000..503843db15 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/asm/sizes.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* DO NOT EDIT!! - this file automatically generated + * from .s file by awk -f s2h.awk + */ +/* Size definitions + * Copyright (C) ARM Limited 1998. All rights reserved. + */ + +#ifndef __sizes_h +#define __sizes_h 1 + +/* handy sizes */ +#define SZ_16 0x00000010 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#define SZ_1K 0x00000400 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 + +#endif + +/* END */ diff --git a/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/gpio.h b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/gpio.h new file mode 100644 index 0000000000..b0473fc4b3 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/gpio.h @@ -0,0 +1,13 @@ +#ifndef __ADM8668_GPIO_H__ +#define __ADM8668_GPIO_H__ + +#define gpio_to_irq(gpio) -1 + +#define gpio_get_value __gpio_get_value +#define gpio_set_value __gpio_set_value + +#define gpio_cansleep __gpio_cansleep + +#include <asm-generic/gpio.h> + +#endif diff --git a/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/irq.h b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/irq.h new file mode 100644 index 0000000000..ea859f03af --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/irq.h @@ -0,0 +1,14 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003 by Ralf Baechle + */ +#ifndef __ASM_MACH_ADM8668_IRQ_H +#define __ASM_MACH_ADM8668_IRQ_H + +#define NR_IRQS 32 +#define MIPS_CPU_IRQ_BASE 16 + +#endif /* __ASM_MACH_ADM8668_IRQ_H */ diff --git a/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/war.h b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/war.h new file mode 100644 index 0000000000..7180043d34 --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/include/asm/mach-adm8668/war.h @@ -0,0 +1,25 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org> + */ +#ifndef __ASM_MIPS_MACH_ADM8668_WAR_H +#define __ASM_MIPS_MACH_ADM8668_WAR_H + +#define R4600_V1_INDEX_ICACHEOP_WAR 0 +#define R4600_V1_HIT_CACHEOP_WAR 0 +#define R4600_V2_HIT_CACHEOP_WAR 0 +#define R5432_CP0_INTERRUPT_WAR 0 +#define BCM1250_M3_WAR 0 +#define SIBYTE_1956_WAR 0 +#define MIPS4K_ICACHE_REFILL_WAR 0 +#define MIPS_CACHE_SYNC_WAR 0 +#define TX49XX_ICACHE_INDEX_INV_WAR 0 +#define RM9000_CDEX_SMP_WAR 0 +#define ICACHE_REFILLS_WORKAROUND_WAR 0 +#define R10000_LLSC_WAR 0 +#define MIPS34K_MISSED_ITLB_WAR 0 + +#endif /* __ASM_MIPS_MACH_ADM8668_WAR_H */ diff --git a/target/linux/adm8668/files-3.14/arch/mips/pci/pci-adm8668.c b/target/linux/adm8668/files-3.14/arch/mips/pci/pci-adm8668.c new file mode 100644 index 0000000000..5cfa54604c --- /dev/null +++ b/target/linux/adm8668/files-3.14/arch/mips/pci/pci-adm8668.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * Copyright (C) 2012 Florian Fainelli <florian@openwrt.org> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Note that this controller is identical to the ADM5120 one + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/types.h> +#include <linux/spinlock.h> + +#include <asm/byteorder.h> +#include <asm/pci.h> +#include <adm8668.h> + +static DEFINE_SPINLOCK(pci_lock); + +#define PCI_ENABLE 0x80000000 +#define ADMPCI_IO_BASE 0x12600000 +#define ADMPCI_IO_SIZE 0x1fffff +#define ADMPCI_MEM_BASE 0x16000000 +#define ADMPCI_MEM_SIZE 0x7ffffff + +static inline void write_cfgaddr(u32 addr) +{ + __raw_writel((addr | PCI_ENABLE), + (void __iomem *)KSEG1ADDR(ADM8668_PCICFG_BASE)); +} + +static inline void write_cfgdata(u32 data) +{ + __raw_writel(data, (void __iomem *)KSEG1ADDR(ADM8668_PCIDAT_BASE)); +} + +static inline u32 read_cfgdata(void) +{ + return __raw_readl((void __iomem *)KSEG1ADDR(ADM8668_PCIDAT_BASE)); +} + +static inline u32 mkaddr(struct pci_bus *bus, unsigned int devfn, int where) +{ + return ((bus->number & 0xff) << 16) | ((devfn & 0xff) << 8) | + (where & 0xfc); +} + +static int pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + unsigned long flags; + u32 data; + + spin_lock_irqsave(&pci_lock, flags); + write_cfgaddr(mkaddr(bus, devfn, where)); + data = read_cfgdata(); + + switch (size) { + case 1: + if (where & 1) + data >>= 8; + if (where & 2) + data >>= 16; + data &= 0xff; + break; + case 2: + if (where & 2) + data >>= 16; + data &= 0xffff; + break; + } + + *val = data; + + spin_unlock_irqrestore(&pci_lock, flags); + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + unsigned long flags; + u32 data; + int s; + + spin_lock_irqsave(&pci_lock, flags); + + write_cfgaddr(mkaddr(bus, devfn, where)); + data = read_cfgdata(); + + switch (size) { + case 1: + s = ((where & 3) << 3); + data &= ~(0xff << s); + data |= ((val & 0xff) << s); + break; + case 2: + s = ((where & 2) << 4); + data &= ~(0xffff << s); + data |= ((val & 0xffff) << s); + break; + case 4: + data = val; + break; + } + + write_cfgdata(data); + + spin_unlock_irqrestore(&pci_lock, flags); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops adm8668_pci_ops = { + .read = pci_read_config, + .write = pci_write_config +}; + + +struct resource pciioport_resource = { + .name = "adm8668_pci", + .start = ADMPCI_IO_BASE, + .end = ADMPCI_IO_BASE + ADMPCI_IO_SIZE, + .flags = IORESOURCE_IO +}; + +struct resource pciiomem_resource = { + .name = "adm8668_pci", + .start = ADMPCI_MEM_BASE, + .end = ADMPCI_MEM_BASE + ADMPCI_MEM_SIZE, + .flags = IORESOURCE_MEM +}; + +struct pci_controller adm8668_pci_controller = { + .pci_ops = &adm8668_pci_ops, + .io_resource = &pciioport_resource, + .mem_resource = &pciiomem_resource, +}; + +int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + switch (slot) { + case 1: + return 14; + case 2: + return 13; + case 3: + return 12; + default: + return dev->irq; + } +} + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +static void adm8668_pci_fixup(struct pci_dev *dev) +{ + if (dev->devfn != 0) + return; + + pr_info("PCI: fixing up ADM8668 controller\n"); + + /* setup COMMAND register */ + pci_write_config_word(dev, PCI_COMMAND, + (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)); + + /* setup CACHE_LINE_SIZE register */ + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 4); + + /* setup BARS */ + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0); +} +DECLARE_PCI_FIXUP_HEADER(0x1317, 0x8688, adm8668_pci_fixup); + +static int __init adm8668_pci_init(void) +{ + void __iomem *io_map_base; + + ioport_resource.start = ADMPCI_IO_BASE; + ioport_resource.end = ADMPCI_IO_BASE + ADMPCI_IO_SIZE; + + io_map_base = ioremap(ADMPCI_IO_BASE, ADMPCI_IO_SIZE); + if (!io_map_base) + printk("io_map_base didn't work.\n"); + + adm8668_pci_controller.io_map_base = (unsigned long)io_map_base; + register_pci_controller(&adm8668_pci_controller); + + return 0; +} +arch_initcall(adm8668_pci_init); diff --git a/target/linux/adm8668/files-3.14/drivers/mtd/maps/adm8668.c b/target/linux/adm8668/files-3.14/drivers/mtd/maps/adm8668.c new file mode 100644 index 0000000000..8737159ddf --- /dev/null +++ b/target/linux/adm8668/files-3.14/drivers/mtd/maps/adm8668.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> + * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org> + * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) + * + * original functions for finding root filesystem from Mike Baker + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * Flash mapping for adm8668 boards + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/slab.h> +#include <linux/mtd/partitions.h> +#include <linux/crc32.h> +#include <linux/magic.h> +#include <asm/io.h> + +#define WINDOW_ADDR 0x10000000 +#define WINDOW_SIZE 0x800000 +#define BANKWIDTH 2 + +/* first a little bit about the headers i need.. */ + +/* just interested in part of the full struct */ +struct squashfs_super_block { + __le32 s_magic; + __le32 pad0[9]; /* it's not really padding */ + __le64 bytes_used; +}; + +#define IH_MAGIC 0x56190527 /* Image Magic Number */ +struct uboot_header { + uint32_t ih_magic; /* Image Header Magic Number */ + uint32_t ih_hcrc; /* Image Header CRC Checksum */ + uint32_t ih_time; /* Image Creation Timestamp */ + uint32_t ih_size; /* Image Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* Image Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* Image Type */ + uint8_t ih_comp; /* Compression Type */ + char ih_name[32]; /* image name */ +}; + +/************************************************/ + +static struct mtd_info *adm8668_mtd; + +struct map_info adm8668_map = { + name: "adm8668-nor", + size: WINDOW_SIZE, + phys: WINDOW_ADDR, + bankwidth: BANKWIDTH, +}; + +/* + * Copied from mtdblock.c + * + * Cache stuff... + * + * Since typical flash erasable sectors are much larger than what Linux's + * buffer cache can handle, we must implement read-modify-write on flash + * sectors for each block write requests. To avoid over-erasing flash sectors + * and to speed things up, we locally cache a whole flash sector while it is + * being written to until a different sector is required. + */ + +static void erase_callback(struct erase_info *done) +{ + wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; + wake_up(wait_q); +} + +static int erase_write (struct mtd_info *mtd, unsigned long pos, + int len, const char *buf) +{ + struct erase_info erase; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + size_t retlen; + int ret; + + /* + * First, let's erase the flash block. + */ + + init_waitqueue_head(&wait_q); + erase.mtd = mtd; + erase.callback = erase_callback; + erase.addr = pos; + erase.len = len; + erase.priv = (u_long)&wait_q; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + ret = mtd->_erase(mtd, &erase); + if (ret) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + printk (KERN_WARNING "erase of region [0x%lx, 0x%x] " + "on \"%s\" failed\n", + pos, len, mtd->name); + return ret; + } + + schedule(); /* Wait for erase to finish. */ + remove_wait_queue(&wait_q, &wait); + + /* + * Next, write data to flash. + */ + + ret = mtd->_write (mtd, pos, len, &retlen, buf); + if (ret) + return ret; + if (retlen != len) + return -EIO; + return 0; +} + +/* decent defaults in case... shrug */ +static struct mtd_partition adm8668_parts[] = { + { name: "linux", offset: 0x40000, size: WINDOW_SIZE-0x40000, }, + { name: "rootfs", offset: 0xe0000, size: 0x140000, }, + { name: "uboot_env", offset: 0x20000, size: 0x20000, }, + { name: NULL, }, +}; + +/* in case i wanna change stuff later, and to clarify the math section... */ +#define PART_LINUX 0 +#define PART_ROOTFS 1 +#define NR_PARTS 3 + +static int __init +init_mtd_partitions(struct mtd_info *mtd, size_t size) +{ + struct uboot_header uhdr; + int off, blocksize; + size_t len, linux_len; + struct squashfs_super_block shdr; + + blocksize = mtd->erasesize; + if (blocksize < 0x10000) + blocksize = 0x10000; + + /* now find squashfs */ + memset(&shdr, 0xe5, sizeof(shdr)); + for (off = adm8668_parts[PART_LINUX].offset; off < size; off += blocksize) { + /* + * Read into buffer + */ + if (mtd->_read(mtd, off, sizeof(shdr), &len, (char *)&shdr) || + len != sizeof(shdr)) + continue; + + if (shdr.s_magic == SQUASHFS_MAGIC) { + uint32_t fs_size = (uint32_t)shdr.bytes_used; + + printk(KERN_INFO "%s: Filesystem type: squashfs, size=%dkB\n", + mtd->name, fs_size>>10); + + /* Update rootfs based on the superblock info, and + * stretch to end of MTD. rootfs_split will split it */ + adm8668_parts[PART_ROOTFS].offset = off; + adm8668_parts[PART_ROOTFS].size = mtd->size - + adm8668_parts[PART_ROOTFS].offset; + + /* kernel ends where rootfs starts + * but we'll keep it full-length for upgrades */ + linux_len = adm8668_parts[PART_LINUX+1].offset - + adm8668_parts[PART_LINUX].offset; +#if 1 + adm8668_parts[PART_LINUX].size = mtd->size - + adm8668_parts[PART_LINUX].offset; +#else + adm8668_parts[PART_LINUX].size = linux_len; +#endif + goto found; + } + } + + printk(KERN_NOTICE + "%s: Couldn't find root filesystem\n", + mtd->name); + return NR_PARTS; + + found: + if (mtd->_read(mtd, adm8668_parts[PART_LINUX].offset, sizeof(uhdr), &len, (char *)&uhdr) || + len != sizeof(uhdr)) + return NR_PARTS; + + /* that's odd. how'd ya boot it then */ + if (uhdr.ih_magic != IH_MAGIC) + return NR_PARTS; + + if (be32_to_cpu(uhdr.ih_size) != (linux_len - sizeof(uhdr))) { + unsigned char *block, *data; + unsigned int offset; + + offset = adm8668_parts[PART_LINUX].offset + + sizeof(struct uboot_header); + data = (unsigned char *)(WINDOW_ADDR | 0xA0000000 | offset); + + printk(KERN_NOTICE "Updating U-boot image:\n"); + printk(KERN_NOTICE " old: [size: %8d crc32: 0x%08x]\n", + be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc)); + + /* Update the data length & crc32 */ + uhdr.ih_size = cpu_to_be32(linux_len - sizeof(uhdr)); + uhdr.ih_dcrc = crc32_le(~0, data, linux_len - sizeof(uhdr)) ^ (~0); + uhdr.ih_dcrc = cpu_to_be32(uhdr.ih_dcrc); + + printk(KERN_NOTICE " new: [size: %8d crc32: 0x%08x]\n", + be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc)); + + /* update header's crc... */ + uhdr.ih_hcrc = 0; + uhdr.ih_hcrc = crc32_le(~0, (unsigned char *)&uhdr, + sizeof(uhdr)) ^ (~0); + uhdr.ih_hcrc = cpu_to_be32(uhdr.ih_hcrc); + + /* read first eraseblock from the image */ + block = kmalloc(mtd->erasesize, GFP_KERNEL); + if (mtd->_read(mtd, adm8668_parts[PART_LINUX].offset, mtd->erasesize, &len, block) || len != mtd->erasesize) { + printk("Error copying first eraseblock\n"); + return 0; + } + + /* Write updated header to the flash */ + memcpy(block, &uhdr, sizeof(uhdr)); + if (mtd->_unlock) + mtd->_unlock(mtd, off, mtd->erasesize); + erase_write(mtd, adm8668_parts[PART_LINUX].offset, mtd->erasesize, block); + if (mtd->_sync) + mtd->_sync(mtd); + kfree(block); + printk(KERN_NOTICE "Done\n"); + } + + return NR_PARTS; +} + +int __init init_adm8668_map(void) +{ + int nr_parts, ret; + + adm8668_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + + if (!adm8668_map.virt) { + printk(KERN_ERR "Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&adm8668_map); + if (!(adm8668_mtd = do_map_probe("cfi_probe", &adm8668_map))) { + printk(KERN_ERR "cfi_probe failed\n"); + iounmap((void *)adm8668_map.virt); + return -ENXIO; + } + + adm8668_mtd->owner = THIS_MODULE; + + nr_parts = init_mtd_partitions(adm8668_mtd, adm8668_mtd->size); + ret = mtd_device_register(adm8668_mtd, adm8668_parts, nr_parts); + if (ret) { + printk(KERN_ERR "Flash: mtd_device_register failed\n"); + goto fail; + } + + return 0; + + fail: + if (adm8668_mtd) + map_destroy(adm8668_mtd); + if (adm8668_map.virt) + iounmap((void *) adm8668_map.virt); + adm8668_map.virt = 0; + return ret; +} + +void __exit cleanup_adm8668_map(void) +{ + mtd_device_unregister(adm8668_mtd); + map_destroy(adm8668_mtd); + iounmap((void *) adm8668_map.virt); + adm8668_map.virt = 0; +} + +module_init(init_adm8668_map); +module_exit(cleanup_adm8668_map); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Scott Nicholas <neutronscott@scottn.us>"); +MODULE_DESCRIPTION("MTD map driver for ADM8668 NOR Flash"); |