From 8c72b797b421258e17ad6f7afcbc2ab9105e8dc8 Mon Sep 17 00:00:00 2001 From: ejka Date: Sun, 18 Mar 2007 09:40:51 +0000 Subject: Add ar7-2.6 port (marked as broken for now). git-svn-id: svn://svn.openwrt.org/openwrt/trunk@6600 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- target/linux/ar7-2.6/files/arch/mips/ar7/Makefile | 15 + target/linux/ar7-2.6/files/arch/mips/ar7/gpio.c | 56 ++ target/linux/ar7-2.6/files/arch/mips/ar7/irq.c | 160 +++ target/linux/ar7-2.6/files/arch/mips/ar7/memory.c | 210 ++++ .../linux/ar7-2.6/files/arch/mips/ar7/platform.c | 386 ++++++++ target/linux/ar7-2.6/files/arch/mips/ar7/prom.c | 266 +++++ target/linux/ar7-2.6/files/arch/mips/ar7/setup.c | 120 +++ target/linux/ar7-2.6/files/arch/mips/ar7/time.c | 59 ++ .../linux/ar7-2.6/files/arch/mips/ar7/vlynq-pci.c | 396 ++++++++ target/linux/ar7-2.6/files/arch/mips/ar7/vlynq.c | 543 ++++++++++ target/linux/ar7-2.6/files/drivers/char/ar7_gpio.c | 160 +++ .../ar7-2.6/files/drivers/char/watchdog/ar7_wdt.c | 332 +++++++ target/linux/ar7-2.6/files/drivers/leds/leds-ar7.c | 132 +++ target/linux/ar7-2.6/files/drivers/mtd/ar7part.c | 139 +++ target/linux/ar7-2.6/files/drivers/net/cpmac.c | 1037 ++++++++++++++++++++ .../linux/ar7-2.6/files/include/asm-generic/gpio.h | 25 + .../linux/ar7-2.6/files/include/asm-mips/ar7/ar7.h | 153 +++ .../ar7-2.6/files/include/asm-mips/ar7/gpio.h | 116 +++ .../ar7-2.6/files/include/asm-mips/ar7/mmzone.h | 29 + .../ar7-2.6/files/include/asm-mips/ar7/spaces.h | 33 + .../ar7-2.6/files/include/asm-mips/ar7/vlynq.h | 92 ++ target/linux/ar7-2.6/files/include/asm-mips/gpio.h | 6 + 22 files changed, 4465 insertions(+) create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/Makefile create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/gpio.c create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/irq.c create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/memory.c create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/platform.c create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/prom.c create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/setup.c create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/time.c create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/vlynq-pci.c create mode 100644 target/linux/ar7-2.6/files/arch/mips/ar7/vlynq.c create mode 100644 target/linux/ar7-2.6/files/drivers/char/ar7_gpio.c create mode 100644 target/linux/ar7-2.6/files/drivers/char/watchdog/ar7_wdt.c create mode 100644 target/linux/ar7-2.6/files/drivers/leds/leds-ar7.c create mode 100644 target/linux/ar7-2.6/files/drivers/mtd/ar7part.c create mode 100644 target/linux/ar7-2.6/files/drivers/net/cpmac.c create mode 100644 target/linux/ar7-2.6/files/include/asm-generic/gpio.h create mode 100644 target/linux/ar7-2.6/files/include/asm-mips/ar7/ar7.h create mode 100644 target/linux/ar7-2.6/files/include/asm-mips/ar7/gpio.h create mode 100644 target/linux/ar7-2.6/files/include/asm-mips/ar7/mmzone.h create mode 100644 target/linux/ar7-2.6/files/include/asm-mips/ar7/spaces.h create mode 100644 target/linux/ar7-2.6/files/include/asm-mips/ar7/vlynq.h create mode 100644 target/linux/ar7-2.6/files/include/asm-mips/gpio.h (limited to 'target/linux/ar7-2.6/files') diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/Makefile b/target/linux/ar7-2.6/files/arch/mips/ar7/Makefile new file mode 100644 index 000000000..0771cfcf2 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/Makefile @@ -0,0 +1,15 @@ + +obj-y := \ + prom.o \ + setup.o \ + memory.o \ + irq.o \ + time.o \ + platform.o \ + gpio.o \ + vlynq.o + +obj-$(CONFIG_PCI) += \ + vlynq-pci.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/gpio.c b/target/linux/ar7-2.6/files/arch/mips/ar7/gpio.c new file mode 100644 index 000000000..8b3d3a958 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/gpio.c @@ -0,0 +1,56 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *ar7_gpio_list[AR7_GPIO_MAX] = { 0, }; + +int gpio_request(unsigned gpio, char *label) +{ + if (gpio >= AR7_GPIO_MAX) + return -EINVAL; + + if (ar7_gpio_list[gpio]) + return -EBUSY; + + if (label) { + ar7_gpio_list[gpio] = label; + } else { + ar7_gpio_list[gpio] = "busy"; + } + + return 0; +} +EXPORT_SYMBOL(gpio_request); + +void gpio_free(unsigned gpio) +{ + BUG_ON(!ar7_gpio_list[gpio]); + ar7_gpio_list[gpio] = NULL; +} +EXPORT_SYMBOL(gpio_free); diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/irq.c b/target/linux/ar7-2.6/files/arch/mips/ar7/irq.c new file mode 100644 index 000000000..3d235dc45 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/irq.c @@ -0,0 +1,160 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +#define EXCEPT_OFFSET 0x80 +#define PACE_OFFSET 0xA0 +#define CHNLS_OFFSET 0x200 + +#define IRQ_NUM(irq) (irq % 40 % 32) +#define REG_OFFSET(irq, reg) (((irq) < 40) ? \ + ((irq) / 32 * 0x4 + reg * 0x10) : \ + (EXCEPT_OFFSET + reg * 0x8)) +#define SR_OFFSET(irq) (REG_OFFSET(irq, 0)) +#define CR_OFFSET(irq) (REG_OFFSET(irq, 1)) +#define ESR_OFFSET(irq) (REG_OFFSET(irq, 2)) +#define ECR_OFFSET(irq) (REG_OFFSET(irq, 3)) +#define PIR_OFFSET (0x40) +#define MSR_OFFSET (0x44) +#define PM_OFFSET(irq) (REG_OFFSET(irq, 5)) +#define TM_OFFSET(irq) (REG_OFFSET(irq, 6)) + +#define REG(addr) (*(volatile u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr)) + +#define CHNL_OFFSET(chnl) (CHNLS_OFFSET + (chnl * 4)) + +static void ar7_unmask_irq(unsigned int irq_nr); +static void ar7_mask_irq(unsigned int irq_nr); +static irqreturn_t ar7_cascade(int interrupt, void *dev); +void ar7_irq_init(int); + +static struct irq_chip ar7_irq_type = { + .name = "AR7", + .unmask = ar7_unmask_irq, + .mask = ar7_mask_irq, +}; + +static int ar7_irq_base; + +static struct irqaction ar7_cascade_action = { + .handler = ar7_cascade, + .name = "AR7 cascade interrupt" +}; + + +static void ar7_unmask_irq(unsigned int irq) +{ + unsigned long flags; + local_irq_save(flags); + /* enable the interrupt channel bit */ + REG(ESR_OFFSET(irq - ar7_irq_base)) = 1 << IRQ_NUM(irq - ar7_irq_base); + local_irq_restore(flags); +} + +static void ar7_mask_irq(unsigned int irq) +{ + unsigned long flags; + local_irq_save(flags); + /* disable the interrupt channel bit */ + REG(ECR_OFFSET(irq - ar7_irq_base)) = 1 << IRQ_NUM(irq - ar7_irq_base); + local_irq_restore(flags); +} + +void __init arch_init_irq(void) { + mips_cpu_irq_init(0); + ar7_irq_init(8); +} + +void __init ar7_irq_init(int base) +{ + int i; + /* + Disable interrupts and clear pending + */ + REG(ECR_OFFSET(0)) = 0xffffffff; + REG(ECR_OFFSET(32)) = 0xff; + REG(ECR_OFFSET(40)) = 0xffffffff; + REG(CR_OFFSET(0)) = 0xffffffff; + REG(CR_OFFSET(32)) = 0xff; + REG(CR_OFFSET(40)) = 0xffffffff; + + for(i = 0; i < 40; i++) { + REG(CHNL_OFFSET(i)) = i; + /* Primary IRQ's */ + irq_desc[i + base].status = IRQ_DISABLED; + irq_desc[i + base].action = 0; + irq_desc[i + base].depth = 1; + irq_desc[i + base].chip = &ar7_irq_type; + /* Secondary IRQ's */ + if (i < 32) { + irq_desc[i + base + 40].status = IRQ_DISABLED; + irq_desc[i + base + 40].action = 0; + irq_desc[i + base + 40].depth = 1; + irq_desc[i + base + 40].chip = + &ar7_irq_type; + } + } + + ar7_irq_base = base; + setup_irq(2, &ar7_cascade_action); + set_c0_status(IE_IRQ0); +} + +static irqreturn_t ar7_cascade(int interrupt, void *dev) +{ + int irq, i; + unsigned long status; + + irq = (REG(PIR_OFFSET) & 0x3F); + if (irq == 40) return IRQ_NONE; + if (irq > 0) { + REG(CR_OFFSET(irq)) = 1 << IRQ_NUM(irq); + } else { + status = REG(SR_OFFSET(40)); + for (i = 0; i < 32; i++) { + if (status & (i << 1)) { + irq = i + 40; + REG(CR_OFFSET(irq)) = 1 << i; + break; + } + } + REG(CR_OFFSET(0)) = 1; + } + return do_IRQ(irq + ar7_irq_base); +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned int pending = read_c0_status() & read_c0_cause(); + if (pending & STATUSF_IP7) /* cpu timer */ + do_IRQ(7); + else if (pending & STATUSF_IP2) /* int0 hardware line */ + do_IRQ(2); + else + spurious_interrupt(); +} diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/memory.c b/target/linux/ar7-2.6/files/arch/mips/ar7/memory.c new file mode 100644 index 000000000..ea5b5bee7 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/memory.c @@ -0,0 +1,210 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * + * Based on arch/mips/mm/init.c + * Copyright (C) 1994 - 2000 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#warning FIXME: use sdram control regs and/or autodetection +static int __init memsize(void) +{ + char *memsize_str; + unsigned int result; + char cmdline[CL_SIZE], *ptr; + + /* Check the command line first for a memsize directive */ + strcpy(cmdline, arcs_cmdline); + ptr = strstr(cmdline, "memsize="); + if (ptr && (ptr != cmdline) && (*(ptr - 1) != ' ')) + ptr = strstr(ptr, " memsize="); + + if (ptr) { + result = memparse(ptr + 8, &ptr); + } else { + /* otherwise look in the environment */ + memsize_str = prom_getenv("memsize"); + if (!memsize_str) { + prom_printf("memsize not set in boot prom, set to default (8Mb)\n"); + result = 0x00800000; + } else { + result = simple_strtol(memsize_str, NULL, 0); + } + } + + return result; +} + +extern unsigned long __initramfs_start, __initramfs_end; + +#ifdef CONFIG_NEED_MULTIPLE_NODES +static bootmem_data_t node_bootmem_data; +pg_data_t __node_data[1] = { + { + .bdata = &node_bootmem_data + }, +}; +EXPORT_SYMBOL(__node_data); + +unsigned long max_mapnr; +struct page *mem_map; +EXPORT_SYMBOL(max_mapnr); +EXPORT_SYMBOL(mem_map); + +static unsigned long setup_zero_pages(void) +{ + unsigned int order = 3; + unsigned long size; + struct page *page; + + empty_zero_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order); + if (!empty_zero_page) + panic("Oh boy, that early out of memory?"); + + page = virt_to_page(empty_zero_page); + split_page(page, order); + while (page < virt_to_page(empty_zero_page + (PAGE_SIZE << order))) { + SetPageReserved(page); + page++; + } + + size = PAGE_SIZE << order; + zero_page_mask = (size - 1) & PAGE_MASK; + + return 1UL << order; +} + +extern void pagetable_init(void); + +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES] = { 0, }; + + pagetable_init(); + + zones_size[ZONE_DMA] = max_low_pfn - min_low_pfn; + + free_area_init_node(0, NODE_DATA(0), zones_size, ARCH_PFN_OFFSET, NULL); +} + +static struct kcore_list kcore_mem, kcore_vmalloc; + +void __init mem_init(void) +{ + unsigned long codesize, reservedpages, datasize, initsize; + unsigned long tmp, ram; + unsigned long kernel_start, kernel_end; + + kernel_start = PFN_DOWN(CPHYSADDR((unsigned long)&_text)); + kernel_end = PFN_UP(CPHYSADDR((unsigned long)&_end)); + for (tmp = min_low_pfn + 1; tmp < kernel_start; tmp++) { + ClearPageReserved(pfn_to_page(tmp)); + init_page_count(pfn_to_page(tmp)); + free_page((unsigned long)__va(tmp << PAGE_SHIFT)); + } + + totalram_pages += free_all_bootmem(); + totalram_pages -= setup_zero_pages(); /* Setup zeroed pages. */ + + reservedpages = ram = 0; + for (tmp = min_low_pfn; tmp <= max_low_pfn; tmp++) { + ram++; + if (PageReserved(pfn_to_page(tmp))) + if ((tmp < kernel_start) || (tmp > kernel_end)) + reservedpages++; + } + num_physpages = ram; + + codesize = (unsigned long) &_etext - (unsigned long) &_text; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + kclist_add(&kcore_mem, __va(min_low_pfn), + (max_low_pfn - min_low_pfn) << PAGE_SHIFT); + kclist_add(&kcore_vmalloc, (void *)VMALLOC_START, + VMALLOC_END - VMALLOC_START); + + printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " + "%ldk reserved, %ldk data, %ldk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + ram << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10); +} +#endif + +void __init prom_meminit(void) +{ +#ifdef CONFIG_NEED_MULTIPLE_NODES + unsigned long kernel_start, kernel_end; + unsigned long pages, free_pages; + unsigned long bootmap_size; +#endif + +#ifdef CONFIG_BLK_DEV_INITRD + initrd_start = (unsigned long)&__initramfs_start; + initrd_end = (unsigned long)&__initramfs_end; +#endif + + pages = memsize() >> PAGE_SHIFT; + add_memory_region(ARCH_PFN_OFFSET << PAGE_SHIFT, pages << + PAGE_SHIFT, BOOT_MEM_RAM); + +#ifdef CONFIG_NEED_MULTIPLE_NODES + kernel_start = PFN_DOWN(CPHYSADDR((unsigned long)&_text)); + kernel_end = PFN_UP(CPHYSADDR((unsigned long)&_end)); + min_low_pfn = ARCH_PFN_OFFSET; + max_low_pfn = ARCH_PFN_OFFSET + pages; + max_mapnr = max_low_pfn; + free_pages = pages - (kernel_end - min_low_pfn); + bootmap_size = init_bootmem_node(NODE_DATA(0), kernel_end, + ARCH_PFN_OFFSET, max_low_pfn); + + free_bootmem(PFN_PHYS(kernel_end), free_pages << PAGE_SHIFT); + memory_present(0, min_low_pfn, max_low_pfn); + reserve_bootmem(PFN_PHYS(kernel_end), bootmap_size); + mem_map = NODE_DATA(0)->node_mem_map; +#endif +} + +unsigned long __init prom_free_prom_memory(void) +{ +/* return freed; +*/ + return 0; +} diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/platform.c b/target/linux/ar7-2.6/files/arch/mips/ar7/platform.c new file mode 100644 index 000000000..ea170edc6 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/platform.c @@ -0,0 +1,386 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct plat_vlynq_data { + struct plat_vlynq_ops ops; + int gpio_bit; + int reset_bit; +}; + + +static int vlynq_on(struct vlynq_device *dev) +{ + int result; + struct plat_vlynq_data *pdata = dev->dev.platform_data; + + if ((result = gpio_request(pdata->gpio_bit, "vlynq"))) + goto out; + + ar7_device_reset(pdata->reset_bit); + + if ((result = ar7_gpio_disable(pdata->gpio_bit))) + goto out_enabled; + + if ((result = ar7_gpio_enable(pdata->gpio_bit))) + goto out_enabled; + + if ((result = gpio_direction_output(pdata->gpio_bit))) + goto out_gpio_enabled; + + gpio_set_value(pdata->gpio_bit, 0); + mdelay(50); + + gpio_set_value(pdata->gpio_bit, 1); + mdelay(50); + + return 0; + +out_gpio_enabled: + ar7_gpio_disable(pdata->gpio_bit); +out_enabled: + ar7_device_disable(pdata->reset_bit); + gpio_free(pdata->gpio_bit); +out: + return result; +} + +static void vlynq_off(struct vlynq_device *dev) +{ + struct plat_vlynq_data *pdata = dev->dev.platform_data; + ar7_gpio_disable(pdata->gpio_bit); + gpio_free(pdata->gpio_bit); + ar7_device_disable(pdata->reset_bit); +} + +static struct resource physmap_flash_resource = { + .name = "mem", + .flags = IORESOURCE_MEM, + .start = 0x10000000, + .end = 0x103fffff, +}; + +static struct resource cpmac_low_res[] = { + { + .name = "regs", + .flags = IORESOURCE_MEM, + .start = AR7_REGS_MAC0, + .end = AR7_REGS_MAC0 + 0x7FF, + }, + { + .name = "irq", + .flags = IORESOURCE_IRQ, + .start = 27, + .end = 27, + }, +}; + +static struct resource cpmac_high_res[] = { + { + .name = "regs", + .flags = IORESOURCE_MEM, + .start = AR7_REGS_MAC1, + .end = AR7_REGS_MAC1 + 0x7FF, + }, + { + .name = "irq", + .flags = IORESOURCE_IRQ, + .start = 41, + .end = 41, + }, +}; + +static struct resource vlynq_low_res[] = { + { + .name = "regs", + .flags = IORESOURCE_MEM, + .start = AR7_REGS_VLYNQ0, + .end = AR7_REGS_VLYNQ0 + 0xff, + }, + { + .name = "irq", + .flags = IORESOURCE_IRQ, + .start = 29, + .end = 29, + }, + { + .name = "mem", + .flags = IORESOURCE_MEM, + .start = 0x04000000, + .end = 0x04ffffff, + }, + { + .name = "devirq", + .flags = IORESOURCE_IRQ, + .start = 80, + .end = 111, + }, +}; + +static struct resource vlynq_high_res[] = { + { + .name = "regs", + .flags = IORESOURCE_MEM, + .start = AR7_REGS_VLYNQ1, + .end = AR7_REGS_VLYNQ1 + 0xFF, + }, + { + .name = "irq", + .flags = IORESOURCE_IRQ, + .start = 33, + .end = 33, + }, + { + .name = "mem", + .flags = IORESOURCE_MEM, + .start = 0x0c000000, + .end = 0x0cffffff, + }, + { + .name = "devirq", + .flags = IORESOURCE_IRQ, + .start = 112, + .end = 143, + }, +}; + +static struct physmap_flash_data physmap_flash_data = { + .width = 2, +}; + +static struct plat_cpmac_data cpmac_low_data = { + .reset_bit = 17, + .power_bit = 20, + .phy_mask = 0x80000000, +}; + +static struct plat_cpmac_data cpmac_high_data = { + .reset_bit = 21, + .power_bit = 22, + .phy_mask = 0x7fffffff, +}; + +static struct plat_vlynq_data vlynq_low_data = { + .ops.on = vlynq_on, + .ops.off = vlynq_off, + .reset_bit = 20, + .gpio_bit = 18, +}; + +static struct plat_vlynq_data vlynq_high_data = { + .ops.on = vlynq_on, + .ops.off = vlynq_off, + .reset_bit = 16, + .gpio_bit = 19, +}; + +static struct platform_device physmap_flash = { + .id = 0, + .name = "physmap-flash", + .dev.platform_data = &physmap_flash_data, + .resource = &physmap_flash_resource, + .num_resources = 1, +}; + +static struct platform_device cpmac_low = { + .id = 0, + .name = "cpmac", + .dev.platform_data = &cpmac_low_data, + .resource = cpmac_low_res, + .num_resources = ARRAY_SIZE(cpmac_low_res), +}; + +static struct platform_device cpmac_high = { + .id = 1, + .name = "cpmac", + .dev.platform_data = &cpmac_high_data, + .resource = cpmac_high_res, + .num_resources = ARRAY_SIZE(cpmac_high_res), +}; + +static struct platform_device vlynq_low = { + .id = 0, + .name = "vlynq", + .dev.platform_data = &vlynq_low_data, + .resource = vlynq_low_res, + .num_resources = ARRAY_SIZE(vlynq_low_res), +}; + +static struct platform_device vlynq_high = { + .id = 1, + .name = "vlynq", + .dev.platform_data = &vlynq_high_data, + .resource = vlynq_high_res, + .num_resources = ARRAY_SIZE(vlynq_high_res), +}; + + +/* This is proper way to define uart ports, but they are then detected + * as xscale and, obviously, don't work... + */ +#if !defined(CONFIG_SERIAL_8250) +static struct plat_serial8250_port uart_data[] = { + { + .mapbase = AR7_REGS_UART0, + .irq = AR7_IRQ_UART0, + .regshift = 2, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + }, + { + .mapbase = AR7_REGS_UART1, + .irq = AR7_IRQ_UART1, + .regshift = 2, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + }, + { + .flags = 0, + }, +}; + +static struct platform_device uart = { + .id = 0, + .name = "serial8250", + .dev.platform_data = uart_data, +}; +#endif + +static inline unsigned char char2hex(char h) +{ + switch (h) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return h - '0'; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + return h - 'A' + 10; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + return h - 'a' + 10; + default: + return 0; + } +} + +static void cpmac_get_mac(int instance, unsigned char *dev_addr) +{ + int i; + char name[5], default_mac[] = "00:00:00:12:34:56", *mac; + + mac = NULL; + sprintf(name, "mac%c", 'a' + instance); + mac = prom_getenv(name); + if (!mac) { + sprintf(name, "mac%c", 'a'); + mac = prom_getenv(name); + } + if (!mac) + mac = default_mac; + for (i = 0; i < 6; i++) + dev_addr[i] = (char2hex(mac[i * 3]) << 4) + + char2hex(mac[i * 3 + 1]); +} + +static int __init ar7_register_devices(void) +{ + int res; + +#if defined(CONFIG_SERIAL_8250) + static struct uart_port uart_port[2]; + + memset(uart_port, 0, sizeof(struct uart_port) * 2); + + uart_port[0].type = PORT_16750; + uart_port[0].line = 0; + uart_port[0].irq = AR7_IRQ_UART0; + uart_port[0].uartclk = ar7_bus_freq() / 2; + uart_port[0].iotype = UPIO_MEM; + uart_port[0].mapbase = AR7_REGS_UART0; + uart_port[0].membase = ioremap(uart_port[0].mapbase, 256); + uart_port[0].regshift = 2; + res = early_serial_setup(&uart_port[0]); + if (res) + return res; + + uart_port[1].type = PORT_16750; + uart_port[1].line = 1; + uart_port[1].irq = AR7_IRQ_UART1; + uart_port[1].uartclk = ar7_bus_freq() / 2; + uart_port[1].iotype = UPIO_MEM; + uart_port[1].mapbase = AR7_REGS_UART1; + uart_port[1].membase = ioremap(uart_port[1].mapbase, 256); + uart_port[1].regshift = 2; + res = early_serial_setup(&uart_port[1]); + if (res) + return res; +#else + uart_data[0].uartclk = ar7_bus_freq() / 2; + uart_data[1].uartclk = uart_data[0].uartclk; + res = platform_device_register(&uart); + if (res) + return res; +#endif + res = platform_device_register(&physmap_flash); + if (res) + return res; + + res = platform_device_register(&vlynq_low); + if (res) + return res; + + ar7_device_disable(vlynq_low_data.reset_bit); + if (ar7_has_high_vlynq()) { + ar7_device_disable(vlynq_high_data.reset_bit); + res = platform_device_register(&vlynq_high); + if (res) + return res; + } + + if (ar7_has_high_cpmac()) { + cpmac_get_mac(1, cpmac_high_data.dev_addr); + res = platform_device_register(&cpmac_high); + if (res) + return res; + } else { + cpmac_low_data.phy_mask = 0xffffffff; + } + + cpmac_get_mac(0, cpmac_low_data.dev_addr); + res = platform_device_register(&cpmac_low); + + return res; +} + + +arch_initcall(ar7_register_devices); diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/prom.c b/target/linux/ar7-2.6/files/arch/mips/ar7/prom.c new file mode 100644 index 000000000..4db430382 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/prom.c @@ -0,0 +1,266 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * Putting things on the screen/serial line using YAMONs facilities. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_ENTRY 80 + +struct env_var { + char *name; + char *value; +}; + +struct psp_chip_map { + u16 chip; + char *names[50]; +}; + +/* I hate this. No. *I* *HATE* *THIS* */ +static __initdata struct psp_chip_map psp_chip_map[] = { + { + .chip = 0x5, + .names = { + "dummy", "cpufrequency", "memsize", + "flashsize", "modetty0", "modetty1", "prompt", + "bootcfg", "maca", "macb", "usb_rndis_mac", + "macap", "my_ipaddress", "server_ipaddress", + "bline_maca", "bline_macb", "bline_rndis", + "bline_atm", "usb_pid", "usb_vid", + "usb_eppolli", "gateway_ipaddress", + "subnet_mask", "usb_serial", "usb_board_mac", + "remote_user", "remote_pass", "remote_dir", + "sysfrequency", "link_timeout", "mac_port", + "path", "hostname", "tftpcfg", "buildops", + "tftp_fo_fname", "tftp_fo_ports", + "console_state", "mipsfrequency", + }, + }, + { + .chip = 0x18, + .names = { + "dummy", "cpufrequency", "memsize", + "flashsize", "modetty0", "modetty1", "prompt", + "bootcfg", "maca", "macb", "usb_rndis_mac", + "macap", "my_ipaddress", "server_ipaddress", + "bline_maca", "bline_macb", "bline_rndis", + "bline_atm", "usb_pid", "usb_vid", + "usb_eppolli", "gateway_ipaddress", + "subnet_mask", "usb_serial", "usb_board_mac", + "remote_user", "remote_pass", "remote_dir", + "sysfrequency", "link_timeout", "mac_port", + "path", "hostname", "tftpcfg", "buildops", + "tftp_fo_fname", "tftp_fo_ports", + "console_state", "mipsfrequency", + }, + }, + { + .chip = 0x2b, + .names = { + "dummy", "cpufrequency", "memsize", + "flashsize", "modetty0", "modetty1", "prompt", + "bootcfg", "maca", "macb", "usb_rndis_mac", + "macap", "my_ipaddress", "server_ipaddress", + "bline_maca", "bline_macb", "bline_rndis", + "bline_atm", "usb_pid", "usb_vid", + "usb_eppolli", "gateway_ipaddress", + "subnet_mask", "usb_serial", "usb_board_mac", + "remote_user", "remote_pass", "remote_dir", + "sysfrequency", "link_timeout", "mac_port", + "path", "hostname", "tftpcfg", "buildops", + "tftp_fo_fname", "tftp_fo_ports", + "console_state", "mipsfrequency", + }, + }, + { + .chip = 0x0, + }, +}; + +static struct env_var adam2_env[MAX_ENTRY] = { { 0, }, }; + +char * __init prom_getenv(char *name) +{ + int i; + for (i = 0; (i < MAX_ENTRY) && adam2_env[i].name; i++) + if (!strcmp(name, adam2_env[i].name)) + return adam2_env[i].value; + + return NULL; +} + +char * __init prom_getcmdline(void) +{ + return &(arcs_cmdline[0]); +} + +static void __init ar7_init_cmdline(int argc, char *argv[]) +{ + char *cp; + int actr; + + actr = 1; /* Always ignore argv[0] */ + + cp = &(arcs_cmdline[0]); + while(actr < argc) { + strcpy(cp, argv[actr]); + cp += strlen(argv[actr]); + *cp++ = ' '; + actr++; + } + if (cp != &(arcs_cmdline[0])) { + /* get rid of trailing space */ + --cp; + *cp = '\0'; + } +} + +struct psbl_rec { + u32 psbl_size; + u32 env_base; + u32 env_size; + u32 ffs_base; + u32 ffs_size; +}; + +static __initdata char psp_env_version[] = "TIENV0.8"; + +struct psp_env_var { + unsigned char num; + unsigned char ctrl; + unsigned short csum; + unsigned char len; + unsigned char data[11]; +}; + +static char psp_env_data[2048] = { 0, }; + +static void __init add_adam2_var(char *name, char *value) +{ + int i; + for (i = 0; i < MAX_ENTRY; i++) { + if (!adam2_env[i].name) { + adam2_env[i].name = name; + adam2_env[i].value = value; + return; + } else if (!strcmp(adam2_env[i].name, name)) { + adam2_env[i].value = value; + return; + } + } +} + +static int __init parse_psp_env(void *start) +{ + int i, j; + u16 chip; + struct psp_chip_map *map; + char *src, *dest, *name, *value; + struct psp_env_var *vars = start; + + chip = get_chip_id(); + for (map = psp_chip_map; map->chip; map++) + if (map->chip == chip) + break; + + if (!map->chip) + return -EINVAL; + + i = 1; + j = 0; + dest = psp_env_data; + while (vars[i].num < 0xff) { + src = vars[i].data; + if (vars[i].num) { + strcpy(dest, map->names[vars[i].num]); + name = dest; + } else { + strcpy(dest, src); + name = dest; + src += strlen(src) + 1; + } + dest += strlen(dest) + 1; + strcpy(dest, src); + value = dest; + dest += strlen(dest) + 1; + add_adam2_var(name, value); + i += vars[i].len; + } + return 0; +} + +static void __init ar7_init_env(struct env_var *env) +{ + int i; + struct psbl_rec *psbl = (struct psbl_rec *)(KSEG1ADDR(0x14000300)); + void *psp_env = (void *)KSEG1ADDR(psbl->env_base); + + if(strcmp(psp_env, psp_env_version) == 0) { + parse_psp_env(psp_env); + } else { + for (i = 0; i < MAX_ENTRY; i++, env++) + if (env->name) + add_adam2_var(env->name, env->value); + } +} + +void __init prom_init(void) +{ + prom_printf("\nLINUX running...\n"); + ar7_init_cmdline(fw_arg0, (char **)fw_arg1); + ar7_init_env((struct env_var *)fw_arg2); +} + +#define PORT(offset) (KSEG1ADDR(AR7_REGS_UART0 + (offset * 4))) +static inline unsigned int serial_in(int offset) +{ + return readb((void *)PORT(offset)); +} + +static inline void serial_out(int offset, int value) +{ + writeb(value, (void *)PORT(offset)); +} + +char prom_getchar(void) +{ + while (!(serial_in(UART_LSR) & UART_LSR_DR)); + return serial_in(UART_RX); +} + +int prom_putchar(char c) +{ + while ((serial_in(UART_LSR) & UART_LSR_THRE) == 0); + serial_out(UART_TX, c); + return 1; +} + +EXPORT_SYMBOL(prom_getenv); diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/setup.c b/target/linux/ar7-2.6/files/arch/mips/ar7/setup.c new file mode 100644 index 000000000..301abe068 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/setup.c @@ -0,0 +1,120 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void ar7_time_init(void); +static void ar7_machine_restart(char *command); +static void ar7_machine_halt(void); +static void ar7_machine_power_off(void); + +static void ar7_machine_restart(char *command) +{ + volatile u32 *softres_reg = (u32 *)ioremap(AR7_REGS_RESET + + AR7_RESET_SOFTWARE, 1); + prom_printf("Reboot\n"); + *softres_reg = 1; +} + +static void ar7_machine_halt(void) +{ + prom_printf("Halt\n"); + while (1); +} + +static void ar7_machine_power_off(void) +{ + volatile u32 *power_reg = (u32 *)ioremap(AR7_REGS_POWER, 1); + u32 power_state = *power_reg | (3 << 30); + prom_printf("Power off\n"); + *power_reg = power_state; + ar7_machine_halt(); +} + +const char *get_system_type(void) +{ + u16 chip_id = get_chip_id(); + switch (chip_id) { + case 0x5: + return "TI AR7 (TNETD7300)"; + case 0x18: + return "TI AR7 (TNETD7100)"; + case 0x2b: + return "TI AR7 (TNETD7200)"; + default: + return "TI AR7 (Unknown)"; + } +} + +static int __init ar7_init_console(void) +{ + return 0; +} + +/* + * Initializes basic routines and structures pointers, memory size (as + * given by the bios and saves the command line. + */ + +void __init plat_mem_setup(void) +{ + unsigned long io_base; + + _machine_restart = ar7_machine_restart; + _machine_halt = ar7_machine_halt; + pm_power_off = ar7_machine_power_off; + board_time_init = ar7_time_init; + panic_timeout = 3; + + io_base = (unsigned long)ioremap(AR7_REGS_BASE, 0x10000); + if (!io_base) panic("Can't remap IO base!\n"); + set_io_port_base(io_base); + + prom_meminit(); + + ioport_resource.start = 0; + ioport_resource.end = ~0; + iomem_resource.start = 0; + iomem_resource.end = ~0; +} + +console_initcall(ar7_init_console); diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/time.c b/target/linux/ar7-2.6/files/arch/mips/ar7/time.c new file mode 100644 index 000000000..6a1ee7a4d --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/time.c @@ -0,0 +1,59 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * Setting up the clock on the MIPS boards. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void __init ar7_time_init(void) +{ + mips_hpt_frequency = ar7_cpu_freq() / 2; +} + +void __init plat_timer_setup(struct irqaction *irq) +{ + setup_irq(7, irq); +} diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/vlynq-pci.c b/target/linux/ar7-2.6/files/arch/mips/ar7/vlynq-pci.c new file mode 100644 index 000000000..eb32de031 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/vlynq-pci.c @@ -0,0 +1,396 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#define VLYNQ_PCI_SLOTS 2 + +struct vlynq_reg_config { + u32 offset; + u32 value; +}; + +struct vlynq_pci_config { + u32 chip_id; + char name[32]; + struct vlynq_mapping rx_mapping[4]; + int irq; + int irq_type; + u32 chip; + u32 class; + int num_regs; + struct vlynq_reg_config regs[10]; +}; + +struct vlynq_pci_private { + u32 latency; + u32 cache_line; + u32 command; + u32 sz_mask; + struct vlynq_pci_config *config; +}; + +static struct vlynq_pci_config known_devices[] = { + { + .chip_id = 0x00000009, .name = "TI ACX111", + .rx_mapping = { + { .size = 0x22000, .offset = 0xf0000000 }, + { .size = 0x40000, .offset = 0xc0000000 }, + { .size = 0x0, .offset = 0x0 }, + { .size = 0x0, .offset = 0x0 }, + }, + .irq = 0, .chip = 0x9066104c, + .class = PCI_CLASS_NETWORK_OTHER, + .num_regs = 5, + .regs = { + { .offset = 0x790, .value = (0xd0000000 - (ARCH_PFN_OFFSET << PAGE_SHIFT)) }, + { .offset = 0x794, .value = (0xd0000000 - (ARCH_PFN_OFFSET << PAGE_SHIFT)) }, + { .offset = 0x740, .value = 0 }, + { .offset = 0x744, .value = 0x00010000 }, + { .offset = 0x764, .value = 0x00010000 }, + }, + }, +}; + +static struct vlynq_device *slots[VLYNQ_PCI_SLOTS] = { NULL, }; + +static struct resource vlynq_io_resource = { + .start = 0x00000000, + .end = 0x00000000, + .name = "pci IO space", + .flags = IORESOURCE_IO +}; + +static struct resource vlynq_mem_resource = { + .start = 0x00000000, + .end = 0x00000000, + .name = "pci memory space", + .flags = IORESOURCE_MEM +}; + +static inline u32 vlynq_get_mapped(struct vlynq_device *dev, int res) +{ + int i; + struct vlynq_pci_private *priv = dev->priv; + u32 ret = dev->mem_start; + if (!priv->config->rx_mapping[res].size) return 0; + for (i = 0; i < res; i++) + ret += priv->config->rx_mapping[i].size; + + return ret; +} + +static inline u32 vlynq_read(u32 val, int size) { + switch (size) { + case 1: + return *(u8 *)&val; + case 2: + return *(u16 *)&val; + } + return val; +} + +static int vlynq_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) +{ + struct vlynq_device *dev; + struct vlynq_pci_private *priv; + int resno, slot = PCI_SLOT(devfn); + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (slot >= VLYNQ_PCI_SLOTS) + return PCIBIOS_DEVICE_NOT_FOUND; + + dev = slots[slot]; + + if (!dev || (PCI_FUNC(devfn) > 0)) + return PCIBIOS_DEVICE_NOT_FOUND; + + priv = dev->priv; + + switch (where) { + case PCI_VENDOR_ID: + *val = vlynq_read(priv->config->chip, size); + break; + case PCI_DEVICE_ID: + *val = priv->config->chip & 0xffff; + case PCI_COMMAND: + *val = priv->command; + case PCI_STATUS: +/* *val = PCI_STATUS_CAP_LIST;*/ + *val = 0; + break; + case PCI_CLASS_REVISION: + *val = priv->config->class; + break; + case PCI_LATENCY_TIMER: + *val = priv->latency; + break; + case PCI_HEADER_TYPE: + *val = PCI_HEADER_TYPE_NORMAL; + break; + case PCI_CACHE_LINE_SIZE: + *val = priv->cache_line; + break; + case PCI_BASE_ADDRESS_0: + case PCI_BASE_ADDRESS_1: + case PCI_BASE_ADDRESS_2: + case PCI_BASE_ADDRESS_3: + resno = (where - PCI_BASE_ADDRESS_0) >> 2; + if (priv->sz_mask & (1 << resno)) { + priv->sz_mask &= ~(1 << resno); + *val = priv->config->rx_mapping[resno].size; + } else { + *val = vlynq_get_mapped(dev, resno); + } + break; + case PCI_BASE_ADDRESS_4: + case PCI_BASE_ADDRESS_5: + case PCI_SUBSYSTEM_VENDOR_ID: + case PCI_SUBSYSTEM_ID: + case PCI_ROM_ADDRESS: + case PCI_INTERRUPT_LINE: + case PCI_CARDBUS_CIS: + case PCI_CAPABILITY_LIST: + case PCI_INTERRUPT_PIN: + *val = 0; + break; + default: + printk("%s: Read of unknown register 0x%x (size %d)\n", + dev->dev.bus_id, where, size); + return PCIBIOS_BAD_REGISTER_NUMBER; + } + return PCIBIOS_SUCCESSFUL; +} + +static int vlynq_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) +{ + struct vlynq_device *dev; + struct vlynq_pci_private *priv; + int resno, slot = PCI_SLOT(devfn); + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (slot >= VLYNQ_PCI_SLOTS) + return PCIBIOS_DEVICE_NOT_FOUND; + + dev = slots[slot]; + + if (!dev || (PCI_FUNC(devfn) > 0)) + return PCIBIOS_DEVICE_NOT_FOUND; + + priv = dev->priv; + + switch (where) { + case PCI_VENDOR_ID: + case PCI_DEVICE_ID: + case PCI_STATUS: + case PCI_CLASS_REVISION: + case PCI_HEADER_TYPE: + case PCI_CACHE_LINE_SIZE: + case PCI_SUBSYSTEM_VENDOR_ID: + case PCI_SUBSYSTEM_ID: + case PCI_INTERRUPT_LINE: + case PCI_INTERRUPT_PIN: + case PCI_CARDBUS_CIS: + case PCI_CAPABILITY_LIST: + return PCIBIOS_FUNC_NOT_SUPPORTED; + case PCI_COMMAND: + priv->command = val; + case PCI_LATENCY_TIMER: + priv->latency = val; + break; + case PCI_BASE_ADDRESS_0: + case PCI_BASE_ADDRESS_1: + case PCI_BASE_ADDRESS_2: + case PCI_BASE_ADDRESS_3: + if (val == 0xffffffff) { + resno = (where - PCI_BASE_ADDRESS_0) >> 2; + priv->sz_mask |= (1 << resno); + break; + } + case PCI_BASE_ADDRESS_4: + case PCI_BASE_ADDRESS_5: + case PCI_ROM_ADDRESS: + break; + default: + printk("%s: Write to unknown register 0x%x (size %d) value=0x%x\n", + dev->dev.bus_id, where, size, val); + return PCIBIOS_BAD_REGISTER_NUMBER; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops vlynq_pci_ops = { + vlynq_config_read, + vlynq_config_write +}; + +static struct pci_controller vlynq_controller = { + .pci_ops = &vlynq_pci_ops, + .io_resource = &vlynq_io_resource, + .mem_resource = &vlynq_mem_resource, +}; + +static int vlynq_pci_probe(struct vlynq_device *dev) +{ + int result, i; + u32 chip_id, addr; + struct vlynq_pci_private *priv; + struct vlynq_mapping mapping[4] = { { 0, }, }; + struct vlynq_pci_config *config = NULL; + + result = vlynq_set_local_irq(dev, 31); + if (result) + return result; + + result = vlynq_set_remote_irq(dev, 30); + if (result) + return result; + + result = vlynq_device_enable(dev); + if (result) + return result; + + chip_id = vlynq_remote_id(dev); + for (i = 0; i < ARRAY_SIZE(known_devices); i++) + if (chip_id == known_devices[i].chip_id) + config = &known_devices[i]; + + if (!config) { + printk("vlynq-pci: skipping unknown device " + "%04x:%04x at %s\n", chip_id >> 16, + chip_id & 0xffff, dev->dev.bus_id); + result = -ENODEV; + goto fail; + } + + printk("vlynq-pci: attaching device %s at %s\n", + config->name, dev->dev.bus_id); + + priv = kmalloc(sizeof(struct vlynq_pci_private), GFP_KERNEL); + if (!priv) { + printk(KERN_ERR "%s: failed to allocate private data\n", + dev->dev.bus_id); + result = -ENOMEM; + goto fail; + } + + memset(priv, 0, sizeof(struct vlynq_pci_private)); + priv->latency = 64; + priv->cache_line = 32; + priv->config = config; + + mapping[0].offset = ARCH_PFN_OFFSET << PAGE_SHIFT; + mapping[0].size = 0x02000000; + vlynq_set_local_mapping(dev, dev->mem_start, mapping); + vlynq_set_remote_mapping(dev, 0, config->rx_mapping); + + addr = (u32)ioremap_nocache(dev->mem_start, 0x10000); + if (!addr) { + printk(KERN_ERR "%s: failed to remap io memory\n", + dev->dev.bus_id); + result = -ENXIO; + goto fail; + } + + for (i = 0; i < config->num_regs; i++) + *(volatile u32 *)(addr + config->regs[i].offset) = + config->regs[i].value; + + dev->priv = priv; + for (i = 0; i < VLYNQ_PCI_SLOTS; i++) { + if (!slots[i]) { + slots[i] = dev; + break; + } + } + + return 0; + +fail: + vlynq_device_disable(dev); + + return result; +} + +static int vlynq_pci_remove(struct vlynq_device *dev) +{ + int i; + struct vlynq_pci_private *priv = dev->priv; + + for (i = 0; i < VLYNQ_PCI_SLOTS; i++) + if (slots[i] == dev) + slots[i] = NULL; + + vlynq_device_disable(dev); + kfree(priv); + + return 0; +} + +static struct vlynq_driver vlynq_pci = { + .name = "PCI over VLYNQ emulation", + .probe = vlynq_pci_probe, + .remove = vlynq_pci_remove, +}; + +int vlynq_pci_init(void) +{ + int res; + res = vlynq_register_driver(&vlynq_pci); + if (res) + return res; + + register_pci_controller(&vlynq_controller); + + return 0; +} + +int pcibios_map_irq(struct pci_dev *pdev, u8 slot, u8 pin) +{ + struct vlynq_device *dev; + struct vlynq_pci_private *priv; + + dev = slots[slot]; + + if (!dev) + return 0; + + priv = dev->priv; + + return vlynq_virq_to_irq(dev, priv->config->irq); +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/target/linux/ar7-2.6/files/arch/mips/ar7/vlynq.c b/target/linux/ar7-2.6/files/arch/mips/ar7/vlynq.c new file mode 100644 index 000000000..80cb836d9 --- /dev/null +++ b/target/linux/ar7-2.6/files/arch/mips/ar7/vlynq.c @@ -0,0 +1,543 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PER_DEVICE_IRQS 32 + +#define VLYNQ_CTRL_PM_ENABLE 0x80000000 +#define VLYNQ_CTRL_CLOCK_INT 0x00008000 +#define VLYNQ_CTRL_CLOCK_DIV(x) ((x & 7) << 16) +#define VLYNQ_CTRL_INT_LOCAL 0x00004000 +#define VLYNQ_CTRL_INT_ENABLE 0x00002000 +#define VLYNQ_CTRL_INT_VECTOR(x) ((x & 0x1f) << 8) +#define VLYNQ_CTRL_INT2CFG 0x00000080 +#define VLYNQ_CTRL_RESET 0x00000001 + +#define VLYNQ_STATUS_RERROR 0x00000100 +#define VLYNQ_STATUS_LERROR 0x00000080 +#define VLYNQ_STATUS_LINK 0x00000001 + +#define VINT_ENABLE 0x00000100 +#define VINT_TYPE_EDGE 0x00000080 +#define VINT_LEVEL_LOW 0x00000040 +#define VINT_VECTOR(x) (x & 0x1f) +#define VINT_OFFSET(irq) (8 * ((irq) % 4)) + +#define VLYNQ_AUTONEGO_V2 0x00010000 + +struct vlynq_regs { + volatile u32 revision; + volatile u32 control; + volatile u32 status; + volatile u32 int_prio; + volatile u32 int_status; + volatile u32 int_pending; + volatile u32 int_ptr; + volatile u32 tx_offset; + volatile struct vlynq_mapping rx_mapping[4]; + volatile u32 chip; + volatile u32 autonego; + volatile u32 unused[6]; + volatile u32 int_device[8]; +} __attribute__ ((packed)); + +#ifdef VLYNQ_DEBUG +static void vlynq_dump_regs(struct vlynq_device *dev) +{ + int i; + printk("VLYNQ local=%p remote=%p\n", dev->local, dev->remote); + for (i = 0; i < 32; i++) { + printk("VLYNQ: local %d: %08x\n", i + 1, ((u32 *)dev->local)[i]); + printk("VLYNQ: remote %d: %08x\n", i + 1, ((u32 *)dev->remote)[i]); + } +} + +static void vlynq_dump_mem(u32 *base, int count) +{ + int i; + for (i = 0; i < (count + 3) / 4; i++) { + if (i % 4 == 0) printk("\nMEM[0x%04x]:", i * 4); + printk(" 0x%08x", *(base + i)); + } + printk("\n"); +} +#endif + +int vlynq_linked(struct vlynq_device *dev) +{ + int i; + for (i = 0; i < 10; i++) + if (dev->local->status & VLYNQ_STATUS_LINK) { + printk("%s: linked\n", dev->dev.bus_id); + return 1; + } else { + mdelay(1); + } + return 0; +} + +static void vlynq_irq_unmask(unsigned int irq) +{ + volatile u32 val; + struct vlynq_device *dev = irq_desc[irq].chip_data; + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = dev->remote->int_device[virq >> 2]; + val |= VINT_ENABLE << VINT_OFFSET(virq); + dev->remote->int_device[virq >> 2] = val; +} + +static void vlynq_irq_mask(unsigned int irq) +{ + volatile u32 val; + struct vlynq_device *dev = irq_desc[irq].chip_data; + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = dev->remote->int_device[virq >> 2]; + val &= ~(VINT_ENABLE << VINT_OFFSET(virq)); + dev->remote->int_device[virq >> 2] = val; +} + +static int vlynq_irq_type(unsigned int irq, unsigned int flow_type) +{ + volatile u32 val; + struct vlynq_device *dev = irq_desc[irq].chip_data; + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = dev->remote->int_device[virq >> 2]; + switch (flow_type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + val |= VINT_TYPE_EDGE << VINT_OFFSET(virq); + val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); + break; + case IRQ_TYPE_LEVEL_HIGH: + val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); + val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); + break; + case IRQ_TYPE_LEVEL_LOW: + val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); + val |= VINT_LEVEL_LOW << VINT_OFFSET(virq); + break; + default: + return -EINVAL; + } + dev->remote->int_device[virq >> 2] = val; + return 0; +} + +static irqreturn_t vlynq_irq(int irq, void *dev_id) +{ + struct vlynq_device *dev = dev_id; + u32 status, ack; + int virq = 0; + + status = dev->local->int_status; + dev->local->int_status = status; + + if (status & (1 << dev->local_irq)) { /* Local vlynq IRQ. Ack */ + ack = dev->local->status; + dev->local->status = ack; + } + + if (status & (1 << dev->remote_irq)) { /* Remote vlynq IRQ. Ack */ + ack = dev->remote->status; + dev->remote->status = ack; + } + + status &= ~((1 << dev->local_irq) | (1 << dev->remote_irq)); + while (status) { + if (status & 1) /* Remote device IRQ. Pass. */ + do_IRQ(dev->irq_start + virq); + status >>= 1; + virq++; + } + + return IRQ_HANDLED; +} + +static struct irq_chip vlynq_irq_chip = { + .name = "vlynq", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .set_type = vlynq_irq_type, +}; + +static int vlynq_setup_irq(struct vlynq_device *dev) +{ + u32 val; + int i; + + if (dev->local_irq == dev->remote_irq) { + printk("%s: local vlynq irq should be different from remote\n", + dev->dev.bus_id); + return -EINVAL; + } + + val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq); + val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL | + VLYNQ_CTRL_INT2CFG; + dev->local->int_ptr = 0x14; + dev->local->control |= val; + + val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq); + val |= VLYNQ_CTRL_INT_ENABLE; + dev->remote->int_ptr = 0x14; + dev->remote->control |= val; + + for (i = 0; i < PER_DEVICE_IRQS; i++) { + if ((i == dev->local_irq) || (i == dev->remote_irq)) + continue; + irq_desc[dev->irq_start + i].status = IRQ_DISABLED; + irq_desc[dev->irq_start + i].action = 0; + irq_desc[dev->irq_start + i].depth = 1; + irq_desc[dev->irq_start + i].chip = &vlynq_irq_chip; + irq_desc[dev->irq_start + i].chip_data = dev; + dev->remote->int_device[i >> 2] = 0; + } + + if (request_irq(dev->irq, vlynq_irq, SA_SHIRQ, "AR7 VLYNQ", dev)) { + printk("%s: request_irq failed\n", dev->dev.bus_id); + return -EAGAIN; + } + + return 0; +} + +static void vlynq_free_irq(struct vlynq_device *dev) +{ + free_irq(dev->irq, dev); +} + +static void vlynq_device_release(struct device *dev) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + kfree(vdev); +} + +static int vlynq_device_probe(struct device *dev) +{ + struct vlynq_driver *drv = to_vlynq_driver(dev->driver); + if (drv->probe) + return drv->probe(to_vlynq_device(dev)); + return 0; +} + +static int vlynq_device_remove(struct device *dev) +{ + struct vlynq_driver *drv = to_vlynq_driver(dev->driver); + if (drv->remove) + return drv->remove(to_vlynq_device(dev)); + return 0; +} + +int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner) +{ + driver->driver.name = driver->name; + driver->driver.bus = &vlynq_bus_type; +/* driver->driver.owner = owner;*/ + return driver_register(&driver->driver); +} +EXPORT_SYMBOL(__vlynq_register_driver); + +void vlynq_unregister_driver(struct vlynq_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL(vlynq_unregister_driver); + +int vlynq_device_enable(struct vlynq_device *dev) +{ + u32 val; + int result; + struct plat_vlynq_ops *ops = dev->dev.platform_data; + + result = ops->on(dev); + if (result) + return result; + + dev->local->control = 0; + dev->remote->control = 0; + + if (vlynq_linked(dev)) + return vlynq_setup_irq(dev); + + for (val = 0; val < 8; val++) { + dev->local->control = VLYNQ_CTRL_CLOCK_DIV(val) | + VLYNQ_CTRL_CLOCK_INT; + if (vlynq_linked(dev)) + return vlynq_setup_irq(dev); + } +} + +void vlynq_device_disable(struct vlynq_device *dev) +{ + struct plat_vlynq_ops *ops = dev->dev.platform_data; + + vlynq_free_irq(dev); + ops->off(dev); +} + +u32 vlynq_local_id(struct vlynq_device *dev) +{ + return dev->local->chip; +} + +u32 vlynq_remote_id(struct vlynq_device *dev) +{ + return dev->remote->chip; +} + +void vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping) +{ + int i; + + dev->local->tx_offset = tx_offset; + for (i = 0; i < 4; i++) { + dev->local->rx_mapping[i].offset = mapping[i].offset; + dev->local->rx_mapping[i].size = mapping[i].size; + } +} + +void vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping) +{ + int i; + + dev->remote->tx_offset = tx_offset; + for (i = 0; i < 4; i++) { + dev->remote->rx_mapping[i].offset = mapping[i].offset; + dev->remote->rx_mapping[i].size = mapping[i].size; + } +} + +int vlynq_virq_to_irq(struct vlynq_device *dev, int virq) +{ + if ((virq < 0) || (virq >= PER_DEVICE_IRQS)) + return -EINVAL; + + if ((virq == dev->local_irq) || (virq == dev->remote_irq)) + return -EINVAL; + + return dev->irq_start + virq; +} + +int vlynq_irq_to_virq(struct vlynq_device *dev, int irq) +{ + if ((irq < dev->irq_start) || (irq >= dev->irq_start + PER_DEVICE_IRQS)) + return -EINVAL; + + return irq - dev->irq_start; +} + +int vlynq_set_local_irq(struct vlynq_device *dev, int virq) +{ + if ((virq < 0) || (virq >= PER_DEVICE_IRQS)) + return -EINVAL; + + if (virq == dev->remote_irq) + return -EINVAL; + + dev->local_irq = virq; + + return 0; +} + +int vlynq_set_remote_irq(struct vlynq_device *dev, int virq) +{ + if ((virq < 0) || (virq >= PER_DEVICE_IRQS)) + return -EINVAL; + + if (virq == dev->local_irq) + return -EINVAL; + + dev->remote_irq = virq; + + return 0; +} + +static int vlynq_probe(struct platform_device *pdev) +{ + struct vlynq_device *dev; + struct resource *regs_res, *mem_res, *irq_res; + int len, result; + + if (strcmp(pdev->name, "vlynq")) + return -ENODEV; + + regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!regs_res) + return -ENODEV; + + mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); + if (!mem_res) + return -ENODEV; + + irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq"); + if (!irq_res) + return -ENODEV; + + dev = kmalloc(sizeof(struct vlynq_device), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR "vlynq: failed to allocate device structure\n"); + return -ENOMEM; + } + + memset(dev, 0, sizeof(struct vlynq_device)); + + dev->id = pdev->id; + dev->dev.bus = &vlynq_bus_type; + dev->dev.parent = &pdev->dev; + snprintf(dev->dev.bus_id, BUS_ID_SIZE, "vlynq%d", dev->id); + dev->dev.bus_id[BUS_ID_SIZE - 1] = 0; + dev->dev.platform_data = pdev->dev.platform_data; + dev->dev.release = vlynq_device_release; + + dev->regs_start = regs_res->start; + dev->regs_end = regs_res->end; + dev->mem_start = mem_res->start; + dev->mem_end = mem_res->end; + + len = regs_res->end - regs_res->start; + if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) { + printk("%s: Can't request vlynq registers\n", dev->dev.bus_id); + result = -ENXIO; + goto fail_request; + } + + dev->local = ioremap_nocache(regs_res->start, len); + if (!dev->local) { + printk("%s: Can't remap vlynq registers\n", dev->dev.bus_id); + result = -ENXIO; + goto fail_remap; + } + + dev->remote = (struct vlynq_regs *)((u32)dev->local + 128); + + dev->irq = platform_get_irq_byname(pdev, "irq"); + dev->irq_start = irq_res->start; + dev->irq_end = irq_res->end; + dev->local_irq = 31; + dev->remote_irq = 30; + + if (device_register(&dev->dev)) + goto fail_register; + platform_set_drvdata(pdev, dev); + + printk("%s: regs 0x%p, irq %d, mem 0x%p\n", + dev->dev.bus_id, (void *)dev->regs_start, dev->irq, + (void *)dev->mem_start); + + return 0; + +fail_register: +fail_remap: + iounmap(dev->local); +fail_request: + release_mem_region(regs_res->start, len); + kfree(dev); + return result; +} + +static int vlynq_remove(struct platform_device *pdev) +{ + struct vlynq_device *dev = platform_get_drvdata(pdev); + + device_unregister(&dev->dev); + release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start); + + kfree(dev); + + return 0; +} + +static struct platform_driver vlynq_driver = { + .driver.name = "vlynq", + .probe = vlynq_probe, + .remove = vlynq_remove, +}; + +struct bus_type vlynq_bus_type = { + .name = "vlynq", + .probe = vlynq_device_probe, + .remove = vlynq_device_remove, +}; +EXPORT_SYMBOL(vlynq_bus_type); + +#ifdef CONFIG_PCI +extern void vlynq_pci_init(void); +#endif +int __init vlynq_init(void) +{ + int res = 0; + + res = bus_register(&vlynq_bus_type); + if (res) + goto fail_bus; + + res = platform_driver_register(&vlynq_driver); + if (res) + goto fail_platform; + +#ifdef CONFIG_PCI + vlynq_pci_init(); +#endif + + return 0; + +fail_platform: + bus_unregister(&vlynq_bus_type); +fail_bus: + return res; +} + +/* +void __devexit vlynq_exit(void) +{ + platform_driver_unregister(&vlynq_driver); + bus_unregister(&vlynq_bus_type); +} +*/ + + +subsys_initcall(vlynq_init); diff --git a/target/linux/ar7-2.6/files/drivers/char/ar7_gpio.c b/target/linux/ar7-2.6/files/drivers/char/ar7_gpio.c new file mode 100644 index 000000000..d8dc2e1ff --- /dev/null +++ b/target/linux/ar7-2.6/files/drivers/char/ar7_gpio.c @@ -0,0 +1,160 @@ +/* + * linux/drivers/char/ar7_gpio.c + * + * Copyright (C) 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define DRVNAME "ar7_gpio" +#define LONGNAME "TI AR7 GPIOs Driver" + +MODULE_AUTHOR("Nicolas Thill "); +MODULE_DESCRIPTION(LONGNAME); +MODULE_LICENSE("GPL"); + +static int ar7_gpio_major = 0; + +static ssize_t ar7_gpio_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + int pin = iminor(file->f_dentry->d_inode); + size_t i; + + for (i = 0; i < len; ++i) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + switch (c) { + case '0': + gpio_set_value(pin, 0); + break; + case '1': + gpio_set_value(pin, 1); + break; + case 'd': + case 'D': + ar7_gpio_disable(pin); + break; + case 'e': + case 'E': + ar7_gpio_enable(pin); + break; + case 'i': + case 'I': + case '<': + gpio_direction_input(pin); + break; + case 'o': + case 'O': + case '>': + gpio_direction_output(pin); + break; + default: + return -EINVAL; + } + } + + return len; +} + +static ssize_t ar7_gpio_read(struct file *file, char __user * buf, + size_t len, loff_t * ppos) +{ + int pin = iminor(file->f_dentry->d_inode); + int value; + + value = gpio_get_value(pin); + if (put_user(value ? '1' : '0', buf)) + return -EFAULT; + + return 1; +} + +static int ar7_gpio_open(struct inode *inode, struct file *file) +{ + int m = iminor(inode); + + if (m >= AR7_GPIO_MAX) + return -EINVAL; + + return nonseekable_open(inode, file); +} + +static int ar7_gpio_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations ar7_gpio_fops = { + .owner = THIS_MODULE, + .write = ar7_gpio_write, + .read = ar7_gpio_read, + .open = ar7_gpio_open, + .release = ar7_gpio_release, + .llseek = no_llseek, +}; + +static struct platform_device *ar7_gpio_device; + +static int __init ar7_gpio_init(void) +{ + int rc; + + ar7_gpio_device = platform_device_alloc(DRVNAME, -1); + if (!ar7_gpio_device) + return -ENOMEM; + + rc = platform_device_add(ar7_gpio_device); + if (rc < 0) + goto out_put; + + rc = register_chrdev(ar7_gpio_major, DRVNAME, &ar7_gpio_fops); + if (rc < 0) + goto out_put; + + ar7_gpio_major = rc; + + goto out; + +out_put: + platform_device_put(ar7_gpio_device); +out: + return rc; +} + +static void __exit ar7_gpio_exit(void) +{ + unregister_chrdev(ar7_gpio_major, DRVNAME); + platform_device_unregister(ar7_gpio_device); +} + +module_init(ar7_gpio_init); +module_exit(ar7_gpio_exit); diff --git a/target/linux/ar7-2.6/files/drivers/char/watchdog/ar7_wdt.c b/target/linux/ar7-2.6/files/drivers/char/watchdog/ar7_wdt.c new file mode 100644 index 000000000..00919e76e --- /dev/null +++ b/target/linux/ar7-2.6/files/drivers/char/watchdog/ar7_wdt.c @@ -0,0 +1,332 @@ +/* + * linux/drivers/char/ar7_wdt.c + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (c) 2005 Enrik Berkhan + * + * Some code taken from: + * National Semiconductor SCx200 Watchdog support + * Copyright (c) 2001,2002 Christer Weinigel + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define DRVNAME "ar7_wdt" +#define LONGNAME "TI AR7 Watchdog Timer" + +MODULE_AUTHOR("Nicolas Thill "); +MODULE_DESCRIPTION(LONGNAME); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +static int margin = 60; +module_param(margin, int, 0); +MODULE_PARM_DESC(margin, "Watchdog margin in seconds"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); + +typedef struct { + u32 kick_lock; + u32 kick; + u32 change_lock; + u32 change ; + u32 disable_lock; + u32 disable; + u32 prescale_lock; + u32 prescale; +} ar7_wdt_t; + +static struct semaphore open_semaphore; +static unsigned expect_close; + +/* XXX correct? assumed to be sysfreq/2. get this dynamically ... */ +#define vbus_freq (ar7_bus_freq() / 2) + +/* XXX currently fixed, allows max margin ~68.72 secs */ +#define prescale_value 0xFFFF + +static void ar7_wdt_kick(u32 value) +{ + volatile ar7_wdt_t *ar7_wdt = (ar7_wdt_t *)ioremap(AR7_REGS_WDT, sizeof(ar7_wdt_t)); + + ar7_wdt->kick_lock = 0x5555; + if ((ar7_wdt->kick_lock & 3) == 1) { + ar7_wdt->kick_lock = 0xAAAA; + if ((ar7_wdt->kick_lock & 3) == 3) { + ar7_wdt->kick = value; + return; + } + } + printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n"); +} + +static void ar7_wdt_prescale(u32 value) +{ + volatile ar7_wdt_t *ar7_wdt = (ar7_wdt_t *)ioremap(AR7_REGS_WDT, sizeof(ar7_wdt_t)); + + ar7_wdt->prescale_lock = 0x5A5A; + if ((ar7_wdt->prescale_lock & 3) == 1) { + ar7_wdt->prescale_lock = 0xA5A5; + if ((ar7_wdt->prescale_lock & 3) == 3) { + ar7_wdt->prescale = value; + return; + } + } + printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n"); +} + +static void ar7_wdt_change(u32 value) +{ + volatile ar7_wdt_t *ar7_wdt = (ar7_wdt_t *)ioremap(AR7_REGS_WDT, sizeof(ar7_wdt_t)); + + ar7_wdt->change_lock = 0x6666; + if ((ar7_wdt->change_lock & 3) == 1) { + ar7_wdt->change_lock = 0xBBBB; + if ((ar7_wdt->change_lock & 3) == 3) { + ar7_wdt->change = value; + return; + } + } + printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n"); +} + +static void ar7_wdt_disable(u32 value) +{ + volatile ar7_wdt_t *ar7_wdt = (ar7_wdt_t *)ioremap(AR7_REGS_WDT, sizeof(ar7_wdt_t)); + + ar7_wdt->disable_lock = 0x7777; + if ((ar7_wdt->disable_lock & 3) == 1) { + ar7_wdt->disable_lock = 0xCCCC; + if ((ar7_wdt->disable_lock & 3) == 2) { + ar7_wdt->disable_lock = 0xDDDD; + if ((ar7_wdt->disable_lock & 3) == 3) { + ar7_wdt->disable = value; + return; + } + } + } + printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n"); +} + +static void ar7_wdt_update_margin(int new_margin) +{ + u32 change; + + change = new_margin * (vbus_freq / prescale_value); + if (change < 1) change = 1; + if (change > 0xFFFF) change = 0xFFFF; + ar7_wdt_change(change); + margin = change * prescale_value / vbus_freq; + printk(KERN_INFO DRVNAME + ": timer margin %d seconds (prescale %d, change %d, freq %d)\n", + margin, prescale_value, change, vbus_freq); +} + +static void ar7_wdt_enable_wdt(void) +{ + printk(KERN_DEBUG DRVNAME ": enabling watchdog timer\n"); + ar7_wdt_disable(1); + ar7_wdt_kick(1); +} + +static void ar7_wdt_disable_wdt(void) +{ + printk(KERN_DEBUG DRVNAME ": disabling watchdog timer\n"); + ar7_wdt_disable(0); +} + +static int ar7_wdt_open(struct inode *inode, struct file *file) +{ + /* only allow one at a time */ + if (down_trylock(&open_semaphore)) + return -EBUSY; + ar7_wdt_enable_wdt(); + expect_close = 0; + + return 0; +} + +static int ar7_wdt_release(struct inode *inode, struct file *file) +{ + if (!expect_close) { + printk(KERN_WARNING DRVNAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n"); + } else if (!nowayout) { + ar7_wdt_disable_wdt(); + } + up(&open_semaphore); + + return 0; +} + +static int ar7_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_HALT || code == SYS_POWER_OFF) + if (!nowayout) + ar7_wdt_disable_wdt(); + + return NOTIFY_DONE; +} + +static struct notifier_block ar7_wdt_notifier = +{ + .notifier_call = ar7_wdt_notify_sys +}; + +static ssize_t ar7_wdt_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + if (ppos != &file->f_pos) + return -ESPIPE; + + /* check for a magic close character */ + if (len) { + size_t i; + + ar7_wdt_kick(1); + + expect_close = 0; + for (i = 0; i < len; ++i) { + char c; + if (get_user(c, data+i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + + } + return len; +} + +static int ar7_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + .identity = LONGNAME, + .firmware_version = 1, + .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING), + }; + int new_margin; + + switch (cmd) { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + if(copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (put_user(0, (int *)arg)) + return -EFAULT; + return 0; + case WDIOC_KEEPALIVE: + ar7_wdt_kick(1); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int *)arg)) + return -EFAULT; + if (new_margin < 1) + return -EINVAL; + + ar7_wdt_update_margin(new_margin); + ar7_wdt_kick(1); + + case WDIOC_GETTIMEOUT: + if (put_user(margin, (int *)arg)) + return -EFAULT; + return 0; + } +} + +static struct file_operations ar7_wdt_fops = { + .owner = THIS_MODULE, + .write = ar7_wdt_write, + .ioctl = ar7_wdt_ioctl, + .open = ar7_wdt_open, + .release = ar7_wdt_release, +}; + +static struct miscdevice ar7_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ar7_wdt_fops, +}; + +static int __init ar7_wdt_init(void) +{ + int rc; + + if (!request_mem_region(AR7_REGS_WDT, sizeof(ar7_wdt_t), LONGNAME)) { + printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n"); + return -EBUSY; + } + + ar7_wdt_disable_wdt(); + ar7_wdt_prescale(prescale_value); + ar7_wdt_update_margin(margin); + + sema_init(&open_semaphore, 1); + + rc = misc_register(&ar7_wdt_miscdev); + if (rc) { + printk(KERN_ERR DRVNAME ": unable to register misc device\n"); + goto out_alloc; + } + + rc = register_reboot_notifier(&ar7_wdt_notifier); + if (rc) { + printk(KERN_ERR DRVNAME ": unable to register reboot notifier\n"); + goto out_register; + } + goto out; + +out_register: + misc_deregister(&ar7_wdt_miscdev); +out_alloc: + release_mem_region(AR7_REGS_WDT, sizeof(ar7_wdt_t)); +out: + return rc; +} + +static void __exit ar7_wdt_cleanup(void) +{ + unregister_reboot_notifier(&ar7_wdt_notifier); + misc_deregister(&ar7_wdt_miscdev); + release_mem_region(AR7_REGS_WDT, sizeof(ar7_wdt_t)); +} + +module_init(ar7_wdt_init); +module_exit(ar7_wdt_cleanup); diff --git a/target/linux/ar7-2.6/files/drivers/leds/leds-ar7.c b/target/linux/ar7-2.6/files/drivers/leds/leds-ar7.c new file mode 100644 index 000000000..fbef7d3bb --- /dev/null +++ b/target/linux/ar7-2.6/files/drivers/leds/leds-ar7.c @@ -0,0 +1,132 @@ +/* + * linux/drivers/leds/leds-ar7.c + * + * Copyright (C) 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include +#include + +#include + +#define DRVNAME "ar7-leds" +#define LONGNAME "TI AR7 LEDs driver" + +MODULE_AUTHOR("Nicolas Thill "); +MODULE_DESCRIPTION(LONGNAME); +MODULE_LICENSE("GPL"); + +static void ar7_status_led_set(struct led_classdev *pled, + enum led_brightness value) +{ + gpio_set_value(AR7_GPIO_BIT_STATUS_LED, value ? 0 : 1); +} + +static struct led_classdev ar7_status_led = { + .name = "ar7:status", + .brightness_set = ar7_status_led_set, +}; + +#ifdef CONFIG_PM +static int ar7_leds_suspend(struct platform_device *dev, + pm_message_t state) +{ + led_classdev_suspend(&ar7_status_led); + return 0; +} + +static int ar7_leds_resume(struct platform_device *dev) +{ + led_classdev_resume(&ar7_status_led); + return 0; +} +#else /* CONFIG_PM */ +#define ar7_leds_suspend NULL +#define ar7_leds_resume NULL +#endif /* CONFIG_PM */ + +static int ar7_leds_probe(struct platform_device *pdev) +{ + int rc; + + rc = led_classdev_register(&pdev->dev, &ar7_status_led); + if (rc < 0 ) + goto out; + + ar7_gpio_enable(AR7_GPIO_BIT_STATUS_LED); + gpio_direction_output(AR7_GPIO_BIT_STATUS_LED); + +out: + return rc; +} + +static int ar7_leds_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&ar7_status_led); + + return 0; +} + +static struct platform_device *ar7_leds_device; + +static struct platform_driver ar7_leds_driver = { + .probe = ar7_leds_probe, + .remove = ar7_leds_remove, + .suspend = ar7_leds_suspend, + .resume = ar7_leds_resume, + .driver = { + .name = DRVNAME, + }, +}; + +static int __init ar7_leds_init(void) +{ + int rc; + + ar7_leds_device = platform_device_alloc(DRVNAME, -1); + if (!ar7_leds_device) + return -ENOMEM; + + rc = platform_device_add(ar7_leds_device); + if (rc < 0) + goto out_put; + + rc = platform_driver_register(&ar7_leds_driver); + if (rc < 0) + goto out_put; + + goto out; + +out_put: + platform_device_put(ar7_leds_device); +out: + return rc; +} + +static void __exit ar7_leds_exit(void) +{ + platform_driver_unregister(&ar7_leds_driver); + platform_device_unregister(ar7_leds_device); +} + +module_init(ar7_leds_init); +module_exit(ar7_leds_exit); diff --git a/target/linux/ar7-2.6/files/drivers/mtd/ar7part.c b/target/linux/ar7-2.6/files/drivers/mtd/ar7part.c new file mode 100644 index 000000000..2b3edf67b --- /dev/null +++ b/target/linux/ar7-2.6/files/drivers/mtd/ar7part.c @@ -0,0 +1,139 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * TI AR7 flash partition table. + * Based on ar7 map by Felix Fietkau. + * + */ + +#include +#include + +#include +#include +#include +#include + +struct ar7_bin_rec { + unsigned int checksum; + unsigned int length; + unsigned int address; +}; + +static struct mtd_partition ar7_parts[5]; + +static int create_mtd_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + unsigned long origin) +{ + char sig[8]; + struct ar7_bin_rec header; + unsigned int offset, new_offset, len; + struct squashfs_super_block sb; + unsigned int block_size = 0x10000, pre_size = 0x10000, + post_size = 0, root_offset = 0xe0000; + unsigned int p = 3; + + printk("Parsing AR7 partition map...\n"); + + ar7_parts[0].name = "loader"; + ar7_parts[0].offset = 0; + ar7_parts[0].size = block_size; + ar7_parts[0].mask_flags = MTD_WRITEABLE; + + ar7_parts[1].name = "config"; + ar7_parts[1].size = block_size; + ar7_parts[1].mask_flags = 0; + + master->read(master, block_size, 8, &len, sig); + if (strncmp(sig, "TIENV0.8", 8)) { + ar7_parts[1].offset = master->size - block_size; + post_size = block_size; + } else { + ar7_parts[1].offset = block_size; + pre_size = block_size * 2; + } + + ar7_parts[2].name = "linux"; + ar7_parts[2].offset = pre_size; + ar7_parts[2].size = master->size - block_size * 2; + ar7_parts[2].mask_flags = 0; + + offset = pre_size; + master->read(master, offset, sizeof(header), &len, (u_char *)&header); + if (header.checksum != 0xfeedfa42) { + printk("Unknown magic: %08x\n", header.checksum); + } else { + while (header.length) { + offset += sizeof(header) + header.length; + master->read(master, offset, sizeof(header), + &len, (u_char *)&header); + } + root_offset = offset + sizeof(header) + 4; + } + + ar7_parts[p].name = "rootfs"; + ar7_parts[p].offset = root_offset; + ar7_parts[p].size = master->size - root_offset - post_size; + ar7_parts[p++].mask_flags = 0; + + master->read(master, root_offset, sizeof(sb), &len, (u_char *)&sb); + if (sb.s_magic == SQUASHFS_MAGIC) { + printk("Squashfs detected (size %Ld)\n", sb.bytes_used); + new_offset = root_offset + sb.bytes_used; + + if ((new_offset % master->erasesize) > 0) + new_offset += master->erasesize - + (new_offset % master->erasesize); + + ar7_parts[p].name = "rootfs_data"; + ar7_parts[p].offset = new_offset; + ar7_parts[p].size = master->size - new_offset - post_size; + ar7_parts[p - 1].size -= ar7_parts[p].size; + ar7_parts[p - 1].mask_flags |= MTD_WRITEABLE; + ar7_parts[p++].mask_flags = 0; + } else { + printk("Squashfs not found. Moving rootfs partition to next erase block\n"); + if ((root_offset % master->erasesize) > 0) + root_offset += master->erasesize - + (root_offset % master->erasesize); + + ar7_parts[p].offset = root_offset; + ar7_parts[p].size = master->size - root_offset - post_size; + } + *pparts = ar7_parts; + return p; +} + +static struct mtd_part_parser ar7_parser = { + .owner = THIS_MODULE, + .parse_fn = create_mtd_partitions, + .name = "ar7part", +}; + +static int __init ar7_parser_init(void) +{ + return register_mtd_parser(&ar7_parser); +} + +module_init(ar7_parser_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Felix Fietkau, Eugene Konev"); +MODULE_DESCRIPTION("MTD partitioning for TI AR7"); diff --git a/target/linux/ar7-2.6/files/drivers/net/cpmac.c b/target/linux/ar7-2.6/files/drivers/net/cpmac.c new file mode 100644 index 000000000..3c4c1b2b6 --- /dev/null +++ b/target/linux/ar7-2.6/files/drivers/net/cpmac.c @@ -0,0 +1,1037 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include +#include /* printk() */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Eugene Konev"); +MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)"); +MODULE_LICENSE("GPL"); + +/* Register definitions */ +struct cpmac_control_regs { + volatile u32 revision; + volatile u32 control; + volatile u32 teardown; + volatile u32 unused; +}; + +struct cpmac_int_regs { + volatile u32 stat_raw; + volatile u32 stat_masked; + volatile u32 enable; + volatile u32 clear; +}; + +struct cpmac_stats { + volatile u32 good; + volatile u32 bcast; + volatile u32 mcast; + volatile u32 pause; + volatile u32 crc_error; + volatile u32 align_error; + volatile u32 oversized; + volatile u32 jabber; + volatile u32 undersized; + volatile u32 fragment; + volatile u32 filtered; + volatile u32 qos_filtered; + volatile u32 octets; +}; + +struct cpmac_regs { + struct cpmac_control_regs tx_ctrl; + struct cpmac_control_regs rx_ctrl; + volatile u32 unused1[56]; + volatile u32 mbp; +/* MBP bits */ +#define MBP_RXPASSCRC 0x40000000 +#define MBP_RXQOS 0x20000000 +#define MBP_RXNOCHAIN 0x10000000 +#define MBP_RXCMF 0x01000000 +#define MBP_RXSHORT 0x00800000 +#define MBP_RXCEF 0x00400000 +#define MBP_RXPROMISC 0x00200000 +#define MBP_PROMISCCHAN(chan) (((chan) & 0x7) << 16) +#define MBP_RXBCAST 0x00002000 +#define MBP_BCASTCHAN(chan) (((chan) & 0x7) << 8) +#define MBP_RXMCAST 0x00000020 +#define MBP_MCASTCHAN(chan) ((chan) & 0x7) + volatile u32 unicast_enable; + volatile u32 unicast_clear; + volatile u32 max_len; + volatile u32 buffer_offset; + volatile u32 filter_flow_threshold; + volatile u32 unused2[2]; + volatile u32 flow_thre[8]; + volatile u32 free_buffer[8]; + volatile u32 mac_control; +#define MAC_TXPTYPE 0x00000200 +#define MAC_TXPACE 0x00000040 +#define MAC_MII 0x00000020 +#define MAC_TXFLOW 0x00000010 +#define MAC_RXFLOW 0x00000008 +#define MAC_MTEST 0x00000004 +#define MAC_LOOPBACK 0x00000002 +#define MAC_FDX 0x00000001 + volatile u32 mac_status; +#define MACST_QOS 0x4 +#define MACST_RXFLOW 0x2 +#define MACST_TXFLOW 0x1 + volatile u32 emc_control; + volatile u32 unused3; + struct cpmac_int_regs tx_int; + volatile u32 mac_int_vector; +/* Int Status bits */ +#define INTST_STATUS 0x80000 +#define INTST_HOST 0x40000 +#define INTST_RX 0x20000 +#define INTST_TX 0x10000 + volatile u32 mac_eoi_vector; + volatile u32 unused4[2]; + struct cpmac_int_regs rx_int; + volatile u32 mac_int_stat_raw; + volatile u32 mac_int_stat_masked; + volatile u32 mac_int_enable; + volatile u32 mac_int_clear; + volatile u32 mac_addr_low[8]; + volatile u32 mac_addr_mid; + volatile u32 mac_addr_high; + volatile u32 mac_hash_low; + volatile u32 mac_hash_high; + volatile u32 boff_test; + volatile u32 pac_test; + volatile u32 rx_pause; + volatile u32 tx_pause; + volatile u32 unused5[2]; + struct cpmac_stats rx_stats; + struct cpmac_stats tx_stats; + volatile u32 unused6[232]; + volatile u32 tx_ptr[8]; + volatile u32 rx_ptr[8]; + volatile u32 tx_ack[8]; + volatile u32 rx_ack[8]; + +}; + +struct cpmac_mdio_regs { + volatile u32 version; + volatile u32 control; +#define MDIOC_IDLE 0x80000000 +#define MDIOC_ENABLE 0x40000000 +#define MDIOC_PREAMBLE 0x00100000 +#define MDIOC_FAULT 0x00080000 +#define MDIOC_FAULTDETECT 0x00040000 +#define MDIOC_INTTEST 0x00020000 +#define MDIOC_CLKDIV(div) ((div) & 0xff) + volatile u32 alive; + volatile u32 link; + struct cpmac_int_regs link_int; + struct cpmac_int_regs user_int; + u32 unused[20]; + volatile u32 access; +#define MDIO_BUSY 0x80000000 +#define MDIO_WRITE 0x40000000 +#define MDIO_REG(reg) (((reg) & 0x1f) << 21) +#define MDIO_PHY(phy) (((phy) & 0x1f) << 16) +#define MDIO_DATA(data) ((data) & 0xffff) + volatile u32 physel; +}; + +/* Descriptor */ +struct cpmac_desc { + u32 hw_next; + u32 hw_data; + u16 buflen; + u16 bufflags; + u16 datalen; + u16 dataflags; +/* Flags bits */ +#define CPMAC_SOP 0x8000 +#define CPMAC_EOP 0x4000 +#define CPMAC_OWN 0x2000 +#define CPMAC_EOQ 0x1000 + u32 jiffies; + struct sk_buff *skb; + struct cpmac_desc *next; +}; + +struct cpmac_priv { + struct net_device_stats stats; + spinlock_t lock; + int free_tx_channels; + struct cpmac_desc *tx_pool; + struct cpmac_desc *rx_channels[8]; + struct cpmac_desc *tx_channels[8]; + struct cpmac_regs *regs; + struct mii_bus *mii_bus; + struct phy_device *phy; + char phy_name[BUS_ID_SIZE]; + unsigned long pages; + int order; + struct plat_cpmac_data *config; + int oldlink, oldspeed, oldduplex; + u32 msg_enable; +}; + +static irqreturn_t cpmac_irq(int, void *); +void cpmac_exit(void); + +#ifdef CPMAC_DEBUG +static void cpmac_dump_regs(u32 *base, int count) +{ + int i; + for (i = 0; i < (count + 3) / 4; i++) { + if (i % 4 == 0) printk("\nCPMAC[0x%04x]:", i * 4); + printk(" 0x%08x", *(base + i)); + } + printk("\n"); +} +#endif + +static int cpmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) +{ + struct cpmac_mdio_regs *regs = (struct cpmac_mdio_regs *)bus->priv; + volatile u32 val; + + while ((val = regs->access) & MDIO_BUSY); + regs->access = MDIO_BUSY | MDIO_REG(regnum & 0x1f) | + MDIO_PHY(phy_id & 0x1f); + while ((val = regs->access) & MDIO_BUSY); + + return val & 0xffff; +} + +static int cpmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val) +{ + struct cpmac_mdio_regs *regs = (struct cpmac_mdio_regs *)bus->priv; + volatile u32 tmp; + + while ((tmp = regs->access) & MDIO_BUSY); + regs->access = MDIO_BUSY | MDIO_WRITE | + MDIO_REG(regnum & 0x1f) | MDIO_PHY(phy_id & 0x1f) | + val; + + return 0; +} + +static int cpmac_mdio_reset(struct mii_bus *bus) +{ + struct cpmac_mdio_regs *regs = (struct cpmac_mdio_regs *)bus->priv; + + ar7_device_reset(AR7_RESET_BIT_MDIO); + regs->control = MDIOC_ENABLE | + MDIOC_CLKDIV(ar7_cpmac_freq() / 2200000 - 1); + + return 0; +} + +static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, }; + +struct mii_bus cpmac_mii = { + .name = "cpmac-mii", + .read = cpmac_mdio_read, + .write = cpmac_mdio_write, + .reset = cpmac_mdio_reset, + .irq = mii_irqs, +}; + +int cpmac_config(struct net_device *dev, struct ifmap *map) +{ + if (dev->flags & IFF_UP) + return -EBUSY; + + /* Don't allow changing the I/O address */ + if (map->base_addr != dev->base_addr) + return -EOPNOTSUPP; + + /* ignore other fields */ + return 0; +} + +int cpmac_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + if (dev->flags & IFF_UP) + return -EBUSY; + + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + + return 0; +} + +void cpmac_set_multicast_list(struct net_device *dev) +{ + struct dev_mc_list *iter; + int i; + int hash, tmp; + int hashlo = 0, hashhi = 0; + struct cpmac_priv *priv = netdev_priv(dev); + + if(dev->flags & IFF_PROMISC) { + priv->regs->mbp &= ~MBP_PROMISCCHAN(0); /* promisc channel 0 */ + priv->regs->mbp |= MBP_RXPROMISC; + } else { + priv->regs->mbp &= ~MBP_RXPROMISC; + if(dev->flags & IFF_ALLMULTI) { + /* enable all multicast mode */ + priv->regs->mac_hash_low = 0xffffffff; + priv->regs->mac_hash_high = 0xffffffff; + } else { + for(i = 0, iter = dev->mc_list; i < dev->mc_count; + i++, iter = iter->next) { + hash = 0; + tmp = iter->dmi_addr[0]; + hash ^= (tmp >> 2) ^ (tmp << 4); + tmp = iter->dmi_addr[1]; + hash ^= (tmp >> 4) ^ (tmp << 2); + tmp = iter->dmi_addr[2]; + hash ^= (tmp >> 6) ^ tmp; + tmp = iter->dmi_addr[4]; + hash ^= (tmp >> 2) ^ (tmp << 4); + tmp = iter->dmi_addr[5]; + hash ^= (tmp >> 4) ^ (tmp << 2); + tmp = iter->dmi_addr[6]; + hash ^= (tmp >> 6) ^ tmp; + hash &= 0x3f; + if(hash < 32) { + hashlo |= 1<regs->mac_hash_low = hashlo; + priv->regs->mac_hash_high = hashhi; + } + } +} + +static void cpmac_rx(struct net_device *dev, int channel) +{ + struct cpmac_desc *pkt; + struct sk_buff *skb; + char *data; + struct cpmac_priv *priv = netdev_priv(dev); + + spin_lock(&priv->lock); + pkt = priv->rx_channels[channel]; + if (!pkt) { + if (printk_ratelimit()) + printk(KERN_NOTICE "%s: rx: spurious interrupt\n", + dev->name); + priv->stats.rx_errors++; + return; + } + + priv->regs->rx_ack[channel] = virt_to_phys(pkt); + dma_cache_inv((u32)pkt, 16); + if (!pkt->datalen) { + if (printk_ratelimit()) + printk(KERN_NOTICE "%s: rx: spurious interrupt\n", + dev->name); + priv->stats.rx_errors++; + return; + } + skb = dev_alloc_skb(1536); + if (!skb) { + if (printk_ratelimit()) + printk(KERN_NOTICE "%s: rx: low on mem - packet dropped\n", + dev->name); + priv->stats.rx_dropped++; + } else { + data = (char *)phys_to_virt(pkt->hw_data); + dma_cache_inv((u32)data, pkt->datalen); + skb_put(pkt->skb, pkt->datalen); + pkt->skb->protocol = eth_type_trans(pkt->skb, dev); + pkt->skb->ip_summed = CHECKSUM_NONE; + priv->stats.rx_packets++; + priv->stats.rx_bytes += pkt->datalen; + netif_rx(pkt->skb); + skb_reserve(skb, 2); + skb->dev = dev; + pkt->skb = skb; + pkt->hw_data = virt_to_phys(skb->data); + } + spin_unlock(&priv->lock); + pkt->buflen = 1500 + ETH_HLEN + 4; + pkt->datalen = 0; + pkt->dataflags = CPMAC_OWN; + dma_cache_wback_inv((u32)pkt, 16); + priv->regs->rx_ptr[channel] = virt_to_phys(pkt); +} + +struct cpmac_desc *cpmac_get_desc(struct net_device *dev) +{ + struct cpmac_desc *pkt; + struct cpmac_priv *priv = netdev_priv(dev); + pkt = priv->tx_pool; + priv->tx_pool = pkt->next; + pkt->next = NULL; + if (priv->tx_pool == NULL) + netif_stop_queue(dev); + return pkt; +} + +void cpmac_release_desc(struct net_device *dev, struct cpmac_desc *pkt) +{ + struct cpmac_priv *priv = netdev_priv(dev); + struct cpmac_desc *p; + p = pkt; + while (p->next) p = p->next; + p->next = priv->tx_pool; + priv->tx_pool = pkt; +} + +int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long flags; + int i, len, frag; + skb_frag_t *this_frag; + void *data; + struct cpmac_desc *head, *tail, *curr; + struct cpmac_priv *priv = netdev_priv(dev); + + BUG_ON(priv->free_tx_channels < 1); + len = skb->len; + if (len < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) { + if (printk_ratelimit()) + printk(KERN_NOTICE "%s: padding failed, dropping\n", + dev->name); + spin_lock_irqsave(&priv->lock, flags); + priv->stats.tx_dropped++; + spin_unlock_irqrestore(&priv->lock, flags); + return -ENOMEM; + } + len = ETH_ZLEN; + } + spin_lock_irqsave(&priv->lock, flags); + dev->trans_start = jiffies; + for (i = 0; i < 8; i++) + if (!priv->tx_channels[i]) + break; + + BUG_ON(i == 8); + + head = cpmac_get_desc(dev); + priv->tx_channels[i] = head; + head->jiffies = dev->trans_start; + if (!(--priv->free_tx_channels)) + netif_stop_queue(dev); + spin_unlock_irqrestore(&priv->lock, flags); + + head->dataflags = CPMAC_SOP | CPMAC_OWN; + head->skb = skb; + head->hw_data = virt_to_phys(skb->data); + dma_cache_wback_inv((u32)skb->data, len); + head->buflen = len; + head->datalen = len; + tail = head; + for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { + dma_cache_wback_inv((u32)tail, 16); + this_frag = &skb_shinfo(skb)->frags[frag]; + curr = cpmac_get_desc(dev); + data = page_address(this_frag->page) + + this_frag->page_offset; + curr->hw_data = virt_to_phys(data); + curr->buflen = this_frag->size; + curr->datalen = this_frag->size; + curr->dataflags = CPMAC_OWN; + dma_cache_wback_inv((u32)data, len); + tail->hw_next = virt_to_phys(curr); + tail->next = curr; + tail = curr; + } + tail->hw_next = 0; + tail->dataflags |= CPMAC_EOP; + dma_cache_wback_inv((u32)tail, 16); + priv->regs->tx_ptr[i] = virt_to_phys(head); + return 0; +} + +void cpmac_end_xmit(struct net_device *dev, int channel) +{ + struct cpmac_desc *pkt; + struct cpmac_priv *priv = netdev_priv(dev); + + spin_lock(&priv->lock); + pkt = priv->tx_channels[channel]; + priv->tx_channels[channel] = NULL; + priv->free_tx_channels++; + priv->regs->tx_ack[channel] = virt_to_phys(pkt); + if (pkt) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += pkt->skb->len; + dev_kfree_skb_irq(pkt->skb); + cpmac_release_desc(dev, pkt); + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } else { + if (printk_ratelimit()) + printk(KERN_NOTICE "%s: end_xmit: spurious interrupt\n", + dev->name); + } + spin_unlock(&priv->lock); +} + +static irqreturn_t cpmac_irq(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct cpmac_priv *priv = netdev_priv(dev); + u32 status; + + if (!dev) + return IRQ_NONE; + + status = priv->regs->mac_int_vector; + + if (status & INTST_TX) { + cpmac_end_xmit(dev, (status & 7)); + } + + if (status & INTST_RX) { + cpmac_rx(dev, (status >> 8) & 7); + } + + if (status & INTST_HOST) { /* host interrupt ??? */ + printk("%s: host int, something bad happened...\n", dev->name); + printk("%s: mac status: 0x%08x\n", dev->name, + priv->regs->mac_status); + } + + if (status & INTST_STATUS) { /* status interrupt ??? */ + printk("%s: status int, what are we gonna do?\n", dev->name); + } + + priv->regs->mac_eoi_vector = 0; + return IRQ_HANDLED; +} + +void cpmac_tx_timeout(struct net_device *dev) +{ + int i; + struct cpmac_priv *priv = netdev_priv(dev); + struct cpmac_desc *pkt = NULL, *tmp; + + priv->stats.tx_errors++; + for (i = 0; i < 8; i++) { + tmp = priv->tx_channels[i]; + if (tmp && (!pkt || (pkt->jiffies > tmp->jiffies))) + pkt = tmp; + } + if (pkt) { + printk("Transmit timeout at %ld, latency %ld\n", jiffies, + jiffies - pkt->jiffies); + for (i = 0; i < 8; i++) + if (priv->tx_channels[i] == pkt) + priv->tx_channels[i] = NULL; + dev_kfree_skb(pkt->skb); + cpmac_release_desc(dev, pkt); + priv->free_tx_channels++; + netif_wake_queue(dev); + } +} + +int cpmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct cpmac_priv *priv = netdev_priv(dev); + if (!(netif_running(dev))) + return -EINVAL; + if (!priv->phy) + return -EINVAL; + return phy_mii_ioctl(priv->phy, if_mii(ifr), cmd); +} + +static int cpmac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct cpmac_priv *priv = netdev_priv(dev); + + if (priv->phy) + return phy_ethtool_gset(priv->phy, cmd); + + return -EINVAL; +} + +static int cpmac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct cpmac_priv *priv = netdev_priv(dev); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (priv->phy) + return phy_ethtool_sset(priv->phy, cmd); + + return -EINVAL; +} + +static void cpmac_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, "cpmac"); + strcpy(info->version, "0.0.3"); + info->fw_version[0] = '\0'; + sprintf(info->bus_info, "%s", "cpmac"); + info->regdump_len = 0; +} + +static const struct ethtool_ops cpmac_ethtool_ops = { + .get_settings = cpmac_get_settings, + .set_settings = cpmac_set_settings, + .get_drvinfo = cpmac_get_drvinfo, + .get_link = ethtool_op_get_link, +}; + +static struct net_device_stats *cpmac_stats(struct net_device *dev) +{ + struct cpmac_priv *priv = netdev_priv(dev); + + if (netif_device_present(dev)) + return &priv->stats; + + return NULL; +} + +static int cpmac_change_mtu(struct net_device *dev, int mtu) +{ + unsigned long flags; + struct cpmac_priv *priv = netdev_priv(dev); + spinlock_t *lock = &priv->lock; + + if ((mtu < 68) || (mtu > 1500)) + return -EINVAL; + + spin_lock_irqsave(lock, flags); + dev->mtu = mtu; + spin_unlock_irqrestore(lock, flags); + + return 0; +} + +static void cpmac_reset(struct net_device *dev) +{ + int i; + struct cpmac_priv *priv = netdev_priv(dev); + + ar7_device_reset(priv->config->reset_bit); + priv->regs->rx_ctrl.control &= ~1; + priv->regs->tx_ctrl.control &= ~1; + for (i = 0; i < 8; i++) { + priv->regs->tx_ptr[i] = 0; + priv->regs->rx_ptr[i] = 0; + } + priv->regs->mac_control &= ~MAC_MII; /* disable mii */ +} + +static void cpmac_adjust_link(struct net_device *dev) +{ + struct cpmac_priv *priv = netdev_priv(dev); + unsigned long flags; + int new_state = 0; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->phy->link) { + if (priv->phy->duplex != priv->oldduplex) { + new_state = 1; + priv->oldduplex = priv->phy->duplex; + } + + if (priv->phy->speed != priv->oldspeed) { + new_state = 1; + priv->oldspeed = priv->phy->speed; + } + + if (!priv->oldlink) { + new_state = 1; + priv->oldlink = 1; + netif_schedule(dev); + } + } else if (priv->oldlink) { + new_state = 1; + priv->oldlink = 0; + priv->oldspeed = 0; + priv->oldduplex = -1; + } + + if (new_state) + phy_print_status(priv->phy); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +int cpmac_open(struct net_device *dev) +{ + int i, j, res; + struct cpmac_priv *priv = netdev_priv(dev); + struct cpmac_desc *pkt; + struct sk_buff *skb; + +/* priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, + 0, PHY_INTERFACE_MODE_MII);*/ + priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, 0); + if (IS_ERR(priv->phy)) { + printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); + return PTR_ERR(priv->phy); + } + + if (!request_mem_region(dev->mem_start, dev->mem_end - + dev->mem_start, dev->name)) { + printk("%s: failed to request registers\n", + dev->name); + res = -ENXIO; + goto fail_reserve; + } + + priv->regs = ioremap_nocache(dev->mem_start, dev->mem_end - + dev->mem_start); + if (!priv->regs) { + printk("%s: failed to remap registers\n", dev->name); + res = -ENXIO; + goto fail_remap; + } + + priv->order = get_order(4096); + priv->pages = __get_dma_pages(GFP_KERNEL, priv->order); + if (!priv->pages) { + res = -ENOMEM; + goto fail_alloc; + } + memset((char *)priv->pages, 0, 4096); + + priv->tx_pool = NULL; + + for (i = 0; i < 4096 / sizeof(struct cpmac_desc); i++) { + pkt = (struct cpmac_desc *) + (priv->pages + i * sizeof(struct cpmac_desc)); + memset(pkt, sizeof(struct cpmac_desc), 0); + if (i < 8) { + skb = alloc_skb(1500 + ETH_HLEN + 6, GFP_KERNEL); + if (!skb) { + for(j = 0; j < i - 1; j++) + kfree_skb(priv->rx_channels[j]->skb); + free_pages(priv->pages, priv->order); + res = -ENOMEM; + goto fail_alloc; + } + skb_reserve(skb, 2); + skb->dev = dev; + pkt->skb = skb; + pkt->hw_data = virt_to_phys(skb->data); + pkt->buflen = 1500 + ETH_HLEN + 4; + pkt->dataflags = CPMAC_OWN; + dma_cache_wback_inv((u32)pkt, 16); + priv->rx_channels[i] = pkt; + priv->tx_channels[i] = NULL; + } else { + pkt->next = priv->tx_pool; + priv->tx_pool = pkt; + } + } + + cpmac_reset(dev); + priv->free_tx_channels = 8; + + for (i = 0; i < 8; i++) { + priv->regs->tx_ptr[i] = 0; + priv->regs->rx_ptr[i] = virt_to_phys(priv->rx_channels[i]); + } + + priv->regs->mbp = MBP_RXNOCHAIN | MBP_RXSHORT | MBP_RXBCAST | + MBP_RXMCAST; + priv->regs->unicast_enable = 0xff; + priv->regs->unicast_clear = 0; + priv->regs->buffer_offset = 0; + for (i = 0; i < 8; i++) + priv->regs->mac_addr_low[i] = dev->dev_addr[5]; + priv->regs->mac_addr_mid = dev->dev_addr[4]; + priv->regs->mac_addr_high = dev->dev_addr[0] | (dev->dev_addr[1] << 8) + | (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24); + priv->regs->max_len = 1536; + priv->regs->rx_int.enable = 0xff; + priv->regs->rx_int.clear = 0; + priv->regs->tx_int.enable = 0xff; + priv->regs->tx_int.clear = 0; + priv->regs->mac_int_enable = 3; + priv->regs->mac_int_clear = 0xfc; + + if((res = request_irq(dev->irq, cpmac_irq, SA_INTERRUPT, + dev->name, dev))) { + printk("%s: failed to obtain irq\n", dev->name); + goto fail_irq; + } + + priv->regs->rx_ctrl.control |= 1; + priv->regs->tx_ctrl.control |= 1; + priv->regs->mac_control |= MAC_MII | MAC_FDX; + + priv->phy->state = PHY_CHANGELINK; + phy_start(priv->phy); + + netif_start_queue(dev); + + return 0; + +fail_irq: + for(i = 0; i < 8; i++) + if (priv->rx_channels[i]->skb) + kfree_skb(priv->rx_channels[i]->skb); + free_pages(priv->pages, priv->order); + +fail_alloc: + iounmap(priv->regs); + +fail_remap: + release_mem_region(dev->mem_start, dev->mem_end - + dev->mem_start); + +fail_reserve: + phy_disconnect(priv->phy); + + return res; +} + +int cpmac_stop(struct net_device *dev) +{ + int i; + struct cpmac_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + phy_stop(priv->phy); + phy_disconnect(priv->phy); + priv->phy = NULL; + + cpmac_reset(dev); + + for (i = 0; i < 8; i++) { + priv->regs->rx_ptr[i] = 0; + priv->regs->tx_ptr[i] = 0; + priv->regs->mbp = 0; + } + + free_irq(dev->irq, dev); + release_mem_region(dev->mem_start, dev->mem_end - + dev->mem_start); + + for(i = 0; i < 8; i++) + if (priv->rx_channels[i]->skb) + kfree_skb(priv->rx_channels[i]->skb); + if (priv->pages) + free_pages(priv->pages, priv->order); + + return 0; +} + +static int external_switch = 0; + +int __devinit cpmac_probe(struct platform_device *pdev) +{ + int i, rc, phy_id; + struct resource *res; + struct cpmac_priv *priv; + struct net_device *dev; + struct plat_cpmac_data *pdata; + + if (strcmp(pdev->name, "cpmac") != 0) + return -ENODEV; + + pdata = pdev->dev.platform_data; + + for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { + if (!(pdata->phy_mask & (1 << phy_id))) + continue; + if (!cpmac_mii.phy_map[phy_id]) + continue; + break; + } + + if (phy_id == PHY_MAX_ADDR) { + if (external_switch) { + phy_id = 0; + } else { + printk("cpmac: no PHY present\n"); + return -ENODEV; + } + } + + dev = alloc_etherdev(sizeof(struct cpmac_priv)); + + if (!dev) { + printk(KERN_ERR "cpmac: Unable to allocate net_device structure!\n"); + return -ENOMEM; + } + + SET_MODULE_OWNER(dev); + platform_set_drvdata(pdev, dev); + priv = netdev_priv(dev); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!res) { + rc = -ENODEV; + goto fail; + } + + dev->mem_start = res->start; + dev->mem_end = res->end; + dev->irq = platform_get_irq_byname(pdev, "irq"); + + dev->mtu = 1500; + dev->open = cpmac_open; + dev->stop = cpmac_stop; + dev->set_config = cpmac_config; + dev->hard_start_xmit = cpmac_start_xmit; + dev->do_ioctl = cpmac_ioctl; + dev->get_stats = cpmac_stats; + dev->change_mtu = cpmac_change_mtu; + dev->set_mac_address = cpmac_set_mac_address; + dev->set_multicast_list = cpmac_set_multicast_list; + dev->tx_timeout = cpmac_tx_timeout; + dev->ethtool_ops = &cpmac_ethtool_ops; + + memset(priv, 0, sizeof(struct cpmac_priv)); + spin_lock_init(&priv->lock); + priv->msg_enable = netif_msg_init(NETIF_MSG_WOL, 0x3fff); + priv->config = pdata; + memcpy(dev->dev_addr, priv->config->dev_addr, sizeof(dev->dev_addr)); + if (phy_id == 31) { + snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, + cpmac_mii.id, phy_id); + } else { + snprintf(priv->phy_name, BUS_ID_SIZE, "fixed@%d:%d", 100, 1); + } + + if ((rc = register_netdev(dev))) { + printk("cpmac: error %i registering device %s\n", + rc, dev->name); + goto fail; + } + + printk("cpmac: device %s (regs: %p, irq: %d, phy: %s, mac: ", + dev->name, (u32 *)dev->mem_start, dev->irq, + priv->phy_name); + for (i = 0; i < 6; i++) { + printk("%02x", dev->dev_addr[i]); + if (i < 5) printk(":"); + else printk(")\n"); + } + + return 0; + +fail: + free_netdev(dev); + return rc; +} + +static int __devexit cpmac_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + unregister_netdev(dev); + free_netdev(dev); + return 0; +} + +static struct platform_driver cpmac_driver = { + .driver.name = "cpmac", + .probe = cpmac_probe, + .remove = cpmac_remove, +}; + +int __devinit cpmac_init(void) +{ + volatile u32 mask; + int i, res; + cpmac_mii.priv = (struct cpmac_mdio_regs *) + ioremap_nocache(AR7_REGS_MDIO, sizeof(struct cpmac_mdio_regs)); + + if (!cpmac_mii.priv) { + printk("Can't ioremap mdio registers\n"); + return -ENXIO; + } + +#warning FIXME: unhardcode gpio&reset bits + ar7_gpio_disable(26); + ar7_gpio_disable(27); +/* ar7_device_reset(17); + ar7_device_reset(21); + ar7_device_reset(26);*/ + + cpmac_mii.reset(&cpmac_mii); + + for (i = 0; i < 300000; i++) { + mask = ((struct cpmac_mdio_regs *)cpmac_mii.priv)->alive; + if (mask) + break; + } + + mask &= 0x7fffffff; + if (mask & (mask - 1)) { + external_switch = 1; + mask = 0; + } + + cpmac_mii.phy_mask = ~(mask | 0x80000000); + + res = mdiobus_register(&cpmac_mii); + if (res) + goto fail_mii; + + res = platform_driver_register(&cpmac_driver); + if (res) + goto fail_cpmac; + + return 0; + +fail_cpmac: + mdiobus_unregister(&cpmac_mii); + +fail_mii: + iounmap(cpmac_mii.priv); + + return res; +} + +void __devexit cpmac_exit(void) +{ + platform_driver_unregister(&cpmac_driver); + mdiobus_unregister(&cpmac_mii); +} + +module_init(cpmac_init); +module_exit(cpmac_exit); diff --git a/target/linux/ar7-2.6/files/include/asm-generic/gpio.h b/target/linux/ar7-2.6/files/include/asm-generic/gpio.h new file mode 100644 index 000000000..2d0aab1d8 --- /dev/null +++ b/target/linux/ar7-2.6/files/include/asm-generic/gpio.h @@ -0,0 +1,25 @@ +#ifndef _ASM_GENERIC_GPIO_H +#define _ASM_GENERIC_GPIO_H + +/* platforms that don't directly support access to GPIOs through I2C, SPI, + * or other blocking infrastructure can use these wrappers. + */ + +static inline int gpio_cansleep(unsigned gpio) +{ + return 0; +} + +static inline int gpio_get_value_cansleep(unsigned gpio) +{ + might_sleep(); + return gpio_get_value(gpio); +} + +static inline void gpio_set_value_cansleep(unsigned gpio, int value) +{ + might_sleep(); + gpio_set_value(gpio, value); +} + +#endif /* _ASM_GENERIC_GPIO_H */ diff --git a/target/linux/ar7-2.6/files/include/asm-mips/ar7/ar7.h b/target/linux/ar7-2.6/files/include/asm-mips/ar7/ar7.h new file mode 100644 index 000000000..fe51a8d67 --- /dev/null +++ b/target/linux/ar7-2.6/files/include/asm-mips/ar7/ar7.h @@ -0,0 +1,153 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __AR7_H__ +#define __AR7_H__ + +#include +#include + +#define AR7_REGS_BASE 0x08610000 + +#define AR7_REGS_MAC0 (AR7_REGS_BASE + 0x0000) +#define AR7_REGS_EMIF (AR7_REGS_BASE + 0x0800) +#define AR7_REGS_GPIO (AR7_REGS_BASE + 0x0900) +#define AR7_REGS_POWER (AR7_REGS_BASE + 0x0a00) +#define AR7_REGS_WDT (AR7_REGS_BASE + 0x0b00) +#define AR7_REGS_UART0 (AR7_REGS_BASE + 0x0e00) +#define AR7_REGS_UART1 (AR7_REGS_BASE + 0x0f00) +#define AR7_REGS_RESET (AR7_REGS_BASE + 0x1600) +#define AR7_REGS_VLYNQ0 (AR7_REGS_BASE + 0x1800) +#define AR7_REGS_VLYNQ1 (AR7_REGS_BASE + 0x1C00) +#define AR7_REGS_MDIO (AR7_REGS_BASE + 0x1E00) +#define AR7_REGS_IRQ (AR7_REGS_BASE + 0x2400) +#define AR7_REGS_MAC1 (AR7_REGS_BASE + 0x2800) + +#define AR7_RESET_PEREPHERIAL 0x0 +#define AR7_RESET_SOFTWARE 0x4 +#define AR7_RESET_STATUS 0x8 + +#define AR7_RESET_BIT_MDIO 22 + +/* GPIO control registers */ +#define AR7_GPIO_INPUT 0x0 +#define AR7_GPIO_OUTPUT 0x4 +#define AR7_GPIO_DIR 0x8 +#define AR7_GPIO_ENABLE 0xC + +#define AR7_GPIO_BIT_STATUS_LED 8 + + +/* Interrupts */ +#define AR7_IRQ_UART0 15 +#define AR7_IRQ_UART1 16 + +struct plat_cpmac_data { + int reset_bit; + int power_bit; + u32 phy_mask; + char dev_addr[6]; +}; + +extern char *prom_getenv(char *envname); + +/* A bunch of small bit-toggling functions */ +static inline u32 get_chip_id(void) +{ + return *((u16 *)KSEG1ADDR(AR7_REGS_GPIO + 0x14)); +} + +static inline int ar7_cpu_freq(void) +{ + u16 chip_id = get_chip_id(); + switch (chip_id) { + case 0x5: + return 150000000; + case 0x18: + case 0x2b: + return 211968000; + default: + return 150000000; + } +} + +static inline int ar7_bus_freq(void) +{ + u16 chip_id = get_chip_id(); + switch (chip_id) { + case 0x5: + return 125000000; + case 0x18: + case 0x2b: + return 105984000; + default: + return 125000000; + } +} +#define ar7_cpmac_freq ar7_bus_freq + +static inline int ar7_has_high_cpmac(void) +{ + u16 chip_id = get_chip_id(); + switch (chip_id) { + case 0x18: + case 0x2b: + return 0; + default: + return 1; + } +} +#define ar7_has_high_vlynq ar7_has_high_cpmac + +static inline void ar7_device_enable(u32 bit) +{ + volatile u32 *reset_reg = (u32 *)KSEG1ADDR(AR7_REGS_RESET + AR7_RESET_PEREPHERIAL); + *reset_reg |= (1 << bit); + mdelay(20); +} + +static inline void ar7_device_disable(u32 bit) +{ + volatile u32 *reset_reg = (u32 *)KSEG1ADDR(AR7_REGS_RESET + AR7_RESET_PEREPHERIAL); + *reset_reg &= ~(1 << bit); + mdelay(20); +} + +static inline void ar7_device_reset(u32 bit) +{ + ar7_device_disable(bit); + ar7_device_enable(bit); +} + +static inline void ar7_device_on(u32 bit) +{ + volatile u32 *power_reg = (u32 *)KSEG1ADDR(AR7_REGS_POWER); + *power_reg |= (1 << bit); + mdelay(20); +} + +static inline void ar7_device_off(u32 bit) +{ + volatile u32 *power_reg = (u32 *)KSEG1ADDR(AR7_REGS_POWER); + *power_reg &= ~(1 << bit); + mdelay(20); +} + +#endif diff --git a/target/linux/ar7-2.6/files/include/asm-mips/ar7/gpio.h b/target/linux/ar7-2.6/files/include/asm-mips/ar7/gpio.h new file mode 100644 index 000000000..73673feff --- /dev/null +++ b/target/linux/ar7-2.6/files/include/asm-mips/ar7/gpio.h @@ -0,0 +1,116 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __AR7_GPIO_H__ +#define __AR7_GPIO_H__ +#include + +#define AR7_GPIO_MAX 32 + +extern int gpio_request(unsigned gpio, char *label); +extern void gpio_free(unsigned gpio); + +/* Common GPIO layer */ +static inline int gpio_direction_input(unsigned gpio) +{ + void __iomem *gpio_dir = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_DIR); + + if (gpio >= AR7_GPIO_MAX) + return -EINVAL; + + __raw_writel(__raw_readl(gpio_dir) | (1 << gpio), gpio_dir); + + return 0; +} + +static inline int gpio_direction_output(unsigned gpio) +{ + void __iomem *gpio_dir = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_DIR); + + if (gpio >= AR7_GPIO_MAX) + return -EINVAL; + + __raw_writel(__raw_readl(gpio_dir) & ~(1 << gpio), gpio_dir); + + return 0; +} + +static inline int gpio_get_value(unsigned gpio) +{ + void __iomem *gpio_in = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_INPUT); + + if (gpio >= AR7_GPIO_MAX) + return -EINVAL; + + return ((__raw_readl(gpio_in) & (1 << gpio)) != 0); +} + +static inline void gpio_set_value(unsigned gpio, int value) +{ + void __iomem *gpio_out = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_OUTPUT); + volatile unsigned tmp; + + if (gpio >= AR7_GPIO_MAX) + return; + + tmp = __raw_readl(gpio_out) & ~(1 << gpio); + if (value) + tmp |= 1 << gpio; + __raw_writel(tmp, gpio_out); +} + +static inline int gpio_to_irq(unsigned gpio) +{ + return -EINVAL; +} + +static inline int irq_to_gpio(unsigned irq) +{ + return -EINVAL; +} + +/* Board specific GPIO functions */ +static inline int ar7_gpio_enable(unsigned gpio) +{ + void __iomem *gpio_en = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_ENABLE); + + if (gpio >= AR7_GPIO_MAX) + return -EINVAL; + + __raw_writel(__raw_readl(gpio_en) | (1 << gpio), gpio_en); + + return 0; +} + +static inline int ar7_gpio_disable(unsigned gpio) +{ + void __iomem *gpio_en = (void __iomem *)KSEG1ADDR(AR7_REGS_GPIO + AR7_GPIO_ENABLE); + + if (gpio >= AR7_GPIO_MAX) + return -EINVAL; + + __raw_writel(__raw_readl(gpio_en) & ~(1 << gpio), gpio_en); + + return 0; +} + +#include + +#endif diff --git a/target/linux/ar7-2.6/files/include/asm-mips/ar7/mmzone.h b/target/linux/ar7-2.6/files/include/asm-mips/ar7/mmzone.h new file mode 100644 index 000000000..885fdb0cb --- /dev/null +++ b/target/linux/ar7-2.6/files/include/asm-mips/ar7/mmzone.h @@ -0,0 +1,29 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ASM_MACH_MMZONE_H +#define _ASM_MACH_MMZONE_H + +extern pg_data_t __node_data[]; +#define NODE_DATA(nid) (&__node_data[nid]) +#define NODE_MEM_MAP(nid) (NODE_DATA(nid)->node_mem_map) +#define pa_to_nid(addr) (((addr) >= ARCH_PFN_OFFSET << PAGE_SHIFT) ? 0 : -1) + +#endif /* _ASM_MACH_MMZONE_H */ diff --git a/target/linux/ar7-2.6/files/include/asm-mips/ar7/spaces.h b/target/linux/ar7-2.6/files/include/asm-mips/ar7/spaces.h new file mode 100644 index 000000000..367fe2d34 --- /dev/null +++ b/target/linux/ar7-2.6/files/include/asm-mips/ar7/spaces.h @@ -0,0 +1,33 @@ +/* + * 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) 1994 - 1999, 2000, 03, 04 Ralf Baechle + * Copyright (C) 2000, 2002 Maciej W. Rozycki + * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc. + */ +#ifndef _ASM_AR7_SPACES_H +#define _ASM_AR7_SPACES_H + + +#define CAC_BASE 0x80000000 +#define IO_BASE 0xa0000000 +#define UNCAC_BASE 0xa0000000 +#define MAP_BASE 0xc0000000 + +/* + * This handles the memory map. + * We handle pages at KSEG0 for kernels with 32 bit address space. + */ +#define PAGE_OFFSET 0x80000000UL +#define ARCH_PFN_OFFSET (0x14000000 >> PAGE_SHIFT) + +/* + * Memory above this physical address will be considered highmem. + */ +#ifndef HIGHMEM_START +#define HIGHMEM_START 0x40000000UL +#endif + +#endif /* __ASM_AR7_SPACES_H */ diff --git a/target/linux/ar7-2.6/files/include/asm-mips/ar7/vlynq.h b/target/linux/ar7-2.6/files/include/asm-mips/ar7/vlynq.h new file mode 100644 index 000000000..63894159e --- /dev/null +++ b/target/linux/ar7-2.6/files/include/asm-mips/ar7/vlynq.h @@ -0,0 +1,92 @@ +/* + * $Id$ + * + * Copyright (C) 2006, 2007 OpenWrt.org + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef __VLYNQ_H__ +#define __VLYNQ_H__ + +struct vlynq_mapping { + u32 size; + u32 offset; +} __attribute__ ((packed)); + +struct vlynq_device_id { + u32 id; +}; + +struct vlynq_regs; +struct vlynq_device { + u32 id; + int irq; + int local_irq; + int remote_irq; + int clock_div; + u32 regs_start, regs_end; + u32 mem_start, mem_end; + u32 irq_start, irq_end; + void *priv; + struct vlynq_regs *local; + struct vlynq_regs *remote; + struct device dev; +}; + +struct vlynq_driver { + char *name; + int (*probe)(struct vlynq_device *dev); + int (*remove)(struct vlynq_device *dev); + struct device_driver driver; +}; + +#define to_vlynq_driver(drv) container_of(drv, struct vlynq_driver, driver) + +struct plat_vlynq_ops { + int (*on)(struct vlynq_device *dev); + void (*off)(struct vlynq_device *dev); +}; + +#define to_vlynq_device(device) container_of(device, struct vlynq_device, dev) + +extern struct bus_type vlynq_bus_type; + +extern int __vlynq_register_driver(struct vlynq_driver *driver, + struct module *owner); + +static inline int vlynq_register_driver(struct vlynq_driver *driver) +{ + return __vlynq_register_driver(driver, THIS_MODULE); +} + +extern void vlynq_unregister_driver(struct vlynq_driver *driver); +extern int vlynq_device_enable(struct vlynq_device *dev); +extern void vlynq_device_disable(struct vlynq_device *dev); +extern u32 vlynq_local_id(struct vlynq_device *dev); +extern u32 vlynq_remote_id(struct vlynq_device *dev); +extern void vlynq_set_local_mapping(struct vlynq_device *dev, + u32 tx_offset, + struct vlynq_mapping *mapping); +extern void vlynq_set_remote_mapping(struct vlynq_device *dev, + u32 tx_offset, + struct vlynq_mapping *mapping); +extern int vlynq_virq_to_irq(struct vlynq_device *dev, int virq); +extern int vlynq_irq_to_virq(struct vlynq_device *dev, int irq); +extern int vlynq_set_local_irq(struct vlynq_device *dev, int virq); +extern int vlynq_set_remote_irq(struct vlynq_device *dev, int virq); + +#endif diff --git a/target/linux/ar7-2.6/files/include/asm-mips/gpio.h b/target/linux/ar7-2.6/files/include/asm-mips/gpio.h new file mode 100644 index 000000000..38a411ab6 --- /dev/null +++ b/target/linux/ar7-2.6/files/include/asm-mips/gpio.h @@ -0,0 +1,6 @@ +#ifndef _ASM_MIPS_GPIO_H +#define _ASM_MIPS_GPIO_H + +#include + +#endif /* _ASM_MIPS_GPIO_H */ -- cgit v1.2.3