diff options
Diffstat (limited to 'target/linux/adm5120/files')
59 files changed, 9221 insertions, 0 deletions
diff --git a/target/linux/adm5120/files/arch/mips/adm5120/Kconfig b/target/linux/adm5120/files/arch/mips/adm5120/Kconfig new file mode 100644 index 000000000..7738d8175 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/Kconfig @@ -0,0 +1,20 @@ +if MIPS_ADM5120 + +menu "ADM5120 Implementation Options" + +config ADM5120_CPU_OVERRIDES + bool "Enable CPU feature overrides" + default y + +config PCI_ADM5120 + bool "Enable PCI support" + select PCI + default y + +endmenu + +config ARM_AMBA + bool + default y + +endif diff --git a/target/linux/adm5120/files/arch/mips/adm5120/Makefile b/target/linux/adm5120/files/arch/mips/adm5120/Makefile new file mode 100644 index 000000000..bb0a7c8b0 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the ADMtek ADM5120 SoC specific parts of the kernel +# + +obj-y := setup.o prom.o irq.o memory.o adm5120_info.o +obj-y += board.o +obj-y += clock.o +obj-y += gpio.o +obj-y += platform.o +obj-y += reset.o +obj-y += time.o + +obj-y += trxsplit.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/target/linux/adm5120/files/arch/mips/adm5120/adm5120_info.c b/target/linux/adm5120/files/arch/mips/adm5120/adm5120_info.c new file mode 100644 index 000000000..e2ee5ca56 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/adm5120_info.c @@ -0,0 +1,93 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_switch.h> + +unsigned int adm5120_product_code; +unsigned int adm5120_revision; +unsigned int adm5120_package; +unsigned int adm5120_nand_boot; +unsigned long adm5120_speed; + +#define SWITCH_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r)) +#define SWITCH_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))=(v) + +/* + * CPU settings detection + */ +#define CODE_GET_PC(c) ((c) & CODE_PC_MASK) +#define CODE_GET_REV(c) (((c) >> CODE_REV_SHIFT) & CODE_REV_MASK) +#define CODE_GET_PK(c) (((c) >> CODE_PK_SHIFT) & CODE_PK_MASK) +#define CODE_GET_CLKS(c) (((c) >> CODE_CLKS_SHIFT) & CODE_CLKS_MASK) +#define CODE_GET_NAB(c) (((c) & CODE_NAB) != 0) + +void adm5120_ndelay(u32 ns) +{ + u32 t; + + SWITCH_WRITE(SWITCH_REG_TIMER, TIMER_PERIOD_DEFAULT); + SWITCH_WRITE(SWITCH_REG_TIMER_INT, (TIMER_INT_TOS | TIMER_INT_TOM)); + + t = (ns+640) / 640; + t &= TIMER_PERIOD_MASK; + SWITCH_WRITE(SWITCH_REG_TIMER, t | TIMER_TE); + + /* wait until the timer expires */ + do { + t = SWITCH_READ(SWITCH_REG_TIMER_INT); + } while ((t & TIMER_INT_TOS) == 0); + + /* leave the timer disabled */ + SWITCH_WRITE(SWITCH_REG_TIMER, TIMER_PERIOD_DEFAULT); + SWITCH_WRITE(SWITCH_REG_TIMER_INT, (TIMER_INT_TOS | TIMER_INT_TOM)); +} + +void __init adm5120_soc_init(void) +{ + u32 code; + u32 clks; + + code = SWITCH_READ(SWITCH_REG_CODE); + + adm5120_product_code = CODE_GET_PC(code); + adm5120_revision = CODE_GET_REV(code); + adm5120_package = (CODE_GET_PK(code) == CODE_PK_BGA) ? + ADM5120_PACKAGE_BGA : ADM5120_PACKAGE_PQFP; + adm5120_nand_boot = CODE_GET_NAB(code); + + clks = CODE_GET_CLKS(code); + adm5120_speed = ADM5120_SPEED_175; + if (clks & 1) + adm5120_speed += 25000000; + if (clks & 2) + adm5120_speed += 50000000; +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/board.c b/target/linux/adm5120/files/arch/mips/adm5120/board.c new file mode 100644 index 000000000..74bacaa2e --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/board.c @@ -0,0 +1,129 @@ +/* + * $Id$ + * + * ADM5120 generic board code + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/device.h> +#include <linux/platform_device.h> + +#include <asm/bootinfo.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_irq.h> +#include <asm/mach-adm5120/adm5120_board.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static LIST_HEAD(adm5120_boards); +static char adm5120_board_name[ADM5120_BOARD_NAMELEN]; + +const char *get_system_type(void) +{ + return adm5120_board_name; +} + +static struct adm5120_board * __init adm5120_board_find(unsigned long machtype) +{ + struct list_head *this; + struct adm5120_board *board; + void *ret; + + ret = NULL; + list_for_each(this, &adm5120_boards) { + board = list_entry(this, struct adm5120_board, list); + if (board->mach_type == machtype) { + ret = board; + break; + } + } + + return ret; +} + +static int __init adm5120_board_setup(void) +{ + struct adm5120_board *board; + int err; + + board = adm5120_board_find(mips_machtype); + if (board == NULL) { + printk(KERN_ALERT "adm5120: no board registered for machtype %lu" + ", trying generic\n", mips_machtype); + board = adm5120_board_find(MACH_ADM5120_GENERIC); + if (board == NULL) + panic("adm5120: unsupported board\n"); + } + + printk(KERN_INFO "adm5120: setting up board '%s'\n", board->name); + + memcpy(&adm5120_board_name, board->name, ADM5120_BOARD_NAMELEN); + + adm5120_board_reset = board->board_reset; + if (board->eth_num_ports > 0) + adm5120_eth_num_ports = board->eth_num_ports; + + if (board->eth_vlans) + memcpy(adm5120_eth_vlans, board->eth_vlans, + sizeof(adm5120_eth_vlans)); + + + if (board->board_setup) + board->board_setup(); + + /* register UARTs */ + amba_device_register(&adm5120_uart0_device, &iomem_resource); + amba_device_register(&adm5120_uart1_device, &iomem_resource); + + /* register PCI controller */ + if (adm5120_package_bga()) + platform_device_register(&adm5120_pci_device); + + /* register board devices */ + if (board->num_devices > 0 && board->devices != NULL ) { + err = platform_add_devices(board->devices, board->num_devices); + if (err) + printk(KERN_ALERT "adm5120: adding board devices failed\n"); + } + + return 0; +} + +void __init adm5120_board_register(struct adm5120_board *board) +{ + list_add(&board->list, &adm5120_boards); + printk(KERN_INFO "adm5120: registered board '%s'\n", board->name); +} + +void __init adm5120_register_boards(struct adm5120_board **boards, + int num) +{ + int i; + + for (i=0; i<num; i++) + adm5120_board_register(boards[i]); +} + +arch_initcall(adm5120_board_setup); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/Makefile b/target/linux/adm5120/files/arch/mips/adm5120/boards/Makefile new file mode 100644 index 000000000..a05c89156 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for platforms based on ADM5120 SoC +# + +obj-y += generic.o +obj-y += cellvision.o +obj-y += compex.o +obj-y += edimax.o +obj-y += infineon.o +obj-y += mikrotik.o +obj-y += zyxel.o diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/cellvision.c b/target/linux/adm5120/files/arch/mips/adm5120/boards/cellvision.c new file mode 100644 index 000000000..f7d44e257 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/cellvision.c @@ -0,0 +1,192 @@ +/* + * $Id$ + * + * Cellvision/SparkLAN boards + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_board.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static void switch_bank_gpio5(unsigned bank) +{ + switch (bank) { + case 0: + gpio_set_value(ADM5120_GPIO_PIN5, 0); + break; + case 1: + gpio_set_value(ADM5120_GPIO_PIN5, 1); + break; + } +} + +static struct mtd_partition cas6xx_partitions[] = { + { + .name = "admboot", + .offset = 0, + .size = 32*1024, + .mask_flags = MTD_WRITEABLE, + } , { + .name = "config", + .offset = MTDPART_OFS_APPEND, + .size = 32*1024, + } , { + .name = "nvfs1", + .offset = MTDPART_OFS_APPEND, + .size = 64*1024, + } , { + .name = "nvfs2", + .offset = MTDPART_OFS_APPEND, + .size = 64*1024, + } , { + .name = "firmware", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct mtd_partition cas7xx_partitions[] = { + { + .name = "admboot", + .offset = 0, + .size = 32*1024, + .mask_flags = MTD_WRITEABLE, + } , { + .name = "config", + .offset = MTDPART_OFS_APPEND, + .size = 32*1024, + } , { + .name = "nvfs", + .offset = MTDPART_OFS_APPEND, + .size = 128*1024, + } , { + .name = "firmware", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct platform_device *cas6xx_devices[] __initdata = { + &adm5120_flash0_device, +}; + +static struct platform_device *cas7xx_devices[] __initdata = { + &adm5120_flash0_device, +}; + +static void __init cas6xx_setup(void) +{ + gpio_request(ADM5120_GPIO_PIN5, NULL); /* for flash A20 line */ + gpio_direction_output(ADM5120_GPIO_PIN5, 0); + + /* setup data for flash0 device */ + adm5120_flash0_data.switch_bank = switch_bank_gpio5; + adm5120_flash0_data.nr_parts = ARRAY_SIZE(cas6xx_partitions); + adm5120_flash0_data.parts = cas6xx_partitions; + + /* TODO: setup mac address */ +} + +static void __init cas7xx_setup(void) +{ + gpio_request(ADM5120_GPIO_PIN5, NULL); /* for flash A20 line */ + gpio_direction_output(ADM5120_GPIO_PIN5, 0); + + /* setup data for flash0 device */ + adm5120_flash0_data.switch_bank = switch_bank_gpio5; + adm5120_flash0_data.nr_parts = ARRAY_SIZE(cas7xx_partitions); + adm5120_flash0_data.parts = cas7xx_partitions; + + /* TODO: setup mac address */ +} + +static struct adm5120_board cas630_board __initdata = { + .mach_type = MACH_ADM5120_CAS630, + .name = "Cellvision CAS-630/630W", + .board_setup = cas6xx_setup, + .eth_num_ports = 1, + .num_devices = ARRAY_SIZE(cas6xx_devices), + .devices = cas6xx_devices, +}; + +static struct adm5120_board cas670_board __initdata = { + .mach_type = MACH_ADM5120_CAS670, + .name = "Cellvision CAS-670/670W", + .board_setup = cas6xx_setup, + .eth_num_ports = 1, + .num_devices = ARRAY_SIZE(cas6xx_devices), + .devices = cas6xx_devices, +}; + +static struct adm5120_board cas700_board __initdata = { + .mach_type = MACH_ADM5120_CAS700, + .name = "Cellvision CAS-700/700W", + .board_setup = cas7xx_setup, + .eth_num_ports = 1, + .num_devices = ARRAY_SIZE(cas7xx_devices), + .devices = cas7xx_devices, +}; + +static struct adm5120_board cas771_board __initdata = { + .mach_type = MACH_ADM5120_CAS771, + .name = "Cellvision CAS-771/771W", + .board_setup = cas7xx_setup, + .eth_num_ports = 1, + .num_devices = ARRAY_SIZE(cas7xx_devices), + .devices = cas7xx_devices, +}; + +static struct adm5120_board cas790_board __initdata = { + .mach_type = MACH_ADM5120_CAS790, + .name = "Cellvision CAS-790", + .board_setup = cas7xx_setup, + .eth_num_ports = 1, + .num_devices = ARRAY_SIZE(cas7xx_devices), + .devices = cas7xx_devices, +}; + +static struct adm5120_board cas861_board __initdata = { + .mach_type = MACH_ADM5120_CAS861, + .name = "Cellvision CAS-861/861W", + .board_setup = cas7xx_setup, + .eth_num_ports = 1, + .num_devices = ARRAY_SIZE(cas7xx_devices), + .devices = cas7xx_devices, +}; + +static int __init register_boards(void) +{ + adm5120_board_register(&cas630_board); + adm5120_board_register(&cas670_board); + adm5120_board_register(&cas700_board); + adm5120_board_register(&cas771_board); + adm5120_board_register(&cas790_board); + adm5120_board_register(&cas861_board); + return 0; +} + +pure_initcall(register_boards); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/compex.c b/target/linux/adm5120/files/arch/mips/adm5120/boards/compex.c new file mode 100644 index 000000000..620179744 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/compex.c @@ -0,0 +1,222 @@ +/* + * $Id$ + * + * Compex boards + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_board.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static void switch_bank_gpio5(unsigned bank) +{ + switch (bank) { + case 0: + gpio_set_value(ADM5120_GPIO_PIN5, 0); + break; + case 1: + gpio_set_value(ADM5120_GPIO_PIN5, 1); + break; + } +} + +static void wp54_reset(void) +{ + gpio_set_value(ADM5120_GPIO_PIN3, 0); +} + +static struct mtd_partition wp54g_wrt_partitions[] = { + { + .name = "cfe", + .offset = 0, + .size = 0x050000, + .mask_flags = MTD_WRITEABLE, + } , { + .name = "trx", + .offset = MTDPART_OFS_APPEND, + .size = 0x3A0000, + } , { + .name = "nvram", + .offset = MTDPART_OFS_APPEND, + .size = 0x010000, + } +}; + +static struct platform_device *np2xg_devices[] __initdata = { + &adm5120_flash0_device, + &adm5120_usbc_device, +}; + +static struct platform_device *wp54_devices[] __initdata = { + &adm5120_flash0_device, +}; + +static void __init np2xg_setup(void) +{ + gpio_request(ADM5120_GPIO_PIN5, NULL); /* for flash A20 line */ + gpio_direction_output(ADM5120_GPIO_PIN5, 0); + + /* setup data for flash0 device */ + adm5120_flash0_data.switch_bank = switch_bank_gpio5; + + /* TODO: setup mac address */ +} + +static void __init wp54_setup(void) +{ + gpio_request(ADM5120_GPIO_PIN5, NULL); /* for flash A20 line */ + gpio_direction_output(ADM5120_GPIO_PIN5, 0); + + gpio_request(ADM5120_GPIO_PIN3, NULL); /* for system reset */ + gpio_direction_output(ADM5120_GPIO_PIN3, 1); + + + /* setup data for flash0 device */ + adm5120_flash0_data.switch_bank = switch_bank_gpio5; + + /* TODO: setup mac address */ +} + +static void __init wp54_wrt_setup(void) +{ + gpio_request(ADM5120_GPIO_PIN5, NULL); /* for flash A20 line */ + gpio_direction_output(ADM5120_GPIO_PIN5, 0); + + gpio_request(ADM5120_GPIO_PIN3, NULL); /* for system reset */ + gpio_direction_output(ADM5120_GPIO_PIN3, 1); + + /* setup data for flash0 device */ + adm5120_flash0_data.switch_bank = switch_bank_gpio5; + adm5120_flash0_data.nr_parts = ARRAY_SIZE(wp54g_wrt_partitions); + adm5120_flash0_data.parts = wp54g_wrt_partitions; + + /* TODO: setup mac address */ +} + +unsigned char np27g_vlans[6] __initdata = { + /* FIXME: untested */ + 0x41, 0x42, 0x44, 0x48, 0x50, 0x00 +}; + +unsigned char np28g_vlans[6] __initdata = { + /* FIXME: untested */ + 0x41, 0x42, 0x44, 0x48, 0x00, 0x00 +}; + +unsigned char wp54_vlans[6] __initdata = { + /* FIXME: untested */ + 0x41, 0x42, 0x00, 0x00, 0x00, 0x00 +}; + +static struct adm5120_board np27g_board __initdata = { + .mach_type = MACH_ADM5120_NP27G, + .name = "Compex NetPassage 27G", + .board_setup = np2xg_setup, + .eth_num_ports = 5, + .eth_vlans = np27g_vlans, + .num_devices = ARRAY_SIZE(np2xg_devices), + .devices = np2xg_devices, +}; + +static struct adm5120_board np28g_board __initdata = { + .mach_type = MACH_ADM5120_NP28G, + .name = "Compex NetPassage 28G", + .board_setup = np2xg_setup, + .eth_num_ports = 4, + .eth_vlans = np28g_vlans, + .num_devices = ARRAY_SIZE(np2xg_devices), + .devices = np2xg_devices, +}; + +static struct adm5120_board wp54ag_board __initdata = { + .mach_type = MACH_ADM5120_WP54AG, + .name = "Compex WP54AG", + .board_setup = wp54_setup, + .board_reset = wp54_reset, + .eth_num_ports = 2, + .eth_vlans = wp54_vlans, + .num_devices = ARRAY_SIZE(wp54_devices), + .devices = wp54_devices, +}; + +static struct adm5120_board wp54g_board __initdata = { + .mach_type = MACH_ADM5120_WP54G, + .name = "Compex WP54G", + .board_setup = wp54_setup, + .board_reset = wp54_reset, + .eth_num_ports = 2, + .eth_vlans = wp54_vlans, + .num_devices = ARRAY_SIZE(wp54_devices), + .devices = wp54_devices, +}; + +static struct adm5120_board wp54g_wrt_board __initdata = { + .mach_type = MACH_ADM5120_WP54G, + .name = "Compex WP54G-WRT", + .board_setup = wp54_wrt_setup, + .board_reset = wp54_reset, + .eth_num_ports = 2, + .eth_vlans = wp54_vlans, + .num_devices = ARRAY_SIZE(wp54_devices), + .devices = wp54_devices, +}; + +static struct adm5120_board wpp54ag_board __initdata = { + .mach_type = MACH_ADM5120_WPP54AG, + .name = "Compex WPP54AG", + .board_setup = wp54_setup, + .board_reset = wp54_reset, + .eth_num_ports = 2, + .eth_vlans = wp54_vlans, + .num_devices = ARRAY_SIZE(wp54_devices), + .devices = wp54_devices, +}; + +static struct adm5120_board wpp54g_board __initdata = { + .mach_type = MACH_ADM5120_WPP54G, + .name = "Compex WPP54G", + .board_setup = wp54_setup, + .board_reset = wp54_reset, + .eth_num_ports = 2, + .eth_vlans = wp54_vlans, + .num_devices = ARRAY_SIZE(wp54_devices), + .devices = wp54_devices, +}; + +static int __init register_boards(void) +{ + adm5120_board_register(&np27g_board); + adm5120_board_register(&np28g_board); + adm5120_board_register(&wp54ag_board); + adm5120_board_register(&wp54g_board); + adm5120_board_register(&wp54g_wrt_board); + adm5120_board_register(&wpp54ag_board); + adm5120_board_register(&wpp54g_board); + return 0; +} + +pure_initcall(register_boards); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/edimax.c b/target/linux/adm5120/files/arch/mips/adm5120/boards/edimax.c new file mode 100644 index 000000000..a184cbce4 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/edimax.c @@ -0,0 +1,84 @@ +/* + * $Id$ + * + * Edimax boards + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_board.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static struct mtd_partition br6104k_partitions[] = { + { + .name = "admboot", + .offset = 0, + .size = 32*1024, + .mask_flags = MTD_WRITEABLE, + } , { + .name = "config", + .offset = MTDPART_OFS_APPEND, + .size = 32*1024, + } , { + .name = "firmware", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct platform_device *br6104k_devices[] __initdata = { + &adm5120_flash0_device, +}; + +static void __init br6104k_setup(void) { + /* setup data for flash0 device */ + adm5120_flash0_data.nr_parts = ARRAY_SIZE(br6104k_partitions); + adm5120_flash0_data.parts = br6104k_partitions; + + /* TODO: setup mac addresses, if possible */ +} + +unsigned char br6104k_vlans[6] = { + 0x41, 0x42, 0x44, 0x48, 0x50, 0x00 +}; + +static struct adm5120_board br6104k_board __initdata = { + .mach_type = MACH_ADM5120_BR6104K, + .name = "Edimax BR-6104K", + .board_setup = br6104k_setup, + .eth_num_ports = 5, + .eth_vlans = br6104k_vlans, + .num_devices = ARRAY_SIZE(br6104k_devices), + .devices = br6104k_devices, +}; + +static int __init register_boards(void) +{ + adm5120_board_register(&br6104k_board); + return 0; +} + +pure_initcall(register_boards); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/generic.c b/target/linux/adm5120/files/arch/mips/adm5120/boards/generic.c new file mode 100644 index 000000000..de78ca40e --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/generic.c @@ -0,0 +1,53 @@ +/* + * $Id$ + * + * Generic ADM5120 based board + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_board.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static struct platform_device *generic_devices[] __initdata = { + &adm5120_flash0_device, +}; + +static struct adm5120_board generic_board __initdata = { + .mach_type = MACH_ADM5120_GENERIC, + .name = "Generic ADM5120 board", + .eth_num_ports = 6, + .num_devices = ARRAY_SIZE(generic_devices), + .devices = generic_devices, +}; + +static int __init register_boards(void) +{ + adm5120_board_register(&generic_board); + return 0; +} + +pure_initcall(register_boards); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/infineon.c b/target/linux/adm5120/files/arch/mips/adm5120/boards/infineon.c new file mode 100644 index 000000000..ebeb5aa87 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/infineon.c @@ -0,0 +1,167 @@ +/* + * $Id$ + * + * Infineon boards + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_board.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static void switch_bank_gpio3(unsigned bank) +{ + switch (bank) { + case 0: + gpio_set_value(ADM5120_GPIO_PIN3, 0); + break; + case 1: + gpio_set_value(ADM5120_GPIO_PIN3, 1); + break; + } +} + +static void switch_bank_gpio5(unsigned bank) +{ + switch (bank) { + case 0: + gpio_set_value(ADM5120_GPIO_PIN5, 0); + break; + case 1: + gpio_set_value(ADM5120_GPIO_PIN5, 1); + break; + } +} + +static struct mtd_partition easy_partitions[] = { + { + .name = "admboot", + .offset = 0, + .size = 64*1024, + .mask_flags = MTD_WRITEABLE, + } , { + .name = "boardcfg", + .offset = MTDPART_OFS_APPEND, + .size = 64*1024, + } , { + .name = "firmware", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct platform_device *easy5120pata_devices[] __initdata = { + &adm5120_flash0_device, + /* TODO: add VINETIC2 device? */ +}; + +static struct platform_device *easy5120rt_devices[] __initdata = { + &adm5120_flash0_device, + &adm5120_usbc_device +}; + +static struct platform_device *easy5120wvoip_devices[] __initdata = { + &adm5120_flash0_device, + /* TODO: add VINETIC2 device? */ +}; + +static struct platform_device *easy83000_devices[] __initdata = { + &adm5120_flash0_device, + /* TODO: add VINAX device? */ +}; + +static void __init easy_setup_pqfp(void) +{ + gpio_request(ADM5120_GPIO_PIN3, NULL); /* for flash A20 line */ + gpio_direction_output(ADM5120_GPIO_PIN3, 0); + + /* setup data for flash0 device */ + adm5120_flash0_data.switch_bank = switch_bank_gpio3; + adm5120_flash0_data.nr_parts = ARRAY_SIZE(easy_partitions); + adm5120_flash0_data.parts = easy_partitions; + + /* TODO: setup mac addresses */ +} + +static void __init easy_setup_bga(void) +{ + gpio_request(ADM5120_GPIO_PIN5, NULL); /* for flash A20 line */ + gpio_direction_output(ADM5120_GPIO_PIN5, 0); + + /* setup data for flash0 device */ + adm5120_flash0_data.switch_bank = switch_bank_gpio5; + adm5120_flash0_data.nr_parts = ARRAY_SIZE(easy_partitions); + adm5120_flash0_data.parts = easy_partitions; + + /* TODO: setup mac addresses */ +} + +static struct adm5120_board easy5120pata_board __initdata = { + .mach_type = MACH_ADM5120_EASY5120PATA, + .name = "Infineon EASY 5120P-ATA Reference Board", + .board_setup = easy_setup_pqfp, + .eth_num_ports = 6, + .num_devices = ARRAY_SIZE(easy5120pata_devices), + .devices = easy5120pata_devices, +}; + +static struct adm5120_board easy5120rt_board __initdata = { + .mach_type = MACH_ADM5120_EASY5120RT, + .name = "Infineon EASY 5120-RT Reference Board", + .board_setup = easy_setup_bga, + .eth_num_ports = 5, + .num_devices = ARRAY_SIZE(easy5120rt_devices), + .devices = easy5120rt_devices, +}; + +static struct adm5120_board easy5120wvoip_board __initdata = { + .mach_type = MACH_ADM5120_EASY5120WVOIP, + .name = "Infineon EASY 5120-WVoIP Reference Board", + .board_setup = easy_setup_bga, + .eth_num_ports = 6, + .num_devices = ARRAY_SIZE(easy5120wvoip_devices), + .devices = easy5120wvoip_devices, +}; + +static struct adm5120_board easy83000_board __initdata = { + .mach_type = MACH_ADM5120_EASY83000, + .name = "Infineon EASY 83000 Reference Board", + .board_setup = easy_setup_pqfp, + .eth_num_ports = 6, + .num_devices = ARRAY_SIZE(easy83000_devices), + .devices = easy83000_devices, +}; + +static int __init register_boards(void) +{ + adm5120_board_register(&easy5120pata_board); + adm5120_board_register(&easy5120rt_board); + adm5120_board_register(&easy5120wvoip_board); + adm5120_board_register(&easy83000_board); + return 0; +} + +pure_initcall(register_boards); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/mikrotik.c b/target/linux/adm5120/files/arch/mips/adm5120/boards/mikrotik.c new file mode 100644 index 000000000..f233772d0 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/mikrotik.c @@ -0,0 +1,172 @@ +/* + * $Id$ + * + * Mikrotik RouterBOARDs 111/112/133/133C/150/153 + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_board.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static struct mtd_partition rb1xx_partitions[] = { + { + .name = "booter", + .offset = 0, + .size = 64*1024, + .mask_flags = MTD_WRITEABLE, + } , { + .name = "firmware", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct platform_device *rb1xx_devices[] __initdata = { + &adm5120_flash0_device, + &adm5120_nand_device, +}; + +static struct platform_device *rb150_devices[] __initdata = { + &adm5120_flash0_device, + /* TODO: nand device is not yet supported */ +}; + +static void __init rb1xx_setup(void) +{ + /* setup data for flash0 device */ + adm5120_flash0_data.nr_parts = ARRAY_SIZE(rb1xx_partitions); + adm5120_flash0_data.parts = rb1xx_partitions; + + /* TODO: setup mac address */ +} + +#if 0 +/* + * RB1xx boards have bad network performance with the default VLAN matrixes. + * Disable it while the ethernet driver gets fixed. + */ +static unsigned char rb11x_vlans[6] __initdata = { + /* FIXME: untested */ + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char rb133_vlans[6] __initdata = { + /* FIXME: untested */ + 0x44, 0x42, 0x41, 0x00, 0x00, 0x00 +}; + +static unsigned char rb133c_vlans[6] __initdata = { + /* FIXME: untested */ + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char rb15x_vlans[6] __initdata = { + /* FIXME: untested */ + 0x41, 0x42, 0x44, 0x48, 0x50, 0x00 +}; +#else +static unsigned char rb_vlans[6] __initdata = { + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#define rb11x_vlans rb_vlans +#define rb133_vlans rb_vlans +#define rb133c_vlans rb_vlans +#define rb15x_vlans rb_vlans +#endif + +static struct adm5120_board rb111_board __initdata = { + .mach_type = MACH_ADM5120_RB_111, + .name = "Mikrotik RouterBOARD 111", + .board_setup = rb1xx_setup, + .eth_num_ports = 1, + .eth_vlans = rb11x_vlans, + .num_devices = ARRAY_SIZE(rb1xx_devices), + .devices = rb1xx_devices, +}; + +static struct adm5120_board rb112_board __initdata = { + .mach_type = MACH_ADM5120_RB_112, + .name = "Mikrotik RouterBOARD 112", + .board_setup = rb1xx_setup, + .eth_num_ports = 1, + .eth_vlans = rb11x_vlans, + .num_devices = ARRAY_SIZE(rb1xx_devices), + .devices = rb1xx_devices, +}; + +static struct adm5120_board rb133_board __initdata = { + .mach_type = MACH_ADM5120_RB_133, + .name = "Mikrotik RouterBOARD 133", + .board_setup = rb1xx_setup, + .eth_num_ports = 3, + .eth_vlans = rb133_vlans, + .num_devices = ARRAY_SIZE(rb1xx_devices), + .devices = rb1xx_devices, +}; + +static struct adm5120_board rb133c_board __initdata = { + .mach_type = MACH_ADM5120_RB_133C, + .name = "Mikrotik RouterBOARD 133C", + .board_setup = rb1xx_setup, + .eth_num_ports = 1, + .eth_vlans = rb133c_vlans, + .num_devices = ARRAY_SIZE(rb1xx_devices), + .devices = rb1xx_devices, +}; + +static struct adm5120_board rb150_board __initdata = { + .mach_type = MACH_ADM5120_RB_150, + .name = "Mikrotik RouterBOARD 150", + .board_setup = rb1xx_setup, + .eth_num_ports = 5, + .eth_vlans = rb15x_vlans, + .num_devices = ARRAY_SIZE(rb150_devices), + .devices = rb150_devices, +}; + +static struct adm5120_board rb153_board __initdata = { + .mach_type = MACH_ADM5120_RB_153, + .name = "Mikrotik RouterBOARD 153", + .board_setup = rb1xx_setup, + .eth_num_ports = 5, + .eth_vlans = rb15x_vlans, + .num_devices = ARRAY_SIZE(rb1xx_devices), + .devices = rb1xx_devices, +}; + +static int __init register_boards(void) +{ + adm5120_board_register(&rb111_board); + adm5120_board_register(&rb112_board); + adm5120_board_register(&rb133_board); + adm5120_board_register(&rb133c_board); + adm5120_board_register(&rb150_board); + adm5120_board_register(&rb153_board); + return 0; +} + +pure_initcall(register_boards); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/boards/zyxel.c b/target/linux/adm5120/files/arch/mips/adm5120/boards/zyxel.c new file mode 100644 index 000000000..0a60205eb --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/boards/zyxel.c @@ -0,0 +1,127 @@ +/* + * $Id$ + * + * ZyXEL Prestige P-334/P-335 boards + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_board.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static void switch_bank_gpio5(unsigned bank) +{ + switch (bank) { + case 0: + gpio_set_value(ADM5120_GPIO_PIN5, 0); + break; + case 1: + gpio_set_value(ADM5120_GPIO_PIN5, 1); + break; + } +} + +static struct mtd_partition p33x_partitions[] = { + { + .name = "bootbase", + .offset = 0, + .size = 16*1024, + .mask_flags = MTD_WRITEABLE, + } , { + .name = "rom", + .offset = MTDPART_OFS_APPEND, + .size = 16*1024, + } , { + .name = "bootext", + .offset = MTDPART_OFS_APPEND, + .size = 96*1024, + .mask_flags = MTD_WRITEABLE, + } , { + .name = "trx", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } , { + .name = "firmware", + .offset = 32*1024, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct platform_device *p334_devices[] __initdata = { + &adm5120_flash0_device, +}; + +static struct platform_device *p335_devices[] __initdata = { + &adm5120_flash0_device, + &adm5120_usbc_device, +}; + +static void __init p33x_setup(void) +{ + gpio_request(ADM5120_GPIO_PIN5, NULL); /* for flash A20 line */ + gpio_direction_output(ADM5120_GPIO_PIN5, 0); + + /* setup data for flash0 device */ + adm5120_flash0_data.switch_bank = switch_bank_gpio5; + adm5120_flash0_data.nr_parts = ARRAY_SIZE(p33x_partitions); + adm5120_flash0_data.parts = p33x_partitions; + + /* TODO: setup mac address */ +} + +unsigned char p33x_vlans[6] __initdata = { + /* FIXME: untested */ + 0x50, 0x48, 0x44, 0x42, 0x41, 0x00 +}; + +static struct adm5120_board p334wt_board __initdata = { + .mach_type = MACH_ADM5120_P334WT, + .name = "ZyXEL Prestige 334WT", + .board_setup = p33x_setup, + .eth_num_ports = 5, + .eth_vlans = p33x_vlans, + .num_devices = ARRAY_SIZE(p334_devices), + .devices = p334_devices, +}; + +static struct adm5120_board p335_board __initdata = { + .mach_type = MACH_ADM5120_P335, + .name = "ZyXEL Prestige 335/335WT", + .board_setup = p33x_setup, + .eth_num_ports = 5, + .eth_vlans = p33x_vlans, + .num_devices = ARRAY_SIZE(p335_devices), + .devices = p335_devices, +}; + +static int __init register_boards(void) +{ + adm5120_board_register(&p334wt_board); + adm5120_board_register(&p335_board); + return 0; +} + +pure_initcall(register_boards); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/clock.c b/target/linux/adm5120/files/arch/mips/adm5120/clock.c new file mode 100644 index 000000000..4a8b69b4c --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/clock.c @@ -0,0 +1,76 @@ +/* + * $Id$ + * + * ADM5120 minimal CLK API implementation + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * + * This file was based on the CLK API implementation in: + * arch/mips/tx4938/toshiba_rbtx4938/setup.c + * Copyright (C) 2000-2001 Toshiba Corporation + * 2003-2005 (c) MontaVista Software, Inc. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/clk.h> + +#include <asm/mach-adm5120/adm5120_defs.h> + +struct clk { + unsigned long rate; +}; + +static struct clk uart_clk = { + .rate = ADM5120_UART_CLOCK +}; + +struct clk *clk_get(struct device *dev, const char *id) +{ + if (!strcmp(id, "UARTCLK")) + return &uart_clk; + + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(clk_get); + +int clk_enable(struct clk *clk) +{ + return 0; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} +EXPORT_SYMBOL(clk_get_rate); + +void clk_put(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_put); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/gpio.c b/target/linux/adm5120/files/arch/mips/adm5120/gpio.c new file mode 100644 index 000000000..d073b659a --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/gpio.c @@ -0,0 +1,356 @@ +/* + * $Id$ + * + * ADM5120 GPIO support + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <asm/addrspace.h> +#include <asm/io.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_switch.h> + +typedef void __iomem * gpio_reg_t; +#define GPIO_READ(r) readl((r)) +#define GPIO_WRITE(v,r) writel((v),(r)) +#define GPIO_REG(r) (gpio_reg_t)(KSEG1ADDR(ADM5120_SWITCH_BASE)+r) + +struct adm5120_gpio_line { + u32 flags; + const char *label; +}; + +#define GPIO_FLAG_VALID 0x01 +#define GPIO_FLAG_USED 0x02 + +struct led_desc { + gpio_reg_t reg; /* LED register address */ + u8 iv_shift; /* shift amount for input bit */ + u8 mode_shift; /* shift amount for mode bits */ +}; + +#define LED_DESC(_port,_led) { \ + .reg = GPIO_REG(SWITCH_REG_PORT0_LED+_port*4), \ + .iv_shift = LED0_IV_SHIFT+_led, \ + .mode_shift = _led*4 \ + } + +static struct led_desc led_table[15] = { + LED_DESC(0, 0), LED_DESC(0, 1), LED_DESC(0, 2), + LED_DESC(1, 0), LED_DESC(1, 1), LED_DESC(1, 2), + LED_DESC(2, 0), LED_DESC(2, 1), LED_DESC(2, 2), + LED_DESC(3, 0), LED_DESC(3, 1), LED_DESC(3, 2), + LED_DESC(4, 0), LED_DESC(4, 1), LED_DESC(4, 2) +}; + +static struct adm5120_gpio_line adm5120_gpio_map[ADM5120_GPIO_COUNT] = { + [ADM5120_GPIO_PIN0] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_PIN1] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_PIN2] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_PIN3] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_PIN4] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_PIN5] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_PIN6] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_PIN7] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P0L0] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P0L1] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P0L2] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P1L0] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P1L1] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P1L2] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P2L0] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P2L1] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P2L2] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P3L0] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P3L1] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P3L2] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P4L0] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P4L1] = {.flags = GPIO_FLAG_VALID}, + [ADM5120_GPIO_P4L2] = {.flags = GPIO_FLAG_VALID} +}; + +#define gpio_is_invalid(g) ( \ + (g) > ADM5120_GPIO_MAX || \ + ((adm5120_gpio_map[(g)].flags & GPIO_FLAG_VALID) == 0) \ + ) + +#define gpio_is_used(g) ((adm5120_gpio_map[(g)].flags & GPIO_FLAG_USED) != 0) + +/* + * Helpers for GPIO lines in GPIO_CONF0 register + */ +#define PIN_IM(p) ((1 << GPIO_CONF0_IM_SHIFT) << p) +#define PIN_IV(p) ((1 << GPIO_CONF0_IV_SHIFT) << p) +#define PIN_OE(p) ((1 << GPIO_CONF0_OE_SHIFT) << p) +#define PIN_OV(p) ((1 << GPIO_CONF0_OV_SHIFT) << p) + +static inline int pins_direction_input(unsigned pin) +{ + gpio_reg_t *reg; + u32 t; + + reg = GPIO_REG(SWITCH_REG_GPIO_CONF0); + + t = GPIO_READ(reg); + t &= ~(PIN_OE(pin)); + t |= PIN_IM(pin); + GPIO_WRITE(t,reg); + + return 0; +} + +static inline int pins_direction_output(unsigned pin, int value) +{ + gpio_reg_t *reg; + u32 t; + + reg = GPIO_REG(SWITCH_REG_GPIO_CONF0); + + t = GPIO_READ(reg); + t &= ~(PIN_IM(pin) | PIN_OV(pin)); + t |= PIN_OE(pin); + + if (value) + t |= PIN_OV(pin); + + GPIO_WRITE(t,reg); + + return 0; +} + +static inline int pins_get_value(unsigned pin) +{ + gpio_reg_t *reg; + u32 t; + + reg = GPIO_REG(SWITCH_REG_GPIO_CONF0); + + t = GPIO_READ(reg); + if ((t & PIN_IM(pin)) != 0) + t &= PIN_IV(pin); + else + t &= PIN_OV(pin); + + return (t) ? 1 : 0; +} + +static inline void pins_set_value(unsigned pin, int value) +{ + gpio_reg_t *reg; + u32 t; + + reg = GPIO_REG(SWITCH_REG_GPIO_CONF0); + + t = GPIO_READ(reg); + if (value == 0) + t &= ~(PIN_OV(pin)); + else + t |= PIN_OV(pin); + + GPIO_WRITE(t,reg); +} + +/* + * Helpers for GPIO lines in PORTx_LED registers + */ +static inline int leds_direction_input(unsigned led) +{ + gpio_reg_t *reg; + u32 t; + + reg = led_table[led].reg; + t = GPIO_READ(reg); + t &= ~(LED_MODE_MASK << led_table[led].mode_shift); + GPIO_WRITE(t,reg); + + return 0; +} + +static inline int leds_direction_output(unsigned led, int value) +{ + gpio_reg_t *reg; + u32 t, s; + + reg = led_table[led].reg; + s = led_table[led].mode_shift; + + t = GPIO_READ(reg); + t &= ~(LED_MODE_MASK << s); + + switch (value) { + case ADM5120_GPIO_LOW: + t |= (LED_MODE_OUT_LOW << s); + break; + case ADM5120_GPIO_FLASH: + case ADM5120_GPIO_LINK: + case ADM5120_GPIO_SPEED: + case ADM5120_GPIO_DUPLEX: + case ADM5120_GPIO_ACT: + case ADM5120_GPIO_COLL: + case ADM5120_GPIO_LINK_ACT: + case ADM5120_GPIO_DUPLEX_COLL: + case ADM5120_GPIO_10M_ACT: + case ADM5120_GPIO_100M_ACT: + t |= ((value & LED_MODE_MASK) << s); + break; + default: + t |= (LED_MODE_OUT_HIGH << s); + break; + } + + GPIO_WRITE(t,reg); + + return 0; +} + +static inline int leds_get_value(unsigned led) +{ + gpio_reg_t *reg; + u32 t, m; + + reg = led_table[led].reg; + + t = GPIO_READ(reg); + m = (t >> led_table[led].mode_shift) & LED_MODE_MASK; + if (m == LED_MODE_INPUT) + return (t >> led_table[led].iv_shift) & 1; + + if (m == LED_MODE_OUT_LOW) + return 0; + + return 1; +} + +/* + * Main GPIO support routines + */ +int adm5120_gpio_direction_input(unsigned gpio) +{ + if (gpio_is_invalid(gpio)) + return -EINVAL; + + if (gpio < ADM5120_GPIO_P0L0) + return pins_direction_input(gpio); + + gpio -= ADM5120_GPIO_P0L0; + return leds_direction_input(gpio); +} + +int adm5120_gpio_direction_output(unsigned gpio, int value) +{ + if (gpio_is_invalid(gpio)) + return -EINVAL; + + if (gpio < ADM5120_GPIO_P0L0) + return pins_direction_output(gpio, value); + + gpio -= ADM5120_GPIO_P0L0; + return leds_direction_output(gpio, value); +} + +int adm5120_gpio_get_value(unsigned gpio) +{ + if (gpio < ADM5120_GPIO_P0L0) + return pins_get_value(gpio); + + gpio -= ADM5120_GPIO_P0L0; + return leds_get_value(gpio); +} + +void adm5120_gpio_set_value(unsigned gpio, int value) +{ + if (gpio < ADM5120_GPIO_P0L0) { + pins_set_value(gpio, value); + return; + } + + gpio -= ADM5120_GPIO_P0L0; + leds_direction_output(gpio, value); +} + +int adm5120_gpio_request(unsigned gpio, const char *label) +{ + if (gpio_is_invalid(gpio)) + return -EINVAL; + + if (gpio_is_used(gpio)) + return -EBUSY; + + adm5120_gpio_map[gpio].flags |= GPIO_FLAG_USED; + adm5120_gpio_map[gpio].label = label; + + return 0; +} + +void adm5120_gpio_free(unsigned gpio) +{ + if (gpio_is_invalid(gpio)) + return; + + adm5120_gpio_map[gpio].flags &= ~GPIO_FLAG_USED; + adm5120_gpio_map[gpio].label = NULL; +} + +int adm5120_gpio_to_irq(unsigned gpio) +{ + /* FIXME: not yet implemented */ + return -EINVAL; +} + +int adm5120_irq_to_gpio(unsigned irq) +{ + /* FIXME: not yet implemented */ + return -EINVAL; +} + +static int __init adm5120_gpio_init(void) +{ + int i; + + if (adm5120_package_pqfp()) { + /* GPIO pins 4-7 are unavailable in ADM5120P */ + for (i=ADM5120_GPIO_PIN4; i<=ADM5120_GPIO_PIN7; i++) + adm5120_gpio_map[i].flags &= ~GPIO_FLAG_VALID; + } + + return 0; +} + +pure_initcall(adm5120_gpio_init); + +EXPORT_SYMBOL(adm5120_gpio_direction_output); +EXPORT_SYMBOL(adm5120_gpio_direction_input); +EXPORT_SYMBOL(adm5120_gpio_get_value); +EXPORT_SYMBOL(adm5120_gpio_set_value); +EXPORT_SYMBOL(adm5120_gpio_request); +EXPORT_SYMBOL(adm5120_gpio_free); +EXPORT_SYMBOL(adm5120_gpio_to_irq); +EXPORT_SYMBOL(adm5120_irq_to_gpio); diff --git a/target/linux/adm5120/files/arch/mips/adm5120/irq.c b/target/linux/adm5120/files/arch/mips/adm5120/irq.c new file mode 100644 index 000000000..8d6fbe6a3 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/irq.c @@ -0,0 +1,203 @@ +/* + * $Id$ + * + * ADM5120 specific interrupt handlers + * + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> + +#include <asm/irq.h> +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/bitops.h> + +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_irq.h> + +#define INTC_REG(r) (*(volatile u32 *)(KSEG1ADDR(ADM5120_INTC_BASE) + r)) + +static void adm5120_intc_irq_unmask(unsigned int irq); +static void adm5120_intc_irq_mask(unsigned int irq); +static int adm5120_intc_irq_set_type(unsigned int irq, unsigned int flow_type); + +static struct irq_chip adm5120_intc_irq_chip = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + .name = "INTC", +#else + .typename = "INTC", +#endif + .unmask = adm5120_intc_irq_unmask, + .mask = adm5120_intc_irq_mask, + .mask_ack = adm5120_intc_irq_mask, + .set_type = adm5120_intc_irq_set_type +}; + +static struct irqaction adm5120_intc_irq_action = { + .handler = no_action, + .name = "cascade [INTC]" +}; + +static void adm5120_intc_irq_unmask(unsigned int irq) +{ + unsigned long flags; + + irq -= ADM5120_INTC_IRQ_BASE; + local_irq_save(flags); + INTC_REG(INTC_REG_IRQ_ENABLE) = (1 << irq); + local_irq_restore(flags); +} + +static void adm5120_intc_irq_mask(unsigned int irq) +{ + unsigned long flags; + + irq -= ADM5120_INTC_IRQ_BASE; + local_irq_save(flags); + INTC_REG(INTC_REG_IRQ_DISABLE) = (1 << irq); + local_irq_restore(flags); +} + +static int adm5120_intc_irq_set_type(unsigned int irq, unsigned int flow_type) +{ + /* TODO: not yet tested */ +#if 1 + unsigned int sense; + unsigned long mode; + int err; + + err = 0; + sense = flow_type & (IRQ_TYPE_SENSE_MASK); + switch (sense) { + case IRQ_TYPE_NONE: + case IRQ_TYPE_LEVEL_HIGH: + break; + case IRQ_TYPE_LEVEL_LOW: + switch (irq) { + case ADM5120_IRQ_GPIO2: + case ADM5120_IRQ_GPIO4: + break; + default: + err = -EINVAL; + break; + } + break; + default: + err = -EINVAL; + break; + } + + if (err) + return err; + + switch (irq) { + case ADM5120_IRQ_GPIO2: + case ADM5120_IRQ_GPIO4: + mode = INTC_REG(INTC_REG_INT_MODE); + if (sense == IRQ_TYPE_LEVEL_LOW) + mode |= (1 << (irq-ADM5120_INTC_IRQ_BASE)); + else + mode &= (1 << (irq-ADM5120_INTC_IRQ_BASE)); + + INTC_REG(INTC_REG_INT_MODE) = mode; + /* fallthrogh */ + default: + irq_desc[irq].status &= ~IRQ_TYPE_SENSE_MASK; + irq_desc[irq].status |= sense; + break; + } +#endif + return 0; +} + +static void adm5120_intc_irq_dispatch(void) +{ + unsigned long status; + int irq; + +#if 1 + /* dispatch only one IRQ at a time */ + status = INTC_REG(INTC_REG_IRQ_STATUS) & INTC_INT_ALL; + + if (status) { + irq = ADM5120_INTC_IRQ_BASE+fls(status)-1; + do_IRQ(irq); + } else + spurious_interrupt(); +#else + status = INTC_REG(INTC_REG_IRQ_STATUS) & INTC_INT_ALL; + if (status) { + for (irq=ADM5120_INTC_IRQ_BASE; irq <= ADM5120_INTC_IRQ_BASE + + INTC_IRQ_LAST; irq++, status >>=1) { + if ((status & 1) == 1) + do_IRQ(irq); + } + } else + spurious_interrupt(); +#endif +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned long pending; + + pending = read_c0_status() & read_c0_cause(); + + if (pending & STATUSF_IP7) + do_IRQ(ADM5120_IRQ_COUNTER); + else if (pending & STATUSF_IP2) + adm5120_intc_irq_dispatch(); + else + spurious_interrupt(); +} + +#define INTC_IRQ_STATUS (IRQ_LEVEL | IRQ_TYPE_LEVEL_HIGH | IRQ_DISABLED) +static void __init adm5120_intc_irq_init(int base) +{ + int i; + + /* disable all interrupts */ + INTC_REG(INTC_REG_IRQ_DISABLE) = INTC_INT_ALL; + /* setup all interrupts to generate IRQ instead of FIQ */ + INTC_REG(INTC_REG_INT_MODE) = 0; + /* set active level for all external interrupts to HIGH */ + INTC_REG(INTC_REG_INT_LEVEL) = 0; + /* disable usage of the TEST_SOURCE register */ + INTC_REG(INTC_REG_IRQ_SOURCE_SELECT) = 0; + + for(i=ADM5120_INTC_IRQ_BASE; i <= ADM5120_INTC_IRQ_BASE+INTC_IRQ_LAST; + i++) { + irq_desc[i].status = INTC_IRQ_STATUS; + set_irq_chip_and_handler(i, &adm5120_intc_irq_chip, + handle_level_irq); + } + + setup_irq(ADM5120_IRQ_INTC, &adm5120_intc_irq_action); +} + +void __init arch_init_irq(void) { + mips_cpu_irq_init(); + adm5120_intc_irq_init(ADM5120_INTC_IRQ_BASE); +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/memory.c b/target/linux/adm5120/files/arch/mips/adm5120/memory.c new file mode 100644 index 000000000..1bde6b02d --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/memory.c @@ -0,0 +1,179 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/kernel.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_switch.h> +#include <asm/mach-adm5120/adm5120_mpmc.h> + +#define SWITCH_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r)) +#define SWITCH_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))=(v) + +#define MPMC_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r)) +#define MPMC_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))=(v) + +#if 1 +# define mem_dbg(f, a...) printk("mem_detect: " f, ## a) +#else +# define mem_dbg(f, a...) +#endif + +#define MEM_WR_DELAY 10000 /* 0.01 usec */ + +unsigned long adm5120_memsize; + +static int __init mem_check_pattern(u8 *addr, unsigned long offs) +{ + volatile u32 *p1 = (volatile u32 *)addr; + volatile u32 *p2 = (volatile u32 *)(addr+offs); + u32 t,u,v; + + /* save original value */ + t = *p1; + u = *p2; + + if (t != u) + return 0; + + v = 0x55555555; + if (u == v) + v = 0xAAAAAAAA; + + mem_dbg("write 0x%08X to 0x%08lX\n", v, (unsigned long)p1); + + *p1 = v; + mem_dbg("delay %d ns\n", MEM_WR_DELAY); + adm5120_ndelay(MEM_WR_DELAY); + u = *p2; + + mem_dbg("pattern at 0x%08lX is 0x%08X\n", (unsigned long)p2, u); + + /* restore original value */ + *p1 = t; + + return (v == u); +} + +static void __init adm5120_detect_memsize(void) +{ + u32 memctrl; + u32 size, maxsize; + u8 *p; + + memctrl = SWITCH_READ(SWITCH_REG_MEMCTRL); + switch (memctrl & MEMCTRL_SDRS_MASK) { + case MEMCTRL_SDRS_4M: + maxsize = 4 << 20; + break; + case MEMCTRL_SDRS_8M: + maxsize = 8 << 20; + break; + case MEMCTRL_SDRS_16M: + maxsize = 16 << 20; + break; + default: + maxsize = 64 << 20; + break; + } + + /* disable buffers for both SDRAM banks */ + mem_dbg("disable buffers for both banks\n"); + MPMC_WRITE(MPMC_REG_DC0, MPMC_READ(MPMC_REG_DC0) & ~DC_BE); + MPMC_WRITE(MPMC_REG_DC1, MPMC_READ(MPMC_REG_DC1) & ~DC_BE); + + mem_dbg("checking for %uMB chip in 1st bank\n", maxsize >> 20); + + /* detect size of the 1st SDRAM bank */ + p = (u8 *)KSEG1ADDR(0); + for (size = 2<<20; size <= (maxsize >> 1); size <<= 1) { + if (mem_check_pattern(p, size)) { + /* mirrored address */ + mem_dbg("mirrored data found at offset 0x%08X\n", size); + break; + } + } + + mem_dbg("chip size in 1st bank is %uMB\n", size >> 20); + adm5120_memsize = size; + + if (size != maxsize) + /* 2nd bank is not supported */ + goto out; + + if ((memctrl & MEMCTRL_SDR1_ENABLE) == 0) + /* 2nd bank is disabled */ + goto out; + + /* + * some bootloaders enable 2nd bank, even if the 2nd SDRAM chip + * are missing. + */ + mem_dbg("check presence of 2nd bank\n"); + + p = (u8 *)KSEG1ADDR(maxsize+size-4); + if (mem_check_pattern(p, 0)) { + adm5120_memsize += size; + } + + if (maxsize != size) { + /* adjusting MECTRL register */ + memctrl &= ~(MEMCTRL_SDRS_MASK); + switch (size>>20) { + case 4: + memctrl |= MEMCTRL_SDRS_4M; + break; + case 8: + memctrl |= MEMCTRL_SDRS_8M; + break; + case 16: + memctrl |= MEMCTRL_SDRS_16M; + break; + default: + memctrl |= MEMCTRL_SDRS_64M; + break; + } + SWITCH_WRITE(SWITCH_REG_MEMCTRL, memctrl); + } + +out: + /* reenable buffer for both SDRAM banks */ + mem_dbg("enable buffers for both banks\n"); + MPMC_WRITE(MPMC_REG_DC0, MPMC_READ(MPMC_REG_DC0) | DC_BE); + MPMC_WRITE(MPMC_REG_DC1, MPMC_READ(MPMC_REG_DC1) | DC_BE); + + mem_dbg("%dx%uMB memory found\n", (adm5120_memsize == size) ? 1 : 2 , + size >>20); +} + +void __init adm5120_mem_init(void) +{ + adm5120_detect_memsize(); + add_memory_region(0, adm5120_memsize, BOOT_MEM_RAM); +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/platform.c b/target/linux/adm5120/files/arch/mips/adm5120/platform.c new file mode 100644 index 000000000..a79893d54 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/platform.c @@ -0,0 +1,181 @@ +/* + * $Id$ + * + * Generic ADM5120 platform devices + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/device.h> +#include <linux/platform_device.h> + +#include <asm/bootinfo.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_irq.h> +#include <asm/mach-adm5120/adm5120_switch.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +static void adm5120_uart_set_mctrl(struct amba_device *dev, void __iomem *base, + unsigned int mctrl); + +#if 1 +/* + * TODO:remove global adm5120_eth* variables when the switch driver will be + * converted into a real platform driver + */ +unsigned int adm5120_eth_num_ports = 6; +EXPORT_SYMBOL_GPL(adm5120_eth_num_ports); + +unsigned char adm5120_eth_macs[6][6] = { + {'\00', 'A', 'D', 'M', '\x51', '\x20' }, + {'\00', 'A', 'D', 'M', '\x51', '\x21' }, + {'\00', 'A', 'D', 'M', '\x51', '\x22' }, + {'\00', 'A', 'D', 'M', '\x51', '\x23' }, + {'\00', 'A', 'D', 'M', '\x51', '\x24' }, + {'\00', 'A', 'D', 'M', '\x51', '\x25' } +}; +EXPORT_SYMBOL_GPL(adm5120_eth_macs); + +unsigned char adm5120_eth_vlans[6] = { + 0x41, 0x42, 0x44, 0x48, 0x50, 0x60 +}; +EXPORT_SYMBOL_GPL(adm5120_eth_vlans); + +#else +/* Built-in ethernet switch */ +struct adm5120_switch_platform_data adm5120_switch_data; +struct platform_device adm5120_switch_device = { + .name = "adm5120-switch", + .id = -1, + .dev.platform_data = &adm5120_switch_data, +}; +#endif + +/* PCI Host Controller */ +struct adm5120_pci_platform_data adm5120_pci_data; +struct platform_device adm5120_pci_device = { + .name = "adm5120-pci", + .id = -1, + .dev.platform_data = &adm5120_pci_data, +}; + +/* USB Host Controller */ +struct resource adm5120_usbc_resources[] = { + [0] = { + .start = ADM5120_USBC_BASE, + .end = ADM5120_USBC_BASE+ADM5120_USBC_SIZE-1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = ADM5120_IRQ_USBC, + .end = ADM5120_IRQ_USBC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device adm5120_usbc_device = { + .name = "adm5120-hcd", + .id = -1, + .num_resources = ARRAY_SIZE(adm5120_usbc_resources), + .resource = adm5120_usbc_resources, +}; + +/* NOR flash 0 */ +struct adm5120_flash_platform_data adm5120_flash0_data; +struct platform_device adm5120_flash0_device = { + .name = "adm5120-flash", + .id = 0, + .dev.platform_data = &adm5120_flash0_data, +}; + +/* NOR flash 1 */ +struct adm5120_flash_platform_data adm5120_flash1_data; +struct platform_device adm5120_flash1_device = { + .name = "adm5120-flash", + .id = 1, + .dev.platform_data = &adm5120_flash1_data, +}; + +/* NAND flash */ +struct resource adm5120_nand_resource[] = { + [0] = { + .start = ADM5120_SRAM1_BASE, + .end = ADM5120_SRAM1_BASE+ADM5120_MPMC_SIZE-1, + .flags = IORESOURCE_MEM, + }, +}; + +struct adm5120_nand_platform_data adm5120_nand_data; + +struct platform_device adm5120_nand_device = { + .name = "adm5120-nand", + .id = -1, + .dev.platform_data = &adm5120_nand_data, + .num_resources = ARRAY_SIZE(adm5120_nand_resource), + .resource = adm5120_nand_resource, +}; + +/* built-in UARTs */ +struct amba_pl010_data adm5120_uart0_data = { + .set_mctrl = adm5120_uart_set_mctrl +}; + +struct amba_device adm5120_uart0_device = { + .dev = { + .bus_id = "APB:UART0", + .platform_data = &adm5120_uart0_data, + }, + .res = { + .start = ADM5120_UART0_BASE, + .end = ADM5120_UART0_BASE + ADM5120_UART_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + .irq = { ADM5120_IRQ_UART0, -1 }, + .periphid = 0x0041010, +}; + +struct amba_pl010_data adm5120_uart1_data = { + .set_mctrl = adm5120_uart_set_mctrl +}; + +struct amba_device adm5120_uart1_device = { + .dev = { + .bus_id = "APB:UART1", + .platform_data = &adm5120_uart1_data, + }, + .res = { + .start = ADM5120_UART1_BASE, + .end = ADM5120_UART1_BASE + ADM5120_UART_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + .irq = { ADM5120_IRQ_UART1, -1 }, + .periphid = 0x0041010, +}; + +static void adm5120_uart_set_mctrl(struct amba_device *dev, void __iomem *base, + unsigned int mctrl) +{ +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/prom.c b/target/linux/adm5120/files/arch/mips/adm5120/prom.c new file mode 100644 index 000000000..2187477d0 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/prom.c @@ -0,0 +1,276 @@ +/* + * $Id$ + * + * ADM5120 specific prom routines + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/mm.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_uart.h> + +#include <prom/cfe.h> +#include <prom/generic.h> +#include <prom/routerboot.h> +#include <prom/myloader.h> +#include <prom/zynos.h> + +unsigned int adm5120_prom_type = ADM5120_PROM_GENERIC; + +struct board_desc { + unsigned long mach_type; + char *name; +}; + +#define DEFBOARD(n, mt) { .mach_type = (mt), .name = (n)} +static struct board_desc common_boards[] __initdata = { + /* Cellvision/SparkLAN boards */ + DEFBOARD("CAS-630", MACH_ADM5120_CAS630), + DEFBOARD("CAS-670", MACH_ADM5120_CAS670), + DEFBOARD("CAS-771", MACH_ADM5120_CAS771), + DEFBOARD("CAS-790", MACH_ADM5120_CAS790), + DEFBOARD("CAS-861", MACH_ADM5120_CAS861), + /* Compex boards */ + DEFBOARD("WP54G-WRT", MACH_ADM5120_WP54G_WRT), + /* Edimax boards */ + DEFBOARD("BR-6104K", MACH_ADM5120_BR6104K), + DEFBOARD("BR-6104KP", MACH_ADM5120_BR6104K), + /* Infineon boards */ + DEFBOARD("EASY 5120P-ATA", MACH_ADM5120_EASY5120PATA), + DEFBOARD("EASY 5120-RT", MACH_ADM5120_EASY5120RT), + DEFBOARD("EASY 5120-WVoIP", MACH_ADM5120_EASY5120WVOIP), + DEFBOARD("EASY 83000", MACH_ADM5120_EASY83000), + /* Mikrotik RouterBOARDs */ + DEFBOARD("111", MACH_ADM5120_RB_111), + DEFBOARD("112", MACH_ADM5120_RB_112), + DEFBOARD("133", MACH_ADM5120_RB_133), + DEFBOARD("133C", MACH_ADM5120_RB_133C), + DEFBOARD("133C3", MACH_ADM5120_RB_133C), + DEFBOARD("150", MACH_ADM5120_RB_153), /* it's intentional */ + DEFBOARD("153", MACH_ADM5120_RB_153), + DEFBOARD("miniROUTER", MACH_ADM5120_RB_150), +}; + +static unsigned long __init find_machtype_byname(char *name) +{ + unsigned long ret; + int i; + + ret = MACH_ADM5120_GENERIC; + if (name == NULL) + goto out; + + if (*name == '\0') + goto out; + + for (i=0; i<ARRAY_SIZE(common_boards); i++) { + if (strcmp(common_boards[i].name, name) == 0) { + ret = common_boards[i].mach_type; + break; + } + } + +out: + return ret; +} + +static unsigned long __init detect_machtype_routerboot(void) +{ + char *name; + + name = routerboot_get_boardname(); + return find_machtype_byname(name); +} + +static unsigned long __init detect_machtype_generic(void) +{ + char *name; + + name = generic_prom_getenv("board_name"); + return find_machtype_byname(name); +} + +unsigned long __init detect_machtype_cfe(void) +{ + char *name; + + name = cfe_getenv("BOARD_NAME"); + return find_machtype_byname(name); +} + +static struct { + unsigned long mach_type; + u16 vendor_id; + u16 board_id; +} zynos_boards[] __initdata = { +#define ZYNOS_BOARD(vi, bi, mt) { .vendor_id = (vi), .board_id = (bi), \ + .mach_type = (mt) } + +#define ZYXEL_BOARD(bi, mt) ZYNOS_BOARD(ZYNOS_VENDOR_ID_ZYXEL, bi, mt) +#define DLINK_BOARD(bi, mt) ZYNOS_BOARD(ZYNOS_VENDOR_ID_DLINK, bi, mt) +#define LUCENT_BOARD(bi, mt) ZYNOS_BOARD(ZYNOS_VENDOR_ID_LUCENT, bi, mt) + ZYXEL_BOARD(ZYNOS_BOARD_HS100, MACH_ADM5120_HS100), + ZYXEL_BOARD(ZYNOS_BOARD_P334, MACH_ADM5120_P334), + ZYXEL_BOARD(ZYNOS_BOARD_P334U, MACH_ADM5120_P334U), + ZYXEL_BOARD(ZYNOS_BOARD_P334W, MACH_ADM5120_P334W), + ZYXEL_BOARD(ZYNOS_BOARD_P334WH, MACH_ADM5120_P334WH), + ZYXEL_BOARD(ZYNOS_BOARD_P334WHD, MACH_ADM5120_P334WHD), + ZYXEL_BOARD(ZYNOS_BOARD_P334WT, MACH_ADM5120_P334WT), + ZYXEL_BOARD(ZYNOS_BOARD_P335, MACH_ADM5120_P335), + ZYXEL_BOARD(ZYNOS_BOARD_P335PLUS, MACH_ADM5120_P335PLUS), + ZYXEL_BOARD(ZYNOS_BOARD_P335U, MACH_ADM5120_P335U) +}; + +static unsigned long __init detect_machtype_bootbase(void) +{ + unsigned long ret; + int i; + + ret = MACH_ADM5120_GENERIC; + for (i=0; i<ARRAY_SIZE(zynos_boards); i++) { + if (zynos_boards[i].vendor_id == bootbase_info.vendor_id && + zynos_boards[i].board_id == bootbase_info.board_id) { + ret = zynos_boards[i].mach_type; + break; + } + } + + return ret; +} + +static struct { + unsigned long mach_type; + u16 vid; + u16 did; + u16 svid; + u16 sdid; +} mylo_boards[] __initdata = { +#define MYLO_BOARD(v,d,sv,sd,mt) { .vid = (v), .did = (d), .svid = (sv), \ + .sdid = (sd), .mach_type = (mt) } +#define COMPEX_BOARD(d,mt) MYLO_BOARD(VENID_COMPEX,(d),VENID_COMPEX,(d),(mt)) + + COMPEX_BOARD(DEVID_COMPEX_NP27G, MACH_ADM5120_NP27G), + COMPEX_BOARD(DEVID_COMPEX_NP28G, MACH_ADM5120_NP28G), + COMPEX_BOARD(DEVID_COMPEX_NP28GHS, MACH_ADM5120_NP28GHS), + COMPEX_BOARD(DEVID_COMPEX_WP54G, MACH_ADM5120_WP54G), + COMPEX_BOARD(DEVID_COMPEX_WP54Gv1C, MACH_ADM5120_WP54Gv1C), + COMPEX_BOARD(DEVID_COMPEX_WP54AG, MACH_ADM5120_WP54AG), + COMPEX_BOARD(DEVID_COMPEX_WPP54G, MACH_ADM5120_WPP54G), + COMPEX_BOARD(DEVID_COMPEX_WPP54AG, MACH_ADM5120_WPP54AG), +}; + +static unsigned long __init detect_machtype_myloader(void) +{ + unsigned long ret; + int i; + + ret = MACH_ADM5120_GENERIC; + for (i=0; i<ARRAY_SIZE(mylo_boards); i++) { + if (mylo_boards[i].vid == myloader_info.vid && + mylo_boards[i].did == myloader_info.did && + mylo_boards[i].svid == myloader_info.svid && + mylo_boards[i].sdid == myloader_info.sdid) { + ret = mylo_boards[i].mach_type; + break; + } + } + + return ret; +} + +static void __init prom_detect_machtype(void) +{ + if (bootbase_present()) { + adm5120_prom_type = ADM5120_PROM_BOOTBASE; + mips_machtype = detect_machtype_bootbase(); + return; + } + + if (cfe_present()) { + adm5120_prom_type = ADM5120_PROM_CFE; + mips_machtype = detect_machtype_cfe(); + return; + } + + if (myloader_present()) { + adm5120_prom_type = ADM5120_PROM_MYLOADER; + mips_machtype = detect_machtype_myloader(); + return; + } + + if (routerboot_present()) { + adm5120_prom_type = ADM5120_PROM_ROUTERBOOT; + mips_machtype = detect_machtype_routerboot(); + return; + } + + if (generic_prom_present()) { + adm5120_prom_type = ADM5120_PROM_GENERIC; + mips_machtype = detect_machtype_generic(); + return; + } + + mips_machtype = MACH_ADM5120_GENERIC; +} + +/* TODO: this is an ugly hack for RouterBOARDS */ +extern char _image_cmdline; +static void __init prom_init_cmdline(void) +{ + char *cmd; + + /* init command line, register a default kernel command line */ + cmd = &_image_cmdline + 8; + if( strlen(cmd) > 0) strcpy( &(arcs_cmdline[0]), cmd); + else strcpy(&(arcs_cmdline[0]), CONFIG_CMDLINE); + +} + +#define UART_READ(r) *(volatile u32 *)(KSEG1ADDR(ADM5120_UART0_BASE)+(r)) +#define UART_WRITE(r,v) *(volatile u32 *)(KSEG1ADDR(ADM5120_UART0_BASE)+(r))=(v) + +void __init prom_putchar(char ch) +{ + while ((UART_READ(UART_REG_FLAG) & UART_FLAG_TXFE) == 0); + UART_WRITE(UART_REG_DATA, ch); + while ((UART_READ(UART_REG_FLAG) & UART_FLAG_TXFE) == 0); +} + +void __init prom_init(void) +{ + mips_machgroup = MACH_GROUP_ADM5120; + prom_detect_machtype(); + + prom_init_cmdline(); +} + +void __init prom_free_prom_memory(void) +{ + /* We do not have to prom memory to free */ +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/prom/Makefile b/target/linux/adm5120/files/arch/mips/adm5120/prom/Makefile new file mode 100644 index 000000000..d844b600f --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/prom/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the ADMtek ADM5120 SoC specific parts of the kernel +# + +lib-y += bootbase.o +lib-y += cfe.o +lib-y += generic.o +lib-y += myloader.o +lib-y += routerboot.o diff --git a/target/linux/adm5120/files/arch/mips/adm5120/prom/bootbase.c b/target/linux/adm5120/files/arch/mips/adm5120/prom/bootbase.c new file mode 100644 index 000000000..7ab9d46bd --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/prom/bootbase.c @@ -0,0 +1,124 @@ +/* + * $Id$ + * + * ZyXEL's Bootbase specific prom routines + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <linux/types.h> +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> +#include <asm/byteorder.h> + +#include <adm5120_defs.h> +#include <prom/zynos.h> +#include "prom_read.h" + +#define ZYNOS_INFO_ADDR KSEG1ADDR(ADM5120_SRAM0_BASE+0x3F90) +#define ZYNOS_HDBG_ADDR KSEG1ADDR(ADM5120_SRAM0_BASE+0x4000) +#define BOOTEXT_ADDR_MIN KSEG1ADDR(ADM5120_SRAM0_BASE) +#define BOOTEXT_ADDR_MAX (BOOTEXT_ADDR_MIN + (2*1024*1024)) + +static int bootbase_found = 0; +static struct zynos_board_info *board_info; + +struct bootbase_info bootbase_info; + +static inline int bootbase_dbgarea_present(u8 *data) +{ + u32 t; + + t = prom_read_be32(data+5); + if (t != ZYNOS_MAGIC_DBGAREA1) + return 0; + + t = prom_read_be32(data+9); + if (t != ZYNOS_MAGIC_DBGAREA2) + return 0; + + return 1; +} + +static inline u32 bootbase_get_bootext_addr(void) +{ + return prom_read_be32(&board_info->bootext_addr); +} + +static inline u16 bootbase_get_vendor_id(void) +{ +#define CHECK_VENDOR(n) (strnicmp(board_info->vendor,(n),strlen(n)) == 0) + unsigned char vendor[ZYNOS_NAME_LEN]; + int i; + + for (i=0; i<ZYNOS_NAME_LEN; i++) + vendor[i]=board_info->vendor[i]; + + if CHECK_VENDOR(ZYNOS_VENDOR_ZYXEL) + return ZYNOS_VENDOR_ID_ZYXEL; + + if CHECK_VENDOR(ZYNOS_VENDOR_DLINK) + return ZYNOS_VENDOR_ID_DLINK; + + if CHECK_VENDOR(ZYNOS_VENDOR_LUCENT) + return ZYNOS_VENDOR_ID_LUCENT; + + if CHECK_VENDOR(ZYNOS_VENDOR_NETGEAR) + return ZYNOS_VENDOR_ID_NETGEAR; + + return ZYNOS_VENDOR_ID_OTHER; +} + +static inline u16 bootbase_get_board_id(void) +{ + return prom_read_be16(&board_info->board_id); +} + +int __init bootbase_present(void) +{ + u32 t; + + if (bootbase_found) + goto out; + + /* check presence of the dbgarea */ + if (bootbase_dbgarea_present((u8 *)ZYNOS_HDBG_ADDR) == 0) + goto out; + + board_info = (struct zynos_board_info *)(ZYNOS_INFO_ADDR); + + /* check for a valid BootExt address */ + t = bootbase_get_bootext_addr(); + if ((t < BOOTEXT_ADDR_MIN) || (t > BOOTEXT_ADDR_MAX)) + goto out; + + bootbase_info.vendor_id = bootbase_get_vendor_id(); + bootbase_info.board_id = bootbase_get_board_id(); + + bootbase_found = 1; + +out: + return bootbase_found; +} + diff --git a/target/linux/adm5120/files/arch/mips/adm5120/prom/cfe.c b/target/linux/adm5120/files/arch/mips/adm5120/prom/cfe.c new file mode 100644 index 000000000..6c03a4e85 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/prom/cfe.c @@ -0,0 +1,86 @@ +/* + * $Id$ + * + * Broadcom's CFE specific prom routines + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <linux/types.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> + +#include <prom/cfe.h> +#include "prom_read.h" + +/* + * CFE based boards + */ +#define CFE_EPTSEAL 0x43464531 /* CFE1 is the magic number to recognize CFE +from other bootloaders */ + +static int cfe_found = 0; + +static u32 cfe_handle; +static u32 cfe_entry; +static u32 cfe_seal; + +int __init cfe_present(void) +{ + /* + * This method only works, when we are booted directly from the CFE. + */ + u32 a1 = (u32) fw_arg1; + + if (cfe_found) + return 1; + + cfe_handle = (u32) fw_arg0; + cfe_entry = (u32) fw_arg2; + cfe_seal = (u32) fw_arg3; + + /* Check for CFE by finding the CFE magic number */ + if (cfe_seal != CFE_EPTSEAL) { + /* We are not booted from CFE */ + return 0; + } + + /* cfe_a1_val must be 0, because only one CPU present in the ADM5120 */ + if (a1 != 0) { + return 0; + } + + /* The cfe_handle, and the cfe_entry must be kernel mode addresses */ + if ((cfe_handle < KSEG0) || (cfe_entry < KSEG0)) { + return 0; + } + + cfe_found = 1; + return 1; +} + +char *cfe_getenv(char *envname) +{ + if (cfe_found == 0) + return NULL; + + return NULL; +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/prom/generic.c b/target/linux/adm5120/files/arch/mips/adm5120/prom/generic.c new file mode 100644 index 000000000..3b5c3bdc0 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/prom/generic.c @@ -0,0 +1,60 @@ +/* + * $Id$ + * + * Generic PROM routines + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> + +#include <prom/generic.h> + +static int *_prom_argc; +static char **_prom_argv; +static char **_prom_envp; + +char *generic_prom_getenv(char *envname) +{ + char **env; + char *ret; + + ret = NULL; + for (env = _prom_envp; *env != NULL; env++) { + if (strcmp(envname, *env++) == 0) { + ret = *env; + break; + } + } + + return ret; +} + +int generic_prom_present(void) +{ + _prom_argc = (int *)fw_arg0; + _prom_argv = (char **)fw_arg1; + _prom_envp = (char **)fw_arg2; + + return 1; +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/prom/myloader.c b/target/linux/adm5120/files/arch/mips/adm5120/prom/myloader.c new file mode 100644 index 000000000..8a44509a0 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/prom/myloader.c @@ -0,0 +1,75 @@ +/* + * $Id$ + * + * Compex's MyLoader specific prom routines + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <linux/types.h> +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> +#include <asm/byteorder.h> + +#include <adm5120_defs.h> +#include <prom/myloader.h> +#include "prom_read.h" + +#define SYS_PARAMS_ADDR KSEG1ADDR(ADM5120_SRAM0_BASE+0x0F000) +#define BOARD_PARAMS_ADDR KSEG1ADDR(ADM5120_SRAM0_BASE+0x0F800) +#define PART_TABLE_ADDR KSEG1ADDR(ADM5120_SRAM0_BASE+0x10000) + +static int myloader_found = 0; + +struct myloader_info myloader_info; + +int __init myloader_present(void) +{ + struct mylo_system_params *sysp; + struct mylo_board_params *boardp; + struct mylo_partition_table *parts; + + if (myloader_found) + goto out; + + sysp = (struct mylo_system_params *)(SYS_PARAMS_ADDR); + boardp = (struct mylo_board_params *)(BOARD_PARAMS_ADDR); + parts = (struct mylo_partition_table *)(PART_TABLE_ADDR); + + /* Check for some magic numbers */ + if ((le32_to_cpu(sysp->magic) != MYLO_MAGIC_SYS_PARAMS) || + (le32_to_cpu(boardp->magic) != MYLO_MAGIC_BOARD_PARAMS) || + (le32_to_cpu(parts->magic) != MYLO_MAGIC_PARTITIONS)) + goto out; + + myloader_info.vid = le32_to_cpu(sysp->vid); + myloader_info.did = le32_to_cpu(sysp->did); + myloader_info.svid = le32_to_cpu(sysp->svid); + myloader_info.sdid = le32_to_cpu(sysp->sdid); + + myloader_found = 1; + +out: + return myloader_found; +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/prom/prom_read.h b/target/linux/adm5120/files/arch/mips/adm5120/prom/prom_read.h new file mode 100644 index 000000000..06e6863fe --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/prom/prom_read.h @@ -0,0 +1,63 @@ +/* + * $Id$ + * + * Generic prom definitions + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ADM5120_PROM_H_ +#define _ADM5120_PROM_H_ + +/* + * Helper routines + */ +static inline u16 prom_read_le16(void *buf) +{ + u8 *p = buf; + + return ((u16)p[0] + ((u16)p[1] << 8)); +} + +static inline u32 prom_read_le32(void *buf) +{ + u8 *p = buf; + + return ((u32)p[0] + ((u32)p[1] << 8) + ((u32)p[2] << 16) + + ((u32)p[3] << 24)); +} + +static inline u16 prom_read_be16(void *buf) +{ + u8 *p = buf; + + return (((u16)p[0] << 8) + (u16)p[1]); +} + +static inline u32 prom_read_be32(void *buf) +{ + u8 *p = buf; + + return (((u32)p[0] << 24) + ((u32)p[1] << 16) + ((u32)p[2] << 8) + + ((u32)p[3])); +} + +#endif /* _ADM5120_PROM_H_ */ + + diff --git a/target/linux/adm5120/files/arch/mips/adm5120/prom/routerboot.c b/target/linux/adm5120/files/arch/mips/adm5120/prom/routerboot.c new file mode 100644 index 000000000..7116f412f --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/prom/routerboot.c @@ -0,0 +1,142 @@ +/* + * $Id$ + * + * Mikrotik's RouterBOOT specific prom routines + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <linux/types.h> +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> + +#include <adm5120_defs.h> +#include <prom/routerboot.h> +#include "prom_read.h" + +static struct rb_hard_settings rb_hs; +static int rb_found = 0; + +static int __init routerboot_load_hs(u8 *buf, u16 buflen) +{ + u16 id,len; + u8 *mac; + int i,j; + + memset(&rb_hs, 0, sizeof(rb_hs)); + + if (buflen < 4) + return -1; + + if (prom_read_le32(buf) != RB_MAGIC_HARD) + return -1; + + /* skip magic value */ + buf += 4; + buflen -= 4; + + while (buflen > 2) { + id = prom_read_le16(buf); + buf += 2; + buflen -= 2; + if (id == RB_ID_TERMINATOR || buflen < 2) + break; + + len = prom_read_le16(buf); + buf += 2; + buflen -= 2; + + if (buflen < len) + break; + + switch (id) { + case RB_ID_BIOS_VERSION: + rb_hs.bios_ver = (char *)buf; + break; + case RB_ID_BOARD_NAME: + rb_hs.name = (char *)buf; + break; + case RB_ID_MEMORY_SIZE: + rb_hs.mem_size = prom_read_le32(buf); + break; + case RB_ID_MAC_ADDRESS_COUNT: + rb_hs.mac_count = prom_read_le32(buf); + break; + case RB_ID_MAC_ADDRESS_PACK: + rb_hs.mac_count = len/RB_MAC_SIZE; + if (rb_hs.mac_count > RB_MAX_MAC_COUNT) + rb_hs.mac_count = RB_MAX_MAC_COUNT; + mac = buf; + for (i=0; i < rb_hs.mac_count; i++) { + for (j=0; j < RB_MAC_SIZE; j++) + rb_hs.macs[i][j] = mac[j]; + mac += RB_MAC_SIZE; + } + break; + } + + buf += len; + buflen -= len; + + } + + return 0; +} + +#define RB_BS_OFFS 0x14 +#define RB_OFFS_MAX (128*1024) + +int __init routerboot_present(void) +{ + struct rb_bios_settings *bs; + u8 *base; + u32 off,len; + + if (rb_found) + goto out; + + base = (u8 *)KSEG1ADDR(ADM5120_SRAM0_BASE); + bs = (struct rb_bios_settings *)(base + RB_BS_OFFS); + + off = prom_read_le32(&bs->hs_offs); + len = prom_read_le32(&bs->hs_size); + if (off > RB_OFFS_MAX) + goto out; + + if (routerboot_load_hs(base+off, len) != 0) + goto out; + + rb_found = 1; + +out: + return rb_found; +} + +char *routerboot_get_boardname(void) +{ + if (rb_found == 0) + return NULL; + + return rb_hs.name; +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/reset.c b/target/linux/adm5120/files/arch/mips/adm5120/reset.c new file mode 100644 index 000000000..abcbb1c8b --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/reset.c @@ -0,0 +1,62 @@ +/* + * $Id$ + * + * ADM5120 specific reset routines + * + * Copyright (C) ADMtek Incorporated. + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_switch.h> + +#define ADM5120_SOFTRESET 0x12000004 + +void (*adm5120_board_reset)(void); + +void adm5120_restart(char *command) +{ + /* TODO: stop switch before reset */ + + if (adm5120_board_reset) + adm5120_board_reset(); + + *(u32*)KSEG1ADDR(ADM5120_SOFTRESET)=1; +} + +void adm5120_halt(void) +{ + printk(KERN_NOTICE "\n** You can safely turn off the power\n"); + while (1); +} + +void adm5120_power_off(void) +{ + adm5120_halt(); +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/setup.c b/target/linux/adm5120/files/arch/mips/adm5120/setup.c new file mode 100644 index 000000000..ed7966b41 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/setup.c @@ -0,0 +1,73 @@ +/* + * $Id$ + * + * ADM5120 specific setup + * + * Copyright (C) ADMtek Incorporated. + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> + +#include <asm/reboot.h> +#include <asm/io.h> +#include <asm/time.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_switch.h> +#include <asm/mach-adm5120/adm5120_board.h> + +static char *prom_names[ADM5120_PROM_LAST+1] __initdata = { + [ADM5120_PROM_GENERIC] = "Generic", + [ADM5120_PROM_CFE] = "CFE", + [ADM5120_PROM_UBOOT] = "U-Boot", + [ADM5120_PROM_MYLOADER] = "MyLoader", + [ADM5120_PROM_ROUTERBOOT] = "RouterBOOT", + [ADM5120_PROM_BOOTBASE] = "Bootbase" +}; + +static void __init adm5120_report(void) +{ + printk(KERN_INFO "SoC : ADM%04X%s revision %d, running at %ldMHz\n", + adm5120_product_code, + (adm5120_package == ADM5120_PACKAGE_BGA) ? "" : "P", + adm5120_revision, (adm5120_speed / 1000000) + ); + printk(KERN_INFO "Bootdev : %s flash\n", adm5120_nand_boot ? "NAND":"NOR"); + printk(KERN_INFO "Prom : %s\n", prom_names[adm5120_prom_type]); +} + +void __init plat_mem_setup(void) +{ + adm5120_soc_init(); + adm5120_mem_init(); + adm5120_report(); + + board_time_init = adm5120_time_init; + + _machine_restart = adm5120_restart; + _machine_halt = adm5120_halt; + pm_power_off = adm5120_power_off; + + set_io_port_base(KSEG1); +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/time.c b/target/linux/adm5120/files/arch/mips/adm5120/time.c new file mode 100644 index 000000000..27393d4fe --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/time.c @@ -0,0 +1,54 @@ +/* + * $Id$ + * + * ADM5120 specific hooks for MIPS CPU Counter/Compare timer + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * + * This file was based on: arch/mips/gt64120/wrppmc/time.c + * Original author: Mark.Zhan + * Copyright (C) 1996, 1997, 2004 by Ralf Baechle + * Copyright (C) 2006, Wind River System Inc. + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/types.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/timex.h> + +#include <asm/irq.h> +#include <asm/cpu.h> +#include <asm/time.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_irq.h> + +void __init adm5120_time_init(void) +{ + mips_hpt_frequency = adm5120_speed / 2; +} + +void __init plat_timer_setup(struct irqaction *irq) +{ + clear_c0_status(ST0_BEV); + + /* Install ISR for CPU Counter interrupt */ + setup_irq(ADM5120_IRQ_COUNTER, irq); +} diff --git a/target/linux/adm5120/files/arch/mips/adm5120/trxsplit.c b/target/linux/adm5120/files/arch/mips/adm5120/trxsplit.c new file mode 100644 index 000000000..941f30695 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/adm5120/trxsplit.c @@ -0,0 +1,208 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/kmod.h> +#include <linux/root_dev.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include <linux/byteorder/generic.h> + +#define PFX "trxsplit: " + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_VERSION 1 +#define TRX_MAX_LEN 0x3A0000 +#define TRX_NO_HEADER 1 /* Do not write TRX header */ +#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */ +#define TRX_MAX_OFFSET 3 +#define TRX_MIN_KERNEL_SIZE 256*1024 + +struct trx_header { + u32 magic; /* "HDR0" */ + u32 len; /* Length of file including header */ + u32 crc32; /* 32-bit CRC from flag_version to end of file */ + u32 flag_version; /* 0:15 flags, 16:31 version */ + u32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ +}; + +#define BLOCK_LEN_MIN 0x10000 + +static struct mtd_info *trx_mtd = NULL; +static unsigned long trx_offset = 0; +static int trx_nr_parts = 0; +static struct mtd_partition trx_parts[TRX_MAX_OFFSET]; +static struct trx_header trx_hdr; + +static int __init trxsplit_checktrx(struct mtd_info *mtd, unsigned long offset) +{ + size_t retlen; + int err; + + err = mtd->read(mtd, offset, sizeof(trx_hdr), &retlen, (void *)&trx_hdr); + if (err) { + printk(KERN_ALERT PFX "unable to read from '%s'\n", mtd->name); + goto err_out; + } + + if (retlen != sizeof(trx_hdr)) { + printk(KERN_ALERT PFX "reading failed on '%s'\n", mtd->name); + goto err_out; + } + + trx_hdr.magic = le32_to_cpu(trx_hdr.magic); + trx_hdr.len = le32_to_cpu(trx_hdr.len); + trx_hdr.crc32 = le32_to_cpu(trx_hdr.crc32); + trx_hdr.flag_version = le32_to_cpu(trx_hdr.flag_version); + trx_hdr.offsets[0] = le32_to_cpu(trx_hdr.offsets[0]); + trx_hdr.offsets[1] = le32_to_cpu(trx_hdr.offsets[1]); + trx_hdr.offsets[2] = le32_to_cpu(trx_hdr.offsets[2]); + + /* sanity checks */ + if (trx_hdr.magic != TRX_MAGIC) + goto err_out; + + if (trx_hdr.len > mtd->size - offset) + goto err_out; + + /* TODO: add crc32 checking too? */ + + return 0; + +err_out: + return -1; +} + +static int __init trxsplit_create_partitions(void) +{ + struct mtd_partition *part = trx_parts; + int ret = 0; + int i; + + if (trx_mtd == NULL) + goto out; + + printk(KERN_INFO PFX "creating TRX partitions in '%s' (%d,%d)\n", + trx_mtd->name, MTD_BLOCK_MAJOR, trx_mtd->index); + + for (i=0; i<TRX_MAX_OFFSET;i++) { + part = &trx_parts[i]; + if (trx_hdr.offsets[i] == 0) + continue; + part->offset = trx_offset + trx_hdr.offsets[i]; + trx_nr_parts++; + } + + for (i=0; i<trx_nr_parts-1; i++) { + trx_parts[i].size = trx_parts[i+1].offset - trx_parts[i].offset; + } + trx_parts[i].size = trx_mtd->size - trx_parts[i].offset; + + i=0; + part = &trx_parts[i]; + if (part->size < TRX_MIN_KERNEL_SIZE) { + part->name = "loader"; + i++; + } + + part = &trx_parts[i]; + part->name = "kernel"; + i++; + + part = &trx_parts[i]; + part->name = "rootfs"; + + ret = add_mtd_partitions(trx_mtd, trx_parts, trx_nr_parts); + if (ret) { + printk(KERN_ALERT PFX "creating TRX partitions failed\n"); + } + +out: + return ret; +} + +static void __init trxsplit_add_mtd(struct mtd_info *mtd) +{ + unsigned long offset; + unsigned long blocklen; + int err; + + if (trx_mtd) + return; + + blocklen = mtd->erasesize; + if (blocklen < BLOCK_LEN_MIN) + blocklen = BLOCK_LEN_MIN; + + printk(KERN_INFO PFX "searching TRX header in '%s'\n", mtd->name); + + err = 0; + for (offset=0; offset < mtd->size; offset+=blocklen) { + err = trxsplit_checktrx(mtd, offset); + if (err == 0) + break; + } + + if (err) { + printk(KERN_ALERT PFX "no TRX header found\n"); + return; + } + + printk(KERN_INFO PFX "TRX header found at 0x%lX\n", offset); + + trx_mtd = mtd; + trx_offset = offset; +} + +static void __init trxsplit_remove_mtd(struct mtd_info *mtd) +{ + /* nothing to do */ +} + +static struct mtd_notifier trxsplit_notifier __initdata = { + .add = trxsplit_add_mtd, + .remove = trxsplit_remove_mtd, +}; + +static void __init trxsplit_find_trx(void) +{ + register_mtd_user(&trxsplit_notifier); + unregister_mtd_user(&trxsplit_notifier); +} + +static int __init trxsplit_init(void) +{ + int ret; + + trxsplit_find_trx(); + + ret = trxsplit_create_partitions(); + return ret; +} + +late_initcall(trxsplit_init); diff --git a/target/linux/adm5120/files/arch/mips/pci/fixup-adm5120.c b/target/linux/adm5120/files/arch/mips/pci/fixup-adm5120.c new file mode 100644 index 000000000..59acf75c1 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/pci/fixup-adm5120.c @@ -0,0 +1,175 @@ +/* + * $Id$ + * + * ADM5120 specific PCI fixups + * + * Copyright (C) ADMtek Incorporated. + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/pci_regs.h> + +#include <asm/delay.h> +#include <asm/bootinfo.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_irq.h> + +struct adm5120_pci_irq { + u8 slot; + u8 func; + u8 pin; + unsigned irq; +}; + +#define PCIIRQ(s,f,p,i) { \ + .slot = (s), \ + .func = (f), \ + .pin = (p), \ + .irq = (i) \ + } + +static struct adm5120_pci_irq default_pci_irqs[] __initdata = { + PCIIRQ(2, 0, 1, ADM5120_IRQ_PCI0), +}; + +static struct adm5120_pci_irq rb1xx_pci_irqs[] __initdata = { + PCIIRQ(1, 0, 1, ADM5120_IRQ_PCI0), + PCIIRQ(2, 0, 1, ADM5120_IRQ_PCI1), + PCIIRQ(3, 0, 1, ADM5120_IRQ_PCI2) +}; + +static struct adm5120_pci_irq cas771_pci_irqs[] __initdata = { + PCIIRQ(2, 0, 1, ADM5120_IRQ_PCI0), + PCIIRQ(3, 0, 1, ADM5120_IRQ_PCI1), + PCIIRQ(3, 2, 3, ADM5120_IRQ_PCI2) +}; + +static struct adm5120_pci_irq np28g_pci_irqs[] __initdata = { + PCIIRQ(2, 0, 1, ADM5120_IRQ_PCI0), + PCIIRQ(3, 0, 1, ADM5120_IRQ_PCI0), + PCIIRQ(3, 1, 2, ADM5120_IRQ_PCI1), + PCIIRQ(3, 2, 3, ADM5120_IRQ_PCI2) +}; + +#define GETMAP(n) do { \ + nr_irqs = ARRAY_SIZE(n ## _pci_irqs); \ + p = n ## _pci_irqs; \ + } while (0) + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + struct adm5120_pci_irq *p; + int nr_irqs; + int i; + int irq; + + irq = -1; + if (slot < 1 || slot > 3) { + printk(KERN_ALERT "PCI: slot number %u is not supported\n", + slot); + goto out; + } + + GETMAP(default); + + switch (mips_machtype) { + case MACH_ADM5120_RB_111: + case MACH_ADM5120_RB_112: + case MACH_ADM5120_RB_133: + case MACH_ADM5120_RB_133C: + case MACH_ADM5120_RB_153: + GETMAP(rb1xx); + break; + case MACH_ADM5120_NP28G: + GETMAP(np28g); + break; + case MACH_ADM5120_P335: + case MACH_ADM5120_P334WT: + /* using default mapping */ + break; + case MACH_ADM5120_CAS771: + GETMAP(cas771); + break; + + case MACH_ADM5120_NP27G: + case MACH_ADM5120_NP28GHS: + case MACH_ADM5120_WP54AG: + case MACH_ADM5120_WP54G: + case MACH_ADM5120_WP54G_WRT: + case MACH_ADM5120_WPP54AG: + case MACH_ADM5120_WPP54G: + default: + printk(KERN_ALERT "PCI: irq map is unknown, using defaults.\n"); + break; + } + + for (i=0; i<nr_irqs; i++, p++) { + if ((p->slot == slot) && (PCI_FUNC(dev->devfn) == p->func) && + (p->pin == pin)) { + irq = p->irq; + break; + } + } + + if (irq < 0) { + printk(KERN_ALERT "PCI: no irq found for %s pin:%u\n", + pci_name(dev), pin); + } else { + printk(KERN_INFO "PCI: mapping irq for %s pin:%u, irq:%d\n", + pci_name(dev), pin, irq); + } + +out: + return irq; +} + +static void adm5120_pci_fixup(struct pci_dev *dev) +{ + if (dev->devfn != 0) + return; + + /* setup COMMAND register */ + pci_write_config_word(dev, PCI_COMMAND, + (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)); + + /* setup CACHE_LINE_SIZE register */ + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 4); + + /* setting up BARS */ + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0); +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ADMTEK, PCI_DEVICE_ID_ADMTEK_ADM5120, + adm5120_pci_fixup); + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + diff --git a/target/linux/adm5120/files/arch/mips/pci/ops-adm5120.c b/target/linux/adm5120/files/arch/mips/pci/ops-adm5120.c new file mode 100644 index 000000000..4a9bc999c --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/pci/ops-adm5120.c @@ -0,0 +1,143 @@ +/* + * $Id$ + * + * ADM5120 specific PCI operations + * + * Copyright (C) ADMtek Incorporated. + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/spinlock.h> + +#include <asm/mach-adm5120/adm5120_defs.h> + +#define DEBUG 0 +#if DEBUG +#define DBG(f, ...) printk(f, ## __VA_ARGS__ ) +#else +#define DBG(f, ...) +#endif + +#define PCI_ENABLE 0x80000000 + +static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED; + +static inline void write_cfgaddr(u32 addr) +{ + *(volatile u32*)KSEG1ADDR(ADM5120_PCICFG_ADDR) = (addr | PCI_ENABLE); +} + +static inline void write_cfgdata(u32 data) +{ + *(volatile u32*)KSEG1ADDR(ADM5120_PCICFG_DATA) = data; + +} + +static inline u32 read_cfgdata(void) +{ + return (*(volatile u32*)KSEG1ADDR(ADM5120_PCICFG_DATA)); +} + +static inline u32 mkaddr(struct pci_bus *bus, unsigned int devfn, int where) +{ + return (((bus->number & 0xFF) << 16) | ((devfn & 0xFF) << 8) | \ + (where & 0xFC)); +} + +static int pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *val) +{ + unsigned long flags; + u32 data; + + spin_lock_irqsave(&pci_lock, flags); + write_cfgaddr(mkaddr(bus,devfn,where)); + data = read_cfgdata(); + + DBG("PCI: cfg_read %02u.%02u.%01u/%02X:%01d, cfg:0x%08X", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, data); + + switch (size) { + case 1: + if (where & 1) + data >>= 8; + if (where & 2) + data >>= 16; + data &= 0xFF; + break; + case 2: + if (where & 2) + data >>= 16; + data &= 0xFFFF; + break; + } + + *val = data; + DBG(", 0x%08X returned\n", data); + spin_unlock_irqrestore(&pci_lock, flags); + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 val) +{ + unsigned long flags; + u32 data; + int s; + + spin_lock_irqsave(&pci_lock, flags); + write_cfgaddr(mkaddr(bus,devfn,where)); + data = read_cfgdata(); + + DBG("PCI: cfg_write %02u.%02u.%01u/%02X:%01d, cfg:0x%08X", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, data); + + switch (size) { + case 1: + s = ((where & 3) << 3); + data &= ~(0xFF << s); + data |= ((val & 0xFF) << s); + break; + case 2: + s = ((where & 2) << 4); + data &= ~(0xFFFF << s); + data |= ((val & 0xFFFF) << s); + break; + case 4: + data = val; + break; + } + + write_cfgdata(data); + DBG(", 0x%08X written\n", data); + spin_unlock_irqrestore(&pci_lock, flags); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops adm5120_pci_ops = { + .read = pci_config_read, + .write = pci_config_write, +}; diff --git a/target/linux/adm5120/files/arch/mips/pci/pci-adm5120.c b/target/linux/adm5120/files/arch/mips/pci/pci-adm5120.c new file mode 100644 index 000000000..2a6477023 --- /dev/null +++ b/target/linux/adm5120/files/arch/mips/pci/pci-adm5120.c @@ -0,0 +1,80 @@ +/* + * $Id$ + * + * ADM5120 PCI Host Controller driver + * + * Copyright (C) ADMtek Incorporated. + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_irq.h> + +extern struct pci_ops adm5120_pci_ops; + +static struct resource pci_io_resource = { + .name = "ADM5120 PCI I/O", + .start = ADM5120_PCIIO_BASE, + .end = ADM5120_PCICFG_ADDR-1, + .flags = IORESOURCE_IO +}; + +static struct resource pci_mem_resource = { + .name = "ADM5120 PCI MEM", + .start = ADM5120_PCIMEM_BASE, + .end = ADM5120_PCIIO_BASE-1, + .flags = IORESOURCE_MEM +}; + +static struct pci_controller adm5120_controller = { + .pci_ops = &adm5120_pci_ops, + .io_resource = &pci_io_resource, + .mem_resource = &pci_mem_resource, +}; + +static int __init adm5120_pci_setup(void) +{ + int pci_bios; + + pci_bios = adm5120_has_pci(); + + printk("adm5120: system has %sPCI BIOS\n", pci_bios ? "" : "no "); + if (pci_bios == 0) + return -1; + + /* Avoid ISA compat ranges. */ + PCIBIOS_MIN_IO = 0x00000000; + PCIBIOS_MIN_MEM = 0x00000000; + + /* Set I/O resource limits. */ + ioport_resource.end = 0x1fffffff; + iomem_resource.end = 0xffffffff; + + register_pci_controller(&adm5120_controller); + return 0; +} + +arch_initcall(adm5120_pci_setup); diff --git a/target/linux/adm5120/files/drivers/leds/leds-adm5120.c b/target/linux/adm5120/files/drivers/leds/leds-adm5120.c new file mode 100644 index 000000000..06189371d --- /dev/null +++ b/target/linux/adm5120/files/drivers/leds/leds-adm5120.c @@ -0,0 +1,361 @@ +/* + * $Id$ + * + * ADM5120 GPIO LED devices + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/err.h> + +#include <linux/gpio_leds.h> + +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/gpio.h> + +#include <asm/mach-adm5120/adm5120_info.h> + +#define NUM_LEDS_MAX 23 + +#define ADM5120_GPIO_xxxx 0x100 /* an unknown pin */ + +struct mach_data { + unsigned long machtype; + unsigned count; + struct gpio_led_platform_data *data; +}; + +struct adm5120_leddev { + struct platform_device pdev; + struct gpio_led_platform_data pdata; +}; + +static int led_count = 0; +static struct adm5120_leddev *led_devs[NUM_LEDS_MAX]; + +#define LED_ARRAY(n) \ +static struct gpio_led_platform_data \ +n ## _leds [] __initdata = + +#define LED_DATA(n,t,g,off,on) { \ + .name = (n), \ + .trigger = (t), \ + .gpio = (g), \ + .value_off = (off), \ + .value_on = (on) \ + } + +#define LED_STD(g,n,t) LED_DATA((n),(t),(g), 0, 1) +#define LED_INV(g,n,t) LED_DATA((n),(t),(g), 1, 0) + +/* + * Compex boards + */ +#if defined(CONFIG_LEDS_ADM5120_EXPERIMENTAL) +LED_ARRAY(np27g) { /* FIXME: untested */ + LED_STD(ADM5120_GPIO_xxxx, "lan1", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "lan2", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "lan3", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "lan4", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "wan_cond", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "wlan", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "wan_act", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "usb1", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "usb2", NULL ), + LED_INV(ADM5120_GPIO_PIN2, "power", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "diag", NULL ), +}; +#endif + +#if defined(CONFIG_LEDS_ADM5120_EXPERIMENTAL) +LED_ARRAY(np28g) { /* FIXME: untested */ + LED_STD(ADM5120_GPIO_xxxx, "lan1", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "lan2", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "lan3", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "wan", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "wlan", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "usb1", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "usb2", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "usb3", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "usb4", NULL ), + LED_INV(ADM5120_GPIO_PIN2, "power", NULL ), + LED_STD(ADM5120_GPIO_xxxx, "diag", NULL ), +}; +#endif + +LED_ARRAY(wp54g) { + LED_INV(ADM5120_GPIO_PIN2, "diag", NULL ), + LED_INV(ADM5120_GPIO_PIN6, "wlan", NULL ), + LED_INV(ADM5120_GPIO_PIN7, "wan", NULL ), + LED_INV(ADM5120_GPIO_P0L0, "lan1", NULL ), + LED_INV(ADM5120_GPIO_P1L0, "lan2", NULL ), +}; + +/* + * Edimax boards + */ +LED_ARRAY(br6104k) { + LED_STD(ADM5120_GPIO_PIN0, "power", NULL ), + LED_INV(ADM5120_GPIO_P0L1, "wan_speed", NULL ), + LED_INV(ADM5120_GPIO_P0L0, "wan_lnkact", NULL ), + LED_INV(ADM5120_GPIO_P1L1, "lan1_speed", NULL ), + LED_INV(ADM5120_GPIO_P1L0, "lan1_lnkact", NULL ), + LED_INV(ADM5120_GPIO_P2L1, "lan2_speed", NULL ), + LED_INV(ADM5120_GPIO_P2L0, "lan2_lnkact", NULL ), + LED_INV(ADM5120_GPIO_P3L1, "lan3_speed", NULL ), + LED_INV(ADM5120_GPIO_P3L0, "lan3_lnkact", NULL ), + LED_INV(ADM5120_GPIO_P4L1, "lan4_speed", NULL ), + LED_INV(ADM5120_GPIO_P4L0, "lan4_lnkact", NULL ), +}; + +/* + * Mikrotik boards + */ +#if defined(CONFIG_LEDS_ADM5120_EXPERIMENTAL) +LED_ARRAY(rb100) { /* FIXME: untested */ + LED_STD(ADM5120_GPIO_PIN6, "power", NULL ), + LED_STD(ADM5120_GPIO_PIN3, "user", NULL ), +}; +#endif + +LED_ARRAY(rb133) { + LED_STD(ADM5120_GPIO_PIN6, "power", NULL ), + LED_STD(ADM5120_GPIO_PIN5, "user", NULL ), +}; + +/* + * ZyXEL boards + */ +#if defined(CONFIG_LEDS_ADM5120_EXPERIMENTAL) +LED_ARRAY(p334) { /* FIXME: untested */ + LED_INV(ADM5120_GPIO_xxxx, "power", NULL ), + LED_INV(ADM5120_GPIO_xxxx, "lan1", NULL ), + LED_INV(ADM5120_GPIO_xxxx, "lan2", NULL ), + LED_INV(ADM5120_GPIO_xxxx, "lan3", NULL ), + LED_INV(ADM5120_GPIO_xxxx, "lan4", NULL ), + LED_INV(ADM5120_GPIO_xxxx, "wan", NULL ), +}; +#endif + +LED_ARRAY(p334wt) { + LED_INV(ADM5120_GPIO_PIN2, "power", NULL ), + LED_INV(ADM5120_GPIO_P3L0, "lan1", NULL ), + LED_INV(ADM5120_GPIO_P2L0, "lan2", NULL ), + LED_INV(ADM5120_GPIO_P1L0, "lan3", NULL ), + LED_INV(ADM5120_GPIO_P0L0, "lan4", NULL ), + LED_INV(ADM5120_GPIO_P4L0, "wan", NULL ), + LED_INV(ADM5120_GPIO_P4L2, "wlan", NULL ), + LED_INV(ADM5120_GPIO_P2L2, "otist", NULL ), + LED_INV(ADM5120_GPIO_P1L2, "hidden", NULL ), +}; + +#if defined(CONFIG_LEDS_ADM5120_EXPERIMENTAL) +LED_ARRAY(p335) { /* FIXME: untested */ + LED_INV(ADM5120_GPIO_PIN2, "power", NULL ), + LED_INV(ADM5120_GPIO_P3L0, "lan1", NULL ), + LED_INV(ADM5120_GPIO_P2L0, "lan2", NULL ), + LED_INV(ADM5120_GPIO_P1L0, "lan3", NULL ), + LED_INV(ADM5120_GPIO_P0L0, "lan4", NULL ), + LED_INV(ADM5120_GPIO_P4L0, "wan", NULL ), + LED_INV(ADM5120_GPIO_P4L2, "wlan", NULL ), + LED_INV(ADM5120_GPIO_P2L2, "otist", NULL ), + LED_INV(ADM5120_GPIO_xxxx, "usb", NULL ), +}; +#endif + +/* + * Generic board + */ +LED_ARRAY(generic) { +#if defined(CONFIG_LEDS_ADM5120_DIAG) + LED_STD(ADM5120_GPIO_PIN0, "gpio0", NULL ), + LED_STD(ADM5120_GPIO_PIN1, "gpio1", NULL ), + LED_STD(ADM5120_GPIO_PIN2, "gpio2", NULL ), + LED_STD(ADM5120_GPIO_PIN3, "gpio3", NULL ), + LED_STD(ADM5120_GPIO_PIN4, "gpio4", NULL ), + LED_STD(ADM5120_GPIO_PIN5, "gpio5", NULL ), + LED_STD(ADM5120_GPIO_PIN6, "gpio6", NULL ), + LED_STD(ADM5120_GPIO_PIN7, "gpio7", NULL ), + LED_STD(ADM5120_GPIO_P0L0, "port0led0", NULL ), + LED_STD(ADM5120_GPIO_P0L1, "port0led1", NULL ), + LED_STD(ADM5120_GPIO_P0L2, "port0led2", NULL ), + LED_STD(ADM5120_GPIO_P1L0, "port1led0", NULL ), + LED_STD(ADM5120_GPIO_P1L1, "port1led1", NULL ), + LED_STD(ADM5120_GPIO_P1L2, "port1led2", NULL ), + LED_STD(ADM5120_GPIO_P2L0, "port2led0", NULL ), + LED_STD(ADM5120_GPIO_P2L1, "port2led1", NULL ), + LED_STD(ADM5120_GPIO_P2L2, "port2led2", NULL ), + LED_STD(ADM5120_GPIO_P3L0, "port3led0", NULL ), + LED_STD(ADM5120_GPIO_P3L1, "port3led1", NULL ), + LED_STD(ADM5120_GPIO_P3L2, "port3led2", NULL ), + LED_STD(ADM5120_GPIO_P4L0, "port4led0", NULL ), + LED_STD(ADM5120_GPIO_P4L1, "port4led1", NULL ), + LED_STD(ADM5120_GPIO_P4L2, "port4led2", NULL ), +#endif +}; + +#define MACH_DATA(m, n) { \ + .machtype = (m), \ + .count = ARRAY_SIZE(n ## _leds), \ + .data = n ## _leds \ +} + +static struct mach_data machines[] __initdata = { + MACH_DATA(MACH_ADM5120_GENERIC, generic), + /* Compex */ + MACH_DATA(MACH_ADM5120_WP54AG, wp54g), + MACH_DATA(MACH_ADM5120_WP54G, wp54g), + MACH_DATA(MACH_ADM5120_WP54G_WRT, wp54g), + MACH_DATA(MACH_ADM5120_WPP54AG, wp54g), + MACH_DATA(MACH_ADM5120_WPP54G, wp54g), + /* Edimax */ + MACH_DATA(MACH_ADM5120_BR6104K, br6104k), + /* Mikrotik */ + MACH_DATA(MACH_ADM5120_RB_133, rb133), + MACH_DATA(MACH_ADM5120_RB_133C, rb133), + /* ZyXEL */ + MACH_DATA(MACH_ADM5120_P334WT, p334wt), +#if defined(CONFIG_LEDS_ADM5120_EXPERIMENTAL) + /* untested */ + MACH_DATA(MACH_ADM5120_P334, p334), + MACH_DATA(MACH_ADM5120_P335, p335), + MACH_DATA(MACH_ADM5120_RB_111, rb100), + MACH_DATA(MACH_ADM5120_RB_112, rb100), + MACH_DATA(MACH_ADM5120_NP27G, np27g), + MACH_DATA(MACH_ADM5120_NP28G, np28g), + MACH_DATA(MACH_ADM5120_NP28GHS, np28g), +#endif +}; + +static struct adm5120_leddev * __init +create_leddev(struct gpio_led_platform_data *data) +{ + struct adm5120_leddev *p; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) + return NULL; + + memcpy(&p->pdata, data, sizeof(p->pdata)); + p->pdev.dev.platform_data = &p->pdata; + + return p; +} + +static void +destroy_leddev(struct adm5120_leddev *led) +{ + if (led) + kfree(led); +} + +static struct mach_data * __init +adm5120_leds_findmach(unsigned long machtype) +{ + struct mach_data *mach; + int i; + + mach = NULL; + for (i=0; i<ARRAY_SIZE(machines); i++) { + if (machines[i].machtype == machtype) { + mach = &machines[i]; + break; + } + }; + +#if defined(CONFIG_LEDS_ADM5120_DIAG) + if (mach == NULL) + mach = machines; +#endif + + return mach; +} + +static int __init +adm5120_leds_init(void) +{ + struct mach_data *mach; + int i, ret; + + if (mips_machgroup != MACH_GROUP_ADM5120) { + ret = -EINVAL; + goto err; + } + + mach = adm5120_leds_findmach(mips_machtype); + if (mach == NULL) { + /* the board is not yet supported */ + ret = -EINVAL; + goto err; + } + + for (i=0; i < mach->count; i++) { + led_devs[i] = create_leddev(&mach->data[i]); + if (led_devs[i] == NULL) { + ret = -ENOMEM; + goto err_destroy; + } + led_devs[i]->pdev.name = "gpio-led"; + led_devs[i]->pdev.id = i; + } + + for (i=0; i < mach->count; i++) { + ret = platform_device_register(&led_devs[i]->pdev); + if (ret) + goto err_unregister; + } + + led_count = mach->count; + return 0; + +err_unregister: + for (i--; i>=0; i--) + platform_device_unregister(&led_devs[i]->pdev); + +err_destroy: + for (i=0; i<led_count; i++) + destroy_leddev(led_devs[i]); +err: + return ret; +} + +static void __exit +adm5120_leds_exit(void) +{ + int i; + + for (i=0; i < led_count; i++) { + platform_device_unregister(&led_devs[i]->pdev); + destroy_leddev(led_devs[i]); + } +} + +module_init(adm5120_leds_init); +module_exit(adm5120_leds_exit); + +MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>"); +MODULE_DESCRIPTION(DRV_DESC); +MODULE_LICENSE("GPL"); + diff --git a/target/linux/adm5120/files/drivers/leds/leds-gpio.c b/target/linux/adm5120/files/drivers/leds/leds-gpio.c new file mode 100755 index 000000000..b1a1f1ce9 --- /dev/null +++ b/target/linux/adm5120/files/drivers/leds/leds-gpio.c @@ -0,0 +1,212 @@ +/* + * $Id$ + * + * Driver for LEDs connected to GPIO lines + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * + * This file was derived from: + * /drivers/led/leds-s3c24xx.c + * (c) 2006 Simtec Electronics, Ben Dooks <ben@simtec.co.uk> + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/err.h> + +#include <linux/gpio_leds.h> + +#include <asm/io.h> +#include <asm/gpio.h> + +#define DRV_NAME "gpio-led" +#define DRV_DESC "GPIO LEDs driver" + +struct gpio_led_device { + struct led_classdev cdev; + struct gpio_led_platform_data *pdata; +}; + +static inline struct gpio_led_device *pdev_to_led(struct platform_device *dev) +{ + return platform_get_drvdata(dev); +} + +static inline struct gpio_led_device *class_to_led(struct led_classdev *led_cdev) +{ + return container_of(led_cdev, struct gpio_led_device, cdev); +} + +static void gpio_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct gpio_led_device *led; + struct gpio_led_platform_data *pdata; + + led = class_to_led(led_cdev); + pdata = led->pdata; + + switch (brightness) { + case LED_FULL: + gpio_direction_output(pdata->gpio, pdata->value_on); + break; + case LED_OFF: + gpio_direction_output(pdata->gpio, pdata->value_off); + break; + default: + gpio_direction_output(pdata->gpio, brightness); + break; + } +} + +static int __devinit gpio_led_probe(struct platform_device *dev) +{ + struct gpio_led_platform_data *pdata; + struct gpio_led_device *led; + int ret; + + pdata = dev->dev.platform_data; + if (pdata == NULL) { + dev_err(&dev->dev, "no platform data, id=%d\n", dev->id); + ret = -EINVAL; + goto err; + } + + if (pdata->name == NULL) { + dev_err(&dev->dev, "no led name specified\n"); + ret = -EINVAL; + goto err; + } + + ret = gpio_request(pdata->gpio, pdata->name); + if (ret) { + dev_err(&dev->dev, "gpio_request failed\n"); + goto err; + } + + led = kzalloc(sizeof(*led), GFP_KERNEL); + if (led == NULL) { + dev_err(&dev->dev, "no memory for device"); + ret = -ENOMEM; + goto err_free_gpio; + } + + platform_set_drvdata(dev, led); + led->pdata = pdata; + led->cdev.name = pdata->name; + led->cdev.brightness_set = gpio_led_set; +#ifdef CONFIG_LEDS_TRIGGERS + led->cdev.default_trigger = pdata->trigger; +#endif + + ret = led_classdev_register(&dev->dev, &led->cdev); + if (ret < 0) { + dev_err(&dev->dev, "led_classdev_register failed"); + goto err_free_led; + } + + return 0; + +err_free_led: + kfree(led); +err_free_gpio: + gpio_free(pdata->gpio); +err: + return ret; +} + +static int __devexit gpio_led_remove(struct platform_device *dev) +{ + struct gpio_led_device *led; + struct gpio_led_platform_data *pdata; + + pdata = dev->dev.platform_data; + + led = pdev_to_led(dev); + led_classdev_unregister(&led->cdev); + kfree(led); + + gpio_free(pdata->gpio); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + struct gpio_led_device *led; + + led = pdev_to_led(dev); + led_classdev_suspend(&led->cdev); + + return 0; +} + +static int gpio_led_resume(struct platform_device *dev) +{ + struct gpio_led_device *led; + + led = pdev_to_led(dev); + led_classdev_resume(&led->cdev); + + return 0; +} +#endif /* CONFIG_PM */ + +static struct platform_driver gpio_led_driver = { + .probe = gpio_led_probe, + .remove = __devexit_p(gpio_led_remove), +#ifdef CONFIG_PM + .suspend = gpio_led_suspend, + .resume = gpio_led_resume, +#endif + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_led_init(void) +{ + int ret; + + ret = platform_driver_register(&gpio_led_driver); + if (ret) + printk(KERN_ALERT DRV_DESC " register failed\n"); + else + printk(KERN_INFO DRV_DESC " registered\n"); + + return ret; +} + +static void __exit gpio_led_exit(void) +{ + platform_driver_unregister(&gpio_led_driver); +} + +module_init(gpio_led_init); +module_exit(gpio_led_exit); + +MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>"); +MODULE_DESCRIPTION(DRV_DESC); +MODULE_LICENSE("GPL"); diff --git a/target/linux/adm5120/files/drivers/mtd/maps/adm5120-flash.c b/target/linux/adm5120/files/drivers/mtd/maps/adm5120-flash.c new file mode 100644 index 000000000..0a2590e60 --- /dev/null +++ b/target/linux/adm5120/files/drivers/mtd/maps/adm5120-flash.c @@ -0,0 +1,574 @@ +/* + * $Id$ + * + * Platform driver for NOR flash devices on ADM5120 based boards + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * + * This file was derived from: drivers/mtd/map/physmap.c + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> + +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> + +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_switch.h> +#include <asm/mach-adm5120/adm5120_mpmc.h> +#include <asm/mach-adm5120/adm5120_platform.h> + +#define DRV_NAME "adm5120-flash" +#define DRV_DESC "ADM5120 flash MAP driver" +#define MAX_PARSED_PARTS 8 + +#ifdef ADM5120_FLASH_DEBUG +#define MAP_DBG(m, f, a...) printk(KERN_INFO "%s: " f, (m->name) , ## a) +#else +#define MAP_DBG(m, f, a...) do {} while (0) +#endif +#define MAP_ERR(m, f, a...) printk(KERN_ERR "%s: " f, (m->name) , ## a) +#define MAP_INFO(m, f, a...) printk(KERN_INFO "%s: " f, (m->name) , ## a) + +struct adm5120_map_info { + struct map_info map; + void (*switch_bank)(unsigned); + unsigned long window_size; +}; + +struct adm5120_flash_info { + struct mtd_info *mtd; + struct resource *res; + struct platform_device *dev; + struct adm5120_map_info amap; +#ifdef CONFIG_MTD_PARTITIONS + int nr_parts; + struct mtd_partition *parts[MAX_PARSED_PARTS]; +#endif +}; + +struct flash_desc { + u32 phys; + u32 srs_shift; + u32 mpmc_reg; +}; + +/* + * Globals + */ +static DEFINE_SPINLOCK(adm5120_flash_spin); +#define FLASH_LOCK() spin_lock(&adm5120_flash_spin) +#define FLASH_UNLOCK() spin_unlock(&adm5120_flash_spin) + +static u32 flash_bankwidths[4] = { 1, 2, 4, 0 }; + +static u32 flash_sizes[8] = { + 0, 512*1024, 1024*1024, 2*1024*1024, + 4*1024*1024, 0, 0, 0 +}; + +static struct flash_desc flash_descs[2] = { + { + .phys = ADM5120_SRAM0_BASE, + .mpmc_reg = MPMC_REG_SC1, + .srs_shift = MEMCTRL_SRS0_SHIFT, + }, { + .phys = ADM5120_SRAM1_BASE, + .mpmc_reg = MPMC_REG_SC0, + .srs_shift = MEMCTRL_SRS1_SHIFT, + } +}; + +static const char *probe_types[] = { + "cfi_probe", + "jedec_probe", + "map_rom", + NULL +}; + +#ifdef CONFIG_MTD_PARTITIONS +static const char *parse_types[] = { + "cmdlinepart", +#ifdef CONFIG_MTD_REDBOOT_PARTS + "RedBoot", +#endif +#ifdef CONFIG_MTD_MYLOADER_PARTS + "MyLoader", +#endif +}; +#endif + +#define BANK_SIZE (2<<20) +#define BANK_SIZE_MAX (4<<20) +#define BANK_OFFS_MASK (BANK_SIZE-1) +#define BANK_START_MASK (~BANK_OFFS_MASK) + +static inline struct adm5120_map_info *map_to_amap(struct map_info *map) +{ + return (struct adm5120_map_info *)map; +} + +static void adm5120_flash_switchbank(struct map_info *map, + unsigned long ofs) +{ + struct adm5120_map_info *amap = map_to_amap(map); + unsigned bank; + + if (amap->switch_bank == NULL) + return; + + bank = (ofs & BANK_START_MASK) >> 21; + if (bank > 1) + BUG(); + + MAP_DBG(map, "switching to bank %u, ofs=%lX\n", bank, ofs); + amap->switch_bank(bank); +} + +static map_word adm5120_flash_read(struct map_info *map, unsigned long ofs) +{ + struct adm5120_map_info *amap = map_to_amap(map); + map_word ret; + + MAP_DBG(map, "reading from ofs %lX\n", ofs); + + if (ofs >= amap->window_size) + return map_word_ff(map); + + FLASH_LOCK(); + adm5120_flash_switchbank(map, ofs); + ret = inline_map_read(map, (ofs & (amap->window_size-1))); + FLASH_UNLOCK(); + + return ret; +} + +static void adm5120_flash_write(struct map_info *map, const map_word datum, + unsigned long ofs) +{ + struct adm5120_map_info *amap = map_to_amap(map); + + MAP_DBG(map,"writing to ofs %lX\n", ofs); + + if (ofs > amap->window_size) + return; + + FLASH_LOCK(); + adm5120_flash_switchbank(map, ofs); + inline_map_write(map, datum, (ofs & (amap->window_size-1))); + FLASH_UNLOCK(); +} + +static void adm5120_flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + struct adm5120_map_info *amap = map_to_amap(map); + char *p; + ssize_t t; + + MAP_DBG(map, "copy_from, to=%lX, from=%lX, len=%lX\n", + (unsigned long)to, from, (unsigned long)len); + + if (from > amap->window_size) + return; + + p = (char *)to; + while (len > 0) { + t = len; + if ((from < BANK_SIZE) && ((from+len) > BANK_SIZE)) + t = BANK_SIZE-from; + + FLASH_LOCK(); + MAP_DBG(map, "copying %lu byte(s) from %lX to %lX\n", + (unsigned long)t, (from & (amap->window_size-1)), + (unsigned long)p); + adm5120_flash_switchbank(map, from); + inline_map_copy_from(map, p, (from & (amap->window_size-1)), t); + FLASH_UNLOCK(); + p += t; + from += t; + len -= t; + } +} + +static int adm5120_flash_initres(struct adm5120_flash_info *info) +{ + struct map_info *map = &info->amap.map; + int err = 0; + + info->res = request_mem_region(map->phys, map->size, map->name); + if (info->res == NULL) { + MAP_ERR(map, "could not reserve memory region\n"); + err = -ENOMEM; + goto out; + } + + map->virt = ioremap_nocache(map->phys, map->size); + if (map->virt == NULL) { + MAP_ERR(map, "failed to ioremap flash region\n"); + err = -ENOMEM; + goto out; + } + +out: + return err; +} + +#define SWITCH_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r)) +#define SWITCH_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))=(v) +#define MPMC_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_MPMC_BASE)+(r)) +#define MPMC_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_MPMC_BASE)+(r))=(v) + +static int adm5120_flash_initinfo(struct adm5120_flash_info *info, + struct platform_device *dev) +{ + struct map_info *map = &info->amap.map; + struct adm5120_flash_platform_data *pdata = dev->dev.platform_data; + struct flash_desc *fdesc; + u32 t; + + map->name = dev->dev.bus_id; + + if (dev->id > 1) { + MAP_ERR(map, "invalid flash id\n"); + goto err_out; + } + + fdesc = &flash_descs[dev->id]; + + /* get memory window size */ + t = SWITCH_READ(SWITCH_REG_MEMCTRL) >> fdesc->srs_shift; + t &= MEMCTRL_SRS_MASK; + info->amap.window_size = flash_sizes[t]; + if (info->amap.window_size == 0) { + MAP_ERR(map, "invalid flash size detected\n"); + goto err_out; + } + + /* get flash bus width */ + t = MPMC_READ(fdesc->mpmc_reg) & SC_MW_MASK; + map->bankwidth = flash_bankwidths[t]; + if (map->bankwidth == 0) { + MAP_ERR(map, "invalid bus width detected\n"); + goto err_out; + } + + map->phys = fdesc->phys; + map->size = BANK_SIZE_MAX; + + simple_map_init(map); + map->read = adm5120_flash_read; + map->write = adm5120_flash_write; + map->copy_from = adm5120_flash_copy_from; + + if (pdata) { + map->set_vpp = pdata->set_vpp; + info->amap.switch_bank = pdata->switch_bank; + } + + info->dev = dev; + + MAP_INFO(map, "probing at 0x%lX, size:%ldKiB, width:%d bits\n", + (unsigned long)map->phys, + (unsigned long)info->amap.window_size >> 10, + map->bankwidth*8); + + return 0; + +err_out: + return -ENODEV; +} + +static void adm5120_flash_initbanks(struct adm5120_flash_info *info) +{ + struct map_info *map = &info->amap.map; + + if (info->mtd->size <= BANK_SIZE) + /* no bank switching needed */ + return; + + if (info->amap.switch_bank) { + info->amap.window_size = info->mtd->size; + return; + } + + MAP_ERR(map, "reduce visibility from %ldKiB to %ldKiB\n", + (unsigned long)map->size >> 10, + (unsigned long)info->mtd->size >> 10); + + info->mtd->size = info->amap.window_size; +} + +#ifdef CONFIG_MTD_PARTITIONS +static int adm5120_flash_initparts(struct adm5120_flash_info *info) +{ + struct adm5120_flash_platform_data *pdata = info->dev->dev.platform_data; + struct map_info *map = &info->amap.map; + int num_parsers; + const char *parser[2]; + int err = 0; + int nr_parts; + int i; + + info->nr_parts = 0; + + if (pdata == NULL) + goto out; + + if (pdata->nr_parts) { + MAP_INFO(map, "adding static partitions\n"); + err = add_mtd_partitions(info->mtd, pdata->parts, + pdata->nr_parts); + if (err == 0) { + info->nr_parts += pdata->nr_parts; + goto out; + } + } + + num_parsers = ARRAY_SIZE(parse_types); + if (num_parsers > MAX_PARSED_PARTS) + num_parsers = MAX_PARSED_PARTS; + + parser[1] = NULL; + for (i=0; i<num_parsers; i++) { + parser[0] = parse_types[i]; + + MAP_INFO(map, "parsing \"%s\" partitions\n", + parser[0]); + nr_parts = parse_mtd_partitions(info->mtd, parser, + &info->parts[i], 0); + + if (nr_parts <= 0) + continue; + + MAP_INFO(map, "adding \"%s\" partitions\n", + parser[0]); + + err = add_mtd_partitions(info->mtd, info->parts[i], nr_parts); + if (err) + break; + + info->nr_parts += nr_parts; + } +out: + return err; +} +#else +static int adm5120_flash_initparts(struct adm5120_flash_info *info) +{ + return 0; +} +#endif /* CONFIG_MTD_PARTITIONS */ + +#ifdef CONFIG_MTD_PARTITIONS +static void adm5120_flash_remove_mtd(struct adm5120_flash_info *info) +{ + int i; + + if (info->nr_parts) { + del_mtd_partitions(info->mtd); + for (i=0; i<MAX_PARSED_PARTS; i++) + if (info->parts[i] != NULL) + kfree(info->parts[i]); + } else { + del_mtd_device(info->mtd); + } +} +#else +static void adm5120_flash_remove_mtd(struct adm5120_flash_info *info) +{ + del_mtd_device(info->mtd); +} +#endif + +static int adm5120_flash_remove(struct platform_device *dev) +{ + struct adm5120_flash_info *info; + + info = platform_get_drvdata(dev); + if (info == NULL) + return 0; + + platform_set_drvdata(dev, NULL); + + if (info->mtd != NULL) { + adm5120_flash_remove_mtd(info); + map_destroy(info->mtd); + } + + if (info->amap.map.virt != NULL) + iounmap(info->amap.map.virt); + + if (info->res != NULL) { + release_resource(info->res); + kfree(info->res); + } + + return 0; +} + +static int adm5120_flash_probe(struct platform_device *dev) +{ + struct adm5120_flash_info *info; + struct map_info *map; + const char **probe_type; + int err; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto err_out; + } + + platform_set_drvdata(dev, info); + + err = adm5120_flash_initinfo(info, dev); + if (err) + goto err_out; + + err = adm5120_flash_initres(info); + if (err) + goto err_out; + + map = &info->amap.map; + for (probe_type = probe_types; info->mtd == NULL && *probe_type != NULL; + probe_type++) + info->mtd = do_map_probe(*probe_type, map); + + if (info->mtd == NULL) { + MAP_ERR(map, "map_probe failed\n"); + err = -ENXIO; + goto err_out; + } + + adm5120_flash_initbanks(info); + + if (info->mtd->size < info->amap.window_size) { + /* readjust resources */ + iounmap(map->virt); + release_resource(info->res); + kfree(info->res); + + info->amap.window_size = info->mtd->size; + map->size = info->mtd->size; + MAP_INFO(map, "reducing map size to %ldKiB\n", + (unsigned long)map->size >> 10); + err = adm5120_flash_initres(info); + if (err) + goto err_out; + } + + MAP_INFO(map, "found at 0x%lX, size:%ldKiB, width:%d bits\n", + (unsigned long)map->phys, (unsigned long)info->mtd->size >> 10, + map->bankwidth*8); + + info->mtd->owner = THIS_MODULE; + + err = adm5120_flash_initparts(info); + if (err) + goto err_out; + + if (info->nr_parts == 0) { + MAP_INFO(map, "no partitions available, registering whole flash\n"); + add_mtd_device(info->mtd); + } + + return 0; + +err_out: + adm5120_flash_remove(dev); + return err; +} + +#ifdef CONFIG_PM +static int adm5120_flash_suspend(struct platform_device *dev, pm_message_t state) +{ + struct adm5120_flash_info *info = platform_get_drvdata(dev); + int ret = 0; + + if (info) + ret = info->mtd->suspend(info->mtd); + + return ret; +} + +static int adm5120_flash_resume(struct platform_device *dev) +{ + struct adm5120_flash_info *info = platform_get_drvdata(dev); + + if (info) + info->mtd->resume(info->mtd); + + return 0; +} + +static void adm5120_flash_shutdown(struct platform_device *dev) +{ + struct adm5120_flash_info *info = platform_get_drvdata(dev); + + if (info && info->mtd->suspend(info->mtd) == 0) + info->mtd->resume(info->mtd); +} +#endif + +static struct platform_driver adm5120_flash_driver = { + .probe = adm5120_flash_probe, + .remove = adm5120_flash_remove, +#ifdef CONFIG_PM + .suspend = adm5120_flash_suspend, + .resume = adm5120_flash_resume, + .shutdown = adm5120_flash_shutdown, +#endif + .driver = { + .name = DRV_NAME, + }, +}; + +static int __init adm5120_flash_init(void) +{ + int err; + + err = platform_driver_register(&adm5120_flash_driver); + + return err; +} + +static void __exit adm5120_flash_exit(void) +{ + platform_driver_unregister(&adm5120_flash_driver); +} + +module_init(adm5120_flash_init); +module_exit(adm5120_flash_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>"); +MODULE_DESCRIPTION(DRV_DESC); diff --git a/target/linux/adm5120/files/drivers/mtd/myloader.c b/target/linux/adm5120/files/drivers/mtd/myloader.c new file mode 100644 index 000000000..ad207e555 --- /dev/null +++ b/target/linux/adm5120/files/drivers/mtd/myloader.c @@ -0,0 +1,178 @@ +/* + * $Id$ + * + * Parse MyLoader-style flash partition tables and produce a Linux partition + * array to match. + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * + * This file was based on drivers/mtd/redboot.c + * Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com> + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/vmalloc.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include <linux/byteorder/generic.h> + +#include <prom/myloader.h> + +#define NAME_LEN_MAX 20 +#define NAME_MYLOADER "MyLoader" +#define NAME_PARTITION_TABLE "Partition Table" +#define BLOCK_LEN_MIN 0x10000 + +int parse_myloader_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + unsigned long origin) +{ + struct mylo_partition_table *tab; + struct mylo_partition *part; + struct mtd_partition *mtd_parts; + struct mtd_partition *mtd_part; + int num_parts; + int ret, i; + size_t retlen; + size_t parts_len; + char *names; + unsigned long offset; + unsigned long blocklen; + + tab = vmalloc(sizeof(*tab)); + if (!tab) { + return -ENOMEM; + goto out; + } + + blocklen = master->erasesize; + if (blocklen < BLOCK_LEN_MIN) + blocklen = BLOCK_LEN_MIN; + + /* Partition Table is always located on the second erase block */ + offset = blocklen; + printk(KERN_NOTICE "%s: searching for MyLoader partition table at " + "offset 0x%lx\n", master->name, offset); + + ret = master->read(master, offset, sizeof(*tab), &retlen, (void *)tab); + if (ret) + goto out; + + if (retlen != sizeof(*tab)) { + ret = -EIO; + goto out_free_buf; + } + + /* Check for Partition Table magic number */ + if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) { + printk(KERN_NOTICE "%s: no MyLoader partition table found\n", + master->name); + ret = 0; + goto out_free_buf; + } + + /* The MyLoader and the Partition Table is always present */ + num_parts = 2; + + /* Detect number of used partitions */ + for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { + part = &tab->partitions[i]; + + if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) + continue; + + num_parts++; + } + + mtd_parts = kzalloc((num_parts*sizeof(*mtd_part) + num_parts*NAME_LEN_MAX), + GFP_KERNEL); + + if (!mtd_parts) { + ret = -ENOMEM; + goto out_free_buf; + } + + mtd_part = mtd_parts; + names = (char *)&mtd_parts[num_parts]; + + strncpy(names, NAME_MYLOADER, NAME_LEN_MAX-1); + mtd_part->name = names; + mtd_part->offset = 0; + mtd_part->size = blocklen; + mtd_part->mask_flags = MTD_WRITEABLE; + mtd_part++; + names += NAME_LEN_MAX; + + strncpy(names, NAME_PARTITION_TABLE, NAME_LEN_MAX-1); + mtd_part->name = names; + mtd_part->offset = blocklen; + mtd_part->size = blocklen; + mtd_part->mask_flags = MTD_WRITEABLE; + mtd_part++; + names += NAME_LEN_MAX; + + for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { + part = &tab->partitions[i]; + + if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) + continue; + + sprintf(names, "partition%d", i); + mtd_part->offset = le32_to_cpu(part->addr); + mtd_part->size = le32_to_cpu(part->size); + mtd_part->name = names; + mtd_part++; + names += NAME_LEN_MAX; + } + + *pparts = mtd_parts; + ret = num_parts; + +out_free_buf: + vfree(tab); +out: + return ret; +} + +static struct mtd_part_parser mylo_mtd_parser = { + .owner = THIS_MODULE, + .parse_fn = parse_myloader_partitions, + .name = NAME_MYLOADER, +}; + +static int __init mylo_mtd_parser_init(void) +{ + return register_mtd_parser(&mylo_mtd_parser); +} + +static void __exit mylo_mtd_parser_exit(void) +{ + deregister_mtd_parser(&mylo_mtd_parser); +} + +module_init(mylo_mtd_parser_init); +module_exit(mylo_mtd_parser_exit); + +MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>"); +MODULE_DESCRIPTION("Parsing code for MyLoader partition tables"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/adm5120/files/drivers/mtd/nand/rbmipsnand.c b/target/linux/adm5120/files/drivers/mtd/nand/rbmipsnand.c new file mode 100644 index 000000000..2e7059d18 --- /dev/null +++ b/target/linux/adm5120/files/drivers/mtd/nand/rbmipsnand.c @@ -0,0 +1,207 @@ +/*==============================================================================*/ +/* rbmipsnand.c */ +/* This module is derived from the 2.4 driver shipped by Microtik for their */ +/* Routerboard 1xx and 5xx series boards. It provides support for the built in */ +/* NAND flash on the Routerboard 1xx series boards for Linux 2.6.19+. */ +/* Licence: Original Microtik code seems not to have a licence. */ +/* Rewritten code all GPL V2. */ +/* Copyright(C) 2007 david.goodenough@linkchoose.co.uk (for rewriten code) */ +/*==============================================================================*/ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/delay.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bootinfo.h> +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_defs.h> + +#define SMEM1(x) (*((volatile unsigned char *) (KSEG1ADDR(ADM5120_SRAM1_BASE) + x))) + +#define NAND_RW_REG 0x0 //data register +#define NAND_SET_CEn 0x1 //CE# low +#define NAND_CLR_CEn 0x2 //CE# high +#define NAND_CLR_CLE 0x3 //CLE low +#define NAND_SET_CLE 0x4 //CLE high +#define NAND_CLR_ALE 0x5 //ALE low +#define NAND_SET_ALE 0x6 //ALE high +#define NAND_SET_SPn 0x7 //SP# low (use spare area) +#define NAND_CLR_SPn 0x8 //SP# high (do not use spare area) +#define NAND_SET_WPn 0x9 //WP# low +#define NAND_CLR_WPn 0xA //WP# high +#define NAND_STS_REG 0xB //Status register + +#define MEM32(x) *((volatile unsigned *) (x)) + +static struct mtd_partition partition_info[] = { + { + name: "RouterBoard NAND Boot", + offset: 0, + size: 4 * 1024 * 1024 + }, + { + name: "rootfs", + offset: MTDPART_OFS_NXTBLK, + size: MTDPART_SIZ_FULL + } +}; + +static struct nand_ecclayout rb_ecclayout = { + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15 }, + .oobavail = 9, + .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1} } +}; + +struct adm5120_nand_info { + struct nand_chip chip; + struct mtd_info mtd; + void __iomem *io_base; +#ifdef CONFIG_MTD_PARTITIONS + int nr_parts; + struct mtd_partition *parts; +#endif + unsigned int init_ok; +}; + +static int rb100_dev_ready(struct mtd_info *mtd) +{ + return SMEM1(NAND_STS_REG) & 0x80; +} + +static void rbmips_hwcontrol100(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + if (ctrl & NAND_CTRL_CHANGE) + { + SMEM1((( ctrl & NAND_CLE) ? NAND_SET_CLE : NAND_CLR_CLE)) = 0x01; + SMEM1((( ctrl & NAND_ALE) ? NAND_SET_ALE : NAND_CLR_ALE)) = 0x01; + SMEM1((( ctrl & NAND_NCE) ? NAND_SET_CEn : NAND_CLR_CEn)) = 0x01; + } + if (cmd != NAND_CMD_NONE) + writeb( cmd, chip->IO_ADDR_W); +} + +/*========================================================================*/ +/* We need to use the OLD Yaffs-1 OOB layout, otherwise the RB bootloader */ +/* will not be able to find the kernel that we load. So set the oobinfo */ +/* when creating the partitions. */ +/*========================================================================*/ + + +unsigned get_rbnand_block_size(struct adm5120_nand_info *data) +{ + return data->init_ok ? data->mtd.writesize : 0; +} + +EXPORT_SYMBOL(get_rbnand_block_size); + +static int rbmips_probe(struct platform_device *pdev) +{ + struct adm5120_nand_info *data; + int res = 0; + + /* Allocate memory for the nand_chip structure */ + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "Failed to allocate device structure\n"); + return -ENOMEM; + + } + + data->io_base = ioremap(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); + + if (data->io_base == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + kfree(data); + return -EIO; + } + + MEM32(0xB2000064) = 0x100; + MEM32(0xB2000008) = 0x1; + SMEM1(NAND_SET_SPn) = 0x01; + SMEM1(NAND_CLR_WPn) = 0x01; + + data->chip.priv = &data; + data->mtd.priv = &data->chip; + data->mtd.owner = THIS_MODULE; + + data->init_ok = 0; + data->chip.IO_ADDR_R = (unsigned char *)KSEG1ADDR(ADM5120_SRAM1_BASE); + data->chip.IO_ADDR_W = data->chip.IO_ADDR_R; + data->chip.cmd_ctrl = rbmips_hwcontrol100; + data->chip.dev_ready = rb100_dev_ready; + data->chip.ecc.mode = NAND_ECC_SOFT; + data->chip.ecc.layout = &rb_ecclayout; + data->chip.chip_delay = 25; + data->chip.options |= NAND_NO_AUTOINCR; + + platform_set_drvdata(pdev, data); + + /* Why do we need to scan 4 times ? */ + if (nand_scan(&data->mtd, 1) && nand_scan(&data->mtd, 1) && nand_scan(&data->mtd, 1) && nand_scan(&data->mtd, 1)) { + printk(KERN_INFO "RB1xxx nand device not found\n"); + res = -ENXIO; + goto out; + } + + add_mtd_partitions(&data->mtd, partition_info, 2); + data->init_ok = 1; + + res = add_mtd_device(&data->mtd); + if (!res) + return res; + + nand_release(&data->mtd); +out: + platform_set_drvdata(pdev, NULL); + iounmap(data->io_base); + kfree(data); + return res; +} + +static int __devexit rbmips_remove(struct platform_device *pdev) +{ + struct adm5120_nand_info *data = platform_get_drvdata(pdev); + + nand_release(&data->mtd); + iounmap(data->io_base); + kfree(data); + + return 0; +} + +static struct platform_driver adm5120_nand_driver = { + .probe = rbmips_probe, + .remove = rbmips_remove, + .driver = { + .name = "adm5120-nand", + .owner = THIS_MODULE, + }, +}; + +static int __init adm5120_nand_init(void) +{ + int err; + err = platform_driver_register(&adm5120_nand_driver); + return err; +} + +static void __exit adm5120_nand_exit(void) +{ + platform_driver_unregister(&adm5120_nand_driver); +} + +module_init(adm5120_nand_init); +module_exit(adm5120_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Goodenough, Florian Fainelli"); +MODULE_DESCRIPTION("RouterBOARD 100 NAND driver"); + diff --git a/target/linux/adm5120/files/drivers/net/adm5120sw.c b/target/linux/adm5120/files/drivers/net/adm5120sw.c new file mode 100644 index 000000000..80c81abf7 --- /dev/null +++ b/target/linux/adm5120/files/drivers/net/adm5120sw.c @@ -0,0 +1,625 @@ +/* + * ADM5120 built in ethernet switch driver + * + * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005 + * + * Inspiration for this driver came from the original ADMtek 2.4 + * driver, Copyright ADMtek Inc. + * + * NAPI extensions by Thomas Langer (Thomas.Langer@infineon.com) + * and Friedrich Beckmann (Friedrich.Beckmann@infineon.com), 2007 + * + * TODO: Add support of high prio queues (currently disabled) + * + */ +#include <linux/autoconf.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <asm/mipsregs.h> +#include <asm/irq.h> +#include <asm/io.h> +#include "adm5120sw.h" + +#include <asm/mach-adm5120/adm5120_info.h> +#include <asm/mach-adm5120/adm5120_irq.h> + +MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)"); +MODULE_DESCRIPTION("ADM5120 ethernet switch driver"); +MODULE_LICENSE("GPL"); + +/* default settings - unlimited TX and RX on all ports, default shaper mode */ +static unsigned char bw_matrix[SW_DEVS] = { + 0, 0, 0, 0, 0, 0 +}; + +static int adm5120_nrdevs; + +static struct net_device *adm5120_devs[SW_DEVS]; +/* Lookup table port -> device */ +static struct net_device *adm5120_port[SW_DEVS]; + +static struct adm5120_dma + adm5120_dma_txh_v[ADM5120_DMA_TXH] __attribute__((aligned(16))), + adm5120_dma_txl_v[ADM5120_DMA_TXL] __attribute__((aligned(16))), + adm5120_dma_rxh_v[ADM5120_DMA_RXH] __attribute__((aligned(16))), + adm5120_dma_rxl_v[ADM5120_DMA_RXL] __attribute__((aligned(16))), + *adm5120_dma_txh, + *adm5120_dma_txl, + *adm5120_dma_rxh, + *adm5120_dma_rxl; +static struct sk_buff + *adm5120_skb_rxh[ADM5120_DMA_RXH], + *adm5120_skb_rxl[ADM5120_DMA_RXL], + *adm5120_skb_txh[ADM5120_DMA_TXH], + *adm5120_skb_txl[ADM5120_DMA_TXL]; +static int adm5120_rxli = 0; +static int adm5120_txli = 0; +/*static int adm5120_txhi = 0;*/ +static int adm5120_if_open = 0; + +static inline void adm5120_set_reg(unsigned int reg, unsigned long val) +{ + *(volatile unsigned long*)(SW_BASE+reg) = val; +} + +static inline unsigned long adm5120_get_reg(unsigned int reg) +{ + return *(volatile unsigned long*)(SW_BASE+reg); +} + +static inline void adm5120_rx_dma_update(struct adm5120_dma *dma, + struct sk_buff *skb, int end) +{ + dma->status = 0; + dma->cntl = 0; + dma->len = ADM5120_DMA_RXSIZE; + dma->data = ADM5120_DMA_ADDR(skb->data) | + ADM5120_DMA_OWN | (end ? ADM5120_DMA_RINGEND : 0); +} + +static int adm5120_rx(struct net_device *dev,int *budget) +{ + struct sk_buff *skb, *skbn; + struct adm5120_sw *priv; + struct net_device *cdev; + struct adm5120_dma *dma; + int port, len, quota; + + quota = min(dev->quota, *budget); + dma = &adm5120_dma_rxl[adm5120_rxli]; + while (!(dma->data & ADM5120_DMA_OWN) && quota) { + port = (dma->status & ADM5120_DMA_PORTID); + port >>= ADM5120_DMA_PORTSHIFT; + cdev = adm5120_port[port]; + if (cdev != dev) { /* The current packet belongs to a different device */ + if ((cdev==NULL) || !netif_running(cdev)) { + /* discard (update with old skb) */ + skb = skbn = NULL; + goto rx_skip; + } + else { + netif_rx_schedule(cdev);/* Start polling next device */ + return 1; /* return 1 -> More packets to process */ + } + + } + skb = adm5120_skb_rxl[adm5120_rxli]; + len = (dma->status & ADM5120_DMA_LEN); + len >>= ADM5120_DMA_LENSHIFT; + len -= ETH_FCS; + + priv = netdev_priv(dev); + if (len <= 0 || len > ADM5120_DMA_RXSIZE || + dma->status & ADM5120_DMA_FCSERR) { + priv->stats.rx_errors++; + skbn = NULL; + } else { + skbn = dev_alloc_skb(ADM5120_DMA_RXSIZE+16); + if (skbn) { + skb_put(skb, len); + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += len; + skb_reserve(skbn, NET_IP_ALIGN); + adm5120_skb_rxl[adm5120_rxli] = skbn; + } else { + printk(KERN_INFO "%s recycling!\n", dev->name); + } + } +rx_skip: + adm5120_rx_dma_update(&adm5120_dma_rxl[adm5120_rxli], + adm5120_skb_rxl[adm5120_rxli], + (ADM5120_DMA_RXL-1==adm5120_rxli)); + if (ADM5120_DMA_RXL == ++adm5120_rxli) + adm5120_rxli = 0; + dma = &adm5120_dma_rxl[adm5120_rxli]; + if (skbn){ + netif_receive_skb(skb); + dev->quota--; + (*budget)--; + quota--; + } + } /* while */ + /* If there are still packets to process, return 1 */ + if (quota){ + /* No more packets to process, so disable the polling and reenable the interrupts */ + netif_rx_complete(dev); + adm5120_set_reg(ADM5120_INT_MASK, + adm5120_get_reg(ADM5120_INT_MASK) & + ~(ADM5120_INT_RXL|ADM5120_INT_LFULL)); + return 0; + + + } + return 1; +} + +static irqreturn_t adm5120_sw_irq(int irq, void *dev_id) +{ + unsigned long intreg, intmask; + int port; + struct net_device *dev; + + intmask = adm5120_get_reg(ADM5120_INT_MASK); /* Remember interrupt mask */ + adm5120_set_reg(ADM5120_INT_MASK, ADM5120_INTMASKALL); /* Disable interrupts */ + + intreg = adm5120_get_reg(ADM5120_INT_ST); /* Read interrupt status */ + adm5120_set_reg(ADM5120_INT_ST, intreg); /* Clear interrupt status */ + + /* In NAPI operation the interrupts are disabled and the polling mechanism + * is activated. The interrupts are finally enabled again in the polling routine. + */ + if (intreg & (ADM5120_INT_RXL|ADM5120_INT_LFULL)) { + /* check rx buffer for port number */ + port = adm5120_dma_rxl[adm5120_rxli].status & ADM5120_DMA_PORTID; + port >>= ADM5120_DMA_PORTSHIFT; + dev = adm5120_port[port]; + if ((dev==NULL) || !netif_running(dev)) { + /* discard (update with old skb) */ + adm5120_rx_dma_update(&adm5120_dma_rxl[adm5120_rxli], + adm5120_skb_rxl[adm5120_rxli], + (ADM5120_DMA_RXL-1==adm5120_rxli)); + if (ADM5120_DMA_RXL == ++adm5120_rxli) + adm5120_rxli = 0; + } + else { + netif_rx_schedule(dev); + intmask |= (ADM5120_INT_RXL|ADM5120_INT_LFULL); /* Disable RX interrupts */ + } + } +#ifdef CONFIG_DEBUG + if (intreg & ~(intmask)) + printk(KERN_INFO "adm5120sw: IRQ 0x%08X unexpected!\n", (unsigned int)(intreg & ~(intmask))); +#endif + + adm5120_set_reg(ADM5120_INT_MASK, intmask); + + return IRQ_HANDLED; +} + +static void adm5120_set_vlan(char *matrix) +{ + unsigned long val; + int vlan_port, port; + + val = matrix[0] + (matrix[1]<<8) + (matrix[2]<<16) + (matrix[3]<<24); + adm5120_set_reg(ADM5120_VLAN_GI, val); + val = matrix[4] + (matrix[5]<<8); + adm5120_set_reg(ADM5120_VLAN_GII, val); + /* Now set/update the port vs. device lookup table */ + for (port=0; port<SW_DEVS; port++) { + for (vlan_port=0; vlan_port<SW_DEVS && !(matrix[vlan_port] & (0x00000001 << port)); vlan_port++); + if (vlan_port <SW_DEVS) + adm5120_port[port] = adm5120_devs[vlan_port]; + else + adm5120_port[port] = NULL; + } +} + +static void adm5120_set_bw(char *matrix) +{ + unsigned long val; + + /* Port 0 to 3 are set using the bandwidth control 0 register */ + val = matrix[0] + (matrix[1]<<8) + (matrix[2]<<16) + (matrix[3]<<24); + adm5120_set_reg(ADM5120_BW_CTL0, val); + + /* Port 4 and 5 are set using the bandwidth control 1 register */ + val = matrix[4]; + if (matrix[5] == 1) + adm5120_set_reg(ADM5120_BW_CTL1, val | 0x80000000); + else + adm5120_set_reg(ADM5120_BW_CTL1, val & ~0x8000000); + + printk(KERN_DEBUG "D: ctl0 0x%lx, ctl1 0x%lx\n", + adm5120_get_reg(ADM5120_BW_CTL0), + adm5120_get_reg(ADM5120_BW_CTL1)); +} + +static int adm5120_sw_open(struct net_device *dev) +{ + unsigned long val; + int i; + + netif_start_queue(dev); + if (!adm5120_if_open++) { + /* enable interrupts on first open */ + adm5120_set_reg(ADM5120_INT_MASK, + adm5120_get_reg(ADM5120_INT_MASK) & + ~(ADM5120_INT_RXL|ADM5120_INT_LFULL)); + } + /* enable (additional) port */ + val = adm5120_get_reg(ADM5120_PORT_CONF0); + for (i=0; i<SW_DEVS; i++) { + if (dev == adm5120_devs[i]) + val &= ~adm5120_eth_vlans[i]; + } + adm5120_set_reg(ADM5120_PORT_CONF0, val); + return 0; +} + +static int adm5120_sw_stop(struct net_device *dev) +{ + unsigned long val; + int i; + + if (!--adm5120_if_open) { + adm5120_set_reg(ADM5120_INT_MASK, ADM5120_INTMASKALL); + } + /* disable port if not assigned to other devices */ + val = adm5120_get_reg(ADM5120_PORT_CONF0) | ADM5120_PORTDISALL; + for (i=0; i<SW_DEVS; i++) { + if ((dev != adm5120_devs[i]) && netif_running(adm5120_devs[i])) + val &= ~adm5120_eth_vlans[i]; + } + adm5120_set_reg(ADM5120_PORT_CONF0, val); + netif_stop_queue(dev); + return 0; +} + +static int adm5120_sw_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct adm5120_dma *dma; + struct sk_buff **skbl = adm5120_skb_txl; + struct adm5120_sw *priv = netdev_priv(dev); + unsigned long data; + + dev->trans_start = jiffies; + dma = &adm5120_dma_txl[adm5120_txli]; + if (dma->data & ADM5120_DMA_OWN) { + /* We want to write a packet but the TX queue is still + * occupied by the DMA. We are faster than the DMA... */ + dev_kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + data = ADM5120_DMA_ADDR(skb->data) | ADM5120_DMA_OWN; + if (adm5120_txli == ADM5120_DMA_TXL-1) + data |= ADM5120_DMA_RINGEND; + dma->status = + ((skb->len<ETH_ZLEN?ETH_ZLEN:skb->len) << ADM5120_DMA_LENSHIFT) | + (0x1 << priv->port); + + dma->len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + + /* free old skbs here instead of tx completion interrupt: + * will hold some more memory allocated but reduces interrupts */ + if (skbl[adm5120_txli]){ + dev_kfree_skb(skbl[adm5120_txli]); + } + skbl[adm5120_txli] = skb; + + dma->data = data; /* Here we enable the buffer for the TX DMA machine */ + adm5120_set_reg(ADM5120_SEND_TRIG, ADM5120_SEND_TRIG_L); + if (++adm5120_txli == ADM5120_DMA_TXL) + adm5120_txli = 0; + return 0; +} + +static void adm5120_tx_timeout(struct net_device *dev) +{ + printk(KERN_INFO "%s: TX timeout\n",dev->name); +} + +static struct net_device_stats *adm5120_sw_stats(struct net_device *dev) +{ + struct adm5120_sw *priv = netdev_priv(dev); + int portmask; + unsigned long adm5120_cpup_conf_reg; + + portmask = adm5120_eth_vlans[priv->port] & 0x3f; + + adm5120_cpup_conf_reg = adm5120_get_reg(ADM5120_CPUP_CONF); + + if (dev->flags & IFF_PROMISC) + adm5120_cpup_conf_reg &= ~((portmask << ADM5120_DISUNSHIFT) & ADM5120_DISUNALL); + else + adm5120_cpup_conf_reg |= (portmask << ADM5120_DISUNSHIFT); + + if (dev->flags & IFF_PROMISC || dev->flags & IFF_ALLMULTI || dev->mc_count) + adm5120_cpup_conf_reg &= ~((portmask << ADM5120_DISMCSHIFT) & ADM5120_DISMCALL); + else + adm5120_cpup_conf_reg |= (portmask << ADM5120_DISMCSHIFT); + + /* If there is any port configured to be in promiscuous mode, then the */ + /* Bridge Test Mode has to be activated. This will result in */ + /* transporting also packets learned in another VLAN to be forwarded */ + /* to the CPU. */ + /* The difficult scenario is when we want to build a bridge on the CPU.*/ + /* Assume we have port0 and the CPU port in VLAN0 and port1 and the */ + /* CPU port in VLAN1. Now we build a bridge on the CPU between */ + /* VLAN0 and VLAN1. Both ports of the VLANs are set in promisc mode. */ + /* Now assume a packet with ethernet source address 99 enters port 0 */ + /* It will be forwarded to the CPU because it is unknown. Then the */ + /* bridge in the CPU will send it to VLAN1 and it goes out at port 1. */ + /* When now a packet with ethernet destination address 99 comes in at */ + /* port 1 in VLAN1, then the switch has learned that this address is */ + /* located at port 0 in VLAN0. Therefore the switch will drop */ + /* this packet. In order to avoid this and to send the packet still */ + /* to the CPU, the Bridge Test Mode has to be activated. */ + + /* Check if there is any vlan in promisc mode. */ + if (~adm5120_cpup_conf_reg & ADM5120_DISUNALL) + adm5120_cpup_conf_reg |= ADM5120_BTM; /* Set the BTM */ + else + adm5120_cpup_conf_reg &= ~ADM5120_BTM; /* Disable the BTM */ + + adm5120_set_reg(ADM5120_CPUP_CONF,adm5120_cpup_conf_reg); + + return &((struct adm5120_sw *)netdev_priv(dev))->stats; +} + +static void adm5120_set_multicast_list(struct net_device *dev) +{ + struct adm5120_sw *priv = netdev_priv(dev); + int portmask; + + portmask = adm5120_eth_vlans[priv->port] & 0x3f; + + if (dev->flags & IFF_PROMISC) + adm5120_set_reg(ADM5120_CPUP_CONF, + adm5120_get_reg(ADM5120_CPUP_CONF) & + ~((portmask << ADM5120_DISUNSHIFT) & ADM5120_DISUNALL)); + else + adm5120_set_reg(ADM5120_CPUP_CONF, + adm5120_get_reg(ADM5120_CPUP_CONF) | + (portmask << ADM5120_DISUNSHIFT)); + + if (dev->flags & IFF_PROMISC || dev->flags & IFF_ALLMULTI || + dev->mc_count) + adm5120_set_reg(ADM5120_CPUP_CONF, + adm5120_get_reg(ADM5120_CPUP_CONF) & + ~((portmask << ADM5120_DISMCSHIFT) & ADM5120_DISMCALL)); + else + adm5120_set_reg(ADM5120_CPUP_CONF, + adm5120_get_reg(ADM5120_CPUP_CONF) | + (portmask << ADM5120_DISMCSHIFT)); +} + +static void adm5120_write_mac(struct net_device *dev) +{ + struct adm5120_sw *priv = netdev_priv(dev); + unsigned char *mac = dev->dev_addr; + + adm5120_set_reg(ADM5120_MAC_WT1, + mac[2] | (mac[3]<<8) | (mac[4]<<16) | (mac[5]<<24)); + adm5120_set_reg(ADM5120_MAC_WT0, (priv->port<<3) | + (mac[0]<<16) | (mac[1]<<24) | ADM5120_MAC_WRITE | ADM5120_VLAN_EN); + + while (!(adm5120_get_reg(ADM5120_MAC_WT0) & ADM5120_MAC_WRITE_DONE)); +} + +static int adm5120_sw_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + adm5120_write_mac(dev); + return 0; +} + +static int adm5120_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + int err; + struct adm5120_sw_info info; + struct adm5120_sw *priv = netdev_priv(dev); + + switch(cmd) { + case SIOCGADMINFO: + info.magic = 0x5120; + info.ports = adm5120_nrdevs; + info.vlan = priv->port; + err = copy_to_user(rq->ifr_data, &info, sizeof(info)); + if (err) + return -EFAULT; + break; + case SIOCSMATRIX: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + err = copy_from_user(adm5120_eth_vlans, rq->ifr_data, + sizeof(adm5120_eth_vlans)); + if (err) + return -EFAULT; + adm5120_set_vlan(adm5120_eth_vlans); + break; + case SIOCGMATRIX: + err = copy_to_user(rq->ifr_data, adm5120_eth_vlans, + sizeof(adm5120_eth_vlans)); + if (err) + return -EFAULT; + break; + case SIOCGETBW: + err = copy_to_user(rq->ifr_data, bw_matrix, sizeof(bw_matrix)); + if (err) + return -EFAULT; + break; + case SIOCSETBW: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + err = copy_from_user(bw_matrix, rq->ifr_data, sizeof(bw_matrix)); + if (err) + return -EFAULT; + adm5120_set_bw(bw_matrix); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static void adm5120_dma_tx_init(struct adm5120_dma *dma, struct sk_buff **skbl, + int num) +{ + memset(dma, 0, sizeof(struct adm5120_dma)*num); + dma[num-1].data |= ADM5120_DMA_RINGEND; + memset(skbl, 0, sizeof(struct skb*)*num); +} + +static void adm5120_dma_rx_init(struct adm5120_dma *dma, struct sk_buff **skbl, + int num) +{ + int i; + + memset(dma, 0, sizeof(struct adm5120_dma)*num); + for (i=0; i<num; i++) { + skbl[i] = dev_alloc_skb(ADM5120_DMA_RXSIZE+16); + if (!skbl[i]) { + i=num; + break; + } + skb_reserve(skbl[i], NET_IP_ALIGN); + adm5120_rx_dma_update(&dma[i], skbl[i], (num-1==i)); + } +} + +static int __init adm5120_sw_init(void) +{ + int i, err; + struct net_device *dev; + + err = request_irq(ADM5120_IRQ_SWITCH, adm5120_sw_irq, 0, "ethernet switch", NULL); + if (err) + goto out; + + adm5120_nrdevs = adm5120_eth_num_ports; + + adm5120_set_reg(ADM5120_CPUP_CONF, + ADM5120_DISCCPUPORT | ADM5120_CRC_PADDING | + ADM5120_DISUNALL | ADM5120_DISMCALL); + adm5120_set_reg(ADM5120_PORT_CONF0, ADM5120_ENMC | ADM5120_ENBP | ADM5120_PORTDISALL); + + adm5120_set_reg(ADM5120_PHY_CNTL2, adm5120_get_reg(ADM5120_PHY_CNTL2) | + ADM5120_AUTONEG | ADM5120_NORMAL | ADM5120_AUTOMDIX); + adm5120_set_reg(ADM5120_PHY_CNTL3, adm5120_get_reg(ADM5120_PHY_CNTL3) | + ADM5120_PHY_NTH); + + /* Force all the packets from all ports are low priority */ + adm5120_set_reg(ADM5120_PRI_CNTL, 0); + + adm5120_set_reg(ADM5120_INT_MASK, ADM5120_INTMASKALL); + adm5120_set_reg(ADM5120_INT_ST, ADM5120_INTMASKALL); + + adm5120_dma_txh = (void *)KSEG1ADDR((u32)adm5120_dma_txh_v); + adm5120_dma_txl = (void *)KSEG1ADDR((u32)adm5120_dma_txl_v); + adm5120_dma_rxh = (void *)KSEG1ADDR((u32)adm5120_dma_rxh_v); + adm5120_dma_rxl = (void *)KSEG1ADDR((u32)adm5120_dma_rxl_v); + + adm5120_dma_tx_init(adm5120_dma_txh, adm5120_skb_txh, ADM5120_DMA_TXH); + adm5120_dma_tx_init(adm5120_dma_txl, adm5120_skb_txl, ADM5120_DMA_TXL); + adm5120_dma_rx_init(adm5120_dma_rxh, adm5120_skb_rxh, ADM5120_DMA_RXH); + adm5120_dma_rx_init(adm5120_dma_rxl, adm5120_skb_rxl, ADM5120_DMA_RXL); + adm5120_set_reg(ADM5120_SEND_HBADDR, KSEG1ADDR(adm5120_dma_txh)); + adm5120_set_reg(ADM5120_SEND_LBADDR, KSEG1ADDR(adm5120_dma_txl)); + adm5120_set_reg(ADM5120_RECEIVE_HBADDR, KSEG1ADDR(adm5120_dma_rxh)); + adm5120_set_reg(ADM5120_RECEIVE_LBADDR, KSEG1ADDR(adm5120_dma_rxl)); + + for (i = 0; i < SW_DEVS; i++) { + adm5120_devs[i] = alloc_etherdev(sizeof(struct adm5120_sw)); + if (!adm5120_devs[i]) { + err = -ENOMEM; + goto out_int; + } + + dev = adm5120_devs[i]; + SET_MODULE_OWNER(dev); + memset(netdev_priv(dev), 0, sizeof(struct adm5120_sw)); + ((struct adm5120_sw*)netdev_priv(dev))->port = i; + dev->base_addr = SW_BASE; + dev->irq = ADM5120_IRQ_SWITCH; + dev->open = adm5120_sw_open; + dev->hard_start_xmit = adm5120_sw_tx; + dev->stop = adm5120_sw_stop; + dev->get_stats = adm5120_sw_stats; + dev->set_multicast_list = adm5120_set_multicast_list; + dev->do_ioctl = adm5120_do_ioctl; + dev->tx_timeout = adm5120_tx_timeout; + dev->watchdog_timeo = ETH_TX_TIMEOUT; + dev->set_mac_address = adm5120_sw_set_mac_address; + dev->poll = adm5120_rx; + dev->weight = 64; + + memcpy(dev->dev_addr, adm5120_eth_macs[i], 6); + adm5120_write_mac(dev); + + if ((err = register_netdev(dev))) { + free_netdev(dev); + goto out_int; + } + printk(KERN_INFO "%s: ADM5120 switch port%d\n", dev->name, i); + } + /* setup vlan/port mapping after devs are filled up */ + adm5120_set_vlan(adm5120_eth_vlans); + + adm5120_set_reg(ADM5120_CPUP_CONF, + ADM5120_CRC_PADDING | ADM5120_DISUNALL | ADM5120_DISMCALL); + + return 0; + +out_int: + /* Undo everything that did succeed */ + for (; i; i--) { + unregister_netdev(adm5120_devs[i-1]); + free_netdev(adm5120_devs[i-1]); + } + free_irq(ADM5120_IRQ_SWITCH, NULL); +out: + printk(KERN_ERR "ADM5120 Ethernet switch init failed\n"); + return err; +} + +static void __exit adm5120_sw_exit(void) +{ + int i; + + for (i = 0; i < SW_DEVS; i++) { + unregister_netdev(adm5120_devs[i]); + free_netdev(adm5120_devs[i]); + } + + free_irq(ADM5120_IRQ_SWITCH, NULL); + + for (i = 0; i < ADM5120_DMA_RXH; i++) { + if (!adm5120_skb_rxh[i]) + break; + kfree_skb(adm5120_skb_rxh[i]); + } + for (i = 0; i < ADM5120_DMA_RXL; i++) { + if (!adm5120_skb_rxl[i]) + break; + kfree_skb(adm5120_skb_rxl[i]); + } +} + +module_init(adm5120_sw_init); +module_exit(adm5120_sw_exit); diff --git a/target/linux/adm5120/files/drivers/net/adm5120sw.h b/target/linux/adm5120/files/drivers/net/adm5120sw.h new file mode 100644 index 000000000..a3b4ec017 --- /dev/null +++ b/target/linux/adm5120/files/drivers/net/adm5120sw.h @@ -0,0 +1,114 @@ +/* + * Defines for ADM5120 built in ethernet switch driver + * + * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005 + * + * Values come from ADM5120 datasheet and original ADMtek 2.4 driver, + * Copyright ADMtek Inc. + */ + +#ifndef _INCLUDE_ADM5120SW_H_ +#define _INCLUDE_ADM5120SW_H_ + +#define SW_BASE KSEG1ADDR(0x12000000) +#define SW_DEVS 6 + +#define ETH_TX_TIMEOUT HZ*400 +#define ETH_FCS 4; + +#define ADM5120_CODE 0x00 /* CPU description */ +#define ADM5120_CODE_PQFP 0x20000000 /* package type */ +#define ADM5120_SW_CONF 0x20 /* Switch configuration register */ +#define ADM5120_SW_CONF_BPM 0x00300000 /* Mask for backpressure mode */ +#define ADM5120_CPUP_CONF 0x24 /* CPU port config */ +#define ADM5120_DISCCPUPORT 0x00000001 /* disable cpu port */ +#define ADM5120_CRC_PADDING 0x00000002 /* software crc */ +#define ADM5120_BTM 0x00000004 /* bridge test mode */ +#define ADM5120_DISUNSHIFT 9 +#define ADM5120_DISUNALL 0x00007e00 /* disable unknown from all */ +#define ADM5120_DISMCSHIFT 16 +#define ADM5120_DISMCALL 0x003f0000 /* disable multicast from all */ +#define ADM5120_PORT_CONF0 0x28 +#define ADM5120_ENMC 0x00003f00 /* Enable MC routing (ex cpu) */ +#define ADM5120_ENBP 0x003f0000 /* Enable Back Pressure */ +#define ADM5120_PORTDISALL 0x0000003F +#define ADM5120_VLAN_GI 0x40 /* VLAN settings */ +#define ADM5120_VLAN_GII 0x44 +#define ADM5120_SEND_TRIG 0x48 +#define ADM5120_SEND_TRIG_L 0x00000001 +#define ADM5120_SEND_TRIG_H 0x00000002 +#define ADM5120_MAC_WT0 0x58 +#define ADM5120_MAC_WRITE 0x00000001 +#define ADM5120_MAC_WRITE_DONE 0x00000002 +#define ADM5120_VLAN_EN 0x00000040 +#define ADM5120_MAC_WT1 0x5c +#define ADM5120_BW_CTL0 0x60 /* Bandwidth control 0 */ +#define ADM5120_BW_CTL1 0x64 /* Bandwidth control 1 */ +#define ADM5120_PHY_CNTL2 0x7c +#define ADM5120_AUTONEG 0x0000001f /* Auto negotiate */ +#define ADM5120_NORMAL 0x01f00000 /* PHY normal mode */ +#define ADM5120_AUTOMDIX 0x3e000000 /* Auto MDIX */ +#define ADM5120_PHY_CNTL3 0x80 +#define ADM5120_PHY_NTH 0x00000400 +#define ADM5120_PRI_CNTL 0x84 +#define ADM5120_INT_ST 0xb0 +#define ADM5120_INT_RXH 0x0000004 +#define ADM5120_INT_RXL 0x0000008 +#define ADM5120_INT_HFULL 0x0000010 +#define ADM5120_INT_LFULL 0x0000020 +#define ADM5120_INT_TXH 0x0000001 +#define ADM5120_INT_TXL 0x0000002 +#define ADM5120_INT_MASK 0xb4 +#define ADM5120_INTMASKALL 0x1FDEFFF /* All interrupts */ +#define ADM5120_INTHANDLE (ADM5120_INT_RXH | ADM5120_INT_RXL | \ + ADM5120_INT_HFULL | ADM5120_INT_LFULL | \ + ADM5120_INT_TXH | ADM5120_INT_TXL) +#define ADM5120_SEND_HBADDR 0xd0 +#define ADM5120_SEND_LBADDR 0xd4 +#define ADM5120_RECEIVE_HBADDR 0xd8 +#define ADM5120_RECEIVE_LBADDR 0xdc + +struct adm5120_dma { + u32 data; + u32 cntl; + u32 len; + u32 status; +} __attribute__ ((packed)); + +#define ADM5120_DMA_MASK 0x01ffffff +#define ADM5120_DMA_OWN 0x80000000 /* buffer owner */ +#define ADM5120_DMA_RINGEND 0x10000000 /* Last in DMA ring */ + +#define ADM5120_DMA_ADDR(ptr) ((u32)(ptr) & ADM5120_DMA_MASK) +#define ADM5120_DMA_PORTID 0x00007000 +#define ADM5120_DMA_PORTSHIFT 12 +#define ADM5120_DMA_LEN 0x07ff0000 +#define ADM5120_DMA_LENSHIFT 16 +#define ADM5120_DMA_FCSERR 0x00000008 + +#define ADM5120_DMA_TXH 2 +#define ADM5120_DMA_TXL 64 +#define ADM5120_DMA_RXH 2 +#define ADM5120_DMA_RXL 64 + +#define ADM5120_DMA_RXSIZE 1550 +#define ADM5120_DMA_EXTRA 20 + +struct adm5120_sw { + int port; + struct net_device_stats stats; +}; + +#define SIOCSMATRIX SIOCDEVPRIVATE +#define SIOCGMATRIX SIOCDEVPRIVATE+1 +#define SIOCGADMINFO SIOCDEVPRIVATE+2 +#define SIOCGETBW SIOCDEVPRIVATE+3 +#define SIOCSETBW SIOCDEVPRIVATE+4 + +struct adm5120_sw_info { + u16 magic; + u16 ports; + u16 vlan; +}; + +#endif /* _INCLUDE_ADM5120SW_H_ */ diff --git a/target/linux/adm5120/files/drivers/serial/adm5120_uart.c b/target/linux/adm5120/files/drivers/serial/adm5120_uart.c new file mode 100644 index 000000000..83c5f7201 --- /dev/null +++ b/target/linux/adm5120/files/drivers/serial/adm5120_uart.c @@ -0,0 +1,520 @@ +/* + * Serial driver for ADM5120 SoC + * + * Derived from drivers/serial/uart00.c + * Copyright 2001 Altera Corporation + * + * Some pieces are derived from the ADMtek 2.4 serial driver. + * Copyright (C) ADMtek Incorporated, 2003 + * daniell@admtek.com.tw + * Which again was derived from drivers/char/serial.c + * Copyright (C) Linus Torvalds et al. + * + * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005 + */ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/console.h> + +#include <asm/mach-adm5120/adm5120_defs.h> +#include <asm/mach-adm5120/adm5120_irq.h> + +#define ADM5120_UART_REG(base, reg) \ + (*(volatile u32 *)KSEG1ADDR((base)+(reg))) + +#define ADM5120_UARTCLK_FREQ 62500000 +#define ADM5120_UART_BAUDDIV(rate) ((unsigned long)(ADM5120_UARTCLK_FREQ/(16*(rate)) - 1)) + +#define ADM5120_UART_BAUD115200 ADM5120_UART_BAUDDIV(115200) + +#define ADM5120_UART_DATA 0x00 +#define ADM5120_UART_RS 0x04 +#define ADM5120_UART_LCR_H 0x08 +#define ADM5120_UART_LCR_M 0x0c +#define ADM5120_UART_LCR_L 0x10 +#define ADM5120_UART_CR 0x14 +#define ADM5120_UART_FR 0x18 +#define ADM5120_UART_IR 0x1c + +#define ADM5120_UART_FE 0x01 +#define ADM5120_UART_PE 0x02 +#define ADM5120_UART_BE 0x04 +#define ADM5120_UART_OE 0x08 +#define ADM5120_UART_ERR 0x0f +#define ADM5120_UART_FIFO_EN 0x10 +#define ADM5120_UART_EN 0x01 +#define ADM5120_UART_TIE 0x20 +#define ADM5120_UART_RIE 0x50 +#define ADM5120_UART_IE 0x78 +#define ADM5120_UART_CTS 0x01 +#define ADM5120_UART_DSR 0x02 +#define ADM5120_UART_DCD 0x04 +#define ADM5120_UART_TXFF 0x20 +#define ADM5120_UART_TXFE 0x80 +#define ADM5120_UART_RXFE 0x10 +#define ADM5120_UART_BRK 0x01 +#define ADM5120_UART_PEN 0x02 +#define ADM5120_UART_EPS 0x04 +#define ADM5120_UART_STP2 0x08 +#define ADM5120_UART_W5 0x00 +#define ADM5120_UART_W6 0x20 +#define ADM5120_UART_W7 0x40 +#define ADM5120_UART_W8 0x60 +#define ADM5120_UART_MIS 0x01 +#define ADM5120_UART_RIS 0x02 +#define ADM5120_UART_TIS 0x04 +#define ADM5120_UART_RTIS 0x08 + +static void adm5120ser_stop_tx(struct uart_port *port) +{ + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) &= ~ADM5120_UART_TIE; +} + +static void adm5120ser_irq_rx(struct uart_port *port) +{ + struct tty_struct *tty = port->info->tty; + unsigned int status, ch, rds, flg, ignored = 0; + + status = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + while (!(status & ADM5120_UART_RXFE)) { + /* + * We need to read rds before reading the + * character from the fifo + */ + rds = ADM5120_UART_REG(port->iobase, ADM5120_UART_RS); + ch = ADM5120_UART_REG(port->iobase, ADM5120_UART_DATA); + port->icount.rx++; + + if (tty->low_latency) + tty_flip_buffer_push(tty); + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + if (rds & ADM5120_UART_ERR) + goto handle_error; + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + error_return: + tty_insert_flip_char(tty, ch, flg); + + ignore_char: + status = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + } + out: + tty_flip_buffer_push(tty); + return; + + handle_error: + ADM5120_UART_REG(port->iobase, ADM5120_UART_RS) = 0xff; + if (rds & ADM5120_UART_BE) { + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } else if (rds & ADM5120_UART_PE) + port->icount.parity++; + else if (rds & ADM5120_UART_FE) + port->icount.frame++; + if (rds & ADM5120_UART_OE) + port->icount.overrun++; + + if (rds & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + rds &= port->read_status_mask; + + if (rds & ADM5120_UART_BE) + flg = TTY_BREAK; + else if (rds & ADM5120_UART_PE) + flg = TTY_PARITY; + else if (rds & ADM5120_UART_FE) + flg = TTY_FRAME; + + if (rds & ADM5120_UART_OE) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + tty_insert_flip_char(tty, ch, flg); + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef CONFIG_MAGIC_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +static void adm5120ser_irq_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + ADM5120_UART_REG(port->iobase, ADM5120_UART_DATA) = + port->x_char; + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + adm5120ser_stop_tx(port); + return; + } + + count = port->fifosize >> 1; + do { + ADM5120_UART_REG(port->iobase, ADM5120_UART_DATA) = + xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + adm5120ser_stop_tx(port); +} + +static void adm5120ser_irq_modem(struct uart_port *port) +{ + unsigned int status; + + status = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + + if (status & ADM5120_UART_DCD) + uart_handle_dcd_change(port, status & ADM5120_UART_DCD); + + if (status & ADM5120_UART_DSR) + port->icount.dsr++; + + if (status & ADM5120_UART_CTS) + uart_handle_cts_change(port, status & ADM5120_UART_CTS); + + wake_up_interruptible(&port->info->delta_msr_wait); +} + +static irqreturn_t adm5120ser_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned long ir = ADM5120_UART_REG(port->iobase, ADM5120_UART_IR); + + if (ir & (ADM5120_UART_RIS | ADM5120_UART_RTIS)) + adm5120ser_irq_rx(port); + if (ir & ADM5120_UART_TIS) + adm5120ser_irq_tx(port); + if (ir & ADM5120_UART_MIS) { + adm5120ser_irq_modem(port); + ADM5120_UART_REG(port->iobase, ADM5120_UART_IR) = 0xff; + } + + return IRQ_HANDLED; +} + +static unsigned int adm5120ser_tx_empty(struct uart_port *port) +{ + unsigned int fr = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + return (fr & ADM5120_UART_TXFE) ? TIOCSER_TEMT : 0; +} + +static void adm5120ser_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int adm5120ser_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int fr = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + + if (fr & ADM5120_UART_CTS) + result |= TIOCM_CTS; + if (fr & ADM5120_UART_DSR) + result |= TIOCM_DSR; + if (fr & ADM5120_UART_DCD) + result |= TIOCM_CAR; + return result; +} + +static void adm5120ser_start_tx(struct uart_port *port) +{ + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) |= ADM5120_UART_TIE; +} + +static void adm5120ser_stop_rx(struct uart_port *port) +{ + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) &= ~ADM5120_UART_RIE; +} + +static void adm5120ser_enable_ms(struct uart_port *port) +{ +} + +static void adm5120ser_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned long lcrh; + + spin_lock_irqsave(&port->lock, flags); + lcrh = ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_H); + if (break_state == -1) + lcrh |= ADM5120_UART_BRK; + else + lcrh &= ~ADM5120_UART_BRK; + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_H) = lcrh; + spin_unlock_irqrestore(&port->lock, flags); +} + +static int adm5120ser_startup(struct uart_port *port) +{ + int ret; + + ret = request_irq(port->irq, adm5120ser_irq, 0, "ADM5120 UART", port); + if (ret) { + printk(KERN_ERR "Couldn't get irq %d\n", port->irq); + return ret; + } + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_H) |= + ADM5120_UART_FIFO_EN; + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) |= + ADM5120_UART_EN | ADM5120_UART_IE; + return 0; +} + +static void adm5120ser_shutdown(struct uart_port *port) +{ + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) &= ~ADM5120_UART_IE; + free_irq(port->irq, port); +} + +static void adm5120ser_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + unsigned int baud, quot, lcrh; + unsigned long flags; + + termios->c_cflag |= CREAD; + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + lcrh = ADM5120_UART_FIFO_EN; + switch (termios->c_cflag & CSIZE) { + case CS5: + lcrh |= ADM5120_UART_W5; + break; + case CS6: + lcrh |= ADM5120_UART_W6; + break; + case CS7: + lcrh |= ADM5120_UART_W7; + break; + default: + lcrh |= ADM5120_UART_W8; + break; + } + if (termios->c_cflag & CSTOPB) + lcrh |= ADM5120_UART_STP2; + if (termios->c_cflag & PARENB) { + lcrh |= ADM5120_UART_PEN; + if (!(termios->c_cflag & PARODD)) + lcrh |= ADM5120_UART_EPS; + } + + spin_lock_irqsave(port->lock, flags); + + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_H) = lcrh; + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = ADM5120_UART_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= ADM5120_UART_FE | ADM5120_UART_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= ADM5120_UART_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= ADM5120_UART_FE | ADM5120_UART_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= ADM5120_UART_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= ADM5120_UART_OE; + } + + quot = ADM5120_UART_BAUD115200; + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_L) = quot & 0xff; + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_M) = quot >> 8; + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *adm5120ser_type(struct uart_port *port) +{ + return port->type == PORT_ADM5120 ? "ADM5120" : NULL; +} + +static void adm5120ser_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_ADM5120; +} + +static void adm5120ser_release_port(struct uart_port *port) +{ + release_mem_region(port->iobase, ADM5120_UART_SIZE); +} + +static int adm5120ser_request_port(struct uart_port *port) +{ + return request_mem_region(port->iobase, ADM5120_UART_SIZE, + "adm5120-uart") != NULL ? 0 : -EBUSY; +} + +static struct uart_ops adm5120ser_ops = { + .tx_empty = adm5120ser_tx_empty, + .set_mctrl = adm5120ser_set_mctrl, + .get_mctrl = adm5120ser_get_mctrl, + .stop_tx = adm5120ser_stop_tx, + .start_tx = adm5120ser_start_tx, + .stop_rx = adm5120ser_stop_rx, + .enable_ms = adm5120ser_enable_ms, + .break_ctl = adm5120ser_break_ctl, + .startup = adm5120ser_startup, + .shutdown = adm5120ser_shutdown, + .set_termios = adm5120ser_set_termios, + .type = adm5120ser_type, + .config_port = adm5120ser_config_port, + .release_port = adm5120ser_release_port, + .request_port = adm5120ser_request_port, +}; + +static void adm5120console_put(const char c) +{ + while ((ADM5120_UART_REG(ADM5120_UART0_BASE, ADM5120_UART_FR) & + ADM5120_UART_TXFF) != 0); + ADM5120_UART_REG(ADM5120_UART0_BASE, ADM5120_UART_DATA) = c; +} + +static void adm5120console_write(struct console *con, const char *s, + unsigned int count) +{ + while (count--) { + if (*s == '\n') + adm5120console_put('\r'); + adm5120console_put(*s); + s++; + } +} + +static int adm5120console_setup(struct console *con, char *options) +{ + /* Set to 115200 baud, 8N1 and enable FIFO */ + ADM5120_UART_REG(ADM5120_UART0_BASE, ADM5120_UART_LCR_L) = + ADM5120_UART_BAUD115200 & 0xff; + ADM5120_UART_REG(ADM5120_UART0_BASE, ADM5120_UART_LCR_M) = + ADM5120_UART_BAUD115200 >> 8; + ADM5120_UART_REG(ADM5120_UART0_BASE, ADM5120_UART_LCR_H) = + ADM5120_UART_W8 | ADM5120_UART_FIFO_EN; + /* Enable port */ + ADM5120_UART_REG(ADM5120_UART0_BASE, ADM5120_UART_CR) = + ADM5120_UART_EN; + + return 0; +} + +static struct uart_driver adm5120ser_reg; + +static struct console adm5120_serconsole = { + .name = "ttyS", + .write = adm5120console_write, + .device = uart_console_device, + .setup = adm5120console_setup, + .flags = CON_PRINTBUFFER, + .cflag = B115200 | CS8 | CREAD, + .index = 0, + .data = &adm5120ser_reg, +}; + +static int __init adm5120console_init(void) +{ + register_console(&adm5120_serconsole); + return 0; +} + +console_initcall(adm5120console_init); + + +static struct uart_port adm5120ser_ports[] = { + { + .iobase = ADM5120_UART0_BASE, + .irq = ADM5120_IRQ_UART0, + .uartclk = ADM5120_UARTCLK_FREQ, + .fifosize = 16, + .ops = &adm5120ser_ops, + .line = 0, + .flags = ASYNC_BOOT_AUTOCONF, + }, +#if (CONFIG_ADM5120_NR_UARTS > 1) + { + .iobase = ADM5120_UART1_BASE, + .irq = ADM5120_IRQ_UART1, + .uartclk = ADM5120_UARTCLK_FREQ, + .fifosize = 16, + .ops = &adm5120ser_ops, + .line = 1, + .flags = ASYNC_BOOT_AUTOCONF, + }, +#endif +}; + +static struct uart_driver adm5120ser_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyS", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = CONFIG_ADM5120_NR_UARTS, + .cons = &adm5120_serconsole, +}; + +static int __init adm5120ser_init(void) +{ + int ret, i; + + ret = uart_register_driver(&adm5120ser_reg); + if (!ret) { + for (i = 0; i < CONFIG_ADM5120_NR_UARTS; i++) + uart_add_one_port(&adm5120ser_reg, &adm5120ser_ports[i]); + } + + return ret; +} + +__initcall(adm5120ser_init); diff --git a/target/linux/adm5120/files/drivers/usb/host/adm5120-hcd.c b/target/linux/adm5120/files/drivers/usb/host/adm5120-hcd.c new file mode 100644 index 000000000..4e8877728 --- /dev/null +++ b/target/linux/adm5120/files/drivers/usb/host/adm5120-hcd.c @@ -0,0 +1,996 @@ +/* + * HCD driver for ADM5120 SoC + * + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * + * Based on the ADMtek 2.4 driver + * (C) Copyright 2003 Junius Chen <juniusc@admtek.com.tw> + * Which again was based on the ohci and uhci drivers. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/usb.h> +#include <linux/platform_device.h> + +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/byteorder.h> +#include <asm/mach-adm5120/adm5120_info.h> + +#include "../core/hcd.h" + +MODULE_DESCRIPTION("ADM5120 USB Host Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)"); + +#define PFX "adm5120-hcd: " + +#define ADMHCD_REG_CONTROL 0x00 +#define ADMHCD_SW_RESET 0x00000008 /* Reset */ +#define ADMHCD_DMAA 0x00000004 /* DMA arbitration control */ +#define ADMHCD_SW_INTREQ 0x00000002 /* request software int */ +#define ADMHCD_HOST_EN 0x00000001 /* Host enable */ +#define ADMHCD_REG_INTSTATUS 0x04 +#define ADMHCD_INT_ACT 0x80000000 /* Interrupt active */ +#define ADMHCD_INT_FATAL 0x40000000 /* Fatal interrupt */ +#define ADMHCD_INT_SW 0x20000000 /* software interrupt */ +#define ADMHCD_INT_TD 0x00100000 /* TD completed */ +#define ADMHCD_FNO 0x00000800 /* Frame number overaflow */ +#define ADMHCD_SO 0x00000400 /* Scheduling overrun */ +#define ADMHCD_INSMI 0x00000200 /* Root hub status change */ +#define ADMHCD_BABI 0x00000100 /* Babble detected, host mode */ +#define ADMHCD_RESI 0x00000020 /* Resume detected */ +#define ADMHCD_SOFI 0x00000010 /* SOF transmitted/received, host mode */ +#define ADMHCD_REG_INTENABLE 0x08 +#define ADMHCD_INT_EN 0x80000000 /* Interrupt enable */ +#define ADMHCD_INTMASK 0x00000001 /* Interrupt mask */ +#define ADMHCD_REG_HOSTCONTROL 0x10 +#define ADMHCD_DMA_EN 0x00000004 /* USB host DMA enable */ +#define ADMHCD_STATE_MASK 0x00000003 +#define ADMHCD_STATE_RST 0x00000000 /* bus state reset */ +#define ADMHCD_STATE_RES 0x00000001 /* bus state resume */ +#define ADMHCD_STATE_OP 0x00000002 /* bus state operational */ +#define ADMHCD_STATE_SUS 0x00000003 /* bus state suspended */ +#define ADMHCD_REG_FMINTERVAL 0x18 +#define ADMHCD_REG_FMNUMBER 0x1c +#define ADMHCD_REG_LSTHRESH 0x70 +#define ADMHCD_REG_RHDESCR 0x74 +#define ADMHCD_CRWE 0x20000000 /* Clear wakeup enable */ +#define ADMHCD_DRWE 0x10000000 /* Device remote wakeup enable */ +#define ADMHCD_HW_OCIC 0x08000000 /* Over current indication change */ +#define ADMHCD_LPSC 0x04000000 /* Local power switch change */ +#define ADMHCD_OCI 0x02000000 /* Over current indication */ +#define ADMHCD_LPS 0x01000000 /* Local power switch/global power switch */ +#define ADMHCD_NOCP 0x00000800 /* No over current protect mode */ +#define ADMHCD_OPCM 0x00000400 /* Over current protect mode */ +#define ADMHCD_NPS 0x00000200 /* No Power Switch */ +#define ADMHCD_PSM 0x00000100 /* Power switch mode */ +#define ADMHCD_REG_PORTSTATUS0 0x78 +#define ADMHCD_CCS 0x00000001 /* current connect status */ +#define ADMHCD_PES 0x00000002 /* port enable status */ +#define ADMHCD_PSS 0x00000004 /* port suspend status */ +#define ADMHCD_POCI 0x00000008 /* port overcurrent indicator */ +#define ADMHCD_PRS 0x00000010 /* port reset status */ +#define ADMHCD_PPS 0x00000100 /* port power status */ +#define ADMHCD_LSDA 0x00000200 /* low speed device attached */ +#define ADMHCD_CSC 0x00010000 /* connect status change */ +#define ADMHCD_PESC 0x00020000 /* enable status change */ +#define ADMHCD_PSSC 0x00040000 /* suspend status change */ +#define ADMHCD_OCIC 0x00080000 /* overcurrent change*/ +#define ADMHCD_PRSC 0x00100000 /* reset status change */ +#define ADMHCD_REG_PORTSTATUS1 0x7c +#define ADMHCD_REG_HOSTHEAD 0x80 + +#define ADMHCD_NUMPORTS 1 +#define ADMHCD_DESC_ALIGN 16 + +struct admhcd_ed { + /* Don't change first four, they used for DMA */ + u32 control; + struct admhcd_td *tail; + struct admhcd_td *head; + struct admhcd_ed *next; + /* the rest is for the driver only: */ + struct admhcd_td *cur; + struct usb_host_endpoint *ep; + struct urb *urb; + struct admhcd_ed *real; +} __attribute__ ((packed)); + +#define ADMHCD_ED_EPSHIFT 7 /* Shift for endpoint number */ +#define ADMHCD_ED_INT 0x00000800 /* Is this an int endpoint */ +#define ADMHCD_ED_SPEED 0x00002000 /* Is it a high speed dev? */ +#define ADMHCD_ED_SKIP 0x00004000 /* Skip this ED */ +#define ADMHCD_ED_FORMAT 0x00008000 /* Is this an isoc endpoint */ +#define ADMHCD_ED_MAXSHIFT 16 /* Shift for max packet size */ + +struct admhcd_td { + /* Don't change first four, they are used for DMA */ + u32 control; + u32 buffer; + u32 buflen; + struct admhcd_td *next; + /* the rest is for the driver only: */ + struct urb *urb; + struct admhcd_td *real; +} __attribute__ ((packed)); + +#define ADMHCD_TD_OWN 0x80000000 +#define ADMHCD_TD_TOGGLE 0x00000000 +#define ADMHCD_TD_DATA0 0x01000000 +#define ADMHCD_TD_DATA1 0x01800000 +#define ADMHCD_TD_OUT 0x00200000 +#define ADMHCD_TD_IN 0x00400000 +#define ADMHCD_TD_SETUP 0x00000000 +#define ADMHCD_TD_ISO 0x00010000 +#define ADMHCD_TD_R 0x00040000 +#define ADMHCD_TD_INTEN 0x00010000 + +static int admhcd_td_err[16] = { + 0, /* No */ + -EREMOTEIO, /* CRC */ + -EREMOTEIO, /* bit stuff */ + -EREMOTEIO, /* data toggle */ + -EPIPE, /* stall */ + -ETIMEDOUT, /* timeout */ + -EPROTO, /* pid err */ + -EPROTO, /* unexpected pid */ + -EREMOTEIO, /* data overrun */ + -EREMOTEIO, /* data underrun */ + -ETIMEDOUT, /* 1010 */ + -ETIMEDOUT, /* 1011 */ + -EREMOTEIO, /* buffer overrun */ + -EREMOTEIO, /* buffer underrun */ + -ETIMEDOUT, /* 1110 */ + -ETIMEDOUT, /* 1111 */ +}; + +#define ADMHCD_TD_ERRMASK 0x38000000 +#define ADMHCD_TD_ERRSHIFT 27 + +#define TD(td) ((struct admhcd_td *)(((u32)(td)) & ~(ADMHCD_DESC_ALIGN-1))) +#define ED(ed) ((struct admhcd_ed *)(((u32)(ed)) & ~(ADMHCD_DESC_ALIGN-1))) + +struct admhcd { + spinlock_t lock; + + /* Root hub registers */ + u32 rhdesca; + u32 rhdescb; + u32 rhstatus; + u32 rhport[2]; + + /* async schedule: control, bulk */ + struct list_head async; + u32 base; + u32 dma_en; + unsigned long flags; +}; + +static inline struct admhcd *hcd_to_admhcd(struct usb_hcd *hcd) +{ + return (struct admhcd *)(hcd->hcd_priv); +} + +static inline struct usb_hcd *admhcd_to_hcd(struct admhcd *admhcd) +{ + return container_of((void *)admhcd, struct usb_hcd, hcd_priv); +} + +static char hcd_name[] = "adm5120-hcd"; + +static u32 admhcd_reg_get(struct admhcd *ahcd, int reg) +{ + return *(volatile u32 *)KSEG1ADDR(ahcd->base+reg); +} + +static void admhcd_reg_set(struct admhcd *ahcd, int reg, u32 val) +{ + *(volatile u32 *)KSEG1ADDR(ahcd->base+reg) = val; +} + +static void admhcd_lock(struct admhcd *ahcd) +{ + spin_lock_irqsave(&ahcd->lock, ahcd->flags); + ahcd->dma_en = admhcd_reg_get(ahcd, ADMHCD_REG_HOSTCONTROL) & + ADMHCD_DMA_EN; + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP); +} + +static void admhcd_unlock(struct admhcd *ahcd) +{ + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, + ADMHCD_STATE_OP | ahcd->dma_en); + spin_unlock_irqrestore(&ahcd->lock, ahcd->flags); +} + +static struct admhcd_td *admhcd_td_alloc(struct admhcd_ed *ed, struct urb *urb) +{ + struct admhcd_td *tdn, *td; + + tdn = kzalloc(sizeof(*tdn)+ADMHCD_DESC_ALIGN, GFP_ATOMIC); + if (!tdn) + return NULL; + + tdn->real = tdn; + tdn = TD(KSEG1ADDR(tdn)); + if (ed->cur == NULL) { + ed->cur = tdn; + ed->head = tdn; + ed->tail = tdn; + td = tdn; + } else { + /* Supply back the old tail and link in new td as tail */ + td = TD(ed->tail); + TD(ed->tail)->next = tdn; + ed->tail = tdn; + } + td->urb = urb; + + return td; +} + +static void admhcd_td_free(struct admhcd_ed *ed, struct urb *urb) +{ + struct admhcd_td *td, **tdp; + + if (urb == NULL) + ed->control |= ADMHCD_ED_SKIP; + tdp = &ed->cur; + td = ed->cur; + do { + if (td->urb == urb) + break; + tdp = &td->next; + td = TD(td->next); + } while (td); + while (td && td->urb == urb) { + *tdp = TD(td->next); + kfree(td->real); + td = *tdp; + } +} + +/* Find an endpoint's descriptor, if needed allocate a new one and link it + in the DMA chain + */ +static struct admhcd_ed *admhcd_get_ed(struct admhcd *ahcd, + struct usb_host_endpoint *ep, struct urb *urb) +{ + struct admhcd_ed *hosthead; + struct admhcd_ed *found = NULL, *ed = NULL; + unsigned int pipe = urb->pipe; + + admhcd_lock(ahcd); + hosthead = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD); + if (hosthead) { + for (ed = hosthead;; ed = ED(ed->next)) { + if (ed->ep == ep) { + found = ed; + break; + } + if (ED(ed->next) == hosthead) + break; + } + } + if (!found) { + found = kzalloc(sizeof(*found)+ADMHCD_DESC_ALIGN, GFP_ATOMIC); + if (!found) + goto out; + found->real = found; + found->ep = ep; + found = ED(KSEG1ADDR(found)); + found->control = usb_pipedevice(pipe) | + (usb_pipeendpoint(pipe) << ADMHCD_ED_EPSHIFT) | + (usb_pipeint(pipe) ? ADMHCD_ED_INT : 0) | + (urb->dev->speed == USB_SPEED_FULL ? ADMHCD_ED_SPEED : 0) | + (usb_pipeisoc(pipe) ? ADMHCD_ED_FORMAT : 0) | + (usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe)) << ADMHCD_ED_MAXSHIFT); + /* Alloc first dummy td */ + admhcd_td_alloc(found, NULL); + if (hosthead) { + found->next = hosthead; + ed->next = found; + } else { + found->next = found; + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, (u32)found); + } + } +out: + admhcd_unlock(ahcd); + return found; +} + +static struct admhcd_td *admhcd_td_fill(u32 control, struct admhcd_td *td, + dma_addr_t data, int len) +{ + td->buffer = data; + td->buflen = len; + td->control = control; + return TD(td->next); +} + +static void admhcd_ed_start(struct admhcd *ahcd, struct admhcd_ed *ed) +{ + struct admhcd_td *td = ed->cur; + + if (ed->urb) + return; + if (td->urb) { + ed->urb = td->urb; + while (1) { + td->control |= ADMHCD_TD_OWN; + if (TD(td->next)->urb != td->urb) { + td->buflen |= ADMHCD_TD_INTEN; + break; + } + td = TD(td->next); + } + } + ed->head = TD(ed->head); + ahcd->dma_en |= ADMHCD_DMA_EN; +} + +static irqreturn_t admhcd_irq(struct usb_hcd *hcd) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + u32 intstatus; + + intstatus = admhcd_reg_get(ahcd, ADMHCD_REG_INTSTATUS); + if (intstatus & ADMHCD_INT_FATAL) { + admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_FATAL); + /* FIXME: handle fatal interrupts */ + } + + if (intstatus & ADMHCD_INT_SW) { + admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_SW); + /* FIXME: handle software interrupts */ + } + + if (intstatus & ADMHCD_INT_TD) { + struct admhcd_ed *ed, *head; + + admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_TD); + + head = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD); + ed = head; + if (ed) do { + /* Is it a finished TD? */ + if (ed->urb && !(ed->cur->control & ADMHCD_TD_OWN)) { + struct admhcd_td *td; + int error; + + td = ed->cur; + error = (td->control & ADMHCD_TD_ERRMASK) >> + ADMHCD_TD_ERRSHIFT; + ed->urb->status = admhcd_td_err[error]; + admhcd_td_free(ed, ed->urb); + // Calculate real length!!! + ed->urb->actual_length = ed->urb->transfer_buffer_length; + ed->urb->hcpriv = NULL; + usb_hcd_giveback_urb(hcd, ed->urb); + ed->urb = NULL; + } + admhcd_ed_start(ahcd, ed); + ed = ED(ed->next); + } while (ed != head); + } + + return IRQ_HANDLED; +} + +static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep, + struct urb *urb, gfp_t mem_flags) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + struct admhcd_ed *ed; + struct admhcd_td *td; + int size = 0, i, zero = 0, ret = 0; + unsigned int pipe = urb->pipe, toggle = 0; + dma_addr_t data = (dma_addr_t)urb->transfer_buffer; + int data_len = urb->transfer_buffer_length; + + ed = admhcd_get_ed(ahcd, ep, urb); + if (!ed) + return -ENOMEM; + + switch(usb_pipetype(pipe)) { + case PIPE_CONTROL: + size = 2; + case PIPE_INTERRUPT: + case PIPE_BULK: + default: + size += urb->transfer_buffer_length / 4096; + if (urb->transfer_buffer_length % 4096) + size++; + if (size == 0) + size++; + else if (urb->transfer_flags & URB_ZERO_PACKET && + !(urb->transfer_buffer_length % + usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe)))) { + size++; + zero = 1; + } + break; + case PIPE_ISOCHRONOUS: + size = urb->number_of_packets; + break; + } + + admhcd_lock(ahcd); + /* Remember the first td */ + td = admhcd_td_alloc(ed, urb); + if (!td) { + ret = -ENOMEM; + goto out; + } + /* Allocate additionall tds first */ + for (i = 1; i < size; i++) { + if (admhcd_td_alloc(ed, urb) == NULL) { + admhcd_td_free(ed, urb); + ret = -ENOMEM; + goto out; + } + } + + if (usb_gettoggle(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) + toggle = ADMHCD_TD_TOGGLE; + else { + toggle = ADMHCD_TD_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), 1); + } + + switch(usb_pipetype(pipe)) { + case PIPE_CONTROL: + td = admhcd_td_fill(ADMHCD_TD_SETUP | ADMHCD_TD_DATA0, + td, (dma_addr_t)urb->setup_packet, 8); + while (data_len > 0) { + td = admhcd_td_fill(ADMHCD_TD_DATA1 + | ADMHCD_TD_R | + (usb_pipeout(pipe) ? + ADMHCD_TD_OUT : ADMHCD_TD_IN), td, + data, data_len % 4097); + data_len -= 4096; + } + admhcd_td_fill(ADMHCD_TD_DATA1 | (usb_pipeout(pipe) ? + ADMHCD_TD_IN : ADMHCD_TD_OUT), td, + data, 0); + break; + case PIPE_INTERRUPT: + case PIPE_BULK: + //info ok for interrupt? + i = 0; + while(data_len > 4096) { + td = admhcd_td_fill((usb_pipeout(pipe) ? + ADMHCD_TD_OUT : + ADMHCD_TD_IN | ADMHCD_TD_R) | + (i ? ADMHCD_TD_TOGGLE : toggle), td, + data, 4096); + data += 4096; + data_len -= 4096; + i++; + } + td = admhcd_td_fill((usb_pipeout(pipe) ? + ADMHCD_TD_OUT : ADMHCD_TD_IN) | + (i ? ADMHCD_TD_TOGGLE : toggle), td, data, data_len); + i++; + if (zero) + admhcd_td_fill((usb_pipeout(pipe) ? + ADMHCD_TD_OUT : ADMHCD_TD_IN) | + (i ? ADMHCD_TD_TOGGLE : toggle), td, 0, 0); + break; + case PIPE_ISOCHRONOUS: + for (i = 0; i < urb->number_of_packets; i++) { + td = admhcd_td_fill(ADMHCD_TD_ISO | + ((urb->start_frame + i) & 0xffff), td, + data + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].length); + } + break; + } + urb->hcpriv = ed; + admhcd_ed_start(ahcd, ed); +out: + admhcd_unlock(ahcd); + return ret; +} + +static int admhcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + struct admhcd_ed *ed; + + admhcd_lock(ahcd); + + ed = urb->hcpriv; + if (ed && ed->urb != urb) + admhcd_td_free(ed, urb); + + admhcd_unlock(ahcd); + return 0; +} + +static void admhcd_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + struct admhcd_ed *ed, *edt, *head; + + admhcd_lock(ahcd); + + head = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD); + if (!head) + goto out; + for (ed = head; ED(ed->next) != head; ed = ED(ed->next)) + if (ed->ep == ep) + break; + if (ed->ep != ep) + goto out; + while (ed->cur) + admhcd_td_free(ed, ed->cur->urb); + if (head == ed) { + if (ED(ed->next) == ed) { + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0); + ahcd->dma_en = 0; + goto out_free; + } + head = ED(ed->next); + for (edt = head; ED(edt->next) != head; edt = ED(edt->next)); + edt->next = ED(ed->next); + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, (u32)ed->next); + goto out_free; + } + for (edt = head; edt->next != ed; edt = edt->next); + edt->next = ed->next; + +out_free: + kfree(ed->real); +out: + admhcd_unlock(ahcd); +} + +static int admhcd_get_frame_number(struct usb_hcd *hcd) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + + return admhcd_reg_get(ahcd, ADMHCD_REG_FMNUMBER) & 0x0000ffff; +} + +static int admhcd_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + int port; + + *buf = 0; + for (port = 0; port < ADMHCD_NUMPORTS; port++) { + if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4) & + (ADMHCD_CSC | ADMHCD_PESC | ADMHCD_PSSC | ADMHCD_OCIC | + ADMHCD_PRSC)) + *buf |= (1 << (port + 1)); + } + return !!*buf; +} + +static __u8 root_hub_hub_des[] = { + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x0a, 0x00, /* __u16 wHubCharacteristics; */ + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0mA */ + 0x00, /* __u8 DeviceRemovable; */ + 0xff, /* __u8 PortPwrCtrlMask; */ +}; + +static int admhcd_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + int retval = 0, len; + unsigned int port = wIndex -1; + + switch (typeReq) { + + case GetHubStatus: + *(__le32 *)buf = cpu_to_le32(0); + break; + case GetPortStatus: + if (port >= ADMHCD_NUMPORTS) + goto err; + *(__le32 *)buf = cpu_to_le32( + admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4)); + break; + case SetHubFeature: /* We don't implement these */ + case ClearHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto err; + } + case SetPortFeature: + if (port >= ADMHCD_NUMPORTS) + goto err; + + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PSS); + break; + case USB_PORT_FEAT_RESET: + if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4) + & ADMHCD_CCS) { + admhcd_reg_set(ahcd, + ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PRS | ADMHCD_CSC); + mdelay(50); + admhcd_reg_set(ahcd, + ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PES | ADMHCD_CSC); + } + break; + case USB_PORT_FEAT_POWER: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PPS); + break; + default: + goto err; + } + break; + case ClearPortFeature: + if (port >= ADMHCD_NUMPORTS) + goto err; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_CCS); + break; + case USB_PORT_FEAT_C_ENABLE: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PESC); + break; + case USB_PORT_FEAT_SUSPEND: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_POCI); + break; + case USB_PORT_FEAT_C_SUSPEND: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PSSC); + case USB_PORT_FEAT_POWER: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_LSDA); + break; + case USB_PORT_FEAT_C_CONNECTION: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_CSC); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_OCIC); + break; + case USB_PORT_FEAT_C_RESET: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PRSC); + break; + default: + goto err; + } + break; + case GetHubDescriptor: + len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength); + memcpy(buf, root_hub_hub_des, len); + break; + default: +err: + retval = -EPIPE; + } + + return retval; +} + +static int admhcd_start(struct usb_hcd *hcd) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + unsigned long flags; + + spin_lock_irqsave(&ahcd->lock, flags); + + /* Initialise the HCD registers */ + admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0); + mdelay(10); + + admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET); + + while (admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET) { + printk(KERN_WARNING PFX "waiting for reset to complete\n"); + mdelay(1); + } + + //hcd->uses_new_polling = 1; + + /* Enable USB host mode */ + admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_HOST_EN); + + /* Set host specific settings */ + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0x00000000); + admhcd_reg_set(ahcd, ADMHCD_REG_FMINTERVAL, 0x20002edf); + admhcd_reg_set(ahcd, ADMHCD_REG_LSTHRESH, 0x628); + + /* Set interrupts */ + admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, ADMHCD_INT_ACT | + ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD); + admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_ACT | + ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD); + + /* Power on all ports */ + admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, ADMHCD_NPS | ADMHCD_LPSC); + + /* HCD is now operationnal */ + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP); + + hcd->state = HC_STATE_RUNNING; + + spin_unlock_irqrestore(&ahcd->lock, flags); + + return 0; +} + +static int admhcd_sw_reset(struct admhcd *ahcd) +{ + int retries = 15; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ahcd->lock, flags); + + admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0); + mdelay(10); + + admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET); + + while (--retries) { + mdelay(1); + if (!(admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET)) + break; + } + if (!retries) { + printk(KERN_WARNING "%s: software reset timeout\n", hcd_name); + ret = -ETIME; + } + spin_unlock_irqrestore(&ahcd->lock, flags); + return ret; +} + +static int admhcd_reset(struct usb_hcd *hcd) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + u32 state = 0; + int ret, timeout = 15; /* ms */ + unsigned long t; + + ret = admhcd_sw_reset(ahcd); + if (ret) + return ret; + + t = jiffies + msecs_to_jiffies(timeout); + do { + spin_lock_irq(&ahcd->lock); + state = admhcd_reg_get(ahcd, ADMHCD_REG_HOSTCONTROL); + spin_unlock_irq(&ahcd->lock); + state &= ADMHCD_STATE_MASK; + if (state == ADMHCD_STATE_RST) + break; + msleep(4); + } while (time_before_eq(jiffies, t)); + + if (state != ADMHCD_STATE_RST) { + printk(KERN_WARNING "%s: device not ready after %dms\n", + hcd_name, timeout); + ret = -ENODEV; + } + + return ret; +} + +static void admhcd_stop(struct usb_hcd *hcd) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&ahcd->lock, flags); + admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0); + + /* Set global control of power for ports */ + val = admhcd_reg_get(ahcd, ADMHCD_REG_RHDESCR); + val &= (~ADMHCD_PSM | ADMHCD_LPS); + admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, val); + + spin_unlock_irqrestore(&ahcd->lock, flags); + + /* Ask for software reset */ + admhcd_sw_reset(ahcd); +} + + +static struct hc_driver adm5120_hc_driver = { + .description = hcd_name, + .product_desc = "ADM5120 HCD", + .hcd_priv_size = sizeof(struct admhcd), + .irq = admhcd_irq, + .flags = HCD_USB11, + .urb_enqueue = admhcd_urb_enqueue, + .urb_dequeue = admhcd_urb_dequeue, + .endpoint_disable = admhcd_endpoint_disable, + .get_frame_number = admhcd_get_frame_number, + .hub_status_data = admhcd_hub_status_data, + .hub_control = admhcd_hub_control, + .start = admhcd_start, + .stop = admhcd_stop, + .reset = admhcd_reset, +}; + +#define resource_len(r) (((r)->end - (r)->start) + 1) + +static int __init adm5120hcd_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct admhcd *ahcd; + struct resource *data; + void __iomem *data_reg; + + int err = 0, irq; + + if (pdev->num_resources < 2) { + printk(KERN_WARNING PFX "not enough resources\n"); + err = -ENODEV; + goto out; + } + + irq = platform_get_irq(pdev, 0); + data = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (pdev->dev.dma_mask) { + printk(KERN_DEBUG PFX "no we won't dma\n"); + return -EINVAL; + } + + if (!data || irq < 0) { + printk(KERN_DEBUG PFX "either IRQ or data resource is invalid\n"); + err = -ENODEV; + goto out; + } + + if (!request_mem_region(data->start, resource_len(data), hcd_name)) { + printk(KERN_DEBUG PFX "cannot request memory regions for the data resource\n"); + err = -EBUSY; + goto out; + } + + data_reg = ioremap(data->start, resource_len(data)); + if (data_reg == NULL) { + printk(KERN_DEBUG PFX "unable to ioremap\n"); + err = -ENOMEM; + goto out_mem; + } + + hcd = usb_create_hcd(&adm5120_hc_driver, &pdev->dev, pdev->dev.bus_id); + if (!hcd) { + printk(KERN_DEBUG PFX "unable to create the hcd\n"); + err = -ENOMEM; + goto out_unmap; + } + + hcd->rsrc_start = data->start; + hcd->rsrc_len = resource_len(data); + hcd->regs = data_reg; + + ahcd = hcd_to_admhcd(hcd); + ahcd->base = (u32)data_reg; + + spin_lock_init(&ahcd->lock); + INIT_LIST_HEAD(&ahcd->async); + + hcd->product_desc = "ADM5120 HCD"; + + err = usb_add_hcd(hcd, irq, IRQF_DISABLED); + if (err) { + printk(KERN_DEBUG PFX "unable to add hcd\n"); + goto out_dev; + } + + return 0; + +out_dev: + usb_put_hcd(hcd); +out_unmap: + iounmap(data_reg); +out_mem: + release_mem_region(data->start, resource_len(data)); +out: + return err; +} + +#ifdef CONFIG_PM +static int adm5120hcd_suspend(struct platform_device *pdev, pm_message_t state) +{ + pdev->dev.power.power_state = state; + mdelay(1); + return 0; +} + +static int adm5120hcd_resume(struct platform_device *pdev, pm_message_t state) +{ + pdev->dev.power.power_state = PMSG_ON; + mdelay(1); + return 0; +} +#else +#define adm5120hcd_suspend NULL +#define adm5120hcd_resume NULL +#endif + +static int __init_or_module adm5120hcd_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct admhcd *ahcd; + + if (!hcd) + return 0; + ahcd = hcd_to_admhcd(hcd); + usb_remove_hcd(hcd); + + usb_put_hcd(hcd); + return 0; +} + +static struct platform_driver adm5120hcd_driver = { + .probe = adm5120hcd_probe, + .remove = adm5120hcd_remove, + .suspend = adm5120hcd_suspend, + .remove = adm5120hcd_resume, + .driver = { + .name = (char *)hcd_name, + .owner = THIS_MODULE, + }, +}; + +static int __init adm5120hcd_init(void) +{ + int ret; + + if (usb_disabled()) { + printk(KERN_DEBUG PFX "USB support is disabled\n"); + return -ENODEV; + } + + if (mips_machgroup != MACH_GROUP_ADM5120) { + printk(KERN_DEBUG PFX "unsupported machine group\n"); + return -ENODEV; + } + + ret = platform_driver_register(&adm5120hcd_driver); + if (ret == 0) + printk(KERN_INFO PFX "registered\n"); + + return ret; +} + +static void __exit adm5120hcd_exit(void) +{ + platform_driver_unregister(&adm5120hcd_driver); + printk(KERN_INFO PFX "driver unregistered\n"); +} + +module_init(adm5120hcd_init); +module_exit(adm5120hcd_exit); diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_board.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_board.h new file mode 100644 index 000000000..23f62a293 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_board.h @@ -0,0 +1,50 @@ +/* + * $Id$ + * + * ADM5120 board definitions + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ADM5120_BOARD_H_ +#define _ADM5120_BOARD_H_ + +#include <linux/init.h> +#include <linux/list.h> + +#define ADM5120_BOARD_NAMELEN 64 + +struct adm5120_board { + unsigned long mach_type; + char name[ADM5120_BOARD_NAMELEN]; + + void (*board_setup)(void); + void (*board_reset)(void); + + unsigned int eth_num_ports; + unsigned char *eth_vlans; + unsigned int num_devices; + struct platform_device **devices; + + struct list_head list; +}; + +extern void adm5120_board_register(struct adm5120_board *) __init; + +#endif /* _ADM5120_BOARD_H_ */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_defs.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_defs.h new file mode 100644 index 000000000..f5ab1b3f1 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_defs.h @@ -0,0 +1,60 @@ +/* + * $Id$ + * + * ADM5120 SoC definitions + * + * This file defines some constants specific to the ADM5120 SoC + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef _ADM5120_DEFS_H +#define _ADM5120_DEFS_H + +#define ADM5120_SDRAM0_BASE 0x00000000 +#define ADM5120_SDRAM1_BASE 0x01000000 +#define ADM5120_SRAM1_BASE 0x10000000 +#define ADM5120_MPMC_BASE 0x11000000 +#define ADM5120_USBC_BASE 0x11200000 +#define ADM5120_PCIMEM_BASE 0x11400000 +#define ADM5120_PCIIO_BASE 0x11500000 +#define ADM5120_PCICFG_ADDR 0x115FFFF0 +#define ADM5120_PCICFG_DATA 0x115FFFF8 +#define ADM5120_SWITCH_BASE 0x12000000 +#define ADM5120_INTC_BASE 0x12200000 +#define ADM5120_UART0_BASE 0x12600000 +#define ADM5120_UART1_BASE 0x12800000 +#define ADM5120_SRAM0_BASE 0x1FC00000 + +#define ADM5120_MPMC_SIZE 0x1000 +#define ADM5120_USBC_SIZE 0x84 +#define ADM5120_PCIMEM_SIZE (ADM5120_PCIIO_BASE - ADM5120_PCIMEM_BASE) +#define ADM5120_PCIIO_SIZE (ADM5120_PCICFG_ADDR - ADM5120_PCIIO_BASE) +#define ADM5120_PCICFG_SIZE 0x10 +#define ADM5120_SWITCH_SIZE 0x114 +#define ADM5120_INTC_SIZE 0x28 +#define ADM5120_UART_SIZE 0x20 + +#define ADM5120_CLK_175 175000000 +#define ADM5120_CLK_200 200000000 +#define ADM5120_CLK_225 225000000 +#define ADM5120_CLK_250 250000000 + +#define ADM5120_UART_CLOCK 62500000 + +#endif /* _ADM5120_DEFS_H */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_info.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_info.h new file mode 100644 index 000000000..b8b9c5d47 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_info.h @@ -0,0 +1,92 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ADM5120_INFO_H +#define _ADM5120_INFO_H + +#include <linux/types.h> + +extern unsigned int adm5120_prom_type; +#define ADM5120_PROM_GENERIC 0 +#define ADM5120_PROM_CFE 1 +#define ADM5120_PROM_MYLOADER 2 +#define ADM5120_PROM_ROUTERBOOT 3 +#define ADM5120_PROM_BOOTBASE 4 +#define ADM5120_PROM_UBOOT 5 +#define ADM5120_PROM_LAST 5 + +extern unsigned int adm5120_product_code; +extern unsigned int adm5120_revision; +extern unsigned int adm5120_nand_boot; + +extern unsigned long adm5120_speed; +#define ADM5120_SPEED_175 175000000 +#define ADM5120_SPEED_200 200000000 +#define ADM5120_SPEED_225 225000000 +#define ADM5120_SPEED_250 250000000 + +extern unsigned int adm5120_package; +#define ADM5120_PACKAGE_PQFP 0 +#define ADM5120_PACKAGE_BGA 1 + +extern unsigned long adm5120_memsize; + +/* + * TODO:remove adm5120_eth* variables when the switch driver will be + * converted into a real platform driver + */ +extern unsigned int adm5120_eth_num_ports; +extern unsigned char adm5120_eth_macs[6][6]; +extern unsigned char adm5120_eth_vlans[6]; + +extern void adm5120_soc_init(void) __init; +extern void adm5120_mem_init(void) __init; +extern void adm5120_time_init(void) __init; +extern void adm5120_ndelay(u32 ns); + +extern void adm5120_restart(char *command); +extern void adm5120_halt(void); +extern void adm5120_power_off(void); + +extern void (*adm5120_board_reset)(void); + +static inline int adm5120_package_pqfp(void) +{ + return (adm5120_package == ADM5120_PACKAGE_PQFP); +} + +static inline int adm5120_package_bga(void) +{ + return (adm5120_package == ADM5120_PACKAGE_BGA); +} + +static inline int adm5120_has_pci(void) +{ + return (adm5120_package == ADM5120_PACKAGE_BGA); +} + +static inline int adm5120_has_gmii(void) +{ + return (adm5120_package == ADM5120_PACKAGE_BGA); +} + +#endif /* _ADM5120_INFO_H */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_intc.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_intc.h new file mode 100644 index 000000000..4d7525951 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_intc.h @@ -0,0 +1,76 @@ +/* + * $Id$ + * + * ADM5120 interrupt controller definitions + * + * This header file defines the hardware registers of the ADM5120 SoC + * built-in interrupt controller. + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ADM5120_INTC_H_ +#define _ADM5120_INTC_H_ + +/* + * INTC register offsets + */ +#define INTC_REG_IRQ_STATUS 0x00 /* Interrupt status after masking */ +#define INTC_REG_IRQ_RAW_STATUS 0x04 /* Interrupt status before masking */ +#define INTC_REG_IRQ_ENABLE 0x08 /* Used to enable the interrupt sources */ +#define INTC_REG_IRQ_ENABLE_CLEAR 0x0C /* Used to disable the interrupt sources */ +#define INTC_REG_IRQ_DISABLE INTC_REG_IRQ_ENABLE_CLEAR +#define INTC_REG_INT_MODE 0x14 /* The interrupt mode of the sources */ +#define INTC_REG_FIQ_STATUS 0x18 /* FIQ status */ +#define INTC_REG_IRQ_TEST_SOURCE 0x1C +#define INTC_REG_IRQ_SOURCE_SELECT 0x20 +#define INTC_REG_INT_LEVEL 0x24 + +/* + * INTC IRQ numbers + */ +#define INTC_IRQ_TIMER 0 /* built in timer */ +#define INTC_IRQ_UART0 1 /* built-in UART0 */ +#define INTC_IRQ_UART1 2 /* built-in UART1 */ +#define INTC_IRQ_USBC 3 /* USB Host Controller */ +#define INTC_IRQ_GPIO2 4 /* GPIO line 2 */ +#define INTC_IRQ_GPIO4 5 /* GPIO line 4 */ +#define INTC_IRQ_PCI0 6 /* PCI slot 2 */ +#define INTC_IRQ_PCI1 7 /* PCI slot 3 */ +#define INTC_IRQ_PCI2 8 /* PCI slot 4 */ +#define INTC_IRQ_SWITCH 9 /* built-in ethernet switch */ +#define INTC_IRQ_LAST INTC_IRQ_SWITCH +#define INTC_IRQ_COUNT 10 + +/* + * INTC register bits + */ +#define INTC_INT_TIMER ( 1 << INTC_IRQ_TIMER ) +#define INTC_INT_UART0 ( 1 << INTC_IRQ_UART0 ) +#define INTC_INT_UART1 ( 1 << INTC_IRQ_UART1 ) +#define INTC_INT_USBC ( 1 << INTC_IRQ_USBC ) +#define INTC_INT_INTX0 ( 1 << INTC_IRQ_INTX0 ) +#define INTC_INT_INTX1 ( 1 << INTC_IRQ_INTX1 ) +#define INTC_INT_PCI0 ( 1 << INTC_IRQ_PCI0 ) +#define INTC_INT_PCI1 ( 1 << INTC_IRQ_PCI1 ) +#define INTC_INT_PCI2 ( 1 << INTC_IRQ_PCI2 ) +#define INTC_INT_SWITCH ( 1 << INTC_IRQ_SWITCH ) +#define INTC_INT_ALL (( 1 << INTC_IRQ_COUNT)-1) + +#endif /* _ADM5120_INTC_H_ */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_irq.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_irq.h new file mode 100644 index 000000000..1b9e21989 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_irq.h @@ -0,0 +1,55 @@ +/* + * $Id$ + * + * ADM5120 specific IRQ numbers + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef _ADM5120_IRQ_H_ +#define _ADM5120_IRQ_H_ + +#include <asm/mach-adm5120/adm5120_intc.h> + +#define MIPS_IRQ_HW0 2 +#define MIPS_IRQ_COUNTER 7 +#define MIPS_IRQ_COUNT 8 + +#define ADM5120_CPU_IRQ_BASE 0 +#define ADM5120_INTC_IRQ_BASE (ADM5120_CPU_IRQ_BASE+MIPS_IRQ_COUNT) +#define ADM5120_SWITCH_IRQ_BASE (ADM5120_INTC_IRQ_BASE+INTC_IRQ_COUNT) + +#define ADM5120_CPU_IRQ(x) (ADM5120_CPU_IRQ_BASE + (x)) +#define ADM5120_INTC_IRQ(x) (ADM5120_INTC_IRQ_BASE + (x)) + +#define ADM5120_IRQ_INTC ADM5120_CPU_IRQ(MIPS_IRQ_HW0) +#define ADM5120_IRQ_COUNTER ADM5120_CPU_IRQ(MIPS_IRQ_COUNTER) + +#define ADM5120_IRQ_TIMER ADM5120_INTC_IRQ(INTC_IRQ_TIMER) +#define ADM5120_IRQ_UART0 ADM5120_INTC_IRQ(INTC_IRQ_UART0) +#define ADM5120_IRQ_UART1 ADM5120_INTC_IRQ(INTC_IRQ_UART1) +#define ADM5120_IRQ_USBC ADM5120_INTC_IRQ(INTC_IRQ_USBC) +#define ADM5120_IRQ_GPIO2 ADM5120_INTC_IRQ(INTC_IRQ_GPIO2) +#define ADM5120_IRQ_GPIO4 ADM5120_INTC_IRQ(INTC_IRQ_GPIO4) +#define ADM5120_IRQ_PCI0 ADM5120_INTC_IRQ(INTC_IRQ_PCI0) +#define ADM5120_IRQ_PCI1 ADM5120_INTC_IRQ(INTC_IRQ_PCI1) +#define ADM5120_IRQ_PCI2 ADM5120_INTC_IRQ(INTC_IRQ_PCI2) +#define ADM5120_IRQ_SWITCH ADM5120_INTC_IRQ(INTC_IRQ_SWITCH) + +#endif diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h new file mode 100644 index 000000000..c821150c6 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h @@ -0,0 +1,87 @@ +/* + * $Id$ + * + * ADM5120 MPMC (Multiport Memory Controller) register definitions + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef _ADM5120_MPMC_H_ +#define _ADM5120_MPMC_H_ + +#define MPMC_REG_CTRL 0x0000 +#define MPMC_REG_STATUS 0x0004 +#define MPMC_REG_CONF 0x0008 +#define MPMC_REG_DC 0x0020 +#define MPMC_REG_DR 0x0024 +#define MPMC_REG_DRP 0x0030 + +#define MPMC_REG_DC0 0x0100 +#define MPMC_REG_DRC0 0x0104 +#define MPMC_REG_DC1 0x0120 +#define MPMC_REG_DRC1 0x0124 +#define MPMC_REG_DC2 0x0140 +#define MPMC_REG_DRC2 0x0144 +#define MPMC_REG_DC3 0x0160 +#define MPMC_REG_DRC3 0x0164 +#define MPMC_REG_SC0 0x0200 /* for F_CS1_N */ +#define MPMC_REG_SC1 0x0220 /* for F_CS0_N */ +#define MPMC_REG_SC2 0x0240 +#define MPMC_REG_SC3 0x0260 + +#define MPMC_CTRL_AM ( 1 << 1 ) + +/* Dynamic Control register bits */ +#define MPMC_DC_CE ( 1 << 0 ) +#define MPMC_DC_DMC ( 1 << 1 ) +#define MPMC_DC_SRR ( 1 << 2 ) +#define MPMC_DC_SI_SHIFT 7 +#define MPMC_DC_SI_MASK ( 3 << 7 ) +#define MPMC_DC_SI_NORMAL ( 0 << 7 ) +#define MPMC_DC_SI_MODE ( 1 << 7 ) +#define MPMC_DC_SI_PALL ( 2 << 7 ) +#define MPMC_DC_SI_NOP ( 3 << 7 ) + +#define SRAM_REG_CONF 0x00 +#define SRAM_REG_WWE 0x04 +#define SRAM_REG_WOE 0x08 +#define SRAM_REG_WRD 0x0C +#define SRAM_REG_WPG 0x10 +#define SRAM_REG_WWR 0x14 +#define SRAM_REG_WTR 0x18 + +/* Dynamic Configuration register bits */ +#define DC_BE (1 << 19) /* buffer enable */ +#define DC_RW_SHIFT 28 /* shift for number of rows */ +#define DC_RW_MASK 0x03 +#define DC_NB_SHIFT 26 /* shift for number of banks */ +#define DC_NB_MASK 0x01 +#define DC_CW_SHIFT 22 /* shift for number of columns */ +#define DC_CW_MASK 0x07 +#define DC_DW_SHIFT 7 /* shift for device width */ +#define DC_DW_MASK 0x03 + +/* Static Configuration register bits */ +#define SC_MW_MASK 0x03 /* memory width mask */ +#define SC_MW_8 0x00 /* 8 bit memory width */ +#define SC_MW_16 0x01 /* 16 bit memory width */ +#define SC_MW_32 0x02 /* 32 bit memory width */ + +#endif /* _ADM5120_MPMC_H_ */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_platform.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_platform.h new file mode 100644 index 000000000..81c76170f --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_platform.h @@ -0,0 +1,85 @@ +/* + * $Id$ + * + * ADM5120 specific platform definitions + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ADM5120_PLATFORM_H_ +#define _ADM5120_PLATFORM_H_ + +#include <linux/device.h> +#include <linux/platform_device.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <linux/amba/bus.h> +#include <linux/amba/serial.h> + +struct adm5120_flash_platform_data { + void (*set_vpp)(struct map_info *, int); + void (*switch_bank)(unsigned); +#ifdef CONFIG_MTD_PARTITIONS + unsigned int nr_parts; + struct mtd_partition *parts; +#endif +}; + +struct adm5120_nand_platform_data { + /* TODO : not yet implemented */ +}; + +struct adm5120_switch_platform_data { + /* TODO: not yet implemented */ +}; + +struct adm5120_pci_irq { + u8 slot; + u8 func; + u8 pin; + unsigned irq; +}; + +struct adm5120_pci_platform_data { + unsigned int nr_irqs; + struct adm5120_pci_irq *irqs; +}; + +extern struct adm5120_flash_platform_data adm5120_flash0_data; +extern struct adm5120_flash_platform_data adm5120_flash1_data; +extern struct adm5120_nand_platform_data adm5120_nand_data; +extern struct adm5120_pci_platform_data adm5120_pci_data; +extern struct adm5120_switch_platform_data adm5120_switch_data; +extern struct amba_pl010_data adm5120_uart0_data; +extern struct amba_pl010_data adm5120_uart1_data; + +extern struct platform_device adm5120_flash0_device; +extern struct platform_device adm5120_flash1_device; +extern struct platform_device adm5120_nand_device; +extern struct platform_device adm5120_usbc_device; +extern struct platform_device adm5120_pci_device; +extern struct platform_device adm5120_switch_device; +extern struct amba_device adm5120_uart0_device; +extern struct amba_device adm5120_uart1_device; + + +#endif /* _ADM5120_PLATFORM_H_ */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_switch.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_switch.h new file mode 100644 index 000000000..eb06bfa60 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_switch.h @@ -0,0 +1,148 @@ +/* + * ADM5120 ethernet switch definitions + * + * This header file defines the hardware registers of the ADM5120 SoC + * built-in Ethernet switch. + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ADM5120_SWITCH_H +#define _ADM5120_SWITCH_H + +#define BITMASK(len) ((1 << (len))-1) +#define ONEBIT(at) (1 << (at)) + +/* Switch register offsets */ +#define SWITCH_REG_CODE 0x0000 +#define SWITCH_REG_SOFT_RESET 0x0004 +#define SWITCH_REG_MEMCTRL 0x001C +#define SWITCH_REG_CPUP_CONF 0x0024 +#define SWITCH_REG_PORT_CONF0 0x0028 +#define SWITCH_REG_PORT_CONF1 0x002C +#define SWITCH_REG_PORT_CONF2 0x0030 +#define SWITCH_REG_VLAN_G1 0x0040 +#define SWITCH_REG_VLAN_G2 0x0044 +#define SWITCH_REG_SEND_TRIG 0x0048 +#define SWITCH_REG_MAC_WT0 0x0058 +#define SWITCH_REG_MAC_WT1 0x005C +#define SWITCH_REG_PHY_CNTL0 0x0068 +#define SWITCH_REG_PHY_CNTL1 0x006C +#define SWITCH_REG_PHY_CNTL2 0x007C +#define SWITCH_REG_PHY_CNTL3 0x0080 +#define SWITCH_REG_PRI_CNTL 0x0084 +#define SWITCH_REG_INT_STATUS 0x00B0 +#define SWITCH_REG_INT_MASK 0x00B4 +#define SWITCH_REG_GPIO_CONF0 0x00B8 +#define SWITCH_REG_GPIO_CONF2 0x00BC +#define SWITCH_REG_WDOG0 0x00C0 +#define SWITCH_REG_WDOG1 0x00C4 +#define SWITCH_REG_PHY_CNTL4 0x00A0 + +#define SWITCH_REG_SEND_HBADDR 0x00D0 +#define SWITCH_REG_SEND_LBADDR 0x00D4 +#define SWITCH_REG_RECV_HBADDR 0x00D8 +#define SWITCH_REG_RECV_LBADDR 0x00DC + +#define SWITCH_REG_TIMER_INT 0x00F0 +#define SWITCH_REG_TIMER 0x00F4 + +#define SWITCH_REG_PORT0_LED 0x0100 +#define SWITCH_REG_PORT1_LED 0x0104 +#define SWITCH_REG_PORT2_LED 0x0108 +#define SWITCH_REG_PORT3_LED 0x010C +#define SWITCH_REG_PORT4_LED 0x0110 + +/* CODE register bits */ +#define CODE_PC_MASK BITMASK(16) /* Product Code */ +#define CODE_REV_SHIFT 16 +#define CODE_REV_MASK BITMASK(4) /* Product Revision */ +#define CODE_CLKS_SHIFT 20 +#define CODE_CLKS_MASK BITMASK(2) /* Clock Speed */ +#define CODE_CLKS_175 0 /* 175 MHz */ +#define CODE_CLKS_200 1 /* 200 MHz */ +#define CODE_CLKS_225 2 /* 225 MHz */ +#define CODE_CLKS_250 3 /* 250 MHz */ +#define CODE_NAB ONEBIT(24) /* NAND boot */ +#define CODE_PK_MASK BITMASK(1) /* Package type */ +#define CODE_PK_SHIFT 29 +#define CODE_PK_BGA 0 /* BGA package */ +#define CODE_PK_PQFP 1 /* PQFP package */ + +/* MEMCTRL register bits */ +#define MEMCTRL_SDRS_MASK BITMASK(3) /* SDRAM bank size */ +#define MEMCTRL_SDRS_4M 0x01 +#define MEMCTRL_SDRS_8M 0x02 +#define MEMCTRL_SDRS_16M 0x03 +#define MEMCTRL_SDRS_64M 0x04 +#define MEMCTRL_SDRS_128M 0x05 +#define MEMCTRL_SDR1_ENABLE ONEBIT(5) /* enable SDRAM bank 1 */ + +#define MEMCTRL_SRS0_SHIFT 8 /* shift for SRAM0 size */ +#define MEMCTRL_SRS1_SHIFT 16 /* shift for SRAM1 size */ +#define MEMCTRL_SRS_MASK BITMASK(3) /* SRAM size mask */ +#define MEMCTRL_SRS_DISABLED 0x00 /* Disabled */ +#define MEMCTRL_SRS_512K 0x01 /* 512KB*/ +#define MEMCTRL_SRS_1M 0x02 /* 1MB */ +#define MEMCTRL_SRS_2M 0x03 /* 2MB */ +#define MEMCTRL_SRS_4M 0x04 /* 4MB */ + +/* GPIO_CONF0 register bits */ +#define GPIO_CONF0_MASK BITMASK(8) +#define GPIO_CONF0_IM_SHIFT 0 +#define GPIO_CONF0_IV_SHIFT 8 +#define GPIO_CONF0_OE_SHIFT 16 +#define GPIO_CONF0_OV_SHIFT 24 +#define GPIO_CONF0_IM_MASK (0xFF << GPIO_CONF0_IM_SHIFT) +#define GPIO_CONF0_IV_MASK (0xFF << GPIO_CONF0_IV_SHIFT) +#define GPIO_CONF0_OE_MASK (0xFF << GPIO_CONF0_OE_SHIFT) +#define GPIO_CONF0_OV_MASK (0xFF << GPIO_CONF0_OV_SHIFT) + +/* TIMER_INT register bits */ +#define TIMER_INT_TOS ONEBIT(1) /* time-out status */ +#define TIMER_INT_TOM ONEBIT(16) /* mask time-out interrupt */ + +/* TIMER register bits */ +#define TIMER_PERIOD_MASK BITMASK(16) /* mask for timer period */ +#define TIMER_PERIOD_DEFAULT 0xFFFF /* default timer period */ +#define TIMER_TE ONEBIT(16) /* timer enable bit */ + +/* PORTx_LED register bits */ +#define LED_MODE_MASK BITMASK(4) +#define LED_MODE_INPUT 0 +#define LED_MODE_FLASH 1 +#define LED_MODE_OUT_HIGH 2 +#define LED_MODE_OUT_LOW 3 +#define LED_MODE_LINK 4 +#define LED_MODE_SPEED 5 +#define LED_MODE_DUPLEX 6 +#define LED_MODE_ACT 7 +#define LED_MODE_COLL 8 +#define LED_MODE_LINK_ACT 9 +#define LED_MODE_DUPLEX_COLL 10 +#define LED_MODE_10M_ACT 11 +#define LED_MODE_100M_ACT 12 +#define LED0_MODE_SHIFT 0 /* LED0 mode shift */ +#define LED1_MODE_SHIFT 4 /* LED1 mode shift */ +#define LED2_MODE_SHIFT 8 /* LED2 mode shift */ +#define LED0_IV_SHIFT 12 /* LED0 input value shift */ +#define LED1_IV_SHIFT 13 /* LED1 input value shift */ +#define LED2_IV_SHIFT 14 /* LED2 input value shift */ + +#endif /* _ADM5120_SWITCH_H */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_uart.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_uart.h new file mode 100755 index 000000000..89c48dee5 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/adm5120_uart.h @@ -0,0 +1,75 @@ +/* + * ADM5120 UART definitions + * + * This header file defines the hardware registers of the ADM5120 SoC + * built-in UARTs. + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ADM5120_UART_H_ +#define _ADM5120_UART_H_ + +#define UART_BAUDDIV(clk, baud) ((clk/(16 * (baud)))-1) + +#define UART_REG_DATA 0x00 +#define UART_REG_RSR 0x04 +#define UART_REG_ECR UART_REG_RSR +#define UART_REG_LCRH 0x08 +#define UART_REG_LCRM 0x0C +#define UART_REG_LCRL 0x10 +#define UART_REG_CTRL 0x14 +#define UART_REG_FLAG 0x18 + +/* Receive Status Register bits */ +#define UART_RSR_FE ( 1 << 0 ) +#define UART_RSR_PE ( 1 << 1 ) +#define UART_RSR_BE ( 1 << 2 ) +#define UART_RSR_OE ( 1 << 3 ) +#define UART_RSR_ERR ( UART_RSR_FE | UART_RSR_PE | UART_RSR_BE ) + +#define UART_ECR_ALL 0xFF + +/* Line Control High register bits */ +#define UART_LCRH_BRK ( 1 << 0 ) /* send break */ +#define UART_LCRH_PEN ( 1 << 1 ) /* parity enable */ +#define UART_LCRH_EPS ( 1 << 2 ) /* even parity select */ +#define UART_LCRH_STP1 ( 0 << 3 ) /* one stop bits select */ +#define UART_LCRH_STP2 ( 1 << 3 ) /* two stop bits select */ +#define UART_LCRH_FEN ( 1 << 4 ) /* FIFO enable */ + +#define UART_LCRH_WLEN5 ( 0 << 5 ) +#define UART_LCRH_WLEN6 ( 1 << 5 ) +#define UART_LCRH_WLEN7 ( 2 << 5 ) +#define UART_LCRH_WLEN8 ( 3 << 5 ) + +/* Control register bits */ +#define UART_CTRL_EN ( 1 << 0 ) + +/* Flag register bits */ +#define UART_FLAG_CTS ( 1 << 0 ) +#define UART_FLAG_DSR ( 1 << 1 ) +#define UART_FLAG_DCD ( 1 << 2 ) +#define UART_FLAG_BUSY ( 1 << 3 ) +#define UART_FLAG_RXFE ( 1 << 4 ) +#define UART_FLAG_TXFF ( 1 << 5 ) +#define UART_FLAG_RXFF ( 1 << 6 ) +#define UART_FLAG_TXFE ( 1 << 7 ) + +#endif /* _ADM5120_UART_H_ */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/asm/sizes.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/asm/sizes.h new file mode 100644 index 000000000..503843db1 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/asm/sizes.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* DO NOT EDIT!! - this file automatically generated + * from .s file by awk -f s2h.awk + */ +/* Size definitions + * Copyright (C) ARM Limited 1998. All rights reserved. + */ + +#ifndef __sizes_h +#define __sizes_h 1 + +/* handy sizes */ +#define SZ_16 0x00000010 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#define SZ_1K 0x00000400 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 + +#endif + +/* END */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/cpu-feature-overrides.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/cpu-feature-overrides.h new file mode 100644 index 000000000..6216b864f --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/cpu-feature-overrides.h @@ -0,0 +1,89 @@ +/* + * $Id$ + * + * ADM5120 specific CPU feature overrides + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * + * This file was derived from: include/asm-mips/cpu-features.h + * Copyright (C) 2003, 2004 Ralf Baechle + * Copyright (C) 2004 Maciej W. Rozycki + * + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef __ASM_MACH_ADM5120_CPU_FEATURE_OVERRIDES_H +#define __ASM_MACH_ADM5120_CPU_FEATURE_OVERRIDES_H + +#ifdef CONFIG_ADM5120_CPU_OVERRIDES + +/* + * The ADM5120 SOC has a built-in MIPS 4Kc core. + */ +#define cpu_has_tlb 1 +#define cpu_has_4kex 1 +#define cpu_has_3k_cache 0 +#define cpu_has_4k_cache 1 +#define cpu_has_tx39_cache 0 +#define cpu_has_sb1_cache 0 +#define cpu_has_fpu 0 +#define cpu_has_32fpr 0 +#define cpu_has_counter 1 +#define cpu_has_watch 1 +#define cpu_has_divec 1 +/* #define cpu_has_vce ? */ +/* #define cpu_has_cache_cdex_p ? */ +/* #define cpu_has_cache_cdex_s ? */ +#define cpu_has_prefetch 1 +/* #define cpu_has_mcheck ? */ +#define cpu_has_ejtag 1 +#define cpu_has_llsc 1 + +#define cpu_has_mips16 0 +#define cpu_has_mdmx 0 +#define cpu_has_mips3d 0 +#define cpu_has_smartmips 0 + +/* #define cpu_has_vtag_icache ? */ +/* #define cpu_has_dc_aliases ? */ +/* #define cpu_has_ic_fills_f_dc ? */ +/* #define cpu_has_pindexed_dcache ? */ + +/* #define cpu_icache_snoops_remote_store ? */ + +#define cpu_has_mips32r1 1 +#define cpu_has_mips32r2 0 +#define cpu_has_mips64r1 0 +#define cpu_has_mips64r2 0 + +#define cpu_has_dsp 0 +#define cpu_has_mipsmt 0 + +/* #define cpu_has_nofpuex ? */ +#define cpu_has_64bits 0 +#define cpu_has_64bit_zero_reg 0 +#define cpu_has_64bit_gp_regs 0 +#define cpu_has_64bit_addresses 0 + +/* #define cpu_has_inclusive_pcaches ? */ + +#define cpu_dcache_line_size() 16 +#define cpu_icache_line_size() 16 + +#endif /* CONFIG_ADM5120_CPU_OVERRIDES */ + + +#endif /* __ASM_MACH_ADM5120_CPU_FEATURE_OVERRIDES_H */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/gpio.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/gpio.h new file mode 100644 index 000000000..684d86cd4 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/gpio.h @@ -0,0 +1,126 @@ +/* + * $Id$ + * + * ADM5120 GPIO wrappers for arch-neutral GPIO calls + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef _ADM5120_GPIO_H_ +#define _ADM5120_GPIO_H_ + +#include <asm/mach-adm5120/adm5120_switch.h> + +#define ADM5120_GPIO_PIN0 0 +#define ADM5120_GPIO_PIN1 1 +#define ADM5120_GPIO_PIN2 2 +#define ADM5120_GPIO_PIN3 3 +#define ADM5120_GPIO_PIN4 4 +#define ADM5120_GPIO_PIN5 5 +#define ADM5120_GPIO_PIN6 6 +#define ADM5120_GPIO_PIN7 7 +#define ADM5120_GPIO_P0L0 8 +#define ADM5120_GPIO_P0L1 9 +#define ADM5120_GPIO_P0L2 10 +#define ADM5120_GPIO_P1L0 11 +#define ADM5120_GPIO_P1L1 12 +#define ADM5120_GPIO_P1L2 13 +#define ADM5120_GPIO_P2L0 14 +#define ADM5120_GPIO_P2L1 15 +#define ADM5120_GPIO_P2L2 16 +#define ADM5120_GPIO_P3L0 17 +#define ADM5120_GPIO_P3L1 18 +#define ADM5120_GPIO_P3L2 19 +#define ADM5120_GPIO_P4L0 20 +#define ADM5120_GPIO_P4L1 21 +#define ADM5120_GPIO_P4L2 22 +#define ADM5120_GPIO_MAX 22 +#define ADM5120_GPIO_COUNT ADM5120_GPIO_MAX+1 + +#define ADM5120_GPIO_LOW 0 +#define ADM5120_GPIO_HIGH 1 + +#define ADM5120_GPIO_SWITCH 0x10 +#define ADM5120_GPIO_FLASH (ADM5120_GPIO_SWITCH | LED_MODE_FLASH) +#define ADM5120_GPIO_LINK (ADM5120_GPIO_SWITCH | LED_MODE_LINK) +#define ADM5120_GPIO_SPEED (ADM5120_GPIO_SWITCH | LED_MODE_SPEED) +#define ADM5120_GPIO_DUPLEX (ADM5120_GPIO_SWITCH | LED_MODE_DUPLEX) +#define ADM5120_GPIO_ACT (ADM5120_GPIO_SWITCH | LED_MODE_ACT) +#define ADM5120_GPIO_COLL (ADM5120_GPIO_SWITCH | LED_MODE_COLL) +#define ADM5120_GPIO_LINK_ACT (ADM5120_GPIO_SWITCH | LED_MODE_LINK_ACT) +#define ADM5120_GPIO_DUPLEX_COLL (ADM5120_GPIO_SWITCH | LED_MODE_DUPLEX_COLL) +#define ADM5120_GPIO_10M_ACT (ADM5120_GPIO_SWITCH | LED_MODE_10M_ACT) +#define ADM5120_GPIO_100M_ACT (ADM5120_GPIO_SWITCH | LED_MODE_100M_ACT) + +extern int adm5120_gpio_direction_input(unsigned gpio); +extern int adm5120_gpio_direction_output(unsigned gpio, int value); +extern int adm5120_gpio_get_value(unsigned gpio); +extern void adm5120_gpio_set_value(unsigned gpio, int value); +extern int adm5120_gpio_request(unsigned gpio, const char *label); +extern void adm5120_gpio_free(unsigned gpio); +extern int adm5120_gpio_to_irq(unsigned gpio); +extern int adm5120_irq_to_gpio(unsigned irq); + +/* + * Wrappers for the generic GPIO layer + */ +static inline int gpio_direction_input(unsigned gpio) +{ + return adm5120_gpio_direction_input(gpio); +} + +static inline int gpio_direction_output(unsigned gpio, int value) +{ + return adm5120_gpio_direction_output(gpio,value); +} + +static inline int gpio_get_value(unsigned gpio) +{ + return adm5120_gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned gpio, int value) +{ + adm5120_gpio_set_value(gpio, value); +} + +static inline int gpio_request(unsigned gpio, const char *label) +{ + return adm5120_gpio_request(gpio, label); +} + +static inline void gpio_free(unsigned gpio) +{ + adm5120_gpio_free(gpio); +} + +static inline int gpio_to_irq(unsigned gpio) +{ + return adm5120_gpio_to_irq(gpio); +} + +static inline int irq_to_gpio(unsigned irq) +{ + return adm5120_irq_to_gpio(irq); +} + +#include <asm-generic/gpio.h> /* cansleep wrappers */ + +#endif diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/cfe.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/cfe.h new file mode 100644 index 000000000..7d202361a --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/cfe.h @@ -0,0 +1,31 @@ +/* + * $Id$ + * + * Broadcom's CFE definitions + * + * Copyright (C) 2006,2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _PROM_CFE_H_ +#define _PROM_CFE_H_ + +extern int cfe_present(void) __init; +extern char *cfe_getenv(char *); + +#endif /*_PROM_CFE_H_*/ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/generic.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/generic.h new file mode 100644 index 000000000..2cfa10367 --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/generic.h @@ -0,0 +1,31 @@ +/* + * $Id$ + * + * Generic prom definitions + * + * Copyright (C) 2006,2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _PROM_GENERIC_H_ +#define _PROM_GENERIC_H_ + +extern int generic_prom_present(void) __init; +extern char *generic_prom_getenv(char *); + +#endif /*_PROM_GENERIC_H_*/ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/myloader.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/myloader.h new file mode 100644 index 000000000..526de865c --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/myloader.h @@ -0,0 +1,191 @@ +/* + * $Id$ + * + * Compex's MyLoader specific definitions + * + * Copyright (C) 2006,2007 Gabor Juhos <juhosg at openwrt.org> + * 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _MYLOADER_H_ +#define _MYLOADER_H_ + +/* + * Firmware file format: + * + * <header> + * [<block descriptor 0>] + * ... + * [<block descriptor n>] + * <null block descriptor> + * [<block data 0>] + * ... + * [<block data n>] + * + * + */ + +/* Myloader specific magic numbers */ +#define MYLO_MAGIC_FIRMWARE 0x4C594D00 +#define MYLO_MAGIC_20021103 0x20021103 +#define MYLO_MAGIC_20021107 0x20021107 + +#define MYLO_MAGIC_SYS_PARAMS MYLO_MAGIC_20021107 +#define MYLO_MAGIC_PARTITIONS MYLO_MAGIC_20021103 +#define MYLO_MAGIC_BOARD_PARAMS MYLO_MAGIC_20021103 + +/* + * Addresses of the data structures provided by MyLoader + */ +#define MYLO_MIPS_SYS_PARAMS 0x80000800 /* System Parameters */ +#define MYLO_MIPS_BOARD_PARAMS 0x80000A00 /* Board Parameters */ +#define MYLO_MIPS_PARTITIONS 0x80000C00 /* Partition Table */ +#define MYLO_MIPS_BOOT_PARAMS 0x80000E00 /* Boot Parameters */ + +/* Vendor ID's (seems to be same as the PCI vendor ID's) */ +#define VENID_COMPEX 0x11F6 + +/* Devices based on the ADM5120 */ +#define DEVID_COMPEX_NP27G 0x0078 +#define DEVID_COMPEX_NP28G 0x044C +#define DEVID_COMPEX_NP28GHS 0x044E +#define DEVID_COMPEX_WP54Gv1C 0x0514 +#define DEVID_COMPEX_WP54G 0x0515 +#define DEVID_COMPEX_WP54AG 0x0546 +#define DEVID_COMPEX_WPP54AG 0x0550 +#define DEVID_COMPEX_WPP54G 0x0555 + +/* Devices based on the IXP422 */ +#define DEVID_COMPEX_WP18 0x047E +#define DEVID_COMPEX_NP18A 0x0489 + +/* Other devices */ +#define DEVID_COMPEX_NP26G8M 0x03E8 +#define DEVID_COMPEX_NP26G16M 0x03E9 + +struct mylo_fw_header { + uint32_t magic; /* must be MYLO_MAGIC_FIRMWARE */ + uint32_t crc; /* CRC of the whole firmware */ + uint32_t res0; /* unknown/unused */ + uint32_t res1; /* unknown/unused */ + uint16_t vid; /* vendor ID */ + uint16_t did; /* device ID */ + uint16_t svid; /* sub vendor ID */ + uint16_t sdid; /* sub device ID */ + uint32_t rev; /* device revision */ + uint32_t fwhi; /* FIXME: firmware version high? */ + uint32_t fwlo; /* FIXME: firmware version low? */ + uint32_t flags; /* firmware flags */ +}; + +#define FW_FLAG_BOARD_PARAMS_WP 0x01 /* board parameters are write protected */ +#define FW_FLAG_BOOT_SECTOR_WE 0x02 /* enable of write boot sectors (below 64K) */ + +struct mylo_fw_blockdesc { + uint32_t type; /* block type */ + uint32_t addr; /* relative address to flash start */ + uint32_t dlen; /* size of block data in bytes */ + uint32_t blen; /* total size of block in bytes */ +}; + +#define FW_DESC_TYPE_UNUSED 0 +#define FW_DESC_TYPE_USED 1 + +struct mylo_partition { + uint16_t flags; /* partition flags */ + uint16_t type; /* type of the partition */ + uint32_t addr; /* relative address of the partition from the + flash start */ + uint32_t size; /* size of the partition in bytes */ + uint32_t param; /* if this is the active partition, the + MyLoader load code to this address */ +}; + +#define PARTITION_FLAG_ACTIVE 0x8000 /* this is the active partition, + * MyLoader loads firmware from here */ +#define PARTITION_FLAG_ISRAM 0x2000 /* FIXME: this is a RAM partition? */ +#define PARTIIION_FLAG_RAMLOAD 0x1000 /* FIXME: load this partition into the RAM? */ +#define PARTITION_FLAG_PRELOAD 0x0800 /* the partition data preloaded to RAM + * before decompression */ +#define PARTITION_FLAG_HAVEHDR 0x0002 /* the partition data have a header */ + +#define PARTITION_TYPE_FREE 0 +#define PARTITION_TYPE_USED 1 + +#define MYLO_MAX_PARTITIONS 8 /* maximum number of partitions in the + partition table */ + +struct mylo_partition_table { + uint32_t magic; /* must be MYLO_MAGIC_PARTITIONS */ + uint32_t res0; /* unknown/unused */ + uint32_t res1; /* unknown/unused */ + uint32_t res2; /* unknown/unused */ + struct mylo_partition partitions[MYLO_MAX_PARTITIONS]; +}; + +struct mylo_partition_header { + uint32_t len; /* length of the partition data */ + uint32_t crc; /* CRC value of the partition data */ +}; + +struct mylo_system_params { + uint32_t magic; /* must be MYLO_MAGIC_SYS_PARAMS */ + uint32_t res0; + uint32_t res1; + uint32_t mylo_ver; + uint16_t vid; /* Vendor ID */ + uint16_t did; /* Device ID */ + uint16_t svid; /* Sub Vendor ID */ + uint16_t sdid; /* Sub Device ID */ + uint32_t rev; /* device revision */ + uint32_t fwhi; + uint32_t fwlo; + uint32_t tftp_addr; + uint32_t prog_start; + uint32_t flash_size; /* Size of boot FLASH in bytes */ + uint32_t dram_size; /* Size of onboard RAM in bytes */ +}; + + +struct mylo_eth_addr { + uint8_t mac[6]; + uint8_t csum[2]; +}; + +#define MYLO_ETHADDR_COUNT 8 /* maximum number of ethernet address + in the board parameters */ + +struct mylo_board_params { + uint32_t magic; /* must be MYLO_MAGIC_BOARD_PARAMS */ + uint32_t res0; + uint32_t res1; + uint32_t res2; + struct mylo_eth_addr addr[MYLO_ETHADDR_COUNT]; +}; + +struct myloader_info { + u32 vid; + u32 did; + u32 svid; + u32 sdid; +}; + +extern struct myloader_info myloader_info; +extern int myloader_present(void) __init; + +#endif /* _MYLOADER_H_*/ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/routerboot.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/routerboot.h new file mode 100644 index 000000000..3c0898b3d --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/routerboot.h @@ -0,0 +1,137 @@ +/* + * $Id$ + * + * Mikrotik's RouterBOOT definitions + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ROUTERBOOT_H +#define _ROUTERBOOT_H + +#define RB_MAC_SIZE 6 +#define RB_MAX_MAC_COUNT 6 + +struct rb_bios_settings { + u32 hs_offs; /* hard settings offset */ + u32 hs_size; /* hard settings size */ + u32 fw_offs; /* firmware offset */ + u32 ss_offs; /* soft settings offset */ + u32 ss_size; /* soft settings size */ +}; + +struct rb_hard_settings { + char *name; /* board name */ + char *bios_ver; /* BIOS version */ + u32 mem_size; /* memory size in bytes */ + u32 mac_count; /* number of mac addresses */ + u8 macs[RB_MAC_SIZE][RB_MAX_MAC_COUNT]; /* mac addresses */ +}; + +/* + * Magic numbers + */ +#define RB_MAGIC_HARD 0x64726148 /* "Hard" */ +#define RB_MAGIC_SOFT 0x74666F53 /* "Soft" */ +#define RB_MAGIC_DAWN 0x6E776144 /* "Dawn" */ + +#define RB_ID_TERMINATOR 0 + +/* + * ID values for Hardware settings + */ +#define RB_ID_HARD_01 1 +#define RB_ID_HARD_02 2 +#define RB_ID_FLASH_INFO 3 +#define RB_ID_MAC_ADDRESS_PACK 4 +#define RB_ID_BOARD_NAME 5 +#define RB_ID_BIOS_VERSION 6 +#define RB_ID_HARD_07 7 +#define RB_ID_SDRAM_TIMINGS 8 +#define RB_ID_DEVICE_TIMINGS 9 +#define RB_ID_SOFTWARE_ID 10 +#define RB_ID_SERIAL_NUMBER 11 +#define RB_ID_HARD_12 12 +#define RB_ID_MEMORY_SIZE 13 +#define RB_ID_MAC_ADDRESS_COUNT 14 + +/* + * ID values for Software settings + */ +#define RB_ID_UART_SPEED 1 +#define RB_ID_BOOT_DELAY 2 +#define RB_ID_BOOT_DEVICE 3 +#define RB_ID_BOOT_KEY 4 +#define RB_ID_CPU_MODE 5 +#define RB_ID_FW_VERSION 6 +#define RB_ID_SOFT_07 7 +#define RB_ID_SOFT_08 8 +#define RB_ID_BOOT_PROTOCOL 9 +#define RB_ID_SOFT_10 10 +#define RB_ID_SOFT_11 11 + +/* + * UART_SPEED values + */ +#define RB_UART_SPEED_115200 0 +#define RB_UART_SPEED_57600 1 +#define RB_UART_SPEED_38400 2 +#define RB_UART_SPEED_19200 3 +#define RB_UART_SPEED_9600 4 +#define RB_UART_SPEED_4800 5 +#define RB_UART_SPEED_2400 6 +#define RB_UART_SPEED_1200 7 + +/* + * BOOT_DELAY values + */ +#define RB_BOOT_DELAY_0SEC 0 +#define RB_BOOT_DELAY_1SEC 1 +#define RB_BOOT_DELAY_2SEC 2 + +/* + * BOOT_DEVICE values + */ +#define RB_BOOT_DEVICE_ETHER 0 +#define RB_BOOT_DEVICE_NANDETH 1 +#define RB_BOOT_DEVICE_ETHONCE 2 +#define RB_BOOT_DEVICE_NANDONLY 3 + +/* + * BOOT_KEY values + */ +#define RB_BOOT_KEY_ANY 0 +#define RB_BOOT_KEY_DEL 1 + +/* + * CPU_MODE values + */ +#define RB_CPU_MODE_POWERSAVE 0 +#define RB_CPU_MODE_REGULAR 1 + +/* + * BOOT_PROTOCOL values + */ +#define RB_BOOT_PROTOCOL_BOOTP 0 +#define RB_BOOT_PROTOCOL_DHCP 1 + +extern int routerboot_present(void) __init; +extern char *routerboot_get_boardname(void); + +#endif /* _ROUTERBOOT_H */ diff --git a/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/zynos.h b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/zynos.h new file mode 100644 index 000000000..e9471b5ad --- /dev/null +++ b/target/linux/adm5120/files/include/asm-mips/mach-adm5120/prom/zynos.h @@ -0,0 +1,98 @@ +/* + * $Id$ + * + * ZyNOS (ZyXEL's Networking OS) definitions + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ZYNOS_H +#define _ZYNOS_H + +#define ZYNOS_NAME_LEN 32 +#define ZYNOS_FEAT_BYTES 22 +#define ZYNOS_MAC_LEN 6 + +struct zynos_board_info { + unsigned char vendor[ZYNOS_NAME_LEN]; + unsigned char product[ZYNOS_NAME_LEN]; + u32 bootext_addr; + u32 res0; + u16 board_id; + u8 res1[6]; + u8 feat_other[ZYNOS_FEAT_BYTES]; + u8 feat_main; + u8 res2; + u8 mac[ZYNOS_MAC_LEN]; + u8 country; + u8 dbgflag; +} __attribute__ ((packed)); + +/* + * Vendor IDs + */ +#define ZYNOS_VENDOR_ID_ZYXEL 0 +#define ZYNOS_VENDOR_ID_NETGEAR 1 +#define ZYNOS_VENDOR_ID_DLINK 2 +#define ZYNOS_VENDOR_ID_OTHER 3 +#define ZYNOS_VENDOR_ID_LUCENT 4 + +/* + * Vendor names + */ +#define ZYNOS_VENDOR_DLINK "D-Link" +#define ZYNOS_VENDOR_LUCENT "LUCENT" +#define ZYNOS_VENDOR_NETGEAR "NetGear" +#define ZYNOS_VENDOR_ZYXEL "ZyXEL" + +/* + * Board IDs (big-endian) + */ +#define ZYNOS_BOARD_ES2108 0x00F2 /* Ethernet Switch 2108 */ +#define ZYNOS_BOARD_ES2108F 0x01AF /* Ethernet Switch 2108-F */ +#define ZYNOS_BOARD_ES2108G 0x00F3 /* Ethernet Switch 2108-G */ +#define ZYNOS_BOARD_ES2108LC 0x00FC /* Ethernet Switch 2108-LC */ +#define ZYNOS_BOARD_ES2108PWR 0x00F4 /* Ethernet Switch 2108PWR */ +#define ZYNOS_BOARD_HS100 0x9FF1 /* HomeSafe 100/100W */ +#define ZYNOS_BOARD_P334 0x9FF5 /* Prestige 334 */ +#define ZYNOS_BOARD_P334U 0x9FDD /* Prestige 334U */ +#define ZYNOS_BOARD_P334W 0x9FF3 /* Prestige 334W */ +#define ZYNOS_BOARD_P334WH 0x00E0 /* Prestige 334WH */ +#define ZYNOS_BOARD_P334WHD 0x00E1 /* Prestige 334WHD */ +#define ZYNOS_BOARD_P334WT 0x9FEF /* Prestige 334WT */ +#define ZYNOS_BOARD_P335 0x9FED /* Prestige 335/335WT */ +#define ZYNOS_BOARD_P335PLUS 0x0025 /* Prestige 335Plus */ +#define ZYNOS_BOARD_P335U 0x9FDC /* Prestige 335U */ + +/* + * Some magic numbers (big-endian) + */ +#define ZYNOS_MAGIC_DBGAREA1 0x48646267 /* "Hdbg" */ +#define ZYNOS_MAGIC_DBGAREA2 0x61726561 /* "area" */ + +struct bootbase_info { + u16 vendor_id; + u16 board_id; + u8 mac[6]; +}; + +extern struct bootbase_info bootbase_info; +extern int bootbase_present(void) __init; + +#endif /* _ZYNOS_H */ diff --git a/target/linux/adm5120/files/include/linux/gpio_leds.h b/target/linux/adm5120/files/include/linux/gpio_leds.h new file mode 100644 index 000000000..5e78217b5 --- /dev/null +++ b/target/linux/adm5120/files/include/linux/gpio_leds.h @@ -0,0 +1,37 @@ +/* + * $Id$ + * + * GPIO LEDs platform data structure + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at 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 Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef _GPIO_LEDS_H_ +#define _GPIO_LEDS_H_ + +struct gpio_led_platform_data { + char *name; + char *trigger; + unsigned gpio; /* GPIO line number */ + int value_off; /* value to turn LED OFF */ + int value_on; /* value to turn LED ON */ +}; + +#endif /* _GPIO_LEDS_H__ */ |