diff options
Diffstat (limited to 'arch/arm/mach-ixp4xx')
-rw-r--r-- | arch/arm/mach-ixp4xx/Kconfig | 14 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/Makefile | 8 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/avila-setup.c | 14 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/common.c | 10 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/dsmg600-power.c | 125 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/dsmg600-setup.c | 147 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/ixdp425-setup.c | 43 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/ixp4xx_npe.c | 741 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/ixp4xx_qmgr.c | 274 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/nas100d-power.c | 69 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/nas100d-setup.c | 188 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/nslu2-power.c | 92 | ||||
-rw-r--r-- | arch/arm/mach-ixp4xx/nslu2-setup.c | 151 |
13 files changed, 1483 insertions, 393 deletions
diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig index 61b2dfcb89d..e774447c059 100644 --- a/arch/arm/mach-ixp4xx/Kconfig +++ b/arch/arm/mach-ixp4xx/Kconfig @@ -189,6 +189,20 @@ config IXP4XX_INDIRECT_PCI need to use the indirect method instead. If you don't know what you need, leave this option unselected. +config IXP4XX_QMGR + tristate "IXP4xx Queue Manager support" + help + This driver supports IXP4xx built-in hardware queue manager + and is automatically selected by Ethernet and HSS drivers. + +config IXP4XX_NPE + tristate "IXP4xx Network Processor Engine support" + select HOTPLUG + select FW_LOADER + help + This driver supports IXP4xx built-in network coprocessors + and is automatically selected by Ethernet and HSS drivers. + endmenu endif diff --git a/arch/arm/mach-ixp4xx/Makefile b/arch/arm/mach-ixp4xx/Makefile index 77e00ade558..c1956882c48 100644 --- a/arch/arm/mach-ixp4xx/Makefile +++ b/arch/arm/mach-ixp4xx/Makefile @@ -23,10 +23,12 @@ obj-$(CONFIG_MACH_AVILA) += avila-setup.o obj-$(CONFIG_MACH_IXDPG425) += coyote-setup.o obj-$(CONFIG_ARCH_ADI_COYOTE) += coyote-setup.o obj-$(CONFIG_MACH_GTWX5715) += gtwx5715-setup.o -obj-$(CONFIG_MACH_NSLU2) += nslu2-setup.o nslu2-power.o -obj-$(CONFIG_MACH_NAS100D) += nas100d-setup.o nas100d-power.o -obj-$(CONFIG_MACH_DSMG600) += dsmg600-setup.o dsmg600-power.o +obj-$(CONFIG_MACH_NSLU2) += nslu2-setup.o +obj-$(CONFIG_MACH_NAS100D) += nas100d-setup.o +obj-$(CONFIG_MACH_DSMG600) += dsmg600-setup.o obj-$(CONFIG_MACH_GATEWAY7001) += gateway7001-setup.o obj-$(CONFIG_MACH_WG302V2) += wg302v2-setup.o obj-$(CONFIG_PCI) += $(obj-pci-$(CONFIG_PCI)) common-pci.o +obj-$(CONFIG_IXP4XX_QMGR) += ixp4xx_qmgr.o +obj-$(CONFIG_IXP4XX_NPE) += ixp4xx_npe.o diff --git a/arch/arm/mach-ixp4xx/avila-setup.c b/arch/arm/mach-ixp4xx/avila-setup.c index d59b8dc7dc7..e38f45fa58a 100644 --- a/arch/arm/mach-ixp4xx/avila-setup.c +++ b/arch/arm/mach-ixp4xx/avila-setup.c @@ -18,6 +18,7 @@ #include <linux/tty.h> #include <linux/serial_8250.h> #include <linux/slab.h> +#include <linux/i2c-gpio.h> #include <asm/types.h> #include <asm/setup.h> @@ -47,18 +48,17 @@ static struct platform_device avila_flash = { .resource = &avila_flash_resource, }; -static struct ixp4xx_i2c_pins avila_i2c_gpio_pins = { +static struct i2c_gpio_platform_data avila_i2c_gpio_data = { .sda_pin = AVILA_SDA_PIN, .scl_pin = AVILA_SCL_PIN, }; -static struct platform_device avila_i2c_controller = { - .name = "IXP4XX-I2C", +static struct platform_device avila_i2c_gpio = { + .name = "i2c-gpio", .id = 0, - .dev = { - .platform_data = &avila_i2c_gpio_pins, + .dev = { + .platform_data = &avila_i2c_gpio_data, }, - .num_resources = 0 }; static struct resource avila_uart_resources[] = { @@ -133,7 +133,7 @@ static struct platform_device avila_pata = { }; static struct platform_device *avila_devices[] __initdata = { - &avila_i2c_controller, + &avila_i2c_gpio, &avila_flash, &avila_uart }; diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index c1271c44924..f6d66dce685 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -442,7 +442,8 @@ static int ixp4xx_set_next_event(unsigned long evt, static void ixp4xx_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { - unsigned long opts, osrt = *IXP4XX_OSRT1 & ~IXP4XX_OST_RELOAD_MASK; + unsigned long opts = *IXP4XX_OSRT1 & IXP4XX_OST_RELOAD_MASK; + unsigned long osrt = *IXP4XX_OSRT1 & ~IXP4XX_OST_RELOAD_MASK; switch (mode) { case CLOCK_EVT_MODE_PERIODIC: @@ -455,12 +456,15 @@ static void ixp4xx_set_mode(enum clock_event_mode mode, opts = IXP4XX_OST_ENABLE | IXP4XX_OST_ONE_SHOT; break; case CLOCK_EVT_MODE_SHUTDOWN: + opts &= ~IXP4XX_OST_ENABLE; + break; + case CLOCK_EVT_MODE_RESUME: + opts |= IXP4XX_OST_ENABLE; + break; case CLOCK_EVT_MODE_UNUSED: default: osrt = opts = 0; break; - case CLOCK_EVT_MODE_RESUME: - break; } *IXP4XX_OSRT1 = osrt | opts; diff --git a/arch/arm/mach-ixp4xx/dsmg600-power.c b/arch/arm/mach-ixp4xx/dsmg600-power.c deleted file mode 100644 index 34717872d07..00000000000 --- a/arch/arm/mach-ixp4xx/dsmg600-power.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * arch/arm/mach-ixp4xx/dsmg600-power.c - * - * DSM-G600 Power/Reset driver - * Author: Michael Westerhof <mwester@dls.net> - * - * Based on nslu2-power.c - * Copyright (C) 2005 Tower Technologies - * Author: Alessandro Zummo <a.zummo@towertech.it> - * - * which was based on nslu2-io.c - * Copyright (C) 2004 Karen Spearel - * - * Maintainers: http://www.nslu2-linux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/module.h> -#include <linux/reboot.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/jiffies.h> -#include <linux/timer.h> - -#include <asm/mach-types.h> - -extern void ctrl_alt_del(void); - -/* This is used to make sure the power-button pusher is serious. The button - * must be held until the value of this counter reaches zero. - */ -static volatile int power_button_countdown; - -/* Must hold the button down for at least this many counts to be processed */ -#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */ - -static void dsmg600_power_handler(unsigned long data); -static DEFINE_TIMER(dsmg600_power_timer, dsmg600_power_handler, 0, 0); - -static void dsmg600_power_handler(unsigned long data) -{ - /* This routine is called twice per second to check the - * state of the power button. - */ - - if (*IXP4XX_GPIO_GPINR & DSMG600_PB_BM) { - - /* IO Pin is 1 (button pushed) */ - if (power_button_countdown == 0) { - /* Signal init to do the ctrlaltdel action, this will bypass - * init if it hasn't started and do a kernel_restart. - */ - ctrl_alt_del(); - - /* Change the state of the power LED to "blink" */ - gpio_line_set(DSMG600_LED_PWR_GPIO, IXP4XX_GPIO_LOW); - } - power_button_countdown--; - - } else { - power_button_countdown = PBUTTON_HOLDDOWN_COUNT; - } - - mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500)); -} - -static irqreturn_t dsmg600_reset_handler(int irq, void *dev_id) -{ - /* This is the paper-clip reset, it shuts the machine down directly. */ - machine_power_off(); - - return IRQ_HANDLED; -} - -static int __init dsmg600_power_init(void) -{ - if (!(machine_is_dsmg600())) - return 0; - - if (request_irq(DSMG600_RB_IRQ, &dsmg600_reset_handler, - IRQF_DISABLED | IRQF_TRIGGER_LOW, "DSM-G600 reset button", - NULL) < 0) { - - printk(KERN_DEBUG "Reset Button IRQ %d not available\n", - DSMG600_RB_IRQ); - - return -EIO; - } - - /* The power button on the D-Link DSM-G600 is on GPIO 15, but - * it cannot handle interrupts on that GPIO line. So we'll - * have to poll it with a kernel timer. - */ - - /* Make sure that the power button GPIO is set up as an input */ - gpio_line_config(DSMG600_PB_GPIO, IXP4XX_GPIO_IN); - - /* Set the initial value for the power button IRQ handler */ - power_button_countdown = PBUTTON_HOLDDOWN_COUNT; - - mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500)); - - return 0; -} - -static void __exit dsmg600_power_exit(void) -{ - if (!(machine_is_dsmg600())) - return; - - del_timer_sync(&dsmg600_power_timer); - - free_irq(DSMG600_RB_IRQ, NULL); -} - -module_init(dsmg600_power_init); -module_exit(dsmg600_power_exit); - -MODULE_AUTHOR("Michael Westerhof <mwester@dls.net>"); -MODULE_DESCRIPTION("DSM-G600 Power/Reset driver"); -MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c index 1e75e105c4f..688659668bd 100644 --- a/arch/arm/mach-ixp4xx/dsmg600-setup.c +++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c @@ -1,24 +1,37 @@ /* * DSM-G600 board-setup * + * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au> * Copyright (C) 2006 Tower Technologies - * Author: Alessandro Zummo <a.zummo@towertech.it> * - * based ixdp425-setup.c: + * based on ixdp425-setup.c: * Copyright (C) 2003-2004 MontaVista Software, Inc. + * based on nslu2-power.c: + * Copyright (C) 2005 Tower Technologies + * based on nslu2-io.c: + * Copyright (C) 2004 Karen Spearel * * Author: Alessandro Zummo <a.zummo@towertech.it> + * Author: Michael Westerhof <mwester@dls.net> + * Author: Rod Whitby <rod@whitby.id.au> * Maintainers: http://www.nslu2-linux.org/ */ -#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/jiffies.h> +#include <linux/timer.h> #include <linux/serial.h> #include <linux/serial_8250.h> +#include <linux/leds.h> +#include <linux/reboot.h> +#include <linux/i2c.h> +#include <linux/i2c-gpio.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/flash.h> #include <asm/mach/time.h> +#include <asm/gpio.h> static struct flash_platform_data dsmg600_flash_data = { .map_name = "cfi_probe", @@ -37,40 +50,47 @@ static struct platform_device dsmg600_flash = { .resource = &dsmg600_flash_resource, }; -static struct ixp4xx_i2c_pins dsmg600_i2c_gpio_pins = { +static struct i2c_gpio_platform_data dsmg600_i2c_gpio_data = { .sda_pin = DSMG600_SDA_PIN, .scl_pin = DSMG600_SCL_PIN, }; -static struct platform_device dsmg600_i2c_controller = { - .name = "IXP4XX-I2C", +static struct platform_device dsmg600_i2c_gpio = { + .name = "i2c-gpio", .id = 0, - .dev.platform_data = &dsmg600_i2c_gpio_pins, + .dev = { + .platform_data = &dsmg600_i2c_gpio_data, + }, }; -#ifdef CONFIG_LEDS_CLASS -static struct resource dsmg600_led_resources[] = { +static struct i2c_board_info __initdata dsmg600_i2c_board_info [] = { { - .name = "power", - .start = DSMG600_LED_PWR_GPIO, - .end = DSMG600_LED_PWR_GPIO, - .flags = IXP4XX_GPIO_HIGH, + I2C_BOARD_INFO("rtc-pcf8563", 0x51), }, +}; + +static struct gpio_led dsmg600_led_pins[] = { { - .name = "wlan", - .start = DSMG600_LED_WLAN_GPIO, - .end = DSMG600_LED_WLAN_GPIO, - .flags = IXP4XX_GPIO_LOW, + .name = "power", + .gpio = DSMG600_LED_PWR_GPIO, }, + { + .name = "wlan", + .gpio = DSMG600_LED_WLAN_GPIO, + .active_low = true, + }, +}; + +static struct gpio_led_platform_data dsmg600_led_data = { + .num_leds = ARRAY_SIZE(dsmg600_led_pins), + .leds = dsmg600_led_pins, }; static struct platform_device dsmg600_leds = { - .name = "IXP4XX-GPIO-LED", - .id = -1, - .num_resources = ARRAY_SIZE(dsmg600_led_resources), - .resource = dsmg600_led_resources, + .name = "leds-gpio", + .id = -1, + .dev.platform_data = &dsmg600_led_data, }; -#endif static struct resource dsmg600_uart_resources[] = { { @@ -116,8 +136,9 @@ static struct platform_device dsmg600_uart = { }; static struct platform_device *dsmg600_devices[] __initdata = { - &dsmg600_i2c_controller, + &dsmg600_i2c_gpio, &dsmg600_flash, + &dsmg600_leds, }; static void dsmg600_power_off(void) @@ -129,6 +150,57 @@ static void dsmg600_power_off(void) gpio_line_set(DSMG600_PO_GPIO, IXP4XX_GPIO_HIGH); } +/* This is used to make sure the power-button pusher is serious. The button + * must be held until the value of this counter reaches zero. + */ +static int power_button_countdown; + +/* Must hold the button down for at least this many counts to be processed */ +#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */ + +static void dsmg600_power_handler(unsigned long data); +static DEFINE_TIMER(dsmg600_power_timer, dsmg600_power_handler, 0, 0); + +static void dsmg600_power_handler(unsigned long data) +{ + /* This routine is called twice per second to check the + * state of the power button. + */ + + if (gpio_get_value(DSMG600_PB_GPIO)) { + + /* IO Pin is 1 (button pushed) */ + if (power_button_countdown > 0) + power_button_countdown--; + + } else { + + /* Done on button release, to allow for auto-power-on mods. */ + if (power_button_countdown == 0) { + /* Signal init to do the ctrlaltdel action, + * this will bypass init if it hasn't started + * and do a kernel_restart. + */ + ctrl_alt_del(); + + /* Change the state of the power LED to "blink" */ + gpio_line_set(DSMG600_LED_PWR_GPIO, IXP4XX_GPIO_LOW); + } else { + power_button_countdown = PBUTTON_HOLDDOWN_COUNT; + } + } + + mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500)); +} + +static irqreturn_t dsmg600_reset_handler(int irq, void *dev_id) +{ + /* This is the paper-clip reset, it shuts the machine down directly. */ + machine_power_off(); + + return IRQ_HANDLED; +} + static void __init dsmg600_timer_init(void) { /* The xtal on this machine is non-standard. */ @@ -153,7 +225,8 @@ static void __init dsmg600_init(void) dsmg600_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; - pm_power_off = dsmg600_power_off; + i2c_register_board_info(0, dsmg600_i2c_board_info, + ARRAY_SIZE(dsmg600_i2c_board_info)); /* The UART is required on the DSM-G600 (Redboot cannot use the * NIC) -- do it here so that it does *not* get removed if @@ -163,10 +236,28 @@ static void __init dsmg600_init(void) platform_add_devices(dsmg600_devices, ARRAY_SIZE(dsmg600_devices)); -#ifdef CONFIG_LEDS_CLASS - /* We don't care whether or not this works. */ - (void)platform_device_register(&dsmg600_leds); -#endif + pm_power_off = dsmg600_power_off; + + if (request_irq(gpio_to_irq(DSMG600_RB_GPIO), &dsmg600_reset_handler, + IRQF_DISABLED | IRQF_TRIGGER_LOW, + "DSM-G600 reset button", NULL) < 0) { + + printk(KERN_DEBUG "Reset Button IRQ %d not available\n", + gpio_to_irq(DSMG600_RB_GPIO)); + } + + /* The power button on the D-Link DSM-G600 is on GPIO 15, but + * it cannot handle interrupts on that GPIO line. So we'll + * have to poll it with a kernel timer. + */ + + /* Make sure that the power button GPIO is set up as an input */ + gpio_line_config(DSMG600_PB_GPIO, IXP4XX_GPIO_IN); + + /* Set the initial value for the power button IRQ handler */ + power_button_countdown = PBUTTON_HOLDDOWN_COUNT; + + mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500)); } MACHINE_START(DSMG600, "D-Link DSM-G600 RevA") diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index d5008d8fc9a..44584afb34a 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -15,6 +15,7 @@ #include <linux/tty.h> #include <linux/serial_8250.h> #include <linux/slab.h> +#include <linux/i2c-gpio.h> #include <linux/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -120,18 +121,17 @@ static struct platform_device ixdp425_flash_nand = { }; #endif /* CONFIG_MTD_NAND_PLATFORM */ -static struct ixp4xx_i2c_pins ixdp425_i2c_gpio_pins = { +static struct i2c_gpio_platform_data ixdp425_i2c_gpio_data = { .sda_pin = IXDP425_SDA_PIN, .scl_pin = IXDP425_SCL_PIN, }; -static struct platform_device ixdp425_i2c_controller = { - .name = "IXP4XX-I2C", +static struct platform_device ixdp425_i2c_gpio = { + .name = "i2c-gpio", .id = 0, - .dev = { - .platform_data = &ixdp425_i2c_gpio_pins, + .dev = { + .platform_data = &ixdp425_i2c_gpio_data, }, - .num_resources = 0 }; static struct resource ixdp425_uart_resources[] = { @@ -177,14 +177,41 @@ static struct platform_device ixdp425_uart = { .resource = ixdp425_uart_resources }; +/* Built-in 10/100 Ethernet MAC interfaces */ +static struct eth_plat_info ixdp425_plat_eth[] = { + { + .phy = 0, + .rxq = 3, + .txreadyq = 20, + }, { + .phy = 1, + .rxq = 4, + .txreadyq = 21, + } +}; + +static struct platform_device ixdp425_eth[] = { + { + .name = "ixp4xx_eth", + .id = IXP4XX_ETH_NPEB, + .dev.platform_data = ixdp425_plat_eth, + }, { + .name = "ixp4xx_eth", + .id = IXP4XX_ETH_NPEC, + .dev.platform_data = ixdp425_plat_eth + 1, + } +}; + static struct platform_device *ixdp425_devices[] __initdata = { - &ixdp425_i2c_controller, + &ixdp425_i2c_gpio, &ixdp425_flash, #if defined(CONFIG_MTD_NAND_PLATFORM) || \ defined(CONFIG_MTD_NAND_PLATFORM_MODULE) &ixdp425_flash_nand, #endif - &ixdp425_uart + &ixdp425_uart, + &ixdp425_eth[0], + &ixdp425_eth[1], }; static void __init ixdp425_init(void) diff --git a/arch/arm/mach-ixp4xx/ixp4xx_npe.c b/arch/arm/mach-ixp4xx/ixp4xx_npe.c new file mode 100644 index 00000000000..83c137ec582 --- /dev/null +++ b/arch/arm/mach-ixp4xx/ixp4xx_npe.c @@ -0,0 +1,741 @@ +/* + * Intel IXP4xx Network Processor Engine driver for Linux + * + * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * The code is based on publicly available information: + * - Intel IXP4xx Developer's Manual and other e-papers + * - Intel IXP400 Access Library Software (BSD license) + * - previous works by Christian Hohnstaedt <chohnstaedt@innominate.com> + * Thanks, Christian. + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <asm/arch/npe.h> + +#define DEBUG_MSG 0 +#define DEBUG_FW 0 + +#define NPE_COUNT 3 +#define MAX_RETRIES 1000 /* microseconds */ +#define NPE_42X_DATA_SIZE 0x800 /* in dwords */ +#define NPE_46X_DATA_SIZE 0x1000 +#define NPE_A_42X_INSTR_SIZE 0x1000 +#define NPE_B_AND_C_42X_INSTR_SIZE 0x800 +#define NPE_46X_INSTR_SIZE 0x1000 +#define REGS_SIZE 0x1000 + +#define NPE_PHYS_REG 32 + +#define FW_MAGIC 0xFEEDF00D +#define FW_BLOCK_TYPE_INSTR 0x0 +#define FW_BLOCK_TYPE_DATA 0x1 +#define FW_BLOCK_TYPE_EOF 0xF + +/* NPE exec status (read) and command (write) */ +#define CMD_NPE_STEP 0x01 +#define CMD_NPE_START 0x02 +#define CMD_NPE_STOP 0x03 +#define CMD_NPE_CLR_PIPE 0x04 +#define CMD_CLR_PROFILE_CNT 0x0C +#define CMD_RD_INS_MEM 0x10 /* instruction memory */ +#define CMD_WR_INS_MEM 0x11 +#define CMD_RD_DATA_MEM 0x12 /* data memory */ +#define CMD_WR_DATA_MEM 0x13 +#define CMD_RD_ECS_REG 0x14 /* exec access register */ +#define CMD_WR_ECS_REG 0x15 + +#define STAT_RUN 0x80000000 +#define STAT_STOP 0x40000000 +#define STAT_CLEAR 0x20000000 +#define STAT_ECS_K 0x00800000 /* pipeline clean */ + +#define NPE_STEVT 0x1B +#define NPE_STARTPC 0x1C +#define NPE_REGMAP 0x1E +#define NPE_CINDEX 0x1F + +#define INSTR_WR_REG_SHORT 0x0000C000 +#define INSTR_WR_REG_BYTE 0x00004000 +#define INSTR_RD_FIFO 0x0F888220 +#define INSTR_RESET_MBOX 0x0FAC8210 + +#define ECS_BG_CTXT_REG_0 0x00 /* Background Executing Context */ +#define ECS_BG_CTXT_REG_1 0x01 /* Stack level */ +#define ECS_BG_CTXT_REG_2 0x02 +#define ECS_PRI_1_CTXT_REG_0 0x04 /* Priority 1 Executing Context */ +#define ECS_PRI_1_CTXT_REG_1 0x05 /* Stack level */ +#define ECS_PRI_1_CTXT_REG_2 0x06 +#define ECS_PRI_2_CTXT_REG_0 0x08 /* Priority 2 Executing Context */ +#define ECS_PRI_2_CTXT_REG_1 0x09 /* Stack level */ +#define ECS_PRI_2_CTXT_REG_2 0x0A +#define ECS_DBG_CTXT_REG_0 0x0C /* Debug Executing Context */ +#define ECS_DBG_CTXT_REG_1 0x0D /* Stack level */ +#define ECS_DBG_CTXT_REG_2 0x0E +#define ECS_INSTRUCT_REG 0x11 /* NPE Instruction Register */ + +#define ECS_REG_0_ACTIVE 0x80000000 /* all levels */ +#define ECS_REG_0_NEXTPC_MASK 0x1FFF0000 /* BG/PRI1/PRI2 levels */ +#define ECS_REG_0_LDUR_BITS 8 +#define ECS_REG_0_LDUR_MASK 0x00000700 /* all levels */ +#define ECS_REG_1_CCTXT_BITS 16 +#define ECS_REG_1_CCTXT_MASK 0x000F0000 /* all levels */ +#define ECS_REG_1_SELCTXT_BITS 0 +#define ECS_REG_1_SELCTXT_MASK 0x0000000F /* all levels */ +#define ECS_DBG_REG_2_IF 0x00100000 /* debug level */ +#define ECS_DBG_REG_2_IE 0x00080000 /* debug level */ + +/* NPE watchpoint_fifo register bit */ +#define WFIFO_VALID 0x80000000 + +/* NPE messaging_status register bit definitions */ +#define MSGSTAT_OFNE 0x00010000 /* OutFifoNotEmpty */ +#define MSGSTAT_IFNF 0x00020000 /* InFifoNotFull */ +#define MSGSTAT_OFNF 0x00040000 /* OutFifoNotFull */ +#define MSGSTAT_IFNE 0x00080000 /* InFifoNotEmpty */ +#define MSGSTAT_MBINT 0x00100000 /* Mailbox interrupt */ +#define MSGSTAT_IFINT 0x00200000 /* InFifo interrupt */ +#define MSGSTAT_OFINT 0x00400000 /* OutFifo interrupt */ +#define MSGSTAT_WFINT 0x00800000 /* WatchFifo interrupt */ + +/* NPE messaging_control register bit definitions */ +#define MSGCTL_OUT_FIFO 0x00010000 /* enable output FIFO */ +#define MSGCTL_IN_FIFO 0x00020000 /* enable input FIFO */ +#define MSGCTL_OUT_FIFO_WRITE 0x01000000 /* enable FIFO + WRITE */ +#define MSGCTL_IN_FIFO_WRITE 0x02000000 + +/* NPE mailbox_status value for reset */ +#define RESET_MBOX_STAT 0x0000F0F0 + +const char *npe_names[] = { "NPE-A", "NPE-B", "NPE-C" }; + +#define print_npe(pri, npe, fmt, ...) \ + printk(pri "%s: " fmt, npe_name(npe), ## __VA_ARGS__) + +#if DEBUG_MSG +#define debug_msg(npe, fmt, ...) \ + print_npe(KERN_DEBUG, npe, fmt, ## __VA_ARGS__) +#else +#define debug_msg(npe, fmt, ...) +#endif + +static struct { + u32 reg, val; +} ecs_reset[] = { + { ECS_BG_CTXT_REG_0, 0xA0000000 }, + { ECS_BG_CTXT_REG_1, 0x01000000 }, + { ECS_BG_CTXT_REG_2, 0x00008000 }, + { ECS_PRI_1_CTXT_REG_0, 0x20000080 }, + { ECS_PRI_1_CTXT_REG_1, 0x01000000 }, + { ECS_PRI_1_CTXT_REG_2, 0x00008000 }, + { ECS_PRI_2_CTXT_REG_0, 0x20000080 }, + { ECS_PRI_2_CTXT_REG_1, 0x01000000 }, + { ECS_PRI_2_CTXT_REG_2, 0x00008000 }, + { ECS_DBG_CTXT_REG_0, 0x20000000 }, + { ECS_DBG_CTXT_REG_1, 0x00000000 }, + { ECS_DBG_CTXT_REG_2, 0x001E0000 }, + { ECS_INSTRUCT_REG, 0x1003C00F }, +}; + +static struct npe npe_tab[NPE_COUNT] = { + { + .id = 0, + .regs = (struct npe_regs __iomem *)IXP4XX_NPEA_BASE_VIRT, + .regs_phys = IXP4XX_NPEA_BASE_PHYS, + }, { + .id = 1, + .regs = (struct npe_regs __iomem *)IXP4XX_NPEB_BASE_VIRT, + .regs_phys = IXP4XX_NPEB_BASE_PHYS, + }, { + .id = 2, + .regs = (struct npe_regs __iomem *)IXP4XX_NPEC_BASE_VIRT, + .regs_phys = IXP4XX_NPEC_BASE_PHYS, + } +}; + +int npe_running(struct npe *npe) +{ + return (__raw_readl(&npe->regs->exec_status_cmd) & STAT_RUN) != 0; +} + +static void npe_cmd_write(struct npe *npe, u32 addr, int cmd, u32 data) +{ + __raw_writel(data, &npe->regs->exec_data); + __raw_writel(addr, &npe->regs->exec_addr); + __raw_writel(cmd, &npe->regs->exec_status_cmd); +} + +static u32 npe_cmd_read(struct npe *npe, u32 addr, int cmd) +{ + __raw_writel(addr, &npe->regs->exec_addr); + __raw_writel(cmd, &npe->regs->exec_status_cmd); + /* Iintroduce extra read cycles after issuing read command to NPE + so that we read the register after the NPE has updated it. + This is to overcome race condition between XScale and NPE */ + __raw_readl(&npe->regs->exec_data); + __raw_readl(&npe->regs->exec_data); + return __raw_readl(&npe->regs->exec_data); +} + +static void npe_clear_active(struct npe *npe, u32 reg) +{ + u32 val = npe_cmd_read(npe, reg, CMD_RD_ECS_REG); + npe_cmd_write(npe, reg, CMD_WR_ECS_REG, val & ~ECS_REG_0_ACTIVE); +} + +static void npe_start(struct npe *npe) +{ + /* ensure only Background Context Stack Level is active */ + npe_clear_active(npe, ECS_PRI_1_CTXT_REG_0); + npe_clear_active(npe, ECS_PRI_2_CTXT_REG_0); + npe_clear_active(npe, ECS_DBG_CTXT_REG_0); + + __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); + __raw_writel(CMD_NPE_START, &npe->regs->exec_status_cmd); +} + +static void npe_stop(struct npe *npe) +{ + __raw_writel(CMD_NPE_STOP, &npe->regs->exec_status_cmd); + __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); /*FIXME?*/ +} + +static int __must_check npe_debug_instr(struct npe *npe, u32 instr, u32 ctx, + u32 ldur) +{ + u32 wc; + int i; + + /* set the Active bit, and the LDUR, in the debug level */ + npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG, + ECS_REG_0_ACTIVE | (ldur << ECS_REG_0_LDUR_BITS)); + + /* set CCTXT at ECS DEBUG L3 to specify in which context to execute + the instruction, and set SELCTXT at ECS DEBUG Level to specify + which context store to access. + Debug ECS Level Reg 1 has form 0x000n000n, where n = context number + */ + npe_cmd_write(npe, ECS_DBG_CTXT_REG_1, CMD_WR_ECS_REG, + (ctx << ECS_REG_1_CCTXT_BITS) | + (ctx << ECS_REG_1_SELCTXT_BITS)); + + /* clear the pipeline */ + __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); + + /* load NPE instruction into the instruction register */ + npe_cmd_write(npe, ECS_INSTRUCT_REG, CMD_WR_ECS_REG, instr); + + /* we need this value later to wait for completion of NPE execution + step */ + wc = __raw_readl(&npe->regs->watch_count); + + /* issue a Step One command via the Execution Control register */ + __raw_writel(CMD_NPE_STEP, &npe->regs->exec_status_cmd); + + /* Watch Count register increments when NPE completes an instruction */ + for (i = 0; i < MAX_RETRIES; i++) { + if (wc != __raw_readl(&npe->regs->watch_count)) + return 0; + udelay(1); + } + + print_npe(KERN_ERR, npe, "reset: npe_debug_instr(): timeout\n"); + return -ETIMEDOUT; +} + +static int __must_check npe_logical_reg_write8(struct npe *npe, u32 addr, + u8 val, u32 ctx) +{ + /* here we build the NPE assembler instruction: mov8 d0, #0 */ + u32 instr = INSTR_WR_REG_BYTE | /* OpCode */ + addr << 9 | /* base Operand */ + (val & 0x1F) << 4 | /* lower 5 bits to immediate data */ + (val & ~0x1F) << (18 - 5);/* higher 3 bits to CoProc instr. */ + return npe_debug_instr(npe, instr, ctx, 1); /* execute it */ +} + +static int __must_check npe_logical_reg_write16(struct npe *npe, u32 addr, + u16 val, u32 ctx) +{ + /* here we build the NPE assembler instruction: mov16 d0, #0 */ + u32 instr = INSTR_WR_REG_SHORT | /* OpCode */ + addr << 9 | /* base Operand */ + (val & 0x1F) << 4 | /* lower 5 bits to immediate data */ + (val & ~0x1F) << (18 - 5);/* higher 11 bits to CoProc instr. */ + return npe_debug_instr(npe, instr, ctx, 1); /* execute it */ +} + +static int __must_check npe_logical_reg_write32(struct npe *npe, u32 addr, + u32 val, u32 ctx) +{ + /* write in 16 bit steps first the high and then the low value */ + if (npe_logical_reg_write16(npe, addr, val >> 16, ctx)) + return -ETIMEDOUT; + return npe_logical_reg_write16(npe, addr + 2, val & 0xFFFF, ctx); +} + +static int npe_reset(struct npe *npe) +{ + u32 val, ctl, exec_count, ctx_reg2; + int i; + + ctl = (__raw_readl(&npe->regs->messaging_control) | 0x3F000000) & + 0x3F3FFFFF; + + /* disable parity interrupt */ + __raw_writel(ctl & 0x3F00FFFF, &npe->regs->messaging_control); + + /* pre exec - debug instruction */ + /* turn off the halt bit by clearing Execution Count register. */ + exec_count = __raw_readl(&npe->regs->exec_count); + __raw_writel(0, &npe->regs->exec_count); + /* ensure that IF and IE are on (temporarily), so that we don't end up + stepping forever */ + ctx_reg2 = npe_cmd_read(npe, ECS_DBG_CTXT_REG_2, CMD_RD_ECS_REG); + npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2 | + ECS_DBG_REG_2_IF | ECS_DBG_REG_2_IE); + + /* clear the FIFOs */ + while (__raw_readl(&npe->regs->watchpoint_fifo) & WFIFO_VALID) + ; + while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE) + /* read from the outFIFO until empty */ + print_npe(KERN_DEBUG, npe, "npe_reset: read FIFO = 0x%X\n", + __raw_readl(&npe->regs->in_out_fifo)); + + while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) + /* step execution of the NPE intruction to read inFIFO using + the Debug Executing Context stack */ + if (npe_debug_instr(npe, INSTR_RD_FIFO, 0, 0)) + return -ETIMEDOUT; + + /* reset the mailbox reg from the XScale side */ + __raw_writel(RESET_MBOX_STAT, &npe->regs->mailbox_status); + /* from NPE side */ + if (npe_debug_instr(npe, INSTR_RESET_MBOX, 0, 0)) + return -ETIMEDOUT; + + /* Reset the physical registers in the NPE register file */ + for (val = 0; val < NPE_PHYS_REG; val++) { + if (npe_logical_reg_write16(npe, NPE_REGMAP, val >> 1, 0)) + return -ETIMEDOUT; + /* address is either 0 or 4 */ + if (npe_logical_reg_write32(npe, (val & 1) * 4, 0, 0)) + return -ETIMEDOUT; + } + + /* Reset the context store = each context's Context Store registers */ + + /* Context 0 has no STARTPC. Instead, this value is used to set NextPC + for Background ECS, to set where NPE starts executing code */ + val = npe_cmd_read(npe, ECS_BG_CTXT_REG_0, CMD_RD_ECS_REG); + val &= ~ECS_REG_0_NEXTPC_MASK; + val |= (0 /* NextPC */ << 16) & ECS_REG_0_NEXTPC_MASK; + npe_cmd_write(npe, ECS_BG_CTXT_REG_0, CMD_WR_ECS_REG, val); + + for (i = 0; i < 16; i++) { + if (i) { /* Context 0 has no STEVT nor STARTPC */ + /* STEVT = off, 0x80 */ + if (npe_logical_reg_write8(npe, NPE_STEVT, 0x80, i)) + return -ETIMEDOUT; + if (npe_logical_reg_write16(npe, NPE_STARTPC, 0, i)) + return -ETIMEDOUT; + } + /* REGMAP = d0->p0, d8->p2, d16->p4 */ + if (npe_logical_reg_write16(npe, NPE_REGMAP, 0x820, i)) + return -ETIMEDOUT; + if (npe_logical_reg_write8(npe, NPE_CINDEX, 0, i)) + return -ETIMEDOUT; + } + + /* post exec */ + /* clear active bit in debug level */ + npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG, 0); + /* clear the pipeline */ + __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); + /* restore previous values */ + __raw_writel(exec_count, &npe->regs->exec_count); + npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2); + + /* write reset values to Execution Context Stack registers */ + for (val = 0; val < ARRAY_SIZE(ecs_reset); val++) + npe_cmd_write(npe, ecs_reset[val].reg, CMD_WR_ECS_REG, + ecs_reset[val].val); + + /* clear the profile counter */ + __raw_writel(CMD_CLR_PROFILE_CNT, &npe->regs->exec_status_cmd); + + __raw_writel(0, &npe->regs->exec_count); + __raw_writel(0, &npe->regs->action_points[0]); + __raw_writel(0, &npe->regs->action_points[1]); + __raw_writel(0, &npe->regs->action_points[2]); + __raw_writel(0, &npe->regs->action_points[3]); + __raw_writel(0, &npe->regs->watch_count); + + val = ixp4xx_read_feature_bits(); + /* reset the NPE */ + ixp4xx_write_feature_bits(val & + ~(IXP4XX_FEATURE_RESET_NPEA << npe->id)); + for (i = 0; i < MAX_RETRIES; i++) { + if (!(ixp4xx_read_feature_bits() & + (IXP4XX_FEATURE_RESET_NPEA << npe->id))) + break; /* reset completed */ + udelay(1); + } + if (i == MAX_RETRIES) + return -ETIMEDOUT; + + /* deassert reset */ + ixp4xx_write_feature_bits(val | + (IXP4XX_FEATURE_RESET_NPEA << npe->id)); + for (i = 0; i < MAX_RETRIES; i++) { + if (ixp4xx_read_feature_bits() & + (IXP4XX_FEATURE_RESET_NPEA << npe->id)) + break; /* NPE is back alive */ + udelay(1); + } + if (i == MAX_RETRIES) + return -ETIMEDOUT; + + npe_stop(npe); + + /* restore NPE configuration bus Control Register - parity settings */ + __raw_writel(ctl, &npe->regs->messaging_control); + return 0; +} + + +int npe_send_message(struct npe *npe, const void *msg, const char *what) +{ + const u32 *send = msg; + int cycles = 0; + + debug_msg(npe, "Trying to send message %s [%08X:%08X]\n", + what, send[0], send[1]); + + if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) { + debug_msg(npe, "NPE input FIFO not empty\n"); + return -EIO; + } + + __raw_writel(send[0], &npe->regs->in_out_fifo); + + if (!(__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNF)) { + debug_msg(npe, "NPE input FIFO full\n"); + return -EIO; + } + + __raw_writel(send[1], &npe->regs->in_out_fifo); + + while ((cycles < MAX_RETRIES) && + (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE)) { + udelay(1); + cycles++; + } + + if (cycles == MAX_RETRIES) { + debug_msg(npe, "Timeout sending message\n"); + return -ETIMEDOUT; + } + + debug_msg(npe, "Sending a message took %i cycles\n", cycles); + return 0; +} + +int npe_recv_message(struct npe *npe, void *msg, const char *what) +{ + u32 *recv = msg; + int cycles = 0, cnt = 0; + + debug_msg(npe, "Trying to receive message %s\n", what); + + while (cycles < MAX_RETRIES) { + if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE) { + recv[cnt++] = __raw_readl(&npe->regs->in_out_fifo); + if (cnt == 2) + break; + } else { + udelay(1); + cycles++; + } + } + + switch(cnt) { + case 1: + debug_msg(npe, "Received [%08X]\n", recv[0]); + break; + case 2: + debug_msg(npe, "Received [%08X:%08X]\n", recv[0], recv[1]); + break; + } + + if (cycles == MAX_RETRIES) { + debug_msg(npe, "Timeout waiting for message\n"); + return -ETIMEDOUT; + } + + debug_msg(npe, "Receiving a message took %i cycles\n", cycles); + return 0; +} + +int npe_send_recv_message(struct npe *npe, void *msg, const char *what) +{ + int result; + u32 *send = msg, recv[2]; + + if ((result = npe_send_message(npe, msg, what)) != 0) + return result; + if ((result = npe_recv_message(npe, recv, what)) != 0) + return result; + + if ((recv[0] != send[0]) || (recv[1] != send[1])) { + debug_msg(npe, "Message %s: unexpected message received\n", + what); + return -EIO; + } + return 0; +} + + +int npe_load_firmware(struct npe *npe, const char *name, struct device *dev) +{ + const struct firmware *fw_entry; + + struct dl_block { + u32 type; + u32 offset; + } *blk; + + struct dl_image { + u32 magic; + u32 id; + u32 size; + union { + u32 data[0]; + struct dl_block blocks[0]; + }; + } *image; + + struct dl_codeblock { + u32 npe_addr; + u32 size; + u32 data[0]; + } *cb; + + int i, j, err, data_size, instr_size, blocks, table_end; + u32 cmd; + + if ((err = request_firmware(&fw_entry, name, dev)) != 0) + return err; + + err = -EINVAL; + if (fw_entry->size < sizeof(struct dl_image)) { + print_npe(KERN_ERR, npe, "incomplete firmware file\n"); + goto err; + } + image = (struct dl_image*)fw_entry->data; + +#if DEBUG_FW + print_npe(KERN_DEBUG, npe, "firmware: %08X %08X %08X (0x%X bytes)\n", + image->magic, image->id, image->size, image->size * 4); +#endif + + if (image->magic == swab32(FW_MAGIC)) { /* swapped file */ + image->id = swab32(image->id); + image->size = swab32(image->size); + } else if (image->magic != FW_MAGIC) { + print_npe(KERN_ERR, npe, "bad firmware file magic: 0x%X\n", + image->magic); + goto err; + } + if ((image->size * 4 + sizeof(struct dl_image)) != fw_entry->size) { + print_npe(KERN_ERR, npe, + "inconsistent size of firmware file\n"); + goto err; + } + if (((image->id >> 24) & 0xF /* NPE ID */) != npe->id) { + print_npe(KERN_ERR, npe, "firmware file NPE ID mismatch\n"); + goto err; + } + if (image->magic == swab32(FW_MAGIC)) + for (i = 0; i < image->size; i++) + image->data[i] = swab32(image->data[i]); + + if (!cpu_is_ixp46x() && ((image->id >> 28) & 0xF /* device ID */)) { + print_npe(KERN_INFO, npe, "IXP46x firmware ignored on " + "IXP42x\n"); + goto err; + } + + if (npe_running(npe)) { + print_npe(KERN_INFO, npe, "unable to load firmware, NPE is " + "already running\n"); + err = -EBUSY; + goto err; + } +#if 0 + npe_stop(npe); + npe_reset(npe); +#endif + + print_npe(KERN_INFO, npe, "firmware functionality 0x%X, " + "revision 0x%X:%X\n", (image->id >> 16) & 0xFF, + (image->id >> 8) & 0xFF, image->id & 0xFF); + + if (!cpu_is_ixp46x()) { + if (!npe->id) + instr_size = NPE_A_42X_INSTR_SIZE; + else + instr_size = NPE_B_AND_C_42X_INSTR_SIZE; + data_size = NPE_42X_DATA_SIZE; + } else { + instr_size = NPE_46X_INSTR_SIZE; + data_size = NPE_46X_DATA_SIZE; + } + + for (blocks = 0; blocks * sizeof(struct dl_block) / 4 < image->size; + blocks++) + if (image->blocks[blocks].type == FW_BLOCK_TYPE_EOF) + break; + if (blocks * sizeof(struct dl_block) / 4 >= image->size) { + print_npe(KERN_INFO, npe, "firmware EOF block marker not " + "found\n"); + goto err; + } + +#if DEBUG_FW + print_npe(KERN_DEBUG, npe, "%i firmware blocks found\n", blocks); +#endif + + table_end = blocks * sizeof(struct dl_block) / 4 + 1 /* EOF marker */; + for (i = 0, blk = image->blocks; i < blocks; i++, blk++) { + if (blk->offset > image->size - sizeof(struct dl_codeblock) / 4 + || blk->offset < table_end) { + print_npe(KERN_INFO, npe, "invalid offset 0x%X of " + "firmware block #%i\n", blk->offset, i); + goto err; + } + + cb = (struct dl_codeblock*)&image->data[blk->offset]; + if (blk->type == FW_BLOCK_TYPE_INSTR) { + if (cb->npe_addr + cb->size > instr_size) + goto too_big; + cmd = CMD_WR_INS_MEM; + } else if (blk->type == FW_BLOCK_TYPE_DATA) { + if (cb->npe_addr + cb->size > data_size) + goto too_big; + cmd = CMD_WR_DATA_MEM; + } else { + print_npe(KERN_INFO, npe, "invalid firmware block #%i " + "type 0x%X\n", i, blk->type); + goto err; + } + if (blk->offset + sizeof(*cb) / 4 + cb->size > image->size) { + print_npe(KERN_INFO, npe, "firmware block #%i doesn't " + "fit in firmware image: type %c, start 0x%X," + " length 0x%X\n", i, + blk->type == FW_BLOCK_TYPE_INSTR ? 'I' : 'D', + cb->npe_addr, cb->size); + goto err; + } + + for (j = 0; j < cb->size; j++) + npe_cmd_write(npe, cb->npe_addr + j, cmd, cb->data[j]); + } + + npe_start(npe); + if (!npe_running(npe)) + print_npe(KERN_ERR, npe, "unable to start\n"); + release_firmware(fw_entry); + return 0; + +too_big: + print_npe(KERN_INFO, npe, "firmware block #%i doesn't fit in NPE " + "memory: type %c, start 0x%X, length 0x%X\n", i, + blk->type == FW_BLOCK_TYPE_INSTR ? 'I' : 'D', + cb->npe_addr, cb->size); +err: + release_firmware(fw_entry); + return err; +} + + +struct npe *npe_request(int id) +{ + if (id < NPE_COUNT) + if (npe_tab[id].valid) + if (try_module_get(THIS_MODULE)) + return &npe_tab[id]; + return NULL; +} + +void npe_release(struct npe *npe) +{ + module_put(THIS_MODULE); +} + + +static int __init npe_init_module(void) +{ + + int i, found = 0; + + for (i = 0; i < NPE_COUNT; i++) { + struct npe *npe = &npe_tab[i]; + if (!(ixp4xx_read_feature_bits() & + (IXP4XX_FEATURE_RESET_NPEA << i))) + continue; /* NPE already disabled or not present */ + if (!(npe->mem_res = request_mem_region(npe->regs_phys, + REGS_SIZE, + npe_name(npe)))) { + print_npe(KERN_ERR, npe, + "failed to request memory region\n"); + continue; + } + + if (npe_reset(npe)) + continue; + npe->valid = 1; + found++; + } + + if (!found) + return -ENOSYS; + return 0; +} + +static void __exit npe_cleanup_module(void) +{ + int i; + + for (i = 0; i < NPE_COUNT; i++) + if (npe_tab[i].mem_res) { + npe_reset(&npe_tab[i]); + release_resource(npe_tab[i].mem_res); + } +} + +module_init(npe_init_module); +module_exit(npe_cleanup_module); + +MODULE_AUTHOR("Krzysztof Halasa"); +MODULE_LICENSE("GPL v2"); + +EXPORT_SYMBOL(npe_names); +EXPORT_SYMBOL(npe_running); +EXPORT_SYMBOL(npe_request); +EXPORT_SYMBOL(npe_release); +EXPORT_SYMBOL(npe_load_firmware); +EXPORT_SYMBOL(npe_send_message); +EXPORT_SYMBOL(npe_recv_message); +EXPORT_SYMBOL(npe_send_recv_message); diff --git a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c new file mode 100644 index 00000000000..e8330132530 --- /dev/null +++ b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c @@ -0,0 +1,274 @@ +/* + * Intel IXP4xx Queue Manager driver for Linux + * + * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/arch/qmgr.h> + +#define DEBUG 0 + +struct qmgr_regs __iomem *qmgr_regs; +static struct resource *mem_res; +static spinlock_t qmgr_lock; +static u32 used_sram_bitmap[4]; /* 128 16-dword pages */ +static void (*irq_handlers[HALF_QUEUES])(void *pdev); +static void *irq_pdevs[HALF_QUEUES]; + +void qmgr_set_irq(unsigned int queue, int src, + void (*handler)(void *pdev), void *pdev) +{ + u32 __iomem *reg = &qmgr_regs->irqsrc[queue / 8]; /* 8 queues / u32 */ + int bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */ + unsigned long flags; + + src &= 7; + spin_lock_irqsave(&qmgr_lock, flags); + __raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit), reg); + irq_handlers[queue] = handler; + irq_pdevs[queue] = pdev; + spin_unlock_irqrestore(&qmgr_lock, flags); +} + + +static irqreturn_t qmgr_irq1(int irq, void *pdev) +{ + int i; + u32 val = __raw_readl(&qmgr_regs->irqstat[0]); + __raw_writel(val, &qmgr_regs->irqstat[0]); /* ACK */ + + for (i = 0; i < HALF_QUEUES; i++) + if (val & (1 << i)) + irq_handlers[i](irq_pdevs[i]); + + return val ? IRQ_HANDLED : 0; +} + + +void qmgr_enable_irq(unsigned int queue) +{ + unsigned long flags; + + spin_lock_irqsave(&qmgr_lock, flags); + __raw_writel(__raw_readl(&qmgr_regs->irqen[0]) | (1 << queue), + &qmgr_regs->irqen[0]); + spin_unlock_irqrestore(&qmgr_lock, flags); +} + +void qmgr_disable_irq(unsigned int queue) +{ + unsigned long flags; + + spin_lock_irqsave(&qmgr_lock, flags); + __raw_writel(__raw_readl(&qmgr_regs->irqen[0]) & ~(1 << queue), + &qmgr_regs->irqen[0]); + spin_unlock_irqrestore(&qmgr_lock, flags); +} + +static inline void shift_mask(u32 *mask) +{ + mask[3] = mask[3] << 1 | mask[2] >> 31; + mask[2] = mask[2] << 1 | mask[1] >> 31; + mask[1] = mask[1] << 1 | mask[0] >> 31; + mask[0] <<= 1; +} + +int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, + unsigned int nearly_empty_watermark, + unsigned int nearly_full_watermark) +{ + u32 cfg, addr = 0, mask[4]; /* in 16-dwords */ + int err; + + if (queue >= HALF_QUEUES) + return -ERANGE; + + if ((nearly_empty_watermark | nearly_full_watermark) & ~7) + return -EINVAL; + + switch (len) { + case 16: + cfg = 0 << 24; + mask[0] = 0x1; + break; + case 32: + cfg = 1 << 24; + mask[0] = 0x3; + break; + case 64: + cfg = 2 << 24; + mask[0] = 0xF; + break; + case 128: + cfg = 3 << 24; + mask[0] = 0xFF; + break; + default: + return -EINVAL; + } + + cfg |= nearly_empty_watermark << 26; + cfg |= nearly_full_watermark << 29; + len /= 16; /* in 16-dwords: 1, 2, 4 or 8 */ + mask[1] = mask[2] = mask[3] = 0; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + spin_lock_irq(&qmgr_lock); + if (__raw_readl(&qmgr_regs->sram[queue])) { + err = -EBUSY; + goto err; + } + + while (1) { + if (!(used_sram_bitmap[0] & mask[0]) && + !(used_sram_bitmap[1] & mask[1]) && + !(used_sram_bitmap[2] & mask[2]) && + !(used_sram_bitmap[3] & mask[3])) + break; /* found free space */ + + addr++; + shift_mask(mask); + if (addr + len > ARRAY_SIZE(qmgr_regs->sram)) { + printk(KERN_ERR "qmgr: no free SRAM space for" + " queue %i\n", queue); + err = -ENOMEM; + goto err; + } + } + + used_sram_bitmap[0] |= mask[0]; + used_sram_bitmap[1] |= mask[1]; + used_sram_bitmap[2] |= mask[2]; + used_sram_bitmap[3] |= mask[3]; + __raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]); + spin_unlock_irq(&qmgr_lock); + +#if DEBUG + printk(KERN_DEBUG "qmgr: requested queue %i, addr = 0x%02X\n", + queue, addr); +#endif + return 0; + +err: + spin_unlock_irq(&qmgr_lock); + module_put(THIS_MODULE); + return err; +} + +void qmgr_release_queue(unsigned int queue) +{ + u32 cfg, addr, mask[4]; + + BUG_ON(queue >= HALF_QUEUES); /* not in valid range */ + + spin_lock_irq(&qmgr_lock); + cfg = __raw_readl(&qmgr_regs->sram[queue]); + addr = (cfg >> 14) & 0xFF; + + BUG_ON(!addr); /* not requested */ + + switch ((cfg >> 24) & 3) { + case 0: mask[0] = 0x1; break; + case 1: mask[0] = 0x3; break; + case 2: mask[0] = 0xF; break; + case 3: mask[0] = 0xFF; break; + } + + while (addr--) + shift_mask(mask); + + __raw_writel(0, &qmgr_regs->sram[queue]); + + used_sram_bitmap[0] &= ~mask[0]; + used_sram_bitmap[1] &= ~mask[1]; + used_sram_bitmap[2] &= ~mask[2]; + used_sram_bitmap[3] &= ~mask[3]; + irq_handlers[queue] = NULL; /* catch IRQ bugs */ + spin_unlock_irq(&qmgr_lock); + + module_put(THIS_MODULE); +#if DEBUG + printk(KERN_DEBUG "qmgr: released queue %i\n", queue); +#endif +} + +static int qmgr_init(void) +{ + int i, err; + mem_res = request_mem_region(IXP4XX_QMGR_BASE_PHYS, + IXP4XX_QMGR_REGION_SIZE, + "IXP4xx Queue Manager"); + if (mem_res == NULL) + return -EBUSY; + + qmgr_regs = ioremap(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE); + if (qmgr_regs == NULL) { + err = -ENOMEM; + goto error_map; + } + + /* reset qmgr registers */ + for (i = 0; i < 4; i++) { + __raw_writel(0x33333333, &qmgr_regs->stat1[i]); + __raw_writel(0, &qmgr_regs->irqsrc[i]); + } + for (i = 0; i < 2; i++) { + __raw_writel(0, &qmgr_regs->stat2[i]); + __raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[i]); /* clear */ + __raw_writel(0, &qmgr_regs->irqen[i]); + } + + for (i = 0; i < QUEUES; i++) + __raw_writel(0, &qmgr_regs->sram[i]); + + err = request_irq(IRQ_IXP4XX_QM1, qmgr_irq1, 0, + "IXP4xx Queue Manager", NULL); + if (err) { + printk(KERN_ERR "qmgr: failed to request IRQ%i\n", + IRQ_IXP4XX_QM1); + goto error_irq; + } + + used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */ + spin_lock_init(&qmgr_lock); + + printk(KERN_INFO "IXP4xx Queue Manager initialized.\n"); + return 0; + +error_irq: + iounmap(qmgr_regs); +error_map: + release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE); + return err; +} + +static void qmgr_remove(void) +{ + free_irq(IRQ_IXP4XX_QM1, NULL); + synchronize_irq(IRQ_IXP4XX_QM1); + iounmap(qmgr_regs); + release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE); +} + +module_init(qmgr_init); +module_exit(qmgr_remove); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Krzysztof Halasa"); + +EXPORT_SYMBOL(qmgr_regs); +EXPORT_SYMBOL(qmgr_set_irq); +EXPORT_SYMBOL(qmgr_enable_irq); +EXPORT_SYMBOL(qmgr_disable_irq); +EXPORT_SYMBOL(qmgr_request_queue); +EXPORT_SYMBOL(qmgr_release_queue); diff --git a/arch/arm/mach-ixp4xx/nas100d-power.c b/arch/arm/mach-ixp4xx/nas100d-power.c deleted file mode 100644 index 29aa98d3a7f..00000000000 --- a/arch/arm/mach-ixp4xx/nas100d-power.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * arch/arm/mach-ixp4xx/nas100d-power.c - * - * NAS 100d Power/Reset driver - * - * Copyright (C) 2005 Tower Technologies - * - * based on nas100d-io.c - * Copyright (C) 2004 Karen Spearel - * - * Author: Alessandro Zummo <a.zummo@towertech.it> - * Maintainers: http://www.nslu2-linux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/module.h> -#include <linux/reboot.h> - -#include <asm/mach-types.h> - -static irqreturn_t nas100d_reset_handler(int irq, void *dev_id) -{ - /* Signal init to do the ctrlaltdel action, this will bypass init if - * it hasn't started and do a kernel_restart. - */ - ctrl_alt_del(); - - return IRQ_HANDLED; -} - -static int __init nas100d_power_init(void) -{ - if (!(machine_is_nas100d())) - return 0; - - set_irq_type(NAS100D_RB_IRQ, IRQT_LOW); - - if (request_irq(NAS100D_RB_IRQ, &nas100d_reset_handler, - IRQF_DISABLED, "NAS100D reset button", NULL) < 0) { - - printk(KERN_DEBUG "Reset Button IRQ %d not available\n", - NAS100D_RB_IRQ); - - return -EIO; - } - - return 0; -} - -static void __exit nas100d_power_exit(void) -{ - if (!(machine_is_nas100d())) - return; - - free_irq(NAS100D_RB_IRQ, NULL); -} - -module_init(nas100d_power_init); -module_exit(nas100d_power_exit); - -MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); -MODULE_DESCRIPTION("NAS100D Power/Reset driver"); -MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c index 78a17413cec..4cecae84837 100644 --- a/arch/arm/mach-ixp4xx/nas100d-setup.c +++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -3,8 +3,14 @@ * * NAS 100d board-setup * - * based ixdp425-setup.c: + * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au> + * + * based on ixdp425-setup.c: * Copyright (C) 2003-2004 MontaVista Software, Inc. + * based on nas100d-power.c: + * Copyright (C) 2005 Tower Technologies + * based on nas100d-io.c + * Copyright (C) 2004 Karen Spearel * * Author: Alessandro Zummo <a.zummo@towertech.it> * Author: Rod Whitby <rod@whitby.id.au> @@ -12,14 +18,22 @@ * */ -#include <linux/kernel.h> +#include <linux/if_ether.h> +#include <linux/irq.h> +#include <linux/jiffies.h> +#include <linux/timer.h> #include <linux/serial.h> #include <linux/serial_8250.h> #include <linux/leds.h> +#include <linux/reboot.h> +#include <linux/i2c.h> +#include <linux/i2c-gpio.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/flash.h> +#include <asm/io.h> +#include <asm/gpio.h> static struct flash_platform_data nas100d_flash_data = { .map_name = "cfi_probe", @@ -38,46 +52,52 @@ static struct platform_device nas100d_flash = { .resource = &nas100d_flash_resource, }; -#ifdef CONFIG_LEDS_IXP4XX -static struct resource nas100d_led_resources[] = { +static struct i2c_board_info __initdata nas100d_i2c_board_info [] = { + { + I2C_BOARD_INFO("rtc-pcf8563", 0x51), + }, +}; + +static struct gpio_led nas100d_led_pins[] = { { .name = "wlan", /* green led */ - .start = 0, - .end = 0, - .flags = IXP4XX_GPIO_LOW, + .gpio = NAS100D_LED_WLAN_GPIO, + .active_low = true, }, { - .name = "ready", /* blue power led (off is flashing!) */ - .start = 15, - .end = 15, - .flags = IXP4XX_GPIO_LOW, + .name = "power", /* blue power led (off=flashing) */ + .gpio = NAS100D_LED_PWR_GPIO, + .active_low = true, }, { .name = "disk", /* yellow led */ - .start = 3, - .end = 3, - .flags = IXP4XX_GPIO_LOW, + .gpio = NAS100D_LED_DISK_GPIO, + .active_low = true, }, }; +static struct gpio_led_platform_data nas100d_led_data = { + .num_leds = ARRAY_SIZE(nas100d_led_pins), + .leds = nas100d_led_pins, +}; + static struct platform_device nas100d_leds = { - .name = "IXP4XX-GPIO-LED", + .name = "leds-gpio", .id = -1, - .num_resources = ARRAY_SIZE(nas100d_led_resources), - .resource = nas100d_led_resources, + .dev.platform_data = &nas100d_led_data, }; -#endif -static struct ixp4xx_i2c_pins nas100d_i2c_gpio_pins = { +static struct i2c_gpio_platform_data nas100d_i2c_gpio_data = { .sda_pin = NAS100D_SDA_PIN, .scl_pin = NAS100D_SCL_PIN, }; -static struct platform_device nas100d_i2c_controller = { - .name = "IXP4XX-I2C", +static struct platform_device nas100d_i2c_gpio = { + .name = "i2c-gpio", .id = 0, - .dev.platform_data = &nas100d_i2c_gpio_pins, - .num_resources = 0, + .dev = { + .platform_data = &nas100d_i2c_gpio_data, + }, }; static struct resource nas100d_uart_resources[] = { @@ -123,12 +143,28 @@ static struct platform_device nas100d_uart = { .resource = nas100d_uart_resources, }; +/* Built-in 10/100 Ethernet MAC interfaces */ +static struct eth_plat_info nas100d_plat_eth[] = { + { + .phy = 0, + .rxq = 3, + .txreadyq = 20, + } +}; + +static struct platform_device nas100d_eth[] = { + { + .name = "ixp4xx_eth", + .id = IXP4XX_ETH_NPEB, + .dev.platform_data = nas100d_plat_eth, + } +}; + static struct platform_device *nas100d_devices[] __initdata = { - &nas100d_i2c_controller, + &nas100d_i2c_gpio, &nas100d_flash, -#ifdef CONFIG_LEDS_IXP4XX &nas100d_leds, -#endif + &nas100d_eth[0], }; static void nas100d_power_off(void) @@ -142,8 +178,63 @@ static void nas100d_power_off(void) gpio_line_set(NAS100D_PO_GPIO, IXP4XX_GPIO_HIGH); } +/* This is used to make sure the power-button pusher is serious. The button + * must be held until the value of this counter reaches zero. + */ +static int power_button_countdown; + +/* Must hold the button down for at least this many counts to be processed */ +#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */ + +static void nas100d_power_handler(unsigned long data); +static DEFINE_TIMER(nas100d_power_timer, nas100d_power_handler, 0, 0); + +static void nas100d_power_handler(unsigned long data) +{ + /* This routine is called twice per second to check the + * state of the power button. + */ + + if (gpio_get_value(NAS100D_PB_GPIO)) { + + /* IO Pin is 1 (button pushed) */ + if (power_button_countdown > 0) + power_button_countdown--; + + } else { + + /* Done on button release, to allow for auto-power-on mods. */ + if (power_button_countdown == 0) { + /* Signal init to do the ctrlaltdel action, + * this will bypass init if it hasn't started + * and do a kernel_restart. + */ + ctrl_alt_del(); + + /* Change the state of the power LED to "blink" */ + gpio_line_set(NAS100D_LED_PWR_GPIO, IXP4XX_GPIO_LOW); + } else { + power_button_countdown = PBUTTON_HOLDDOWN_COUNT; + } + } + + mod_timer(&nas100d_power_timer, jiffies + msecs_to_jiffies(500)); +} + +static irqreturn_t nas100d_reset_handler(int irq, void *dev_id) +{ + /* This is the paper-clip reset, it shuts the machine down directly. */ + machine_power_off(); + + return IRQ_HANDLED; +} + static void __init nas100d_init(void) { + DECLARE_MAC_BUF(mac_buf); + uint8_t __iomem *f; + int i; + ixp4xx_sys_init(); /* gpio 14 and 15 are _not_ clocks */ @@ -153,7 +244,8 @@ static void __init nas100d_init(void) nas100d_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; - pm_power_off = nas100d_power_off; + i2c_register_board_info(0, nas100d_i2c_board_info, + ARRAY_SIZE(nas100d_i2c_board_info)); /* * This is only useful on a modified machine, but it is valuable @@ -163,6 +255,48 @@ static void __init nas100d_init(void) (void)platform_device_register(&nas100d_uart); platform_add_devices(nas100d_devices, ARRAY_SIZE(nas100d_devices)); + + pm_power_off = nas100d_power_off; + + if (request_irq(gpio_to_irq(NAS100D_RB_GPIO), &nas100d_reset_handler, + IRQF_DISABLED | IRQF_TRIGGER_LOW, + "NAS100D reset button", NULL) < 0) { + + printk(KERN_DEBUG "Reset Button IRQ %d not available\n", + gpio_to_irq(NAS100D_RB_GPIO)); + } + + /* The power button on the Iomega NAS100d is on GPIO 14, but + * it cannot handle interrupts on that GPIO line. So we'll + * have to poll it with a kernel timer. + */ + + /* Make sure that the power button GPIO is set up as an input */ + gpio_line_config(NAS100D_PB_GPIO, IXP4XX_GPIO_IN); + + /* Set the initial value for the power button IRQ handler */ + power_button_countdown = PBUTTON_HOLDDOWN_COUNT; + + mod_timer(&nas100d_power_timer, jiffies + msecs_to_jiffies(500)); + + /* + * Map in a portion of the flash and read the MAC address. + * Since it is stored in BE in the flash itself, we need to + * byteswap it if we're in LE mode. + */ + f = ioremap(IXP4XX_EXP_BUS_BASE(0), 0x1000000); + if (f) { + for (i = 0; i < 6; i++) +#ifdef __ARMEB__ + nas100d_plat_eth[0].hwaddr[i] = readb(f + 0xFC0FD8 + i); +#else + nas100d_plat_eth[0].hwaddr[i] = readb(f + 0xFC0FD8 + (i^3)); +#endif + iounmap(f); + } + printk(KERN_INFO "NAS100D: Using MAC address %s for port 0\n", + print_mac(mac_buf, nas100d_plat_eth[0].hwaddr)); + } MACHINE_START(NAS100D, "Iomega NAS 100d") diff --git a/arch/arm/mach-ixp4xx/nslu2-power.c b/arch/arm/mach-ixp4xx/nslu2-power.c deleted file mode 100644 index acd71e9c38a..00000000000 --- a/arch/arm/mach-ixp4xx/nslu2-power.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * arch/arm/mach-ixp4xx/nslu2-power.c - * - * NSLU2 Power/Reset driver - * - * Copyright (C) 2005 Tower Technologies - * - * based on nslu2-io.c - * Copyright (C) 2004 Karen Spearel - * - * Author: Alessandro Zummo <a.zummo@towertech.it> - * Maintainers: http://www.nslu2-linux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/module.h> -#include <linux/reboot.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/reboot.h> - -#include <asm/mach-types.h> - -static irqreturn_t nslu2_power_handler(int irq, void *dev_id) -{ - /* Signal init to do the ctrlaltdel action, this will bypass init if - * it hasn't started and do a kernel_restart. - */ - ctrl_alt_del(); - - return IRQ_HANDLED; -} - -static irqreturn_t nslu2_reset_handler(int irq, void *dev_id) -{ - /* This is the paper-clip reset, it shuts the machine down directly. - */ - machine_power_off(); - - return IRQ_HANDLED; -} - -static int __init nslu2_power_init(void) -{ - if (!(machine_is_nslu2())) - return 0; - - *IXP4XX_GPIO_GPISR = 0x20400000; /* read the 2 irqs to clr */ - - set_irq_type(NSLU2_RB_IRQ, IRQT_LOW); - set_irq_type(NSLU2_PB_IRQ, IRQT_HIGH); - - if (request_irq(NSLU2_RB_IRQ, &nslu2_reset_handler, - IRQF_DISABLED, "NSLU2 reset button", NULL) < 0) { - - printk(KERN_DEBUG "Reset Button IRQ %d not available\n", - NSLU2_RB_IRQ); - - return -EIO; - } - - if (request_irq(NSLU2_PB_IRQ, &nslu2_power_handler, - IRQF_DISABLED, "NSLU2 power button", NULL) < 0) { - - printk(KERN_DEBUG "Power Button IRQ %d not available\n", - NSLU2_PB_IRQ); - - return -EIO; - } - - return 0; -} - -static void __exit nslu2_power_exit(void) -{ - if (!(machine_is_nslu2())) - return; - - free_irq(NSLU2_RB_IRQ, NULL); - free_irq(NSLU2_PB_IRQ, NULL); -} - -module_init(nslu2_power_init); -module_exit(nslu2_power_exit); - -MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); -MODULE_DESCRIPTION("NSLU2 Power/Reset driver"); -MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c index 9bf8ccbcacc..acaebcbce53 100644 --- a/arch/arm/mach-ixp4xx/nslu2-setup.c +++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -3,26 +3,35 @@ * * NSLU2 board-setup * - * based ixdp425-setup.c: + * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au> + * + * based on ixdp425-setup.c: * Copyright (C) 2003-2004 MontaVista Software, Inc. + * based on nslu2-power.c: + * Copyright (C) 2005 Tower Technologies * * Author: Mark Rakes <mrakes at mac.com> * Author: Rod Whitby <rod@whitby.id.au> + * Author: Alessandro Zummo <a.zummo@towertech.it> * Maintainers: http://www.nslu2-linux.org/ * - * Fixed missing init_time in MACHINE_START kas11 10/22/04 - * Changed to conform to new style __init ixdp425 kas11 10/22/04 */ -#include <linux/kernel.h> +#include <linux/if_ether.h> +#include <linux/irq.h> #include <linux/serial.h> #include <linux/serial_8250.h> #include <linux/leds.h> +#include <linux/reboot.h> +#include <linux/i2c.h> +#include <linux/i2c-gpio.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/flash.h> #include <asm/mach/time.h> +#include <asm/io.h> +#include <asm/gpio.h> static struct flash_platform_data nslu2_flash_data = { .map_name = "cfi_probe", @@ -41,52 +50,55 @@ static struct platform_device nslu2_flash = { .resource = &nslu2_flash_resource, }; -static struct ixp4xx_i2c_pins nslu2_i2c_gpio_pins = { +static struct i2c_gpio_platform_data nslu2_i2c_gpio_data = { .sda_pin = NSLU2_SDA_PIN, .scl_pin = NSLU2_SCL_PIN, }; -#ifdef CONFIG_LEDS_IXP4XX -static struct resource nslu2_led_resources[] = { +static struct i2c_board_info __initdata nslu2_i2c_board_info [] = { + { + I2C_BOARD_INFO("rtc-x1205", 0x6f), + }, +}; + +static struct gpio_led nslu2_led_pins[] = { { .name = "ready", /* green led */ - .start = NSLU2_LED_GRN_GPIO, - .end = NSLU2_LED_GRN_GPIO, - .flags = IXP4XX_GPIO_HIGH, + .gpio = NSLU2_LED_GRN_GPIO, }, { .name = "status", /* red led */ - .start = NSLU2_LED_RED_GPIO, - .end = NSLU2_LED_RED_GPIO, - .flags = IXP4XX_GPIO_HIGH, + .gpio = NSLU2_LED_RED_GPIO, }, { .name = "disk-1", - .start = NSLU2_LED_DISK1_GPIO, - .end = NSLU2_LED_DISK1_GPIO, - .flags = IXP4XX_GPIO_LOW, + .gpio = NSLU2_LED_DISK1_GPIO, + .active_low = true, }, { .name = "disk-2", - .start = NSLU2_LED_DISK2_GPIO, - .end = NSLU2_LED_DISK2_GPIO, - .flags = IXP4XX_GPIO_LOW, + .gpio = NSLU2_LED_DISK2_GPIO, + .active_low = true, }, }; +static struct gpio_led_platform_data nslu2_led_data = { + .num_leds = ARRAY_SIZE(nslu2_led_pins), + .leds = nslu2_led_pins, +}; + static struct platform_device nslu2_leds = { - .name = "IXP4XX-GPIO-LED", + .name = "leds-gpio", .id = -1, - .num_resources = ARRAY_SIZE(nslu2_led_resources), - .resource = nslu2_led_resources, + .dev.platform_data = &nslu2_led_data, }; -#endif -static struct platform_device nslu2_i2c_controller = { - .name = "IXP4XX-I2C", +static struct platform_device nslu2_i2c_gpio = { + .name = "i2c-gpio", .id = 0, - .dev.platform_data = &nslu2_i2c_gpio_pins, - .num_resources = 0, + .dev = { + .platform_data = &nslu2_i2c_gpio_data, + }, }; static struct platform_device nslu2_beeper = { @@ -138,13 +150,29 @@ static struct platform_device nslu2_uart = { .resource = nslu2_uart_resources, }; +/* Built-in 10/100 Ethernet MAC interfaces */ +static struct eth_plat_info nslu2_plat_eth[] = { + { + .phy = 1, + .rxq = 3, + .txreadyq = 20, + } +}; + +static struct platform_device nslu2_eth[] = { + { + .name = "ixp4xx_eth", + .id = IXP4XX_ETH_NPEB, + .dev.platform_data = nslu2_plat_eth, + } +}; + static struct platform_device *nslu2_devices[] __initdata = { - &nslu2_i2c_controller, + &nslu2_i2c_gpio, &nslu2_flash, &nslu2_beeper, -#ifdef CONFIG_LEDS_IXP4XX &nslu2_leds, -#endif + &nslu2_eth[0], }; static void nslu2_power_off(void) @@ -158,6 +186,25 @@ static void nslu2_power_off(void) gpio_line_set(NSLU2_PO_GPIO, IXP4XX_GPIO_HIGH); } +static irqreturn_t nslu2_power_handler(int irq, void *dev_id) +{ + /* Signal init to do the ctrlaltdel action, this will bypass init if + * it hasn't started and do a kernel_restart. + */ + ctrl_alt_del(); + + return IRQ_HANDLED; +} + +static irqreturn_t nslu2_reset_handler(int irq, void *dev_id) +{ + /* This is the paper-clip reset, it shuts the machine down directly. + */ + machine_power_off(); + + return IRQ_HANDLED; +} + static void __init nslu2_timer_init(void) { /* The xtal on this machine is non-standard. */ @@ -173,13 +220,18 @@ static struct sys_timer nslu2_timer = { static void __init nslu2_init(void) { + DECLARE_MAC_BUF(mac_buf); + uint8_t __iomem *f; + int i; + ixp4xx_sys_init(); nslu2_flash_resource.start = IXP4XX_EXP_BUS_BASE(0); nslu2_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; - pm_power_off = nslu2_power_off; + i2c_register_board_info(0, nslu2_i2c_board_info, + ARRAY_SIZE(nslu2_i2c_board_info)); /* * This is only useful on a modified machine, but it is valuable @@ -189,6 +241,43 @@ static void __init nslu2_init(void) (void)platform_device_register(&nslu2_uart); platform_add_devices(nslu2_devices, ARRAY_SIZE(nslu2_devices)); + + pm_power_off = nslu2_power_off; + + if (request_irq(gpio_to_irq(NSLU2_RB_GPIO), &nslu2_reset_handler, + IRQF_DISABLED | IRQF_TRIGGER_LOW, + "NSLU2 reset button", NULL) < 0) { + + printk(KERN_DEBUG "Reset Button IRQ %d not available\n", + gpio_to_irq(NSLU2_RB_GPIO)); + } + + if (request_irq(gpio_to_irq(NSLU2_PB_GPIO), &nslu2_power_handler, + IRQF_DISABLED | IRQF_TRIGGER_HIGH, + "NSLU2 power button", NULL) < 0) { + + printk(KERN_DEBUG "Power Button IRQ %d not available\n", + gpio_to_irq(NSLU2_PB_GPIO)); + } + + /* + * Map in a portion of the flash and read the MAC address. + * Since it is stored in BE in the flash itself, we need to + * byteswap it if we're in LE mode. + */ + f = ioremap(IXP4XX_EXP_BUS_BASE(0), 0x40000); + if (f) { + for (i = 0; i < 6; i++) +#ifdef __ARMEB__ + nslu2_plat_eth[0].hwaddr[i] = readb(f + 0x3FFB0 + i); +#else + nslu2_plat_eth[0].hwaddr[i] = readb(f + 0x3FFB0 + (i^3)); +#endif + iounmap(f); + } + printk(KERN_INFO "NSLU2: Using MAC address %s for port 0\n", + print_mac(mac_buf, nslu2_plat_eth[0].hwaddr)); + } MACHINE_START(NSLU2, "Linksys NSLU2") |