Index: linux-2.6.16/arch/arm/common/vic.c =================================================================== --- linux-2.6.16.orig/arch/arm/common/vic.c +++ linux-2.6.16/arch/arm/common/vic.c @@ -26,18 +26,18 @@ #include #include -static void __iomem *vic_base; - static void vic_mask_irq(unsigned int irq) { - irq -= IRQ_VIC_START; - writel(1 << irq, vic_base + VIC_INT_ENABLE_CLEAR); + void __iomem *base = get_irq_chipdata(irq); + irq &= 31; + writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); } static void vic_unmask_irq(unsigned int irq) { - irq -= IRQ_VIC_START; - writel(1 << irq, vic_base + VIC_INT_ENABLE); + void __iomem *base = get_irq_chipdata(irq); + irq &= 31; + writel(1 << irq, base + VIC_INT_ENABLE); } static struct irqchip vic_chip = { @@ -46,43 +46,42 @@ static struct irqchip vic_chip = { .unmask = vic_unmask_irq, }; -void __init vic_init(void __iomem *base, u32 vic_sources) +void __init vic_init(void __iomem *base, unsigned int irq_start, + u32 vic_sources) { unsigned int i; - vic_base = base; - /* Disable all interrupts initially. */ - writel(0, vic_base + VIC_INT_SELECT); - writel(0, vic_base + VIC_INT_ENABLE); - writel(~0, vic_base + VIC_INT_ENABLE_CLEAR); - writel(0, vic_base + VIC_IRQ_STATUS); - writel(0, vic_base + VIC_ITCR); - writel(~0, vic_base + VIC_INT_SOFT_CLEAR); + writel(0, base + VIC_INT_SELECT); + writel(~0, base + VIC_INT_ENABLE_CLEAR); + writel(0, base + VIC_ITCR); + writel(~0, base + VIC_INT_SOFT_CLEAR); /* * Make sure we clear all existing interrupts */ - writel(0, vic_base + VIC_VECT_ADDR); + writel(0, base + VIC_VECT_ADDR); for (i = 0; i < 19; i++) { unsigned int value; - value = readl(vic_base + VIC_VECT_ADDR); - writel(value, vic_base + VIC_VECT_ADDR); + value = readl(base + VIC_VECT_ADDR); + writel(value, base + VIC_VECT_ADDR); } for (i = 0; i < 16; i++) { - void __iomem *reg = vic_base + VIC_VECT_CNTL0 + (i * 4); - writel(VIC_VECT_CNTL_ENABLE | i, reg); + void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); +// writel(VIC_VECT_CNTL_ENABLE | i, reg); + writel(0, reg); } - writel(32, vic_base + VIC_DEF_VECT_ADDR); + writel(32, base + VIC_DEF_VECT_ADDR); for (i = 0; i < 32; i++) { - unsigned int irq = IRQ_VIC_START + i; + unsigned int irq = irq_start + i; set_irq_chip(irq, &vic_chip); + set_irq_chipdata(irq, base); if (vic_sources & (1 << i)) { set_irq_handler(irq, do_level_IRQ); Index: linux-2.6.16/include/asm-arm/hardware/vic.h =================================================================== --- linux-2.6.16.orig/include/asm-arm/hardware/vic.h +++ linux-2.6.16/include/asm-arm/hardware/vic.h @@ -39,7 +39,7 @@ #define VIC_VECT_CNTL_ENABLE (1 << 5) #ifndef __ASSEMBLY__ -void vic_init(void __iomem *base, u32 vic_sources); +void vic_init(void __iomem *base, unsigned int irq_start, u32 vic_sources); #endif #endif Index: linux-2.6.16/include/asm-arm/mach/irq.h =================================================================== --- linux-2.6.16.orig/include/asm-arm/mach/irq.h +++ linux-2.6.16/include/asm-arm/mach/irq.h @@ -61,7 +61,7 @@ struct irqdesc { struct irqchip *chip; struct irqaction *action; struct list_head pend; - void *chipdata; + void __iomem *chipdata; void *data; unsigned int disable_depth; Index: linux-2.6.16/arch/arm/Kconfig =================================================================== --- linux-2.6.16.orig/arch/arm/Kconfig +++ linux-2.6.16/arch/arm/Kconfig @@ -8,6 +8,7 @@ mainmenu "Linux Kernel Configuration" config ARM bool default y + select RTC_LIB help The ARM series is a line of low-power-consumption RISC chip designs licensed by ARM Ltd and targeted at embedded applications and @@ -108,6 +109,13 @@ config ARCH_EBSA110 Ethernet interface, two PCMCIA sockets, two serial ports and a parallel port. +config ARCH_EP93XX + bool "EP93xx-based" + select ARM_AMBA + select ARM_VIC + help + This enables support for the Cirrus EP93xx series of CPUs. + config ARCH_FOOTBRIDGE bool "FootBridge" select FOOTBRIDGE @@ -250,6 +258,8 @@ endchoice source "arch/arm/mach-clps711x/Kconfig" +source "arch/arm/mach-ep93xx/Kconfig" + source "arch/arm/mach-footbridge/Kconfig" source "arch/arm/mach-integrator/Kconfig" @@ -819,6 +829,8 @@ source "drivers/usb/Kconfig" source "drivers/mmc/Kconfig" +source "drivers/rtc/Kconfig" + endmenu source "fs/Kconfig" Index: linux-2.6.16/arch/arm/Makefile =================================================================== --- linux-2.6.16.orig/arch/arm/Makefile +++ linux-2.6.16/arch/arm/Makefile @@ -105,6 +105,7 @@ endif machine-$(CONFIG_ARCH_AAEC2000) := aaec2000 machine-$(CONFIG_ARCH_REALVIEW) := realview machine-$(CONFIG_ARCH_AT91RM9200) := at91rm9200 + machine-$(CONFIG_ARCH_EP93XX) := ep93xx ifeq ($(CONFIG_ARCH_EBSA110),y) # This is what happens if you forget the IOCS16 line. Index: linux-2.6.16/arch/arm/mach-ep93xx/Kconfig =================================================================== --- /dev/null +++ linux-2.6.16/arch/arm/mach-ep93xx/Kconfig @@ -0,0 +1,32 @@ +if ARCH_EP93XX + +menu "Cirrus EP93xx Implementation Options" + +config CRUNCH + bool "Support for MaverickCrunch" + help + Enable kernel support for MaverickCrunch. + +comment "EP93xx Platforms" + +config MACH_GESBC9312 + bool "Support Glomation GESBC-9312-sx" + help + Say 'Y' here if you want your kernel to support the Glomation + GESBC-9312-sx board. + +config MACH_MICRO9 + bool "Support Contec Hypercontrol Micro9" + help + Say 'Y' here if you want your kernel to support the + Contec Hypercontrol Micro9 development board. + +config MACH_TS72XX + bool "Support Technologic Systems TS-72xx SBC" + help + Say 'Y' here if you want your kernel to support the + Technologic Systems TS-72xx board. + +endmenu + +endif Index: linux-2.6.16/arch/arm/mach-ep93xx/Makefile =================================================================== --- /dev/null +++ linux-2.6.16/arch/arm/mach-ep93xx/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the linux kernel. +# +obj-y := core.o +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_MACH_GESBC9312) += gesbc9312.o +obj-$(CONFIG_MACH_MICRO9) += micro9.o +obj-$(CONFIG_MACH_TS72XX) += ts72xx.o Index: linux-2.6.16/arch/arm/mach-ep93xx/Makefile.boot =================================================================== --- /dev/null +++ linux-2.6.16/arch/arm/mach-ep93xx/Makefile.boot @@ -0,0 +1,2 @@ + zreladdr-y := 0x00008000 +params_phys-y := 0x00000100 Index: linux-2.6.16/arch/arm/mach-ep93xx/core.c =================================================================== --- /dev/null +++ linux-2.6.16/arch/arm/mach-ep93xx/core.c @@ -0,0 +1,479 @@ +/* + * arch/arm/mach-ep93xx/core.c + * Core routines for Cirrus EP93xx chips. + * + * Copyright (C) 2006 Lennert Buytenhek + * + * Thanks go to Michael Burian and Ray Lehtiniemi for their key + * role in the ep93xx linux community. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +/************************************************************************* + * Static I/O mappings that are needed for all EP93xx platforms + *************************************************************************/ +static struct map_desc ep93xx_io_desc[] __initdata = { + { + .virtual = EP93XX_AHB_VIRT_BASE, + .pfn = __phys_to_pfn(EP93XX_AHB_PHYS_BASE), + .length = EP93XX_AHB_SIZE, + .type = MT_DEVICE, + }, { + .virtual = EP93XX_APB_VIRT_BASE, + .pfn = __phys_to_pfn(EP93XX_APB_PHYS_BASE), + .length = EP93XX_APB_SIZE, + .type = MT_DEVICE, + }, +}; + +void __init ep93xx_map_io(void) +{ + iotable_init(ep93xx_io_desc, ARRAY_SIZE(ep93xx_io_desc)); +} + + +/************************************************************************* + * Timer handling for EP93xx + ************************************************************************* + * The ep93xx has four internal timers. Timers 1, 2 (both 16 bit) and + * 3 (32 bit) count down at 508 kHz, are self-reloading, and can generate + * an interrupt on underflow. Timer 4 (40 bit) counts down at 983.04 kHz, + * is free-running, and can't generate interrupts. + * + * The 508 kHz timers are ideal for use for the timer interrupt, as the + * most common values of HZ divide 508 kHz nicely. We pick one of the 16 + * bit timers (timer 1) since we don't need more than 16 bits of reload + * value as long as HZ >= 8. + * + * The higher clock rate of timer 4 makes it a better choice than the + * other timers for use in gettimeoffset(), while the fact that it can't + * generate interrupts means we don't have to worry about not being able + * to use this timer for something else. We also use timer 4 for keeping + * track of lost jiffies. + */ +static unsigned int last_jiffy_time; + +#define TIMER4_TICKS_PER_JIFFY ((CLOCK_TICK_RATE + (HZ/2)) / HZ) + +static int ep93xx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + write_seqlock(&xtime_lock); + + __raw_writel(1, EP93XX_TIMER1_CLEAR); + while (__raw_readl(EP93XX_TIMER4_VALUE_LOW) - last_jiffy_time + >= TIMER4_TICKS_PER_JIFFY) { + last_jiffy_time += TIMER4_TICKS_PER_JIFFY; + timer_tick(regs); + } + + write_sequnlock(&xtime_lock); + + return IRQ_HANDLED; +} + +static struct irqaction ep93xx_timer_irq = { + .name = "ep93xx timer", + .flags = SA_INTERRUPT | SA_TIMER, + .handler = ep93xx_timer_interrupt, +}; + +static void __init ep93xx_timer_init(void) +{ + /* Enable periodic HZ timer. */ + __raw_writel(0x48, EP93XX_TIMER1_CONTROL); + __raw_writel((508000 / HZ) - 1, EP93XX_TIMER1_LOAD); + __raw_writel(0xc8, EP93XX_TIMER1_CONTROL); + + /* Enable lost jiffy timer. */ + __raw_writel(0x100, EP93XX_TIMER4_VALUE_HIGH); + + setup_irq(IRQ_EP93XX_TIMER1, &ep93xx_timer_irq); +} + +static unsigned long ep93xx_gettimeoffset(void) +{ + int offset; + + offset = __raw_readl(EP93XX_TIMER4_VALUE_LOW) - last_jiffy_time; + + /* Calculate (1000000 / 983040) * offset. */ + return offset + (53 * offset / 3072); +} + +struct sys_timer ep93xx_timer = { + .init = ep93xx_timer_init, + .offset = ep93xx_gettimeoffset, +}; + + +/************************************************************************* + * GPIO handling for EP93xx + *************************************************************************/ +static unsigned char gpio_int_enable[2]; +static unsigned char gpio_int_debounce[2]; +static unsigned char gpio_int_type1[2]; +static unsigned char gpio_int_type2[2]; + +static void update_gpio_ab_int_params(int port) +{ + if (port == 0) { + __raw_writeb(0, EP93XX_GPIO_A_INT_ENABLE); + __raw_writeb(gpio_int_debounce[0], EP93XX_GPIO_A_INT_DEBOUNCE); + __raw_writeb(gpio_int_type2[0], EP93XX_GPIO_A_INT_TYPE2); + __raw_writeb(gpio_int_type1[0], EP93XX_GPIO_A_INT_TYPE1); + __raw_writeb(gpio_int_enable[0], EP93XX_GPIO_A_INT_ENABLE); + } else if (port == 1) { + __raw_writeb(0, EP93XX_GPIO_B_INT_ENABLE); + __raw_writeb(gpio_int_debounce[1], EP93XX_GPIO_B_INT_DEBOUNCE); + __raw_writeb(gpio_int_type2[1], EP93XX_GPIO_B_INT_TYPE2); + __raw_writeb(gpio_int_type1[1], EP93XX_GPIO_B_INT_TYPE1); + __raw_writeb(gpio_int_enable[1], EP93XX_GPIO_B_INT_ENABLE); + } +} + + +static unsigned char data_register_offset[8] = { + 0x00, 0x04, 0x08, 0x0c, 0x20, 0x30, 0x38, 0x40, +}; + +static unsigned char data_direction_register_offset[8] = { + 0x10, 0x14, 0x18, 0x1c, 0x24, 0x34, 0x3c, 0x44, +}; + +void gpio_line_config(int line, int direction) +{ + unsigned int data_direction_register; + unsigned long flags; + unsigned char v; + + data_direction_register = + EP93XX_GPIO_REG(data_direction_register_offset[line >> 3]); + + local_irq_save(flags); + if (direction == GPIO_OUT) { + if (line >= 0 && line < 16) { + irq_desc[IRQ_EP93XX_GPIO(line)].valid = 0; + + gpio_int_enable[line >> 3] &= ~(1 << (line & 7)); + update_gpio_ab_int_params(line >> 3); + } + + v = __raw_readb(data_direction_register); + v |= 1 << (line & 7); + __raw_writeb(v, data_direction_register); + } else if (direction == GPIO_IN) { + v = __raw_readb(data_direction_register); + v &= ~(1 << (line & 7)); + __raw_writeb(v, data_direction_register); + } + local_irq_restore(flags); +} +EXPORT_SYMBOL(gpio_line_config); + +int gpio_line_get(int line) +{ + unsigned int data_register; + + data_register = EP93XX_GPIO_REG(data_register_offset[line >> 3]); + + return !!(__raw_readb(data_register) & (1 << (line & 7))); +} +EXPORT_SYMBOL(gpio_line_get); + +void gpio_line_set(int line, int value) +{ + unsigned int data_register; + unsigned long flags; + unsigned char v; + + data_register = EP93XX_GPIO_REG(data_register_offset[line >> 3]); + + local_irq_save(flags); + if (value == EP93XX_GPIO_HIGH) { + v = __raw_readb(data_register); + v |= 1 << (line & 7); + __raw_writeb(v, data_register); + } else if (value == EP93XX_GPIO_LOW) { + v = __raw_readb(data_register); + v &= ~(1 << (line & 7)); + __raw_writeb(v, data_register); + } + local_irq_restore(flags); +} +EXPORT_SYMBOL(gpio_line_set); + + +/************************************************************************* + * EP93xx IRQ handling + *************************************************************************/ +static void ep93xx_gpio_ab_irq_handler(unsigned int irq, + struct irqdesc *desc, struct pt_regs *regs) +{ + unsigned char status; + int i; + + status = __raw_readb(EP93XX_GPIO_A_INT_STATUS); + for (i = 0; i < 8; i++) { + if (status & (1 << i)) { + desc = irq_desc + IRQ_EP93XX_GPIO(0) + i; + desc_handle_irq(IRQ_EP93XX_GPIO(0) + i, desc, regs); + } + } + + status = __raw_readb(EP93XX_GPIO_B_INT_STATUS); + for (i = 0; i < 8; i++) { + if (status & (1 << i)) { + desc = irq_desc + IRQ_EP93XX_GPIO(8) + i; + desc_handle_irq(IRQ_EP93XX_GPIO(8) + i, desc, regs); + } + } +} + +static void ep93xx_gpio_ab_irq_mask_ack(unsigned int irq) +{ + int line = irq - IRQ_EP93XX_GPIO(0); + int port = line >> 3; + + gpio_int_enable[port] &= ~(1 << (line & 7)); + update_gpio_ab_int_params(port); + + if (line >> 3) { + __raw_writel(1 << (line & 7), EP93XX_GPIO_B_INT_ACK); + } else { + __raw_writel(1 << (line & 7), EP93XX_GPIO_A_INT_ACK); + } +} + +static void ep93xx_gpio_ab_irq_mask(unsigned int irq) +{ + int line = irq - IRQ_EP93XX_GPIO(0); + int port = line >> 3; + + gpio_int_enable[port] &= ~(1 << (line & 7)); + update_gpio_ab_int_params(port); +} + +static void ep93xx_gpio_ab_irq_unmask(unsigned int irq) +{ + int line = irq - IRQ_EP93XX_GPIO(0); + int port = line >> 3; + + gpio_int_enable[port] |= 1 << (line & 7); + update_gpio_ab_int_params(port); +} + + +/* + * gpio_int_type1 controls whether the interrupt is level (0) or + * edge (1) triggered, while gpio_int_type2 controls whether it + * triggers on low/falling (0) or high/rising (1). + */ +static int ep93xx_gpio_ab_irq_type(unsigned int irq, unsigned int type) +{ + int port; + int line; + + line = irq - IRQ_EP93XX_GPIO(0); + gpio_line_config(line, GPIO_IN); + + port = line >> 3; + line &= 7; + + if (type & IRQT_RISING) { + gpio_int_type1[port] |= 1 << line; + gpio_int_type2[port] |= 1 << line; + } else if (type & IRQT_FALLING) { + gpio_int_type1[port] |= 1 << line; + gpio_int_type2[port] &= ~(1 << line); + } else if (type & IRQT_HIGH) { + gpio_int_type1[port] &= ~(1 << line); + gpio_int_type2[port] |= 1 << line; + } else if (type & IRQT_LOW) { + gpio_int_type1[port] &= ~(1 << line); + gpio_int_type2[port] &= ~(1 << line); + } + + if (type & IRQT_DEBOUNCE) { + gpio_int_debounce[port] |= 1 << line; + } else { + gpio_int_debounce[port] &= ~(1 << line); + } + + update_gpio_ab_int_params(port); + + irq_desc[irq].valid = 1; + + return 0; +} + +static struct irqchip ep93xx_gpio_ab_irq_chip = { + .ack = ep93xx_gpio_ab_irq_mask_ack, + .mask = ep93xx_gpio_ab_irq_mask, + .unmask = ep93xx_gpio_ab_irq_unmask, + .set_type = ep93xx_gpio_ab_irq_type, +}; + + +void __init ep93xx_init_irq(void) +{ + int irq; + + vic_init((void *)EP93XX_VIC1_BASE, 0, EP93XX_VIC1_VALID_IRQ_MASK); + vic_init((void *)EP93XX_VIC2_BASE, 32, EP93XX_VIC2_VALID_IRQ_MASK); + + for (irq = IRQ_EP93XX_GPIO(0) ; irq <= IRQ_EP93XX_GPIO(15); irq++) { + set_irq_chip(irq, &ep93xx_gpio_ab_irq_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, 0); + } + set_irq_chained_handler(IRQ_EP93XX_GPIO_AB, ep93xx_gpio_ab_irq_handler); +} + + +/************************************************************************* + * EP93xx peripheral handling + *************************************************************************/ +static struct amba_device uart1_device = { + .dev = { + .bus_id = "apb:uart1", + }, + .res = { + .start = EP93XX_UART1_PHYS_BASE, + .end = EP93XX_UART1_PHYS_BASE + 0x0fff, + .flags = IORESOURCE_MEM, + }, + .irq = { IRQ_EP93XX_UART1, NO_IRQ }, + .periphid = 0x00041010, +}; + +static struct amba_device uart2_device = { + .dev = { + .bus_id = "apb:uart2", + }, + .res = { + .start = EP93XX_UART2_PHYS_BASE, + .end = EP93XX_UART2_PHYS_BASE + 0x0fff, + .flags = IORESOURCE_MEM, + }, + .irq = { IRQ_EP93XX_UART2, NO_IRQ }, + .periphid = 0x00041010, +}; + +static struct amba_device uart3_device = { + .dev = { + .bus_id = "apb:uart3", + }, + .res = { + .start = EP93XX_UART3_PHYS_BASE, + .end = EP93XX_UART3_PHYS_BASE + 0x0fff, + .flags = IORESOURCE_MEM, + }, + .irq = { IRQ_EP93XX_UART3, NO_IRQ }, + .periphid = 0x00041010, +}; + +static struct resource ep93xx_ohci_resources[] = { + [0] = { + .start = EP93XX_USB_PHYS_BASE, + .end = EP93XX_USB_PHYS_BASE + 0x0fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_EP93XX_USB, + .end = IRQ_EP93XX_USB, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ep93xx_ohci_device = { + .name = "ep93xx-ohci", + .id = -1, + .dev = { + .dma_mask = (void *)0xffffffff, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(ep93xx_ohci_resources), + .resource = ep93xx_ohci_resources, +}; + +static struct ep93xx_i2c_pins ep93xx_i2c_gpio_pins = { + .sda_pin = EP93XX_GPIO_LINE_EEDAT, + .scl_pin = EP93XX_GPIO_LINE_EECLK, +}; + +static struct platform_device ep93xx_i2c_device = { + .name = "ep93xx-i2c", + .id = 0, + .dev.platform_data = &ep93xx_i2c_gpio_pins, + .num_resources = 0, +}; + + +static struct platform_device ep93xx_rtc_device = { + .name = "ep93xx-rtc", + .id = -1, + .num_resources = 0, +}; + + +void __init ep93xx_init_devices(void) +{ + unsigned int v; + + /* + * Disallow access to MaverickCrunch initially. + */ + v = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG); + v &= ~EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE; + __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); + __raw_writel(v, EP93XX_SYSCON_DEVICE_CONFIG); + + amba_device_register(&uart1_device, &iomem_resource); + amba_device_register(&uart2_device, &iomem_resource); + amba_device_register(&uart3_device, &iomem_resource); + + platform_device_register(&ep93xx_ohci_device); + platform_device_register(&ep93xx_i2c_device); + platform_device_register(&ep93xx_rtc_device); +} Index: linux-2.6.16/arch/arm/mach-ep93xx/gesbc9312.c =================================================================== --- /dev/null +++ linux-2.6.16/arch/arm/mach-ep93xx/gesbc9312.c @@ -0,0 +1,40 @@ +/* + * arch/arm/mach-ep93xx/gesbc9312.c + * Glomation GESBC-9312-sx support. + * + * Copyright (C) 2006 Lennert Buytenhek + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __init gesbc9312_init_machine(void) +{ + ep93xx_init_devices(); + physmap_configure(0x60000000, 0x00800000, 4, NULL); +} + +MACHINE_START(GESBC9312, "Glomation GESBC-9312-sx") + /* Maintainer: Lennert Buytenhek */ + .phys_io = EP93XX_APB_PHYS_BASE, + .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, + .boot_params = 0x00000100, + .map_io = ep93xx_map_io, + .init_irq = ep93xx_init_irq, + .timer = &ep93xx_timer, + .init_machine = gesbc9312_init_machine, +MACHINE_END Index: linux-2.6.16/arch/arm/mach-ep93xx/ts72xx.c =================================================================== --- /dev/null +++ linux-2.6.16/arch/arm/mach-ep93xx/ts72xx.c @@ -0,0 +1,189 @@ +/* + * arch/arm/mach-ep93xx/ts72xx.c + * Technologic Systems TS72xx SBC support. + * + * Copyright (C) 2006 Lennert Buytenhek + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct map_desc ts72xx_io_desc[] __initdata = { + { + .virtual = TS72XX_MODEL_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_MODEL_PHYS_BASE), + .length = TS72XX_MODEL_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_OPTIONS_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_OPTIONS_PHYS_BASE), + .length = TS72XX_OPTIONS_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_OPTIONS2_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_OPTIONS2_PHYS_BASE), + .length = TS72XX_OPTIONS2_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_RTC_INDEX_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_RTC_INDEX_PHYS_BASE), + .length = TS72XX_RTC_INDEX_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_RTC_DATA_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_RTC_DATA_PHYS_BASE), + .length = TS72XX_RTC_DATA_SIZE, + .type = MT_DEVICE, + } +}; + +static struct map_desc ts72xx_nand_io_desc[] __initdata = { + { + .virtual = TS72XX_NAND_DATA_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_NAND1_DATA_PHYS_BASE), + .length = TS72XX_NAND_DATA_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_NAND_CONTROL_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_NAND1_CONTROL_PHYS_BASE), + .length = TS72XX_NAND_CONTROL_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_NAND_BUSY_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_NAND1_BUSY_PHYS_BASE), + .length = TS72XX_NAND_BUSY_SIZE, + .type = MT_DEVICE, + } +}; + +static struct map_desc ts72xx_alternate_nand_io_desc[] __initdata = { + { + .virtual = TS72XX_NAND_DATA_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_NAND2_DATA_PHYS_BASE), + .length = TS72XX_NAND_DATA_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_NAND_CONTROL_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_NAND2_CONTROL_PHYS_BASE), + .length = TS72XX_NAND_CONTROL_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_NAND_BUSY_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_NAND2_BUSY_PHYS_BASE), + .length = TS72XX_NAND_BUSY_SIZE, + .type = MT_DEVICE, + } +}; + +static void __init ts72xx_map_io(void) +{ + ep93xx_map_io(); + iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc)); + + /* + * The TS-7200 has NOR flash, the other models have NAND flash. + */ + if (board_is_ts7200()) { + physmap_configure(TS72XX_NOR_PHYS_BASE, 0x01000000, 1, NULL); + } else { + if (is_ts9420_installed()) { + iotable_init(ts72xx_alternate_nand_io_desc, + ARRAY_SIZE(ts72xx_alternate_nand_io_desc)); + } else { + iotable_init(ts72xx_nand_io_desc, + ARRAY_SIZE(ts72xx_nand_io_desc)); + } + } +} + +static unsigned char ts72xx_rtc_readb(unsigned long addr) +{ + __raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE); + return __raw_readb(TS72XX_RTC_DATA_VIRT_BASE); +} + +static void ts72xx_rtc_writeb(unsigned char value, unsigned long addr) +{ + __raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE); + __raw_writeb(value, TS72XX_RTC_DATA_VIRT_BASE); +} + +static struct m48t86_ops ts72xx_rtc_ops = { + .readb = ts72xx_rtc_readb, + .writeb = ts72xx_rtc_writeb, +}; + +static struct platform_device ts72xx_rtc_device = { + .name = "rtc-m48t86", + .id = -1, + .dev = { + .platform_data = &ts72xx_rtc_ops, + }, + .num_resources = 0, +}; + +static struct resource ts7200_cf_resources[] = { + [0] = { + .start = TS7200_CF_CMD_PHYS_BASE, + .end = TS7200_CF_CMD_PHYS_BASE + 7, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = TS7200_CF_AUX_PHYS_BASE, + .end = TS7200_CF_AUX_PHYS_BASE + 1, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = TS7200_CF_DATA_PHYS_BASE, + .end = TS7200_CF_DATA_PHYS_BASE + 2, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ts7200_cf_device = { + .name = "ts7200-cf", + .id = -1, + .dev = { + .dma_mask = (void *)0xffffffff, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(ts7200_cf_resources), + .resource = ts7200_cf_resources, +}; + +static void __init ts72xx_init_machine(void) +{ + ep93xx_init_devices(); + + platform_device_register(&ts72xx_rtc_device); + if (board_is_ts7200()) + platform_device_register(&ts7200_cf_device); +} + +MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC") + /* Maintainer: Lennert Buytenhek */ + .phys_io = EP93XX_APB_PHYS_BASE, + .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, + .boot_params = 0x00000100, + .map_io = ts72xx_map_io, + .init_irq = ep93xx_init_irq, + .timer = &ep93xx_timer, + .init_machine = ts72xx_init_machine, +MACHINE_END Index: linux-2.6.16/arch/arm/mm/Kconfig =================================================================== --- linux-2.6.16.orig/arch/arm/mm/Kconfig +++ linux-2.6.16/arch/arm/mm/Kconfig @@ -62,7 +62,7 @@ config CPU_ARM720T # ARM920T config CPU_ARM920T bool "Support ARM920T processor" if !ARCH_S3C2410 - depends on ARCH_INTEGRATOR || ARCH_S3C2410 || ARCH_IMX || ARCH_AAEC2000 || ARCH_AT91RM9200 + depends on ARCH_EP93XX || ARCH_INTEGRATOR || ARCH_S3C2410 || ARCH_IMX || ARCH_AAEC2000 || ARCH_AT91RM9200 default y if ARCH_S3C2410 || ARCH_AT91RM9200 select CPU_32v4 select CPU_ABRT_EV4T Index: linux-2.6.16/include/asm-arm/arch-ep93xx/debug-macro.S =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/debug-macro.S @@ -0,0 +1,36 @@ +/* + * linux/include/asm-arm/arch-ep93xx/debug-macro.S + * Debugging macro include header + * + * Copyright (C) 2006 Lennert Buytenhek + * + * 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. + */ +#include + + .macro addruart,rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + ldreq \rx, =EP93XX_APB_PHYS_BASE @ Physical base + ldrne \rx, =EP93XX_APB_VIRT_BASE @ virtual base + orr \rx, \rx, #0x000c0000 + .endm + + .macro senduart,rd,rx + strb \rd, [\rx] + .endm + + .macro busyuart,rd,rx +1001: ldrb \rd, [\rx, #0x18] + tst \rd, #0x08 + bne 1001b + .endm + + .macro waituart,rd,rx + nop + nop + nop + .endm Index: linux-2.6.16/include/asm-arm/arch-ep93xx/dma.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/dma.h @@ -0,0 +1,3 @@ +/* + * linux/include/asm-arm/arch-ep93xx/dma.h + */ Index: linux-2.6.16/include/asm-arm/arch-ep93xx/entry-macro.S =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/entry-macro.S @@ -0,0 +1,53 @@ +/* + * linux/include/asm-arm/arch-ep93xx/entry-macro.S + * IRQ demultiplexing for EP93xx + * + * Copyright (C) 2006 Lennert Buytenhek + * + * 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. + */ +#include + + .macro disable_fiq + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + ldr \base, =(EP93XX_AHB_VIRT_BASE) + orr \base, \base, #0x000b0000 + mov \irqnr, #0 + ldr \irqstat, [\base] @ lower 32 interrupts + cmp \irqstat, #0 + bne 1001f + + eor \base, \base, #0x00070000 + ldr \irqstat, [\base] @ upper 32 interrupts + cmp \irqstat, #0 + beq 1002f + mov \irqnr, #0x20 + +1001: + movs \tmp, \irqstat, lsl #16 + movne \irqstat, \tmp + addeq \irqnr, \irqnr, #16 + + movs \tmp, \irqstat, lsl #8 + movne \irqstat, \tmp + addeq \irqnr, \irqnr, #8 + + movs \tmp, \irqstat, lsl #4 + movne \irqstat, \tmp + addeq \irqnr, \irqnr, #4 + + movs \tmp, \irqstat, lsl #2 + movne \irqstat, \tmp + addeq \irqnr, \irqnr, #2 + + movs \tmp, \irqstat, lsl #1 + addeq \irqnr, \irqnr, #1 + orrs \base, \base, #1 + +1002: + .endm Index: linux-2.6.16/include/asm-arm/arch-ep93xx/ep93xx-regs.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/ep93xx-regs.h @@ -0,0 +1,153 @@ +/* + * linux/include/asm-arm/arch-ep93xx/ep93xx-regs.h + */ + +#ifndef __ASM_ARCH_EP93XX_REGS_H +#define __ASM_ARCH_EP93XX_REGS_H + +/* + * EP93xx linux memory map: + * + * virt phys size + * fe800000 5M per-platform mappings + * fed00000 80800000 2M APB + * fef00000 80000000 1M AHB + */ + +#define EP93XX_AHB_PHYS_BASE 0x80000000 +#define EP93XX_AHB_VIRT_BASE 0xfef00000 +#define EP93XX_AHB_SIZE 0x00100000 + +#define EP93XX_APB_PHYS_BASE 0x80800000 +#define EP93XX_APB_VIRT_BASE 0xfed00000 +#define EP93XX_APB_SIZE 0x00200000 + + +/* AHB peripherals */ +#define EP93XX_DMA_BASE (EP93XX_AHB_VIRT_BASE + 0x00000000) + +#define EP93XX_ETHERNET_BASE (EP93XX_AHB_VIRT_BASE + 0x00010000) + +#define EP93XX_USB_BASE (EP93XX_AHB_VIRT_BASE + 0x00020000) +#define EP93XX_USB_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00020000) + +#define EP93XX_RASTER_BASE (EP93XX_AHB_VIRT_BASE + 0x00030000) + +#define EP93XX_GRAPHICS_ACCEL_BASE (EP93XX_AHB_VIRT_BASE + 0x00040000) + +#define EP93XX_SDRAM_CONTROLLER_BASE (EP93XX_AHB_VIRT_BASE + 0x00060000) + +#define EP93XX_PCMCIA_CONTROLLER_BASE (EP93XX_AHB_VIRT_BASE + 0x00080000) + +#define EP93XX_BOOT_ROM_BASE (EP93XX_AHB_VIRT_BASE + 0x00090000) + +#define EP93XX_IDE_BASE (EP93XX_AHB_VIRT_BASE + 0x000a0000) +#define EP93XX_IDE_REG(x) (EP93XX_IDE_BASE + (x)) +#define EP93XX_IDE_CTRL EP93XX_IDE_REG(0x0000) +#define EP93XX_IDE_CFG EP93XX_IDE_REG(0x0004) +#define EP93XX_IDE_DATAOUT EP93XX_IDE_REG(0x0010) +#define EP93XX_IDE_DATAIN EP93XX_IDE_REG(0x0014) + +#define EP93XX_IDE_CTRL_CS0n (1L << 0) +#define EP93XX_IDE_CTRL_CS1n (1L << 1) +#define EP93XX_IDE_CTRL_DA_MASK 0x1C +#define EP93XX_IDE_CTRL_DA(x) ((x << 2) & EP93XX_IDE_CTRL_DA_MASK) +#define EP93XX_IDE_CTRL_DA_CS_MASK (EP93XX_IDE_CTRL_DA_MASK | EP93XX_IDE_CTRL_CS0n | EP93XX_IDE_CTRL_CS1n) +#define EP93XX_IDE_CTRL_DA_CS(x) (((x)) & EP93XX_IDE_CTRL_DA_CS_MASK) +#define EP93XX_IDE_CTRL_DIORn (1L << 5) +#define EP93XX_IDE_CTRL_DIOWn (1L << 6) +#define EP93XX_IDE_CTRL_DASPn (1L << 7) +#define EP93XX_IDE_CTRL_DMARQ (1L << 8) +#define EP93XX_IDE_CTRL_INTRQ (1L << 9) +#define EP93XX_IDE_CTRL_IORDY (1L << 10) + +#define EP93XX_IDE_CFG_IDEEN (1L << 0) +#define EP93XX_IDE_CFG_PIO (1L << 1) +#define EP93XX_IDE_CFG_MDMA (1L << 2) +#define EP93XX_IDE_CFG_UDMA (1L << 3) +#define EP93XX_IDE_CFG_MODE(x) ((x & 0x0F) << 4) +#define EP93XX_IDE_CFG_WST(x) ((x & 0x03) << 8) + + +#define EP93XX_VIC1_BASE (EP93XX_AHB_VIRT_BASE + 0x000b0000) + +#define EP93XX_VIC2_BASE (EP93XX_AHB_VIRT_BASE + 0x000c0000) + + +/* APB peripherals */ +#define EP93XX_TIMER_BASE (EP93XX_APB_VIRT_BASE + 0x00010000) +#define EP93XX_TIMER_REG(x) (EP93XX_TIMER_BASE + (x)) +#define EP93XX_TIMER1_LOAD EP93XX_TIMER_REG(0x00) +#define EP93XX_TIMER1_VALUE EP93XX_TIMER_REG(0x04) +#define EP93XX_TIMER1_CONTROL EP93XX_TIMER_REG(0x08) +#define EP93XX_TIMER1_CLEAR EP93XX_TIMER_REG(0x0c) +#define EP93XX_TIMER2_LOAD EP93XX_TIMER_REG(0x20) +#define EP93XX_TIMER2_VALUE EP93XX_TIMER_REG(0x24) +#define EP93XX_TIMER2_CONTROL EP93XX_TIMER_REG(0x28) +#define EP93XX_TIMER2_CLEAR EP93XX_TIMER_REG(0x2c) +#define EP93XX_TIMER4_VALUE_LOW EP93XX_TIMER_REG(0x60) +#define EP93XX_TIMER4_VALUE_HIGH EP93XX_TIMER_REG(0x64) +#define EP93XX_TIMER3_LOAD EP93XX_TIMER_REG(0x80) +#define EP93XX_TIMER3_VALUE EP93XX_TIMER_REG(0x84) +#define EP93XX_TIMER3_CONTROL EP93XX_TIMER_REG(0x88) +#define EP93XX_TIMER3_CLEAR EP93XX_TIMER_REG(0x8c) + +#define EP93XX_I2S_BASE (EP93XX_APB_VIRT_BASE + 0x00020000) + +#define EP93XX_SECURITY_BASE (EP93XX_APB_VIRT_BASE + 0x00030000) + +#define EP93XX_GPIO_BASE (EP93XX_APB_VIRT_BASE + 0x00040000) +#define EP93XX_GPIO_REG(x) (EP93XX_GPIO_BASE + (x)) +#define EP93XX_GPIO_A_INT_TYPE1 EP93XX_GPIO_REG(0x90) +#define EP93XX_GPIO_A_INT_TYPE2 EP93XX_GPIO_REG(0x94) +#define EP93XX_GPIO_A_INT_ACK EP93XX_GPIO_REG(0x98) +#define EP93XX_GPIO_A_INT_ENABLE EP93XX_GPIO_REG(0x9c) +#define EP93XX_GPIO_A_INT_STATUS EP93XX_GPIO_REG(0xa0) +#define EP93XX_GPIO_A_INT_DEBOUNCE EP93XX_GPIO_REG(0xa8) +#define EP93XX_GPIO_B_INT_TYPE1 EP93XX_GPIO_REG(0xac) +#define EP93XX_GPIO_B_INT_TYPE2 EP93XX_GPIO_REG(0xb0) +#define EP93XX_GPIO_B_INT_ACK EP93XX_GPIO_REG(0xb4) +#define EP93XX_GPIO_B_INT_ENABLE EP93XX_GPIO_REG(0xb8) +#define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc) +#define EP93XX_GPIO_B_INT_DEBOUNCE EP93XX_GPIO_REG(0xc4) + +#define EP93XX_AAC_BASE (EP93XX_APB_VIRT_BASE + 0x00080000) + +#define EP93XX_SPI_BASE (EP93XX_APB_VIRT_BASE + 0x000a0000) + +#define EP93XX_IRDA_BASE (EP93XX_APB_VIRT_BASE + 0x000b0000) + +#define EP93XX_UART1_BASE (EP93XX_APB_VIRT_BASE + 0x000c0000) +#define EP93XX_UART1_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000c0000) + +#define EP93XX_UART2_BASE (EP93XX_APB_VIRT_BASE + 0x000d0000) +#define EP93XX_UART2_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000d0000) + +#define EP93XX_UART3_BASE (EP93XX_APB_VIRT_BASE + 0x000e0000) +#define EP93XX_UART3_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000e0000) + +#define EP93XX_KEY_MATRIX_BASE (EP93XX_APB_VIRT_BASE + 0x000f0000) + +#define EP93XX_ADC_BASE (EP93XX_APB_VIRT_BASE + 0x00100000) +#define EP93XX_TOUCHSCREEN_BASE (EP93XX_APB_VIRT_BASE + 0x00100000) + +#define EP93XX_PWM_BASE (EP93XX_APB_VIRT_BASE + 0x00110000) + +#define EP93XX_RTC_BASE (EP93XX_APB_VIRT_BASE + 0x00120000) + +#define EP93XX_SYSCON_BASE (EP93XX_APB_VIRT_BASE + 0x00130000) +#define EP93XX_SYSCON_REG(x) (EP93XX_SYSCON_BASE + (x)) +#define EP93XX_SYSCON_POWER_STATE EP93XX_SYSCON_REG(0x00) +#define EP93XX_SYSCON_CLOCK_CONTROL EP93XX_SYSCON_REG(0x04) +#define EP93XX_SYSCON_CLOCK_UARTBAUD 0x20000000 +#define EP93XX_SYSCON_CLOCK_USH_EN 0x10000000 +#define EP93XX_SYSCON_HALT EP93XX_SYSCON_REG(0x08) +#define EP93XX_SYSCON_STANDBY EP93XX_SYSCON_REG(0x0c) +#define EP93XX_SYSCON_DEVICE_CONFIG EP93XX_SYSCON_REG(0x80) +#define EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE 0x00800000 +#define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0) + +#define EP93XX_WATCHDOG_BASE (EP93XX_APB_VIRT_BASE + 0x00140000) + + +#endif Index: linux-2.6.16/include/asm-arm/arch-ep93xx/gesbc9312.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/gesbc9312.h @@ -0,0 +1,3 @@ +/* + * linux/include/asm-arm/arch-ep93xx/gesbc9312.h + */ Index: linux-2.6.16/include/asm-arm/arch-ep93xx/hardware.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/hardware.h @@ -0,0 +1,13 @@ +/* + * linux/include/asm-arm/arch-ep93xx/hardware.h + */ + +#include "ep93xx-regs.h" + +#define pcibios_assign_all_busses() 0 + +#include "platform.h" + +#include "gesbc9312.h" +#include "micro9.h" +#include "ts72xx.h" Index: linux-2.6.16/include/asm-arm/arch-ep93xx/io.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/io.h @@ -0,0 +1,8 @@ +/* + * linux/include/asm-arm/arch-ep93xx/io.h + */ + +#define IO_SPACE_LIMIT 0xffffffff + +#define __io(p) ((void __iomem *)(p)) +#define __mem_pci(p) (p) Index: linux-2.6.16/include/asm-arm/arch-ep93xx/irqs.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/irqs.h @@ -0,0 +1,80 @@ +/* + * linux/include/asm-arm/arch-ep93xx/irqs.h + */ + +#ifndef __ASM_ARCH_IRQS_H +#define __ASM_ARCH_IRQS_H + +#define IRQ_EP93XX_COMMRX 2 +#define IRQ_EP93XX_COMMTX 3 +#define IRQ_EP93XX_TIMER1 4 +#define IRQ_EP93XX_TIMER2 5 +#define IRQ_EP93XX_AACINTR 6 +#define IRQ_EP93XX_DMAM2P0 7 +#define IRQ_EP93XX_DMAM2P1 8 +#define IRQ_EP93XX_DMAM2P2 9 +#define IRQ_EP93XX_DMAM2P3 10 +#define IRQ_EP93XX_DMAM2P4 11 +#define IRQ_EP93XX_DMAM2P5 12 +#define IRQ_EP93XX_DMAM2P6 13 +#define IRQ_EP93XX_DMAM2P7 14 +#define IRQ_EP93XX_DMAM2P8 15 +#define IRQ_EP93XX_DMAM2P9 16 +#define IRQ_EP93XX_DMAM2M0 17 +#define IRQ_EP93XX_DMAM2M1 18 +#define IRQ_EP93XX_GPIO0MUX 20 +#define IRQ_EP93XX_GPIO1MUX 21 +#define IRQ_EP93XX_GPIO2MUX 22 +#define IRQ_EP93XX_GPIO3MUX 22 +#define IRQ_EP93XX_UART1RX 23 +#define IRQ_EP93XX_UART1TX 24 +#define IRQ_EP93XX_UART2RX 25 +#define IRQ_EP93XX_UART2TX 26 +#define IRQ_EP93XX_UART3RX 27 +#define IRQ_EP93XX_UART3TX 28 +#define IRQ_EP93XX_KEY 29 +#define IRQ_EP93XX_TOUCH 30 +#define EP93XX_VIC1_VALID_IRQ_MASK 0x7ffffffc + +#define IRQ_EP93XX_EXT0 32 +#define IRQ_EP93XX_EXT1 33 +#define IRQ_EP93XX_EXT2 34 +#define IRQ_EP93XX_64HZ 35 +#define IRQ_EP93XX_WATCHDOG 36 +#define IRQ_EP93XX_RTC 37 +#define IRQ_EP93XX_IRDA 38 +#define IRQ_EP93XX_ETHERNET 39 +#define IRQ_EP93XX_EXT3 40 +#define IRQ_EP93XX_PROG 41 +#define IRQ_EP93XX_1HZ 42 +#define IRQ_EP93XX_VSYNC 43 +#define IRQ_EP93XX_VIDEO_FIFO 44 +#define IRQ_EP93XX_SSP1RX 45 +#define IRQ_EP93XX_SSP1TX 46 +#define IRQ_EP93XX_GPIO4MUX 47 +#define IRQ_EP93XX_GPIO5MUX 48 +#define IRQ_EP93XX_GPIO6MUX 49 +#define IRQ_EP93XX_GPIO7MUX 50 +#define IRQ_EP93XX_TIMER3 51 +#define IRQ_EP93XX_UART1 52 +#define IRQ_EP93XX_SSP 53 +#define IRQ_EP93XX_UART2 54 +#define IRQ_EP93XX_UART3 55 +#define IRQ_EP93XX_USB 56 +#define IRQ_EP93XX_ETHERNET_PME 57 +#define IRQ_EP93XX_DSP 58 +#define IRQ_EP93XX_GPIO_AB 59 +#define IRQ_EP93XX_SAI 60 +#define EP93XX_VIC2_VALID_IRQ_MASK 0x1fffffff + +#define IRQ_EP93XX_GPIO(x) (64 + (x)) + +#define NR_EP93XX_IRQS IRQ_EP93XX_GPIO(16) + +#define EP93XX_BOARD_IRQ(x) (NR_EP93XX_IRQS + (x)) +#define EP93XX_BOARD_IRQS 32 + +#define NR_IRQS (NR_EP93XX_IRQS + EP93XX_BOARD_IRQS) + + +#endif Index: linux-2.6.16/include/asm-arm/arch-ep93xx/memory.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/memory.h @@ -0,0 +1,14 @@ +/* + * linux/include/asm-arm/arch-ep93xx/memory.h + */ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +#define PHYS_OFFSET UL(0x00000000) + +#define __bus_to_virt(x) __phys_to_virt(x) +#define __virt_to_bus(x) __virt_to_phys(x) + + +#endif Index: linux-2.6.16/include/asm-arm/arch-ep93xx/param.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/param.h @@ -0,0 +1,3 @@ +/* + * linux/include/asm-arm/arch-ep93xx/param.h + */ Index: linux-2.6.16/include/asm-arm/arch-ep93xx/platform.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/platform.h @@ -0,0 +1,19 @@ +/* + * linux/include/asm-arm/arch-ep93xx/platform.h + */ + +#ifndef __ASSEMBLY__ + +void ep93xx_map_io(void); +void ep93xx_init_irq(void); +void ep93xx_init_time(unsigned long); +void ep93xx_init_devices(void); +extern struct sys_timer ep93xx_timer; + +struct ep93xx_i2c_pins { + unsigned long sda_pin; + unsigned long scl_pin; +}; + + +#endif Index: linux-2.6.16/include/asm-arm/arch-ep93xx/system.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/system.h @@ -0,0 +1,26 @@ +/* + * linux/include/asm-arm/arch-ep93xx/system.h + */ + +#include + +static inline void arch_idle(void) +{ + cpu_do_idle(); +} + +static inline void arch_reset(char mode) +{ + u32 devicecfg; + + local_irq_disable(); + + devicecfg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG); + __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); + __raw_writel(devicecfg | 0x80000000, EP93XX_SYSCON_DEVICE_CONFIG); + __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); + __raw_writel(devicecfg & ~0x80000000, EP93XX_SYSCON_DEVICE_CONFIG); + + while (1) + ; +} Index: linux-2.6.16/include/asm-arm/arch-ep93xx/timex.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/timex.h @@ -0,0 +1,5 @@ +/* + * linux/include/asm-arm/arch-ep93xx/timex.h + */ + +#define CLOCK_TICK_RATE 983040 Index: linux-2.6.16/include/asm-arm/arch-ep93xx/ts72xx.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/ts72xx.h @@ -0,0 +1,112 @@ +/* + * linux/include/asm-arm/arch-ep93xx/ts72xx.h + */ + +/* + * TS72xx memory map: + * + * virt phys size + * febff000 22000000 4K model number register + * febfe000 22400000 4K options register + * febfd000 22800000 4K options register #2 + * febfc000 [67]0000000 4K NAND data register + * febfb000 [67]0400000 4K NAND control register + * febfa000 [67]0800000 4K NAND busy register + * febf9000 10800000 4K TS-5620 RTC index register + * febf8000 11700000 4K TS-5620 RTC data register + */ + +#define TS72XX_MODEL_PHYS_BASE 0x22000000 +#define TS72XX_MODEL_VIRT_BASE 0xfebff000 +#define TS72XX_MODEL_SIZE 0x00001000 + +#define TS72XX_MODEL_TS7200 0x00 +#define TS72XX_MODEL_TS7250 0x01 +#define TS72XX_MODEL_TS7260 0x02 + + +#define TS72XX_OPTIONS_PHYS_BASE 0x22400000 +#define TS72XX_OPTIONS_VIRT_BASE 0xfebfe000 +#define TS72XX_OPTIONS_SIZE 0x00001000 + +#define TS72XX_OPTIONS_COM2_RS485 0x02 +#define TS72XX_OPTIONS_MAX197 0x01 + + +#define TS72XX_OPTIONS2_PHYS_BASE 0x22800000 +#define TS72XX_OPTIONS2_VIRT_BASE 0xfebfd000 +#define TS72XX_OPTIONS2_SIZE 0x00001000 + +#define TS72XX_OPTIONS2_TS9420 0x04 +#define TS72XX_OPTIONS2_TS9420_BOOT 0x02 + + +#define TS72XX_NOR_PHYS_BASE 0x60000000 +#define TS72XX_NOR2_PHYS_BASE 0x62000000 + +#define TS72XX_NAND1_DATA_PHYS_BASE 0x60000000 +#define TS72XX_NAND2_DATA_PHYS_BASE 0x70000000 +#define TS72XX_NAND_DATA_VIRT_BASE 0xfebfc000 +#define TS72XX_NAND_DATA_SIZE 0x00001000 + +#define TS72XX_NAND1_CONTROL_PHYS_BASE 0x60400000 +#define TS72XX_NAND2_CONTROL_PHYS_BASE 0x70400000 +#define TS72XX_NAND_CONTROL_VIRT_BASE 0xfebfb000 +#define TS72XX_NAND_CONTROL_SIZE 0x00001000 + +#define TS72XX_NAND1_BUSY_PHYS_BASE 0x60800000 +#define TS72XX_NAND2_BUSY_PHYS_BASE 0x70800000 +#define TS72XX_NAND_BUSY_VIRT_BASE 0xfebfa000 +#define TS72XX_NAND_BUSY_SIZE 0x00001000 + +/* TS7200 CF memory map: + * + * phys size description + * 11000001 7 CF registers (8-bit) + * 10400006 2 CF aux registers (8-bit) + * 21000000 2 CF data register (16-bit) + */ + +#define TS7200_CF_CMD_PHYS_BASE 0x11000000 +#define TS7200_CF_AUX_PHYS_BASE 0x10400006 +#define TS7200_CF_DATA_PHYS_BASE 0x21000000 + +#define TS72XX_RTC_INDEX_VIRT_BASE 0xfebf9000 +#define TS72XX_RTC_INDEX_PHYS_BASE 0x10800000 +#define TS72XX_RTC_INDEX_SIZE 0x00001000 + +#define TS72XX_RTC_DATA_VIRT_BASE 0xfebf8000 +#define TS72XX_RTC_DATA_PHYS_BASE 0x11700000 +#define TS72XX_RTC_DATA_SIZE 0x00001000 + + +#ifndef __ASSEMBLY__ +#include + +static inline int board_is_ts7200(void) +{ + return __raw_readb(TS72XX_MODEL_VIRT_BASE) == TS72XX_MODEL_TS7200; +} + +static inline int board_is_ts7250(void) +{ + return __raw_readb(TS72XX_MODEL_VIRT_BASE) == TS72XX_MODEL_TS7250; +} + +static inline int board_is_ts7260(void) +{ + return __raw_readb(TS72XX_MODEL_VIRT_BASE) == TS72XX_MODEL_TS7260; +} + +static inline int is_max197_installed(void) +{ + return !!(__raw_readb(TS72XX_OPTIONS_VIRT_BASE) & + TS72XX_OPTIONS_MAX197); +} + +static inline int is_ts9420_installed(void) +{ + return !!(__raw_readb(TS72XX_OPTIONS2_VIRT_BASE) & + TS72XX_OPTIONS2_TS9420); +} +#endif Index: linux-2.6.16/include/asm-arm/arch-ep93xx/uncompress.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/uncompress.h @@ -0,0 +1,138 @@ +/* + * linux/include/asm-arm/arch-ep93xx/uncompress.h + * + * Copyright (C) 2006 Lennert Buytenhek + * + * 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. + */ + +#include + +static unsigned char __raw_readb(unsigned int ptr) +{ + return *((volatile unsigned char *)ptr); +} + +static unsigned int __raw_readl(unsigned int ptr) +{ + return *((volatile unsigned int *)ptr); +} + +static void __raw_writeb(unsigned char value, unsigned int ptr) +{ + *((volatile unsigned char *)ptr) = value; +} + +static void __raw_writel(unsigned int value, unsigned int ptr) +{ + *((volatile unsigned int *)ptr) = value; +} + + +#define PHYS_UART1_DATA 0x808c0000 +#define PHYS_UART1_FLAG 0x808c0018 +#define UART1_FLAG_TXFF 0x20 + +static __inline__ void putc(char c) +{ + int i; + + for (i = 0; i < 1000; i++) { + /* Transmit fifo not full? */ + if (!(__raw_readb(PHYS_UART1_FLAG) & UART1_FLAG_TXFF)) + break; + } + + __raw_writeb(c, PHYS_UART1_DATA); +} + +static void putstr(const char *s) +{ + while (*s) { + putc(*s); + if (*s == '\n') + putc('\r'); + s++; + } +} + + +/* + * Some bootloaders don't turn off DMA from the ethernet MAC before + * jumping to linux, which means that we might end up with bits of RX + * status and packet data scribbled over the uncompressed kernel image. + * Work around this by resetting the ethernet MAC before we uncompress. + */ +#define PHYS_ETH_SELF_CTL 0x80010020 +#define ETH_SELF_CTL_RESET 0x00000001 + +static void ethernet_reset(void) +{ + unsigned int v; + + /* Reset the ethernet MAC. */ + v = __raw_readl(PHYS_ETH_SELF_CTL); + __raw_writel(v | ETH_SELF_CTL_RESET, PHYS_ETH_SELF_CTL); + + /* Wait for reset to finish. */ + while (__raw_readl(PHYS_ETH_SELF_CTL) & ETH_SELF_CTL_RESET) + ; +} + + +/* + * Some bootloaders don't turn on the UARTBAUD bit, which means that + * the UARTs will be running off a divided 7.3728 MHz clock instead of + * the 14.7456 MHz peripheral clock when linux boots. + * + * We detect that condition here and fix it by turning on UARTBAUD, and + * then reprogramming the divisors on all enabled UARTs to twice what + * they were before we turned UARTBAUD on, to preserve the programmed + * baud rate. + */ +#define PHYS_SYSCON_CLOCK_CONTROL 0x80930004 +#define SYSCON_CLOCK_UARTBAUD 0x20000000 +#define PHYS_UART1_BASE 0x808c0000 +#define PHYS_UART2_BASE 0x808d0000 +#define PHYS_UART3_BASE 0x808e0000 + +static void uart_divisor_times_two(unsigned int base) +{ + u16 divisor; + + divisor = __raw_readb(base + 0x0c) << 8; + divisor |= __raw_readb(base + 0x10); + if (divisor) { + divisor = (2 * (divisor + 1)) - 1; + __raw_writeb(divisor >> 8, base + 0x0c); + __raw_writeb(divisor & 0xff, base + 0x10); + __raw_writeb(__raw_readb(base + 0x08), base + 0x08); + } +} + +static void fix_uart_base(void) +{ + unsigned int v; + + v = __raw_readl(PHYS_SYSCON_CLOCK_CONTROL); + if ((v & SYSCON_CLOCK_UARTBAUD) == 0) { + v |= SYSCON_CLOCK_UARTBAUD, + __raw_writel(v, PHYS_SYSCON_CLOCK_CONTROL); + + uart_divisor_times_two(PHYS_UART1_BASE); + uart_divisor_times_two(PHYS_UART2_BASE); + uart_divisor_times_two(PHYS_UART3_BASE); + } +} + + +static void arch_decomp_setup(void) +{ + ethernet_reset(); + fix_uart_base(); +} + +#define arch_decomp_wdog() Index: linux-2.6.16/include/asm-arm/arch-ep93xx/vmalloc.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/vmalloc.h @@ -0,0 +1,5 @@ +/* + * linux/include/asm-arm/arch-ep93xx/vmalloc.h + */ + +#define VMALLOC_END 0xfe800000 Index: linux-2.6.16/arch/arm/mach-ep93xx/micro9.c =================================================================== --- /dev/null +++ linux-2.6.16/arch/arm/mach-ep93xx/micro9.c @@ -0,0 +1,252 @@ +/* + * linux/arch/arm/mach-ep93xx/micro9.c + * + * Copyright (C) 2004 Contec Steuerungstechnik & Automation GmbH + * Manfred Gruber + * + * 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 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * FPGA extension board + */ +#ifdef CONFIG_MICRO9_FPGA_EXTENSION_BOARD +#include +#endif + +#ifdef CONFIG_ARCH_EP93XX_GPIO_IRQ +#ifdef CONFIG_GENERIC_HARDIRQS +#include +#endif +#include "ep93xx_gpio_irq.h" +#endif + +#ifdef CONFIG_LEDS + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +#define LED_ON 1 + +void micro9_leds_event(led_event_t evt) +{ + unsigned long flags; + +/* Has to be checked if wee need locking here, because of locking in gpio code */ +#ifdef CONFIG_PREEMPT_RT + raw_local_irq_save(flags); +#else + local_irq_save(flags); +#endif + switch (evt) { + case led_start: + led_state = LED_STATE_ENABLED; + hw_led_state = LED_ON ; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + hw_led_state = 0; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = LED_ON; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_timer: + break; + + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~LED_ON; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= LED_ON; + break; + + case led_halted: + break; + + case led_amber_on: + break; + + case led_amber_off: + break; + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= LED_ON; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~LED_ON; + break; + + default: + break; + } + + gpio_line_config(EP93XX_GPIO_LINE_E(1), GPIO_OUT); + + if (led_state & LED_STATE_ENABLED) + { + if(hw_led_state) + gpio_line_set(EP93XX_GPIO_LINE_E(1), EP93XX_GPIO_HIGH); + else + gpio_line_set(EP93XX_GPIO_LINE_E(1), EP93XX_GPIO_LOW); + } + +/* Has to be checked if wee need locking here, because of locking in gpio code */ +#ifdef CONFIG_PREEMPT_RT + raw_local_irq_restore(flags); +#else + local_irq_restore(flags); +#endif +} + +#else +#define micro9_leds_event 0 +#endif + +static void __init micro9_init_machine(void) +{ + ep93xx_init_devices(); + +#ifdef CONFIG_MTD_PHYSMAP + physmap_configure(0x10000000 , SZ_64M , 4, NULL); + /* OLD platform_device_register(&cfi_flash_device); */ +#endif + + /* + * CAN memory, can't be used by other things. + */ + if (!request_mem_region(MICRO9_CAN1_IO_START, MICRO9_CAN1_IO_LEN, + "OKI CAN 1")) { + printk(KERN_ERR "OKI CAN1: unable to reserve memory for " + "OKI can1 controller\n"); + } + + if (!request_mem_region(MICRO9_CAN2_IO_START, MICRO9_CAN2_IO_LEN, + "OKI CAN 2")) { + printk(KERN_ERR "OKI CAN2: unable to reserve memory for " + "OKI can2 controller\n"); + } + +#ifdef CONFIG_MICRO9_FPGA_EXTENSION_BOARD + /* + * FPGA extension board. + */ + if (!request_mem_region(FPGA_IO_START_1, FPGA_IO_LEN_1, "FPGA 1")) { + printk(KERN_ERR "FPGA: unable to reserve memory 1 for " + "Micro9 FPGA extension board\n"); + } + + if (!request_mem_region(FPGA_IO_START_2, FPGA_IO_LEN_2, "FPGA 2")) { + printk(KERN_ERR "FPGA: unable to reserve memory 2 for " + "Micro9 FPGA extension board\n"); + } +#endif + +#ifdef CONFIG_LEDS + leds_event = micro9_leds_event; + leds_event(led_start); +#endif + +#ifdef CONFIG_RASTER_BUS_ARBITRATION + /* This is run default with a display, bad latencies because + * usb, mac, dma is allowed to take bus also when irq is active + * bus prio 1 raster + * bus prio 2 raster cursor + * bus prio 3 mac + * bus prio 4 usb + * bus prio 5 core + * bus prio 6 dma + */ + outl(0x1, SYSCON_BMAR); +#endif + +#ifdef CONFIG_IRQ_BUS_ARBITRATION + /* This is run on PREEMPT_RT with a display, not so well latencies + * but a lot better than CONFIG_RASTER_BUS_ARBITRATION + * bus accesss for usb, mac, dma is not allowed when a irq is active + * core has not the highest prio + * bus prio 1 raster + * bus prio 2 raster cursor + * bus prio 3 mac + * bus prio 4 usb + * bus prio 5 core + * bus prio 6 dma + */ + outl(0x151, SYSCON_BMAR); +#endif + +#ifdef CONFIG_CORE_IRQ_BUS_ARBITRATION + /* This is run on PREEMPT_RT without a display, good latencies + * core has highest prio !!! + * don't allow usb, mac, dma to bus if irq is active !! + * bus prio 1 raster + * bus prio 2 raster cursor + * bus prio 3 mac + * bus prio 4 usb + * bus prio 5 core + * bus prio 6 dma + */ + outl(0x159, SYSCON_BMAR); +#endif +} + +MACHINE_START(MICRO9, "Contec Hypercontrol Micro9") + /* Maintainer: Manfred Gruber */ + .phys_io = EP93XX_APB_PHYS_BASE, + .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, + .boot_params = 0x00000100, + .map_io = ep93xx_map_io, + .init_irq = ep93xx_init_irq, + .timer = &ep93xx_timer, + .init_machine = micro9_init_machine, +MACHINE_END Index: linux-2.6.16/include/asm-arm/arch-ep93xx/micro9.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/micro9.h @@ -0,0 +1,8 @@ +/* + * linux/include/asm-arm/arch-ep93xx/micro9.h + */ + +#define MICRO9_CAN1_IO_START 0x20000000 +#define MICRO9_CAN1_IO_LEN 0x01000000 +#define MICRO9_CAN2_IO_START 0x21000000 +#define MICRO9_CAN2_IO_LEN 0x01000000 Index: linux-2.6.16/include/asm-arm/fpstate.h =================================================================== --- linux-2.6.16.orig/include/asm-arm/fpstate.h +++ linux-2.6.16/include/asm-arm/fpstate.h @@ -71,6 +71,16 @@ union fp_state { #define FP_SIZE (sizeof(union fp_state) / sizeof(int)) +struct crunch_struct { + unsigned int mvdx[16][2]; + unsigned int mvax[4][3]; + unsigned int dspsc[2]; +}; + +union crunch_state { + struct crunch_struct crunch; +}; + #endif #endif Index: linux-2.6.16/arch/arm/kernel/asm-offsets.c =================================================================== --- linux-2.6.16.orig/arch/arm/kernel/asm-offsets.c +++ linux-2.6.16/arch/arm/kernel/asm-offsets.c @@ -60,6 +60,9 @@ int main(void) #ifdef CONFIG_IWMMXT DEFINE(TI_IWMMXT_STATE, offsetof(struct thread_info, fpstate.iwmmxt)); #endif +#ifdef CONFIG_CRUNCH + DEFINE(TI_CRUNCH_STATE, offsetof(struct thread_info, crunchstate)); +#endif BLANK(); DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0)); DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1)); Index: linux-2.6.16/arch/arm/kernel/process.c =================================================================== --- linux-2.6.16.orig/arch/arm/kernel/process.c +++ linux-2.6.16/arch/arm/kernel/process.c @@ -331,6 +331,9 @@ void flush_thread(void) memset(thread->used_cp, 0, sizeof(thread->used_cp)); memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); +#if defined(CONFIG_CRUNCH) + crunch_task_release(thread); +#endif #if defined(CONFIG_IWMMXT) iwmmxt_task_release(thread); #endif @@ -348,6 +351,9 @@ void release_thread(struct task_struct * #if defined(CONFIG_IWMMXT) iwmmxt_task_release(task_thread_info(dead_task)); #endif +#if defined(CONFIG_CRUNCH) + crunch_task_release(task_thread_info(dead_task)); +#endif } asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); Index: linux-2.6.16/arch/arm/kernel/entry-armv.S =================================================================== --- linux-2.6.16.orig/arch/arm/kernel/entry-armv.S +++ linux-2.6.16/arch/arm/kernel/entry-armv.S @@ -477,6 +477,12 @@ call_fpe: mov r7, #1 add r6, r10, #TI_USED_CP strb r7, [r6, r8, lsr #8] @ set appropriate used_cp[] +#ifdef CONFIG_CRUNCH + @ Test if we need to give access to MaverickCrunch coprocessors + subs r7, r8, #(0x00000400) + rsbcss r7, r7, #(0x00000200) + bcs crunch_task_enable +#endif #ifdef CONFIG_IWMMXT @ Test if we need to give access to iWMMXt coprocessors ldr r5, [r10, #TI_FLAGS] @@ -592,6 +598,9 @@ ENTRY(__switch_to) bic r4, r4, #FPEXC_ENABLE VFPFMXR FPEXC, r4 #endif +#ifdef CONFIG_CRUNCH + bl crunch_task_switch +#endif #if defined(CONFIG_IWMMXT) bl iwmmxt_task_switch #elif defined(CONFIG_CPU_XSCALE) Index: linux-2.6.16/arch/arm/kernel/Makefile =================================================================== --- linux-2.6.16.orig/arch/arm/kernel/Makefile +++ linux-2.6.16/arch/arm/kernel/Makefile @@ -22,6 +22,9 @@ obj-$(CONFIG_PCI) += bios32.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o +obj-$(CONFIG_CRUNCH) += crunch.o +AFLAGS_crunch.o := -Wa,-mcpu=ep9312 + obj-$(CONFIG_IWMMXT) += iwmmxt.o AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt Index: linux-2.6.16/arch/arm/kernel/crunch.S =================================================================== --- /dev/null +++ linux-2.6.16/arch/arm/kernel/crunch.S @@ -0,0 +1,359 @@ +/* + * arch/arm/kernel/crunch.S + * Cirrus MaverickCrunch context switching and handling + * + * Copyright (C) 2006 Lennert Buytenhek + * + * Shamelessly stolen from the iWMMXt code by Nicolas Pitre, which is + * Copyright (c) 2003-2004, 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 version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +/* + * We can't use hex constants here due to a bug in gas. + */ +#define CRUNCH_MVDX0 0 +#define CRUNCH_MVDX1 8 +#define CRUNCH_MVDX2 16 +#define CRUNCH_MVDX3 24 +#define CRUNCH_MVDX4 32 +#define CRUNCH_MVDX5 40 +#define CRUNCH_MVDX6 48 +#define CRUNCH_MVDX7 56 +#define CRUNCH_MVDX8 64 +#define CRUNCH_MVDX9 72 +#define CRUNCH_MVDX10 80 +#define CRUNCH_MVDX11 88 +#define CRUNCH_MVDX12 96 +#define CRUNCH_MVDX13 104 +#define CRUNCH_MVDX14 112 +#define CRUNCH_MVDX15 120 +#define CRUNCH_MVAX0L 128 +#define CRUNCH_MVAX0M 132 +#define CRUNCH_MVAX0H 136 +#define CRUNCH_MVAX1L 140 +#define CRUNCH_MVAX1M 144 +#define CRUNCH_MVAX1H 148 +#define CRUNCH_MVAX2L 152 +#define CRUNCH_MVAX2M 156 +#define CRUNCH_MVAX2H 160 +#define CRUNCH_MVAX3L 164 +#define CRUNCH_MVAX3M 168 +#define CRUNCH_MVAX3H 172 +#define CRUNCH_DSPSC 176 + +#define CRUNCH_SIZE 184 + + .text + +/* + * Lazy switching of crunch coprocessor context + * + * r10 = struct thread_info pointer + * r9 = ret_from_exception + * lr = undefined instr exit + * + * called from prefetch exception handler with interrupts disabled + */ +ENTRY(crunch_task_enable) + ldr r8, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr + + ldr r1, [r8, #0x80] + tst r1, #0x00800000 @ access to crunch enabled? + movne pc, lr @ if so no business here + mov r3, #0xaa @ unlock syscon swlock + str r3, [r8, #0xc0] + orr r1, r1, #0x00800000 @ enable access to crunch + str r1, [r8, #0x80] + + ldr r3, =crunch_owner + add r0, r10, #TI_CRUNCH_STATE @ get task crunch save area + ldr r2, [sp, #60] @ current task pc value + ldr r1, [r3] @ get current crunch owner + str r0, [r3] @ this task now owns crunch + sub r2, r2, #4 @ adjust pc back + str r2, [sp, #60] + + ldr r2, [r8, #0x80] + mov r2, r2 @ flush out enable (@@@) + + teq r1, #0 @ test for last ownership + mov lr, r9 @ normal exit from exception + beq crunch_load @ no owner, skip save + +crunch_save: + cfstr64 mvdx0, [r1, #CRUNCH_MVDX0] @ save 64b registers + cfstr64 mvdx1, [r1, #CRUNCH_MVDX1] + cfstr64 mvdx2, [r1, #CRUNCH_MVDX2] + cfstr64 mvdx3, [r1, #CRUNCH_MVDX3] + cfstr64 mvdx4, [r1, #CRUNCH_MVDX4] + cfstr64 mvdx5, [r1, #CRUNCH_MVDX5] + cfstr64 mvdx6, [r1, #CRUNCH_MVDX6] + cfstr64 mvdx7, [r1, #CRUNCH_MVDX7] + cfstr64 mvdx8, [r1, #CRUNCH_MVDX8] + cfstr64 mvdx9, [r1, #CRUNCH_MVDX9] + cfstr64 mvdx10, [r1, #CRUNCH_MVDX10] + cfstr64 mvdx11, [r1, #CRUNCH_MVDX11] + cfstr64 mvdx12, [r1, #CRUNCH_MVDX12] + cfstr64 mvdx13, [r1, #CRUNCH_MVDX13] + cfstr64 mvdx14, [r1, #CRUNCH_MVDX14] + cfstr64 mvdx15, [r1, #CRUNCH_MVDX15] + +#ifdef __ARMEB__ +#error fix me for ARMEB +#endif + + cfmv32al mvfx0, mvax0 @ save 72b accumulators + cfstr32 mvfx0, [r1, #CRUNCH_MVAX0L] + cfmv32am mvfx0, mvax0 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX0M] + cfmv32ah mvfx0, mvax0 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX0H] + cfmv32al mvfx0, mvax1 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX1L] + cfmv32am mvfx0, mvax1 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX1M] + cfmv32ah mvfx0, mvax1 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX1H] + cfmv32al mvfx0, mvax2 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX2L] + cfmv32am mvfx0, mvax2 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX2M] + cfmv32ah mvfx0, mvax2 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX2H] + cfmv32al mvfx0, mvax3 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX3L] + cfmv32am mvfx0, mvax3 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX3M] + cfmv32ah mvfx0, mvax3 + cfstr32 mvfx0, [r1, #CRUNCH_MVAX3H] + + cfmv32sc mvdx0, dspsc @ save status word + cfstr64 mvdx0, [r1, #CRUNCH_DSPSC] + + teq r0, #0 @ anything to load? + cfldr64eq mvdx0, [r1, #CRUNCH_MVDX0] @ mvdx0 was clobbered + moveq pc, lr + +crunch_load: + cfldr64 mvdx0, [r0, #CRUNCH_DSPSC] @ load status word + cfmvsc32 dspsc, mvdx0 + + cfldr32 mvfx0, [r0, #CRUNCH_MVAX0L] @ load 72b accumulators + cfmval32 mvax0, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX0M] + cfmvam32 mvax0, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX0H] + cfmvah32 mvax0, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX1L] + cfmval32 mvax1, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX1M] + cfmvam32 mvax1, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX1H] + cfmvah32 mvax1, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX2L] + cfmval32 mvax2, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX2M] + cfmvam32 mvax2, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX2H] + cfmvah32 mvax2, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX3L] + cfmval32 mvax3, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX3M] + cfmvam32 mvax3, mvfx0 + cfldr32 mvfx0, [r0, #CRUNCH_MVAX3H] + cfmvah32 mvax3, mvfx0 + + cfldr64 mvdx0, [r0, #CRUNCH_MVDX0] @ load 64b registers + cfldr64 mvdx1, [r0, #CRUNCH_MVDX1] + cfldr64 mvdx2, [r0, #CRUNCH_MVDX2] + cfldr64 mvdx3, [r0, #CRUNCH_MVDX3] + cfldr64 mvdx4, [r0, #CRUNCH_MVDX4] + cfldr64 mvdx5, [r0, #CRUNCH_MVDX5] + cfldr64 mvdx6, [r0, #CRUNCH_MVDX6] + cfldr64 mvdx7, [r0, #CRUNCH_MVDX7] + cfldr64 mvdx8, [r0, #CRUNCH_MVDX8] + cfldr64 mvdx9, [r0, #CRUNCH_MVDX9] + cfldr64 mvdx10, [r0, #CRUNCH_MVDX10] + cfldr64 mvdx11, [r0, #CRUNCH_MVDX11] + cfldr64 mvdx12, [r0, #CRUNCH_MVDX12] + cfldr64 mvdx13, [r0, #CRUNCH_MVDX13] + cfldr64 mvdx14, [r0, #CRUNCH_MVDX14] + cfldr64 mvdx15, [r0, #CRUNCH_MVDX15] + + mov pc, lr + +/* + * Back up crunch regs to save area and disable access to them + * (mainly for gdb or sleep mode usage) + * + * r0 = struct thread_info pointer of target task or NULL for any + */ +ENTRY(crunch_task_disable) + stmfd sp!, {r4, r5, lr} + + mrs ip, cpsr + orr r2, ip, #PSR_I_BIT @ disable interrupts + msr cpsr_c, r2 + + ldr r4, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr + + ldr r3, =crunch_owner + add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area + ldr r1, [r3] @ get current crunch owner + teq r1, #0 @ any current owner? + beq 1f @ no: quit + teq r0, #0 @ any owner? + teqne r1, r2 @ or specified one? + bne 1f @ no: quit + + ldr r5, [r4, #0x80] @ enable access to crunch + mov r2, #0xaa + str r2, [r4, #0xc0] + orr r5, r5, #0x00800000 + str r5, [r4, #0x80] + + mov r0, #0 @ nothing to load + str r0, [r3] @ no more current owner + ldr r2, [r4, #0x80] @ flush out enable (@@@) + mov r2, r2 + bl crunch_save + + mov r2, #0xaa @ disable access to crunch + str r2, [r4, #0xc0] + bic r5, r5, #0x00800000 + str r5, [r4, #0x80] + ldr r5, [r4, #0x80] @ flush out enable (@@@) + mov r5, r5 + +1: msr cpsr_c, ip @ restore interrupt mode + ldmfd sp!, {r4, r5, pc} + +/* + * Copy crunch state to given memory address + * + * r0 = struct thread_info pointer of target task + * r1 = memory address where to store crunch state + * + * this is called mainly in the creation of signal stack frames + */ +ENTRY(crunch_task_copy) + mrs ip, cpsr + orr r2, ip, #PSR_I_BIT @ disable interrupts + msr cpsr_c, r2 + + ldr r3, =crunch_owner + add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area + ldr r3, [r3] @ get current crunch owner + teq r2, r3 @ does this task own it... + beq 1f + + @ current crunch values are in the task save area + msr cpsr_c, ip @ restore interrupt mode + mov r0, r1 + mov r1, r2 + mov r2, #CRUNCH_SIZE + b memcpy + +1: @ this task owns crunch regs -- grab a copy from there + mov r0, #0 @ nothing to load + mov r3, lr @ preserve return address + bl crunch_save + msr cpsr_c, ip @ restore interrupt mode + mov pc, r3 + +/* + * Restore crunch state from given memory address + * + * r0 = struct thread_info pointer of target task + * r1 = memory address where to get crunch state from + * + * this is used to restore crunch state when unwinding a signal stack frame + */ +ENTRY(crunch_task_restore) + mrs ip, cpsr + orr r2, ip, #PSR_I_BIT @ disable interrupts + msr cpsr_c, r2 + + ldr r3, =crunch_owner + add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area + ldr r3, [r3] @ get current crunch owner + teq r2, r3 @ does this task own it... + beq 1f + + @ this task doesn't own crunch regs -- use its save area + msr cpsr_c, ip @ restore interrupt mode + mov r0, r2 + mov r2, #CRUNCH_SIZE + b memcpy + +1: @ this task owns crunch regs -- load them directly + mov r0, r1 + mov r1, #0 @ nothing to save + mov r3, lr @ preserve return address + bl crunch_load + msr cpsr_c, ip @ restore interrupt mode + mov pc, r3 + +/* + * crunch handling on task switch + * + * r0 = previous task_struct pointer (must be preserved) + * r1 = previous thread_info pointer + * r2 = next thread_info.cpu_domain pointer (must be preserved) + * + * Called only from __switch_to with task preemption disabled. + * No need to care about preserving r4 and above. + */ +ENTRY(crunch_task_switch) + ldr r4, =(EP93XX_APB_VIRT_BASE + 0x00130000) + ldr r7, [r4, #0x80] + tst r7, #0x00800000 @ crunch accessible? + bne 1f @ yes: block them for next task + + ldr r5, =crunch_owner + add r6, r2, #(TI_CRUNCH_STATE - TI_CPU_DOMAIN) @ get next task crunch save area + ldr r5, [r5] @ get current crunch owner + teq r5, r6 @ next task owns it? + movne pc, lr @ no: leave crunch disabled + +1: eor r7, r7, #0x00800000 @ flip crunch access + mov r6, #0xaa + str r6, [r4, #0xc0] + str r7, [r4, #0x80] + + ldr r7, [r4, #0x80] @ flush out write (@@@) and return + sub pc, lr, r7, lsr #32 + +/* + * Remove crunch ownership of given task + * + * r0 = struct thread_info pointer + */ +ENTRY(crunch_task_release) + mrs r2, cpsr + orr ip, r2, #PSR_I_BIT @ disable interrupts + msr cpsr_c, ip + + ldr r3, =crunch_owner + add r0, r0, #TI_CRUNCH_STATE @ get task crunch save area + ldr r1, [r3] @ get current crunch owner + eors r0, r0, r1 @ if equal... + streq r0, [r3] @ then clean ownership + + msr cpsr_c, r2 @ restore interrupts + mov pc, lr + + + .data +crunch_owner: + .word 0 Index: linux-2.6.16/include/asm-arm/thread_info.h =================================================================== --- linux-2.6.16.orig/include/asm-arm/thread_info.h +++ linux-2.6.16/include/asm-arm/thread_info.h @@ -59,6 +59,7 @@ struct thread_info { struct cpu_context_save cpu_context; /* cpu context */ __u8 used_cp[16]; /* thread used copro */ unsigned long tp_value; + union crunch_state crunchstate; union fp_state fpstate __attribute__((aligned(8))); union vfp_state vfpstate; struct restart_block restart_block; @@ -101,6 +102,11 @@ extern void free_thread_info(struct thre #define thread_saved_fp(tsk) \ ((unsigned long)(task_thread_info(tsk)->cpu_context.fp)) +extern void crunch_task_disable(struct thread_info *); +extern void crunch_task_copy(struct thread_info *, void *); +extern void crunch_task_restore(struct thread_info *, void *); +extern void crunch_task_release(struct thread_info *); + extern void iwmmxt_task_disable(struct thread_info *); extern void iwmmxt_task_copy(struct thread_info *, void *); extern void iwmmxt_task_restore(struct thread_info *, void *); Index: linux-2.6.16/include/asm-arm/ptrace.h =================================================================== --- linux-2.6.16.orig/include/asm-arm/ptrace.h +++ linux-2.6.16/include/asm-arm/ptrace.h @@ -26,6 +26,9 @@ #define PTRACE_SET_SYSCALL 23 +#define PTRACE_GETCRUNCHREGS 24 +#define PTRACE_SETCRUNCHREGS 25 + /* * PSR bits */ Index: linux-2.6.16/include/asm-arm/arch-ep93xx/gpio.h =================================================================== --- /dev/null +++ linux-2.6.16/include/asm-arm/arch-ep93xx/gpio.h @@ -0,0 +1,107 @@ +/* + * linux/include/asm-arm/arch-ep93xx/gpio.h + */ + +#ifndef __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H + +#define GPIO_IN 0 +#define GPIO_OUT 1 + +#define EP93XX_GPIO_LOW 0 +#define EP93XX_GPIO_HIGH 1 + +extern void gpio_line_config(int line, int direction); +extern int gpio_line_get(int line); +extern void gpio_line_set(int line, int value); + +/* GPIO port A. */ +#define EP93XX_GPIO_LINE_A(x) ((x) + 0) +#define EP93XX_GPIO_LINE_EGPIO0 EP93XX_GPIO_LINE_A(0) +#define EP93XX_GPIO_LINE_EGPIO1 EP93XX_GPIO_LINE_A(1) +#define EP93XX_GPIO_LINE_EGPIO2 EP93XX_GPIO_LINE_A(2) +#define EP93XX_GPIO_LINE_EGPIO3 EP93XX_GPIO_LINE_A(3) +#define EP93XX_GPIO_LINE_EGPIO4 EP93XX_GPIO_LINE_A(4) +#define EP93XX_GPIO_LINE_EGPIO5 EP93XX_GPIO_LINE_A(5) +#define EP93XX_GPIO_LINE_EGPIO6 EP93XX_GPIO_LINE_A(6) +#define EP93XX_GPIO_LINE_EGPIO7 EP93XX_GPIO_LINE_A(7) + +/* GPIO port B. */ +#define EP93XX_GPIO_LINE_B(x) ((x) + 8) +#define EP93XX_GPIO_LINE_EGPIO8 EP93XX_GPIO_LINE_B(0) +#define EP93XX_GPIO_LINE_EGPIO9 EP93XX_GPIO_LINE_B(1) +#define EP93XX_GPIO_LINE_EGPIO10 EP93XX_GPIO_LINE_B(2) +#define EP93XX_GPIO_LINE_EGPIO11 EP93XX_GPIO_LINE_B(3) +#define EP93XX_GPIO_LINE_EGPIO12 EP93XX_GPIO_LINE_B(4) +#define EP93XX_GPIO_LINE_EGPIO13 EP93XX_GPIO_LINE_B(5) +#define EP93XX_GPIO_LINE_EGPIO14 EP93XX_GPIO_LINE_B(6) +#define EP93XX_GPIO_LINE_EGPIO15 EP93XX_GPIO_LINE_B(7) + +/* GPIO port C. */ +#define EP93XX_GPIO_LINE_C(x) ((x) + 16) +#define EP93XX_GPIO_LINE_ROW0 EP93XX_GPIO_LINE_C(0) +#define EP93XX_GPIO_LINE_ROW1 EP93XX_GPIO_LINE_C(1) +#define EP93XX_GPIO_LINE_ROW2 EP93XX_GPIO_LINE_C(2) +#define EP93XX_GPIO_LINE_ROW3 EP93XX_GPIO_LINE_C(3) +#define EP93XX_GPIO_LINE_ROW4 EP93XX_GPIO_LINE_C(4) +#define EP93XX_GPIO_LINE_ROW5 EP93XX_GPIO_LINE_C(5) +#define EP93XX_GPIO_LINE_ROW6 EP93XX_GPIO_LINE_C(6) +#define EP93XX_GPIO_LINE_ROW7 EP93XX_GPIO_LINE_C(7) + +/* GPIO port D. */ +#define EP93XX_GPIO_LINE_D(x) ((x) + 24) +#define EP93XX_GPIO_LINE_COL0 EP93XX_GPIO_LINE_D(0) +#define EP93XX_GPIO_LINE_COL1 EP93XX_GPIO_LINE_D(1) +#define EP93XX_GPIO_LINE_COL2 EP93XX_GPIO_LINE_D(2) +#define EP93XX_GPIO_LINE_COL3 EP93XX_GPIO_LINE_D(3) +#define EP93XX_GPIO_LINE_COL4 EP93XX_GPIO_LINE_D(4) +#define EP93XX_GPIO_LINE_COL5 EP93XX_GPIO_LINE_D(5) +#define EP93XX_GPIO_LINE_COL6 EP93XX_GPIO_LINE_D(6) +#define EP93XX_GPIO_LINE_COL7 EP93XX_GPIO_LINE_D(7) + +/* GPIO port E. */ +#define EP93XX_GPIO_LINE_E(x) ((x) + 32) +#define EP93XX_GPIO_LINE_GRLED EP93XX_GPIO_LINE_E(0) +#define EP93XX_GPIO_LINE_RDLED EP93XX_GPIO_LINE_E(1) +#define EP93XX_GPIO_LINE_DIORn EP93XX_GPIO_LINE_E(2) +#define EP93XX_GPIO_LINE_IDECS1n EP93XX_GPIO_LINE_E(3) +#define EP93XX_GPIO_LINE_IDECS2n EP93XX_GPIO_LINE_E(4) +#define EP93XX_GPIO_LINE_IDEDA0 EP93XX_GPIO_LINE_E(5) +#define EP93XX_GPIO_LINE_IDEDA1 EP93XX_GPIO_LINE_E(6) +#define EP93XX_GPIO_LINE_IDEDA2 EP93XX_GPIO_LINE_E(7) + +/* GPIO port F. */ +#define EP93XX_GPIO_LINE_F(x) ((x) + 40) +#define EP93XX_GPIO_LINE_WP EP93XX_GPIO_LINE_F(0) +#define EP93XX_GPIO_LINE_MCCD1 EP93XX_GPIO_LINE_F(1) +#define EP93XX_GPIO_LINE_MCCD2 EP93XX_GPIO_LINE_F(2) +#define EP93XX_GPIO_LINE_MCBVD1 EP93XX_GPIO_LINE_F(3) +#define EP93XX_GPIO_LINE_MCBVD2 EP93XX_GPIO_LINE_F(4) +#define EP93XX_GPIO_LINE_VS1 EP93XX_GPIO_LINE_F(5) +#define EP93XX_GPIO_LINE_READY EP93XX_GPIO_LINE_F(6) +#define EP93XX_GPIO_LINE_VS2 EP93XX_GPIO_LINE_F(7) + +/* GPIO port G. */ +#define EP93XX_GPIO_LINE_G(x) ((x) + 48) +#define EP93XX_GPIO_LINE_EECLK EP93XX_GPIO_LINE_G(0) +#define EP93XX_GPIO_LINE_EEDAT EP93XX_GPIO_LINE_G(1) +#define EP93XX_GPIO_LINE_SLA0 EP93XX_GPIO_LINE_G(2) +#define EP93XX_GPIO_LINE_SLA1 EP93XX_GPIO_LINE_G(3) +#define EP93XX_GPIO_LINE_DD12 EP93XX_GPIO_LINE_G(4) +#define EP93XX_GPIO_LINE_DD13 EP93XX_GPIO_LINE_G(5) +#define EP93XX_GPIO_LINE_DD14 EP93XX_GPIO_LINE_G(6) +#define EP93XX_GPIO_LINE_DD15 EP93XX_GPIO_LINE_G(7) + +/* GPIO port H. */ +#define EP93XX_GPIO_LINE_H(x) ((x) + 56) +#define EP93XX_GPIO_LINE_DD0 EP93XX_GPIO_LINE_H(0) +#define EP93XX_GPIO_LINE_DD1 EP93XX_GPIO_LINE_H(1) +#define EP93XX_GPIO_LINE_DD2 EP93XX_GPIO_LINE_H(2) +#define EP93XX_GPIO_LINE_DD3 EP93XX_GPIO_LINE_H(3) +#define EP93XX_GPIO_LINE_DD4 EP93XX_GPIO_LINE_H(4) +#define EP93XX_GPIO_LINE_DD5 EP93XX_GPIO_LINE_H(5) +#define EP93XX_GPIO_LINE_DD6 EP93XX_GPIO_LINE_H(6) +#define EP93XX_GPIO_LINE_DD7 EP93XX_GPIO_LINE_H(7) + + +#endif Index: linux-2.6.16/include/asm-arm/irq.h =================================================================== --- linux-2.6.16.orig/include/asm-arm/irq.h +++ linux-2.6.16/include/asm-arm/irq.h @@ -41,6 +41,7 @@ extern void enable_irq(unsigned int); #define IRQT_LOW (__IRQT_LOWLVL) #define IRQT_HIGH (__IRQT_HIGHLVL) #define IRQT_PROBE (1 << 4) +#define IRQT_DEBOUNCE (1 << 5) int set_irq_type(unsigned int irq, unsigned int type); void disable_irq_wake(unsigned int irq); Index: linux-2.6.16/drivers/serial/amba-pl010.c =================================================================== --- linux-2.6.16.orig/drivers/serial/amba-pl010.c +++ linux-2.6.16/drivers/serial/amba-pl010.c @@ -54,11 +54,8 @@ #include #include -#define UART_NR 2 - #define SERIAL_AMBA_MAJOR 204 #define SERIAL_AMBA_MINOR 16 -#define SERIAL_AMBA_NR UART_NR #define AMBA_ISR_PASS_LIMIT 256 @@ -93,8 +90,8 @@ * UART0 7 6 * UART1 5 4 */ -#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) -#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) +//#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +//#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) /* * We wrap our port structure around the generic uart_port. @@ -330,8 +327,8 @@ static void pl010_set_mctrl(struct uart_ else ctrls |= uap->dtr_mask; - __raw_writel(ctrls, SC_CTRLS); - __raw_writel(ctrlc, SC_CTRLC); +// __raw_writel(ctrls, SC_CTRLS); +// __raw_writel(ctrlc, SC_CTRLC); } static void pl010_break_ctl(struct uart_port *port, int break_state) @@ -556,13 +553,13 @@ static struct uart_ops amba_pl010_pops = .verify_port = pl010_verify_port, }; -static struct uart_amba_port amba_ports[UART_NR] = { +static struct uart_amba_port amba_ports[] = { { .port = { - .membase = (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE), - .mapbase = INTEGRATOR_UART0_BASE, + .membase = (void *)EP93XX_UART1_BASE, + .mapbase = EP93XX_APB_PHYS_BASE + 0x000c0000, .iotype = UPIO_MEM, - .irq = IRQ_UARTINT0, + .irq = IRQ_EP93XX_UART1, .uartclk = 14745600, .fifosize = 16, .ops = &amba_pl010_pops, @@ -574,10 +571,10 @@ static struct uart_amba_port amba_ports[ }, { .port = { - .membase = (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE), - .mapbase = INTEGRATOR_UART1_BASE, + .membase = (void *)EP93XX_UART2_BASE, + .mapbase = EP93XX_APB_PHYS_BASE + 0x000d0000, .iotype = UPIO_MEM, - .irq = IRQ_UARTINT1, + .irq = IRQ_EP93XX_UART2, .uartclk = 14745600, .fifosize = 16, .ops = &amba_pl010_pops, @@ -586,7 +583,22 @@ static struct uart_amba_port amba_ports[ }, .dtr_mask = 1 << 7, .rts_mask = 1 << 6, - } + }, + { + .port = { + .membase = (void *)EP93XX_UART3_BASE, + .mapbase = EP93XX_APB_PHYS_BASE + 0x000e0000, + .iotype = UPIO_MEM, + .irq = IRQ_EP93XX_UART3, + .uartclk = 14745600, + .fifosize = 16, + .ops = &amba_pl010_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 2, + }, + .dtr_mask = 1 << 3, + .rts_mask = 1 << 2, + }, }; #ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE @@ -669,7 +681,7 @@ static int __init pl010_console_setup(st * if so, search for the first available port that does have * console support. */ - if (co->index >= UART_NR) + if (co->index >= ARRAY_SIZE(amba_ports)) co->index = 0; port = &amba_ports[co->index].port; @@ -721,7 +733,7 @@ static struct uart_driver amba_reg = { .dev_name = "ttyAM", .major = SERIAL_AMBA_MAJOR, .minor = SERIAL_AMBA_MINOR, - .nr = UART_NR, + .nr = ARRAY_SIZE(amba_ports), .cons = AMBA_CONSOLE, }; @@ -729,7 +741,7 @@ static int pl010_probe(struct amba_devic { int i; - for (i = 0; i < UART_NR; i++) { + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) { if (amba_ports[i].port.mapbase != dev->res.start) continue; Index: linux-2.6.16/drivers/usb/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/usb/Kconfig +++ linux-2.6.16/drivers/usb/Kconfig @@ -22,6 +22,7 @@ config USB_ARCH_HAS_OHCI default y if ARCH_LH7A404 default y if ARCH_S3C2410 default y if PXA27x + default y if ARCH_EP93XX # PPC: default y if STB03xxx default y if PPC_MPC52xx Index: linux-2.6.16/drivers/usb/host/ohci-ep93xx.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/usb/host/ohci-ep93xx.c @@ -0,0 +1,229 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * + * Bus Glue for ep93xx. + * + * Written by Christopher Hoover + * Based on fragments of previous driver by Russell King et al. + * + * Modified for LH7A404 from ohci-sa1111.c + * by Durgesh Pattamatta + * + * Modified for pxa27x from ohci-lh7a404.c + * by Nick Bane 26-8-2004 + * + * Modified for ep93xx from ohci-pxa27x.c + * by Lennert Buytenhek 28-2-2006 + * Based on an earlier driver by Ray Lehtiniemi + * + * This file is licenced under the GPL. + */ + +#include +#include +#include + +#include +#include + +static void ep93xx_start_hc(struct device *dev) +{ + unsigned int clock_control; + + clock_control = __raw_readl(EP93XX_SYSCON_CLOCK_CONTROL); + clock_control |= EP93XX_SYSCON_CLOCK_USH_EN; + __raw_writel(clock_control, EP93XX_SYSCON_CLOCK_CONTROL); +} + +static void ep93xx_stop_hc(struct device *dev) +{ + unsigned int clock_control; + + clock_control = __raw_readl(EP93XX_SYSCON_CLOCK_CONTROL); + clock_control &= ~EP93XX_SYSCON_CLOCK_USH_EN; + __raw_writel(clock_control, EP93XX_SYSCON_CLOCK_CONTROL); +} + +int usb_hcd_ep93xx_probe(const struct hc_driver *driver, + struct platform_device *pdev) +{ + int retval; + struct usb_hcd *hcd; + + if (pdev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ"); + return -ENOMEM; + } + + hcd = usb_create_hcd(driver, &pdev->dev, "ep93xx"); + if (hcd == NULL) + return -ENOMEM; + + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + usb_put_hcd(hcd); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + pr_debug("ioremap failed"); + retval = -ENOMEM; + goto err2; + } + + ep93xx_start_hc(&pdev->dev); + + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, pdev->resource[1].start, SA_INTERRUPT); + if (retval == 0) + return retval; + + ep93xx_stop_hc(&pdev->dev); + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + + return retval; +} + +void usb_hcd_ep93xx_remove(struct usb_hcd *hcd, struct platform_device *pdev) +{ + usb_remove_hcd(hcd); + ep93xx_stop_hc(&pdev->dev); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +} + +static int __devinit ohci_ep93xx_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + if ((ret = ohci_run(ohci)) < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + + return 0; +} + +static struct hc_driver ohci_ep93xx_hc_driver = { + .description = hcd_name, + .product_desc = "EP93xx OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + .start = ohci_ep93xx_start, + .stop = ohci_stop, + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + .get_frame_number = ohci_get_frame, + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +extern int usb_disabled(void); + +static int ohci_hcd_ep93xx_drv_probe(struct platform_device *pdev) +{ + int ret; + + ret = -ENODEV; + if (!usb_disabled()) + ret = usb_hcd_ep93xx_probe(&ohci_ep93xx_hc_driver, pdev); + + return ret; +} + +static int ohci_hcd_ep93xx_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_hcd_ep93xx_remove(hcd, pdev); + + return 0; +} + +#ifdef CONFIG_PM +static int ohci_hcd_ep93xx_drv_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ochi_hcd *ohci = hcd_to_ohci(hcd); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + ep93xx_stop_hc(&pdev->dev); + hcd->state = HC_STATE_SUSPENDED; + pdev->dev.power.power_state = PMSG_SUSPEND; + + return 0; +} + +static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int status; + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + if ((status = ep93xx_start_hc(&pdev->dev)) < 0) + return status; + + pdev->dev.power.power_state = PMSG_ON; + usb_hcd_resume_root_hub(hcd); + + return 0; +} +#endif + + +static struct platform_driver ohci_hcd_ep93xx_driver = { + .probe = ohci_hcd_ep93xx_drv_probe, + .remove = ohci_hcd_ep93xx_drv_remove, +#ifdef CONFIG_PM + .suspend = ohci_hcd_ep93xx_drv_suspend, + .resume = ohci_hcd_ep93xx_drv_resume, +#endif + .driver = { + .name = "ep93xx-ohci", + }, +}; + +static int __init ohci_hcd_ep93xx_init(void) +{ + return platform_driver_register(&ohci_hcd_ep93xx_driver); +} + +static void __exit ohci_hcd_ep93xx_cleanup(void) +{ + platform_driver_unregister(&ohci_hcd_ep93xx_driver); +} + +module_init(ohci_hcd_ep93xx_init); +module_exit(ohci_hcd_ep93xx_cleanup); Index: linux-2.6.16/drivers/usb/host/ohci-hcd.c =================================================================== --- linux-2.6.16.orig/drivers/usb/host/ohci-hcd.c +++ linux-2.6.16/drivers/usb/host/ohci-hcd.c @@ -897,6 +897,10 @@ MODULE_LICENSE ("GPL"); #include "ohci-pxa27x.c" #endif +#ifdef CONFIG_ARCH_EP93XX +#include "ohci-ep93xx.c" +#endif + #ifdef CONFIG_SOC_AU1X00 #include "ohci-au1xxx.c" #endif @@ -911,6 +915,7 @@ MODULE_LICENSE ("GPL"); || defined(CONFIG_ARCH_OMAP) \ || defined (CONFIG_ARCH_LH7A404) \ || defined (CONFIG_PXA27x) \ + || defined (CONFIG_ARCH_EP93XX) \ || defined (CONFIG_SOC_AU1X00) \ || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \ ) Index: linux-2.6.16/drivers/net/arm/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/net/arm/Kconfig +++ linux-2.6.16/drivers/net/arm/Kconfig @@ -31,3 +31,12 @@ config ARM_ETHERH help If you have an Acorn system with one of these network cards, you should say Y to this option if you wish to use it with Linux. + +config EP93XX_ETHERNET + tristate "EP93xx Ethernet support" + depends on NET_ETHERNET && ARM && ARCH_EP93XX + select CRC32 + select MII + help + This is a driver for the ethernet hardware included in EP93xx CPUs. + Say Y if you are building a kernel for EP93xx based devices. Index: linux-2.6.16/drivers/net/arm/Makefile =================================================================== --- linux-2.6.16.orig/drivers/net/arm/Makefile +++ linux-2.6.16/drivers/net/arm/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_ARM_AM79C961A) += am79c961a obj-$(CONFIG_ARM_ETHERH) += etherh.o obj-$(CONFIG_ARM_ETHER3) += ether3.o obj-$(CONFIG_ARM_ETHER1) += ether1.o +obj-$(CONFIG_EP93XX_ETHERNET) += ep93xx_eth_need_rewrite.o Index: linux-2.6.16/drivers/net/arm/ep93xx_eth_need_rewrite.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/net/arm/ep93xx_eth_need_rewrite.c @@ -0,0 +1,1417 @@ +/* + * ep93xx_eth.c + * Ethernet Device Driver for Cirrus Logic EP93xx. + * + * Copyright (C) 2003 by Cirrus Logic www.cirrus.com + * This software may be used and distributed according to the terms + * of the GNU Public License. + * + * This driver was written based on skeleton.c by Donald Becker and + * smc9194.c by Erik Stahlman. + * + * Getting a MAC address: + * + * Former versions of this driver got their MAC from I2C EEPROM or even used + * hardcoded ones. Unfortunately I had to remove the board dependant I2C stuff, + * and use a random generated MAC instead of the hardcoded one. Good news is + * the there is support for setting the MAC from userspace now. (see below) + * + * first consider some potential problems if you use this random generated MAC: + * + * you can no longer count on it to be really unique + * identifying a particular board over network will be difficult + * DHCP servers can no longer use the MAC for assigning static IPs + * DHCP servers with long leasetimes quickly run out of leases + * ... + * + * So how can you set a valid MAC from userspace then? + * + * Let's say you've just bought your OUI from IEEE and it's "aa:bb:cc" + * Now you'd like to assign the MAC for your EP93xx board with serial #5 + * MAC = OUI<<24 + serial number = aa:bb:cc:00:00:05 + * + * ifconfig eth0 hw ether aa:bb:cc:00:00:05 # first set the MAC + * ifconfig eth0 192.168.1.1 netmask 255.255.255.0 up # then set the IP + * + * Apart from hardcoding this lines in your startup scripts you could also use + * some userspace utility to read (and set) the MAC from eeprom, flash, ... + * + * History: + * 07/19/01 0.1 Sungwook Kim initial release + * 10/16/01 0.2 Sungwook Kim add workaround for ignorance of Tx request + * while sending frame + * add some error stuations handling + * + * 03/25/03 Melody Lee Modified for EP93xx + * + * 2004/2005 Michael Burian porting to linux-2.6 + * 2005-10-12 Michael Burian fix problems when setting MAC with ifconfig + * 2005-10-30 Michael Burian cleanups, ethtool support + */ + +/* TODO: + * 1. try if it's possible to use skbuff directly for RX/TX (avoid memcpy) + * 2. use kzalloc instead of kmalloc+memset + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ep93xx_eth_need_rewrite.h" + +#define DRV_NAME "ep93xx_eth" +#define DRV_VERSION "2.8" +#define DRV_DATE "2005-11-08" + +static const char *version = DRV_NAME ": version " DRV_VERSION " " DRV_DATE \ + " Cirrus Logic\n"; + +/* total number of device instance, 0 means the 1st instance. */ +static int num_of_instance; +static struct net_device * ep93xx_etherdev; + +/* + * A List of default device port configuration for auto probing. + * At this time, the CPU has only one Ethernet device, + * but better to support multiple device configuration. + * Keep in mind that the array must end in zero. + */ + +/* We get the MAC_BASE from include/asm-arm/arch-ep93xx/regmap.h */ +static struct { + unsigned int base_addr; /* base address, (0:end mark) */ + int irq; /* IRQ number, (0:auto detect) */ +} port_list[] __initdata = { + { EP93XX_ETHERNET_BASE, IRQ_EP93XX_ETHERNET }, + { 0 /* end mark */ , 0 }, +}; + +/* + * Some definitions belong to the operation of this driver. + * You should understand how it affect to driver before any modification. + */ + +/* Interrupt Sources in Use */ +#define DEF_INT_SRC (IntEn_TxSQIE|IntEn_RxEOFIE|IntEn_RxEOBIE|IntEn_RxHDRIE) + +/* + * Length of Device Queue in number of entries + * (must be less than or equal to 255) + */ + +/* length of Rx Descriptor Queue (4 or bigger) Must be power of 2. */ +#define LQRXD 64 +#define LQRXS LQRXD /* length of Rx Status Queue */ + +/* length of Tx Descriptor Queue (4 or bigger) Must be power of 2. */ +#define LQTXD 8 +#define LQTXS LQTXD /* length of Tx Status Queue */ + +/* Tx Queue fill-up level control */ +#define LVL_TXSTOP LQTXD - 2 /* level to ask the stack to stop Tx */ +#define LVL_TXRESUME 2 /* level to ask the stack to resume Tx */ + +/* Rx Buffer length in byte */ +#define LRXB (1518+2+16) /* length of Rx buf, must be 4-byte aligned */ +#define LTXB LRXB + +#define EP93XX_DEF_MSG (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR | \ + NETIF_MSG_HW) + +#define DBG(lvl,msg,args...) do { printk(lvl"%s:%d: %s(): " msg, __FILE__, \ + __LINE__, __FUNCTION__, ## args); } while (0) + +/* + * Custom Data Structures + */ + +/* + * the information about the buffer passed to device. + * there are matching bufdsc informations + * for each Tx/Rx Descriptor Queue entry to trace + * the buffer within those queues. + */ +struct bufdsc { + /* virtual address representing the buffer passed to device */ + void *vaddr; + /* free routine */ + int (*free_rout) (void *buf); +}; + +/* device private information */ +struct ep93xx_priv { + /* static device information */ + int id; /* device instance ID */ + /* pointers to various queues (virtual address) */ + struct rx_dsc *rdq; /* Rx Descriptor Queue */ + struct rx_sts *rsq; /* Rx Status Queue */ + struct tx_dsc *tdq; /* Tx Descriptor Queue */ + struct tx_sts *tsq; /* Tx Status Queue */ + unsigned char *rxbuf; /* base of Rx Buffer pool */ + unsigned char *txbuf; /* base of Tx Buffer pool */ + struct bufdsc *rxbd; /* Rx Buffers info */ + struct bufdsc *txbd; /* Tx Buffers info */ + /* physical addresses of the same queues */ + dma_addr_t p_qbase; /* base */ + dma_addr_t p_rdq; /* Rx Descriptor Queue */ + dma_addr_t p_rsq; /* Rx Status Queue */ + dma_addr_t p_tdq; /* Tx Descriptor Queue */ + dma_addr_t p_tsq; /* Tx Status Queue */ + dma_addr_t p_rxbuf; /* Rx Buffer pool */ + dma_addr_t p_txbuf; /* Tx Buffer pool */ + /* MII Bus ID of Ethernet PHY */ + struct mii_if_info mii; + /* lock for mii when using ioctls */ + spinlock_t mii_lock; + /* dynamic information, subject to clear when device open */ + struct net_device_stats stats; /* statistic data */ + /* next processing index of device queues */ + int idx_rdq; + int idx_rsq; + int idx_tdqhead; + int idx_tdqtail; + int idx_tsq; + void __iomem *base_addr; /* base address */ + u32 msg_enable; + int regs_len; +}; + +/* + * Internal Routines + */ + +static inline unsigned int next_index(unsigned int idx_cur, unsigned int len) +{ + return (idx_cur + 1) % len; /* next array index */ +} + +static inline u32 _rdl(struct net_device *dev, u32 reg) +{ + return readl(((struct ep93xx_priv *)(netdev_priv(dev)))->base_addr + reg); +} + +static inline void _wrl(struct net_device *dev, u32 val, u32 reg) +{ + writel(val, ((struct ep93xx_priv *)(netdev_priv(dev)))->base_addr + reg); +} + +static inline void _wrw(struct net_device *dev, u16 val, u32 reg) +{ + writew(val, ((struct ep93xx_priv *)(netdev_priv(dev)))->base_addr + reg); +} + +static inline void _wrb(struct net_device *dev, u8 val, u32 reg) +{ + writeb(val, ((struct ep93xx_priv *)(netdev_priv(dev)))->base_addr + reg); +} + +/** + * wait_on_reg() + */ +static int wait_on_reg(struct net_device *dev, int reg, unsigned long mask, unsigned long expect) +{ + int i, dt; + + for (i = 0; i < 10000; i++) { + dt = _rdl(dev, reg); + dt = (dt ^ expect) & mask; + if (0 == dt) + break; + } + return dt; +} + +/** + * mdio_write() + */ +static void mdio_write(struct net_device *dev, int idPhy, int reg, int dt) +{ + wait_on_reg(dev, REG_MIISts, MIISts_Busy, ~MIISts_Busy); + _wrl(dev, dt, REG_MIIData); + _wrl(dev, MIICmd_OP_WR | ((idPhy & 0x1f) << 5) | ((reg & 0x1f) << 0), REG_MIICmd); +} + +/** + * mdio_read() + */ +static int mdio_read(struct net_device *dev, int idPhy, int reg) +{ + int dt; + + wait_on_reg(dev, REG_MIISts, MIISts_Busy, ~MIISts_Busy); + _wrl(dev, MIICmd_OP_RD | ((idPhy & 0x1f) << 5) | ((reg & 0x1f) << 0), REG_MIICmd); + + wait_on_reg(dev, REG_MIISts, MIISts_Busy, ~MIISts_Busy); + dt = _rdl(dev, REG_MIIData); + + return dt & 0xffff; +} + +/** + * phy_init() + */ +static void phy_init(struct net_device *dev) +{ + + u32 oldval; + struct ep93xx_priv *priv = netdev_priv(dev); + + oldval = _rdl(dev, REG_SelfCTL); + +#if 0 + /* Set MDC clock to be divided by 8 and disable PreambleSuppress bit */ + _wrl(dev, 0x0e00, REG_SelfCTL); +#else + /* Set MDC clock to be divided by 32 and disable PreambleSuppress bit */ + _wrl(dev, 0x6200, REG_SelfCTL); +#endif + + if (mii_link_ok(&(priv->mii))) + mii_check_media(&(priv->mii), netif_msg_link(priv), 1); + + /* restore the old value */ + _wrl(dev, oldval, REG_SelfCTL); +} + +/** + * devQue_start() + * + * make descriptor queues active + * allocate queue entries if needed + * and set device registers up to make it operational + * assume device has been initialized + */ +static int devQue_start(struct net_device *dev) +{ + int err; + int i; + void *buf; + u32 phy_addr; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* turn off device bus mastering */ + _wrl(dev, BMCtl_RxDis | BMCtl_TxDis | _rdl(dev, REG_BMCtl), REG_BMCtl); + err = wait_on_reg(dev, REG_BMSts, BMSts_TxAct, ~BMSts_TxAct); + err |= wait_on_reg(dev, REG_BMSts, BMSts_RxAct, ~BMSts_RxAct); + if (err && netif_msg_hw(priv)) + DBG(KERN_ERR, "%s: BM does not stop\n", dev->name); + + /* Tx Status Queue */ + memset(priv->tsq, 0, sizeof(priv->tsq[0]) * LQTXS); + priv->idx_tsq = 0; + _wrl(dev, priv->p_tsq, REG_TxSBA); + _wrl(dev, priv->p_tsq, REG_TxSCA); + _wrw(dev, sizeof(priv->tsq[0]) * LQTXS, REG_TxSBL); + _wrw(dev, sizeof(priv->tsq[0]) * LQTXS, REG_TxSCL); + + /* Tx Descriptor Queue */ + memset(priv->tdq, 0, sizeof(priv->tdq[0]) * LQTXD); + priv->idx_tdqhead = priv->idx_tdqtail = 0; + _wrl(dev, priv->p_tdq, REG_TxDBA); + _wrl(dev, priv->p_tdq, REG_TxDCA); + _wrw(dev, sizeof(priv->tdq[0]) * LQTXD, REG_TxDBL); + _wrw(dev, sizeof(priv->tdq[0]) * LQTXD, REG_TxDCL); + + /* Rx Status Queue */ + memset(priv->rsq, 0, sizeof(priv->rsq[0]) * LQRXS); + priv->idx_rsq = 0; + _wrl(dev, priv->p_rsq, REG_RxSBA); + _wrl(dev, priv->p_rsq, REG_RxSCA); + _wrw(dev, sizeof(priv->rsq[0]) * LQRXS, REG_RxSBL); + _wrw(dev, sizeof(priv->rsq[0]) * LQRXS, REG_RxSCL); + + /* Rx Descriptor Queue */ + memset(priv->rdq, 0, sizeof(priv->rdq[0]) * LQRXD); + phy_addr = priv->p_rxbuf; + for (i = 0; i < LQRXD; i++) { + priv->rdq[i].bi = i; /* index */ + priv->rdq[i].ba = phy_addr; /* physical address */ + priv->rdq[i].bl = LRXB; /* length */ + phy_addr += LRXB; + } + priv->idx_rdq = 0; + _wrl(dev, priv->p_rdq, REG_RxDBA); + _wrl(dev, priv->p_rdq, REG_RxDCA); + _wrw(dev, sizeof(priv->rdq[0]) * LQRXD, REG_RxDBL); + _wrw(dev, sizeof(priv->rdq[0]) * LQRXD, REG_RxDCL); + + /* init Rx Buffer Descriptors */ + buf = priv->rxbuf; + for (i = 0; i < LQRXD; i++) { + priv->rxbd[i].vaddr = buf; + priv->rxbd[i].free_rout = NULL; + buf += LRXB; + } + + /* init Tx Buffer Descriptors */ + memset(priv->txbd, 0x0, sizeof(*priv->txbd) * LQTXD); + + buf = priv->txbuf; + for (i = 0; i < LQTXD; i++) { + priv->txbd[i].vaddr = buf; + priv->txbd[i].free_rout = NULL; + buf += LTXB; + } + + /* turn on device bus mastering */ + _wrl(dev, BMCtl_TxEn | BMCtl_RxEn | _rdl(dev, REG_BMCtl), REG_BMCtl); + err = wait_on_reg(dev, REG_BMSts, BMSts_TxAct | BMSts_TxAct, BMSts_TxAct | BMSts_TxAct); + if (err && netif_msg_hw(priv)) + DBG(KERN_ERR, "%s: BM does not start\n", dev->name); + + /* Enqueue whole entries; this must be done after BM activation */ + _wrl(dev, LQRXS, REG_RxSEQ); /* Rx Status Queue */ + _wrl(dev, LQRXD, REG_RxDEQ); /* Rx Desc. queue */ + + return 0; +} + +/** + * devQue_init() + * init device descriptor queues at system level + * device access is not recommended at this point + * + */ +static int devQue_init(struct net_device *dev) +{ + void *buf; + void *tmp; + int size, size2; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* verify device Tx/Rx Descriptor/Status Queue data size */ + if (8 != sizeof(struct rx_dsc)) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "sizeof rx_dsc != 8 bytes!\n"); + return -ENOMEM; + } else if (8 != sizeof(struct rx_sts)) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "sizeof rx_sts != 8 bytes!\n"); + return -ENOMEM; + } else if (8 != sizeof(struct tx_dsc)) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "sizeof tx_dsc != 8 bytes!\n"); + return -ENOMEM; + } else if (4 != sizeof(struct tx_sts)) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "sizeof tx_sts != 4 bytes!\n"); + return -ENOMEM; + } + + /* + allocate kernel memory for whole queues + best if non-cached memory block due to DMA access by the device + if CPU doesn't have bus snooping + */ + size = sizeof(struct rx_dsc) * (LQRXD + 1) + + sizeof(struct rx_sts) * (LQRXS + 1) + + sizeof(struct tx_dsc) * (LQTXD + 1) + + sizeof(struct tx_sts) * (LQTXS + 1) + sizeof(unsigned long) * 4; + + buf = tmp = dma_alloc_coherent(NULL, size, &priv->p_qbase, GFP_KERNEL | GFP_DMA); + if (!buf) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "no memory for queue\n"); + return -ENOMEM; + } + + /* + * assign memory to each queue + */ + priv->rdq = buf; + buf = buf + sizeof(struct rx_dsc) * (LQRXD + 1); + priv->rsq = buf; + buf = buf + sizeof(struct rx_sts) * (LQRXS + 1); + priv->tdq = buf; + buf = buf + sizeof(struct tx_dsc) * (LQTXD + 1); + priv->tsq = buf; + buf = buf + sizeof(struct tx_sts) * (LQTXS + 1); + + /* + * store physical address of each queue + */ + priv->p_rdq = priv->p_qbase; + priv->p_rsq = priv->p_rdq + ((u32) priv->rsq - (u32) priv->rdq); + priv->p_tdq = priv->p_rdq + ((u32) priv->tdq - (u32) priv->rdq); + priv->p_tsq = priv->p_rdq + ((u32) priv->tsq - (u32) priv->rdq); + + /* + * init queue entries + */ + memset(priv->rdq, 0, sizeof(struct rx_dsc) * LQRXD); + memset(priv->rsq, 0, sizeof(struct rx_sts) * LQRXS); + memset(priv->tdq, 0, sizeof(struct tx_dsc) * LQTXD); + memset(priv->tsq, 0, sizeof(struct tx_sts) * LQTXS); + + /* Allocate Rx Buffer + (We might need to copy from Rx buf to skbuff in whatever case, + because device bus master requires 32bit aligned Rx buffer address + but Linux network stack requires odd 16bit aligned Rx buf address) */ + priv->rxbuf = dma_alloc_coherent(NULL, LRXB * LQRXD, &priv->p_rxbuf, GFP_KERNEL | GFP_DMA); + + if (!priv->rxbuf) { + priv->rxbuf = NULL; + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "no memory for RxBuf\n"); + goto err_free_qbase_1; + } + + /* Allocate Tx Buffer */ + priv->txbuf = dma_alloc_coherent(NULL, LTXB * LQTXD, &priv->p_txbuf, GFP_KERNEL | GFP_DMA); + + if (!priv->txbuf) { + priv->txbuf = NULL; + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "no memory for TxBuf\n"); + goto err_free_rxbuf_2; + } + + /* + * allocate kernel memory for buffer descriptors + */ + size2 = sizeof(struct bufdsc) * (LQRXD + LQTXD); + buf = kmalloc(size2, GFP_KERNEL); + if (!buf) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "no memory for buf desc\n"); + goto err_free_txbuf_3; + } + memset(buf, 0x0, size2); /* clear with 0 */ + priv->rxbd = buf; + priv->txbd = buf + sizeof(struct bufdsc) * LQRXD; + + return 0; + +err_free_txbuf_3: + dma_free_coherent(NULL, LTXB * LQTXD, priv->txbuf, priv->p_txbuf); +err_free_rxbuf_2: + dma_free_coherent(NULL, LRXB * LQRXD, priv->rxbuf, priv->p_rxbuf); +err_free_qbase_1: + dma_free_coherent(NULL, size, tmp, priv->p_qbase); + return -ENOMEM; +} + +/** + * devQue_cleanup() + * Release queue, Tx buffers and Rx buffers memory + * Only call after unregister_netdev + */ +static void devQue_cleanup(struct net_device *dev) +{ + int size; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* descriptor queues size */ + size = sizeof(struct rx_dsc) * (LQRXD + 1) + + sizeof(struct rx_sts) * (LQRXS + 1) + + sizeof(struct tx_dsc) * (LQTXD + 1) + + sizeof(struct tx_sts) * (LQTXS + 1) + sizeof(unsigned long) * 4; + + dma_free_coherent(NULL, size, priv->rdq, priv->p_qbase); + dma_free_coherent(NULL, LTXB * LQTXD, priv->txbuf, priv->p_txbuf); + dma_free_coherent(NULL, LRXB * LQRXD, priv->rxbuf, priv->p_rxbuf); + kfree(priv->rxbd); + +} + +/** + * set_multicast_tbl() + */ +static void set_multicast_tbl(struct net_device *dev, u8 *buf) +{ + int i; + unsigned char position; + struct dev_mc_list *cur_addr; + + memset(buf, 0x00, 8); + + cur_addr = dev->mc_list; + for (i = 0; i < dev->mc_count; i++, cur_addr = cur_addr->next) { + + if (!cur_addr) + break; + if (!(*cur_addr->dmi_addr & 1)) + continue; /* make sure multicast addr */ + position = ether_crc_le(6, cur_addr->dmi_addr) >> 26; + buf[position >> 3] |= 1 << (position & 0x07); + } +} + +/** + * ind_addr_wr() + */ +static int ind_addr_wr(struct net_device *dev, int afp, char *buf) +{ + u32 rxctl; + int i, len; + struct ep93xx_priv *priv = netdev_priv(dev); + afp &= 0x07; + if (4 == afp || 5 == afp) { + if (netif_msg_hw(priv)) + DBG(KERN_ERR, "invalid afp value\n"); + return -1; + } + len = (AFP_AFP_HASH == afp) ? 8 : 6; + + rxctl = _rdl(dev, REG_RxCTL); /* turn Rx off */ + _wrl(dev, ~RxCTL_SRxON & rxctl, REG_RxCTL); + _wrl(dev, afp, REG_AFP); /* load new address pattern */ + for (i = 0; i < len; i++) + _wrb(dev, buf[i], REG_IndAD + i); + _wrl(dev, rxctl, REG_RxCTL); /* turn Rx back */ + + return 0; +} + +/** + * rx_ctl() + */ +static int rx_ctl(struct net_device *dev, int sw) +{ + unsigned long tmp = _rdl(dev, REG_RxCTL); + + /* + * Workaround for MAC lost 60-byte-long frames: + * must enable Runt_CRC_Accept bit + */ + if (sw) + _wrl(dev, tmp | RxCTL_SRxON | RxCTL_RCRCA, REG_RxCTL); + else + _wrl(dev, tmp & ~RxCTL_SRxON, REG_RxCTL); + + return 0; +} + +/** + * chk_tx_lvl() + */ +static void chk_tx_lvl(struct net_device *dev) +{ + int filled; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* check Tx Descriptor Queue fill-up level */ + filled = priv->idx_tdqhead - priv->idx_tdqtail; + if (filled < 0) + filled += LQTXD; + + if (filled <= (LVL_TXRESUME + 1)) + netif_wake_queue(dev); +} + +/** + * cleanup_tx() + */ +static void cleanup_tx(struct net_device *dev) +{ + struct tx_sts *txsts; + int idxsts, bi; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* + * process Tx Status Queue (no need to limit processing of TxStatus + * Queue because each queue entry consist of 1 dword) + */ + while (priv->tsq[priv->idx_tsq].flags & TXSTS_TXFP) { + idxsts = priv->idx_tsq; + priv->idx_tsq = next_index(priv->idx_tsq, LQTXS); + txsts = &priv->tsq[idxsts]; + if (!(txsts->flags & TXSTS_TXFP)) { /* empty? */ + if (netif_msg_tx_err(priv)) + DBG(KERN_ERR, "QueTxSts is empty\n"); + return; + } + txsts->flags &= ~TXSTS_TXFP; /* mark processed */ + + bi = txsts->bi & TXSTS_BI; /* buffer index */ + + /* statistics collection */ + if (txsts->flags & TXSTS_TXWE) { /* Sent without error */ + priv->stats.tx_packets++; + priv->stats.tx_bytes += ((struct tx_dsc *)(priv->txbd[bi].vaddr))->bl_af & TXDSC_BL; + } else { /* Tx failed due to error */ + if (netif_msg_tx_err(priv)) + DBG(KERN_ERR, "Tx failed QueTxSts"); + priv->stats.tx_errors++; + if (txsts->flags & TXSTS_LCRS) + priv->stats.tx_carrier_errors++; + if (txsts->flags & TXSTS_TXU) + priv->stats.tx_fifo_errors++; + if (txsts->flags & TXSTS_ECOLL) + priv->stats.collisions++; + } + + /* free Tx buffer */ + if (priv->txbd[bi].free_rout) { + (*priv->txbd[bi].free_rout)(priv->txbd[bi].vaddr); + priv->txbd[bi].free_rout = NULL; + } + + /* ahead Tx Descriptor Queue tail index */ + priv->idx_tdqtail = next_index(priv->idx_tdqtail, LQTXD); + } +} + +/** + * restart_tx() + */ +static int restart_tx(struct net_device *dev) +{ + int i; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* disable int */ + + /* turn off master INT control */ + _wrl(dev, _rdl(dev, REG_GIntMsk) & ~GIntMsk_IntEn, REG_GIntMsk); + + /* stop Tx and disable Tx DMA */ + _wrl(dev, _rdl(dev, REG_TxCTL) & ~TxCTL_STxON, REG_TxCTL); + _wrl(dev, _rdl(dev, REG_BMCtl) | BMCtl_TxDis, REG_BMCtl); + + /* reset Tx DMA */ + _wrl(dev, BMCtl_TxChR | _rdl(dev, REG_BMCtl), REG_BMCtl); + + /* release Tx buffers */ + for (i = 0; i < LQTXD; i++) { + if (priv->txbd[i].free_rout) { + priv->txbd[i].free_rout(priv->txbd[i].vaddr); + priv->txbd[i].free_rout = NULL; + } + priv->stats.tx_dropped++; + } + + /* init Tx Queues and flush cache */ + memset(priv->tsq, 0, sizeof(priv->tsq[0]) * LQTXS); + + /* init variables */ + priv->idx_tsq = priv->idx_tdqhead = priv->idx_tdqtail = 0; + + /* init registers */ + wait_on_reg(dev, REG_BMSts, BMCtl_TxChR, ~BMCtl_TxChR); + _wrl(dev, priv->p_tsq, REG_TxSBA); + _wrl(dev, priv->p_tsq, REG_TxSCA); + _wrw(dev, sizeof(priv->tsq[0]) * LQTXS, REG_TxSBL); + _wrw(dev, sizeof(priv->tsq[0]) * LQTXS, REG_TxSCL); + _wrl(dev, priv->p_tdq, REG_TxDBA); + _wrl(dev, priv->p_tdq, REG_TxDCA); + _wrw(dev, sizeof(priv->tdq[0]) * LQTXD, REG_TxDBL); + _wrw(dev, sizeof(priv->tdq[0]) * LQTXD, REG_TxDCL); + + /* start Tx and enable Tx DMA */ + _wrl(dev, _rdl(dev, REG_TxCTL) | TxCTL_STxON, REG_TxCTL); + _wrl(dev, _rdl(dev, REG_BMCtl) | BMCtl_TxEn, REG_BMCtl); + + /* enable int again */ + _wrl(dev, _rdl(dev, REG_GIntMsk) | GIntMsk_IntEn, REG_GIntMsk); + + return 0; +} + +/** + * reset() + */ +static void reset(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + /* soft reset command */ + _wrb(dev, SelfCTL_RESET, REG_SelfCTL); + if (wait_on_reg(dev, REG_SelfCTL, SelfCTL_RESET, ~SelfCTL_RESET)) + if (netif_msg_drv(priv)) + DBG(KERN_WARNING, "Soft Reset does not self-clear\n"); +} + +/** + * eth_shutdown()- closes down the Ethernet module + * + * Make sure to: + * 1. disable all interrupt mask + * 2. disable Rx + * 3. disable Tx + * + * TODO: + * (1) maybe utilize power down mode. + * Why not yet? Because while the chip will go into power down mode, + * the manual says that it will wake up in response to any I/O requests + * in the register space. Empirical results do not show this working. + */ +static int eth_shutdown(struct net_device *dev) +{ + reset(dev); + return 0; +} + +/** + * eth_init() - Reset and initialize the device. + * + * Device should be initialized enough to function in polling mode. + * Tx and Rx must be disabled and no INT generation. + */ +static int eth_init(struct net_device *dev) +{ + /* reset device */ + reset(dev); + + /* init PHY */ + phy_init(dev); + + /* init MAC */ + + /*i Set MDC clock to be divided by 8 and enable PreambleSuppress bit */ + _wrl(dev, 0x0f00, REG_SelfCTL); + /* mask Interrupt */ + _wrl(dev, 0x00, REG_GIntMsk); + /* no Rx on at this point */ + _wrl(dev, RxCTL_BA | RxCTL_IA0, REG_RxCTL); + _wrl(dev, 0x00, REG_TxCTL); + _wrl(dev, 0x00, REG_GT); + _wrl(dev, 0x00, REG_BMCtl); + /* Buffer Threshold */ + _wrl(dev, (0x80 << 16) | (0x40 << 0), REG_RxBTH); + _wrl(dev, (0x80 << 16) | (0x40 << 0), REG_TxBTH); + /* Status Threshold */ + _wrl(dev, (4 << 16) | (2 << 0), REG_RxSTH); + _wrl(dev, (4 << 16) | (2 << 0), REG_TxSTH); + /* Descriptor Threshold */ + _wrl(dev, (4 << 16) | (2 << 0), REG_RxDTH); + _wrl(dev, (4 << 16) | (2 << 0), REG_TxDTH); + /* Max Frame Length & Tx Start Threshold */ + _wrl(dev, ((1518 + 1) << 16) | (944 << 0), REG_MaxFL); + + _rdl(dev, REG_TxCollCnt); /* clear Tx Collision Count */ + _rdl(dev, REG_RxMissCnt); /* clear Rx Miss Counter */ + _rdl(dev, REG_RxRntCnt); /* clear Rx Runt Counter */ + + /* clear Pending INT */ + _rdl(dev, REG_IntStsC); + /* Tx on */ + _wrl(dev, TxCTL_STxON | _rdl(dev, REG_TxCTL), REG_TxCTL); + + /* Set MAC address */ + ind_addr_wr(dev, AFP_AFP_IA0, &dev->dev_addr[0]); + + /* init queue */ + devQue_start(dev); + + return 0; +} + +/** + * rx_isr() - Receive Interrupt Service Routine + */ +static void rx_isr(struct net_device *dev) +{ + struct rx_sts *rxsts; + /* index of Rx Status Queue Head from device (next put point) */ + int idx_rsqhead; + int idxsts; + int cnt_sts_processed, cnt_desc_processed; + char *dest; + struct sk_buff *skb; + int len; + unsigned int dt; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* get Current Rx Status Queue pointer */ + dt = _rdl(dev, REG_RxSCA); + + /* convert to array index */ + idx_rsqhead = (dt - priv->p_rsq) / sizeof(priv->rsq[0]); + if (!(0 <= idx_rsqhead && idx_rsqhead < LQRXS)) { + if (netif_msg_rx_err(priv)) + DBG(KERN_ERR, " invalid REG_RxSCA\n"); + return; + } + + /* process Rx (limit to idx_rsqhead due to cache) */ + cnt_sts_processed = cnt_desc_processed = 0; + while (idx_rsqhead != priv->idx_rsq) { + idxsts = priv->idx_rsq; + priv->idx_rsq = next_index(priv->idx_rsq, LQRXS); + rxsts = &priv->rsq[idxsts]; + if (!(rxsts->w1 & RXSTS_RFP)) { /* empty? */ + if (netif_msg_rx_err(priv)) + DBG(KERN_ERR, "QueRxSts empty\n"); + return; + } + rxsts->w1 &= ~RXSTS_RFP; /* mark processed */ + + cnt_sts_processed++; + + if (!(rxsts->w1 & RXSTS_EOB)) /* buffer has no data */ + continue; + + if ((rxsts->bi & RXSTS_BI) != priv->idx_rdq) { + if (netif_msg_rx_err(priv)) + DBG(KERN_ERR, "unmatching idx_rdq\n"); + continue; + } + priv->idx_rdq = next_index(priv->idx_rdq, LQRXD); + cnt_desc_processed++; + + /* received a frame with error */ + if (!((rxsts->w1 & RXSTS_EOF) && (rxsts->w1 & RXSTS_RWE))) { + if (netif_msg_rx_err(priv)) + DBG(KERN_WARNING, "Rx error RxSts\n"); + priv->stats.rx_errors++; + if (rxsts->w1 & RXSTS_OE) + priv->stats.rx_fifo_errors++; + if (rxsts->w1 & RXSTS_FE) + priv->stats.rx_frame_errors++; + if ((rxsts->w1 & RXSTS_RUNT) || (rxsts->w1 & RXSTS_EDATA)) + priv->stats.rx_length_errors++; + if (rxsts->w1 & RXSTS_CRCE) + priv->stats.rx_crc_errors++; + continue; + } + + len = rxsts->fl; + + /* alloc buffer for protocal stack */ + skb = dev_alloc_skb(len + 5); + if (NULL == skb) { + if (netif_msg_rx_err(priv)) + DBG(KERN_ERR, "Low Memory, Rx dropped\n"); + priv->stats.rx_dropped++; + continue; + } + + /* odd 16 bit alignment to make protocal stack happy */ + skb_reserve(skb, 2); + skb->dev = dev; + dest = skb_put(skb, len); + memcpy(dest, priv->rxbd[(rxsts->bi & RXSTS_BI)].vaddr, len); + skb->protocol = eth_type_trans(skb, dev); + /* pass Rx packet to system */ + netif_rx(skb); + dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += len; + if (RXSTS_AM == (rxsts->w1 & RXSTS_AM)) + priv->stats.multicast++; + } + + /* enqueue */ + _wrl(dev, cnt_sts_processed, REG_RxSEQ); + _wrl(dev, cnt_desc_processed, REG_RxDEQ); +} + +/** + * tx_isr() - Transmit Interrupt Service Routine + */ +static int tx_isr(struct net_device *dev) +{ + cleanup_tx(dev); + chk_tx_lvl(dev); /* resume Tx if it was stopped */ + return 0; +} + +/** + * ep93xx_isr() + */ +static irqreturn_t ep93xx_isr(int irq, void *dev_id, struct pt_regs *pRegs) +{ + struct net_device *dev = dev_id; + int lpCnt; + u32 intS; + + lpCnt = 0; + do { + /* get INT status and then clear */ + intS = _rdl(dev, REG_IntStsC); + + if (!intS) + break; /* no INT */ + if (IntSts_RxSQ & intS) + rx_isr(dev); /* Rx INT */ + if (IntSts_TxSQ & intS) + tx_isr(dev); /* Tx INT */ + } while (lpCnt++ < 64); /* limit loop to serve other interrupts too */ + return IRQ_HANDLED; +} + +/* Exposed Driver Routines to the Outside World */ + +/** + * ep93xx_get_stats() + */ +static struct net_device_stats *ep93xx_get_stats(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return &priv->stats; +} + +/** + * ep93xx_set_multicast_list() + */ +static void ep93xx_set_multicast_list(struct net_device *dev) +{ + u8 tbl[8 + 1]; + + if (IFF_PROMISC & dev->flags) { + _wrl(dev, RxCTL_PA | _rdl(dev, REG_RxCTL), REG_RxCTL); + + } else if (IFF_ALLMULTI & dev->flags) { /* receive all multicast addr */ + _wrl(dev, RxCTL_MA | (~RxCTL_PA & _rdl(dev, REG_RxCTL)), REG_RxCTL); + memset(tbl, 0xff, 8); + ind_addr_wr(dev, AFP_AFP_HASH, &tbl[0]); + + } else if (dev->mc_count) { /* set H/W multicasting filter */ + _wrl(dev, RxCTL_MA | (~RxCTL_PA & _rdl(dev, REG_RxCTL)), REG_RxCTL); + set_multicast_tbl(dev, &tbl[0]); + ind_addr_wr(dev, AFP_AFP_HASH, &tbl[0]); + + } else { /* no multicasting */ + _wrl(dev, ~(RxCTL_PA | RxCTL_MA) & _rdl(dev, REG_RxCTL), REG_RxCTL); + } +} + +/** + * ep93xx_tx_timeout() + */ +static void ep93xx_tx_timeout(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + if (netif_msg_tx_err(priv)) + DBG(KERN_WARNING, "transmit timed out\n"); + + phy_init(dev); + + /* kick Tx engine */ + restart_tx(dev); + + /* ask the Network Stack to resume Tx if there is room available */ + chk_tx_lvl(dev); +} + +/** + * ep93xx_start_xmit() + */ +static int ep93xx_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +/* @swk check H/W defect of Tx Underrun Error caused by certain frame length */ + struct tx_dsc *txdsc; + int idx_tdqhd; + int filled; + struct ep93xx_priv *priv = netdev_priv(dev); + + idx_tdqhd = priv->idx_tdqhead; + txdsc = &priv->tdq[idx_tdqhd]; + + /* check Tx Descriptor Queue fill-up level */ + filled = idx_tdqhd - priv->idx_tdqtail; + if (filled < 0) + filled += LQTXD; + filled += 1; + + /* check Queue level */ + if (LVL_TXSTOP <= filled) { + netif_stop_queue(dev); /* no more Tx allowed */ + if (netif_msg_tx_err(priv)) + DBG(KERN_INFO, "%s: Tx STOP requested\n", dev->name); + if (LVL_TXSTOP < filled) { + /* this situation can not be happen */ + if (netif_msg_tx_err(priv)) + DBG(KERN_ERR, "%s: Tx Request while stopped\n", dev->name); + return NETDEV_TX_BUSY; + } + } + + /* fill up Tx Descriptor Queue entry */ + if (skb->len < ETH_ZLEN) { + txdsc->bl_af = ETH_ZLEN & TXDSC_BL; /* also clears AF! */ + skb = skb_padto(skb, ETH_ZLEN); + if (skb == NULL) + return NETDEV_TX_OK; + } else { + txdsc->bl_af = skb->len & TXDSC_BL; /* also clears AF! */ + } + txdsc->ba = priv->p_txbuf + (idx_tdqhd * LTXB); + txdsc->bi_eof = (idx_tdqhd & TXDSC_BI) | TXDSC_EOF; + + dev->trans_start = jiffies; + + /* copy data to Tx buffer */ + memcpy(priv->txbd[idx_tdqhd].vaddr, skb->data, skb->len); + priv->txbd[idx_tdqhd].free_rout = NULL; + + /* Free the data buffer passed by upper layer */ + dev_kfree_skb_any(skb); + + /* ahead Tx Desc Queue */ + priv->idx_tdqhead = next_index(priv->idx_tdqhead, LQTXD); + wmb(); + + /* Enqueue a Tx Descriptor to the device */ + _wrl(dev, 1, REG_TxDEQ); + + if (netif_msg_tx_queued(priv)) + DBG(KERN_DEBUG, "%s: Tx packet queued\n", dev->name); + + return NETDEV_TX_OK; +} + +/** + * ep93xx_close() + * + * this makes the board clean up everything that it can + * and not talk to the outside world. Caused by + *. an 'ifconfig ethX down' + */ +static int ep93xx_close(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + free_irq(dev->irq, dev); + + netif_stop_queue(dev); + eth_shutdown(dev); + + if (netif_msg_ifdown(priv)) + DBG(KERN_INFO, "%s: ep93xx_close()\n", dev->name); + + return 0; +} + +/** + * ep93xx_open() - Open and Initialize the board + * + * Set up everything, reset the card, etc .. + */ +static int ep93xx_open(struct net_device *dev) +{ + int status; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* clear dynamic device info */ + memset(&priv->stats, 0, sizeof(priv->stats)); + priv->idx_rdq = 0; + priv->idx_rsq = 0; + priv->idx_tdqhead = 0; + priv->idx_tdqtail = 0; + priv->idx_tsq = 0; + + /* reset/init device */ + status = eth_init(dev); + if (status != 0) { + return -EAGAIN; + } + + /* turn on INT, turn on Rx */ + status = request_irq(dev->irq, &ep93xx_isr, 0, DRV_NAME, dev); + if (status) { + if (netif_msg_ifup(priv)) + DBG(KERN_ERR, "%s: unable to get IRQ\n", dev->name); + return status; + } + + /* Enable interrupt driven operation. Also turn on Rx but no Tx. */ + /* setup Interrupt sources */ + _wrl(dev, DEF_INT_SRC, REG_IntEn); + /* turn on INT */ + _wrl(dev, GIntMsk_IntEn, REG_GIntMsk); + /* turn on Rx */ + rx_ctl(dev, 1); + + /* link to upper layer */ + netif_start_queue(dev); + + if (netif_msg_ifup(priv)) + DBG(KERN_INFO, "%s: irq=%d\n", dev->name, dev->irq); + + return 0; +} + +static int ep93xx_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + int rc; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irq(&priv->mii_lock); + rc = generic_mii_ioctl(&priv->mii, if_mii(rq), cmd, NULL); + spin_unlock_irq(&priv->mii_lock); + + return rc; +} + +/* + * Ethtool support + */ + +static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); +} + +static int ep93xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + spin_lock_irq(&priv->mii_lock); + mii_ethtool_gset(&priv->mii, cmd); + spin_unlock_irq(&priv->mii_lock); + return 0; +} + +static int ep93xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + int rc; + spin_lock_irq(&priv->mii_lock); + rc = mii_ethtool_sset(&priv->mii, cmd); + spin_unlock_irq(&priv->mii_lock); + return rc; +} + +static int ep93xx_nway_reset(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return mii_nway_restart(&priv->mii); +} + +static u32 ep93xx_get_link(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return mii_link_ok(&priv->mii); +} + +static u32 ep93xx_get_msglevel(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void ep93xx_set_msglevel(struct net_device *dev, u32 datum) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + priv->msg_enable = datum; +} + +static void ep93xx_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + + spin_lock_irq(&priv->mii_lock); + memcpy_fromio(regbuf, priv->base_addr, regs->len); + spin_unlock_irq(&priv->mii_lock); +} + +static int ep93xx_get_regs_len(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return priv->regs_len; +} + +static struct ethtool_ops ep93xx_ethtool_ops = { + .get_drvinfo = ep93xx_get_drvinfo, + .get_settings = ep93xx_get_settings, + .set_settings = ep93xx_set_settings, + .get_regs = ep93xx_get_regs, + .get_regs_len = ep93xx_get_regs_len, + .nway_reset = ep93xx_nway_reset, + .get_link = ep93xx_get_link, + .get_msglevel = ep93xx_get_msglevel, + .set_msglevel = ep93xx_set_msglevel, +}; + +/** + * driver_init() - Logical driver initialization for an individual device + * + * Minimum device H/W access at this point + * + * Task: + * Initialize the structure if needed + * print out my vanity message if not done so already + * print out what type of hardware is detected + * print out the ethernet address + * find the IRQ + * set up my private data + * configure the dev structure with my subroutines + * actually GRAB the irq. + * GRAB the region + * + */ +static int __init driver_init(struct net_device *dev, u32 baseA, int irq) +{ + int i; + struct resource *res; + struct sockaddr sa; + struct ep93xx_priv *priv = netdev_priv(dev); + + if (0 == num_of_instance) + printk("%s", version); + + /* skip probing for a second one, we _know_ that it does not exist */ + if (1 == num_of_instance) + return -ENODEV; + + memset(dev->priv, 0x00, sizeof(struct ep93xx_priv)); + + /* device instance ID */ + priv->id = num_of_instance; + + /* mii stuff */ + spin_lock_init(&priv->mii_lock); + priv->mii.dev = dev; + priv->mii.mdio_read = mdio_read; + priv->mii.mdio_write = mdio_write; + priv->mii.phy_id_mask = 0x1f; + priv->mii.reg_num_mask = 0x1f; + priv->msg_enable = EP93XX_DEF_MSG; + priv->regs_len = DEV_REG_SPACE; + + priv->base_addr = (void *)(baseA); + + dev->irq = irq; + + res = request_mem_region(baseA, DEV_REG_SPACE, DRV_NAME); + if (res == NULL) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "request_mem_region failed!\n"); + goto err_free_priv_1; + } + + dev->open = &ep93xx_open; + dev->stop = &ep93xx_close; + dev->do_ioctl = &ep93xx_ioctl; + dev->hard_start_xmit = &ep93xx_start_xmit; + dev->tx_timeout = &ep93xx_tx_timeout; + dev->watchdog_timeo = HZ * 5; + dev->get_stats = &ep93xx_get_stats; + dev->set_multicast_list = &ep93xx_set_multicast_list; + dev->ethtool_ops = &ep93xx_ethtool_ops; + + ether_setup(dev); + + if (devQue_init(dev)) + goto err_free_bd_memregion_irq_2; + + reset(dev); + + /* + * use a random MAC for now - + * don't forget to set a valid MAC later on with ifconfig + */ + sa.sa_family = AF_INET; +// random_ether_addr(sa.sa_data); + memcpy(sa.sa_data, "\x00\xd0\x69\x40\x2b\x13", 6); + dev->set_mac_address(dev, &sa); + + if (netif_msg_probe(priv)) { + printk(KERN_INFO DRV_NAME ": #%d at 0x%p IRQ:%d\n", priv->id, priv->base_addr, dev->irq); + printk(KERN_INFO DRV_NAME ": using random number"); + for (i = 0; i < 6; i++) + printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); + + printk(" as MAC, don't forget to assign a valid MAC later!\n"); + } + num_of_instance++; + return 0; + +err_free_bd_memregion_irq_2: + kfree(priv->rxbd); + disable_irq(dev->irq); + free_irq(dev->irq, dev); + release_mem_region((u32)priv->base_addr, DEV_REG_SPACE); +err_free_priv_1: + kfree(dev->priv); + return -EAGAIN; +} + +/** + * ep93xx_probe - probe for ep93xx device + * + * Probe for the one (and only) ethernet device found on + * EP93xx SOC CPU. + */ +static int __init ep93xx_probe(void) +{ + int err; + struct ep93xx_priv *priv; + struct net_device *dev = alloc_etherdev(sizeof(struct ep93xx_priv)); + + if (!dev) + return -ENODEV; + + priv = netdev_priv(dev); + + SET_MODULE_OWNER(dev); + + sprintf(dev->name, "eth0"); + + priv->base_addr = (void *)(port_list[0].base_addr); + + dev->irq = port_list[0].irq; + + err = driver_init(dev, (u32)priv->base_addr, dev->irq); + if (err) + goto err_free_netdev; + + err = register_netdev(dev); + if (err) + goto err_free_memregion_irq_1; + + ep93xx_etherdev = dev; + disable_irq(dev->irq); + return 0; + +err_free_memregion_irq_1: + kfree(priv->rxbd); + disable_irq(dev->irq); + free_irq(dev->irq, dev); + release_mem_region((u32)priv->base_addr, DEV_REG_SPACE); +err_free_netdev: + free_netdev(dev); + return err; +} + +static void __exit ep93xx_exit(void) +{ + struct net_device *dev = ep93xx_etherdev; + struct ep93xx_priv *priv = netdev_priv(dev); + + if (dev) { + unregister_netdev(dev); + devQue_cleanup(dev); + free_irq(dev->irq, dev); + release_mem_region((u32)priv->base_addr, DEV_REG_SPACE); + free_netdev(dev); + } +} + +module_init(ep93xx_probe); +module_exit(ep93xx_exit); +MODULE_LICENSE("GPL"); Index: linux-2.6.16/drivers/net/arm/ep93xx_eth_need_rewrite.h =================================================================== --- /dev/null +++ linux-2.6.16/drivers/net/arm/ep93xx_eth_need_rewrite.h @@ -0,0 +1,355 @@ +/* + * ep93xx_eth.h + * : header file of Ethernet Device Driver for Cirrus Logic EP93xx. + * + * Copyright (C) 2003 by Cirrus Logic www.cirrus.com + * This software may be used and distributed according to the terms + * of the GNU Public License. + * + * This file contains device related information like register info + * and register access method macros for the Ethernet device + * embedded within Cirrus Logic's EP93xx SOC chip. + * + * Information contained in this file was obtained from + * the EP9312 Manual Revision 0.12 and 0.14 from Cirrus Logic. + * + * History + * 05/18/01 Sungwook Kim Initial release + * 03/25/2003 Melody Modified for EP92xx + */ + +#ifndef _EP93xx_ETH_H_ +#define _EP93xx_ETH_H_ + +/* + * Definition of the registers. + * For details, refer to the datasheet . + * + * Basically, most registers are 32 bits width register. + * But some are 16 bits and some are 6 or 8 bytes long. + */ + +#define REG_RxCTL 0x0000 /*offset to Receiver Control Reg */ +#define RxCTL_PauseA (1<<20) +#define RxCTL_RxFCE1 (1<<19) +#define RxCTL_RxFCE0 (1<<18) +#define RxCTL_BCRC (1<<17) +#define RxCTL_SRxON (1<<16) +#define RxCTL_RCRCA (1<<13) +#define RxCTL_RA (1<<12) +#define RxCTL_PA (1<<11) +#define RxCTL_BA (1<<10) +#define RxCTL_MA (1<<9) +#define RxCTL_IAHA (1<<8) +#define RxCTL_IA3 (1<<3) +#define RxCTL_IA2 (1<<2) +#define RxCTL_IA1 (1<<1) +#define RxCTL_IA0 (1<<0) + +#define REG_TxCTL 0x0004 /*offset to Transmit Control Reg */ +#define TxCTL_DefDis (1<<7) +#define TxCTL_MBE (1<<6) +#define TxCTL_ICRC (1<<5) +#define TxCTL_TxPD (1<<5) +#define TxCTL_OColl (1<<3) +#define TxCTL_SP (1<<2) +#define TxCTL_PB (1<<1) +#define TxCTL_STxON (1<<0) + +#define REG_TestCTL 0x0008 /*Test Control Reg, R/W */ +#define TestCTL_MACF (1<<7) +#define TestCTL_MFDX (1<<6) +#define TestCTL_DB (1<<5) +#define TestCTL_MIIF (1<<4) + +#define REG_MIICmd 0x0010 /*offset to MII Command Reg, R/W */ +#define MIICmd_OP (0x03<<14) +#define MIICmd_OP_RD (2<<14) +#define MIICmd_OP_WR (1<<14) +#define MIICmd_PHYAD (0x1f<<5) +#define MIICmd_REGAD (0x1f<<0) + +#define REG_MIIData 0x0014 /*offset to MII Data Reg, R/W */ +#define MIIData_MIIData (0xffff<<0) + +#define REG_MIISts 0x0018 /*offset to MII Status Reg, R */ +#define MIISts_Busy (1<<0) + +#define REG_SelfCTL 0x0020 /*offset to Self Control Reg */ +#define SelfCTL_RWP (1<<7) /*Remote Wake Pin */ +#define SelfCTL_GPO0 (1<<5) +#define SelfCTL_PUWE (1<<4) +#define SelfCTL_PDWE (1<<3) +#define SelfCTL_MIIL (1<<2) +#define SelfCTL_RESET (1<<0