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) + +#define REG_IntEn 0x0024 /*Interrupt Enable Reg, R/W */ +#define IntEn_RWIE (1<<30) +#define IntEn_RxMIE (1<<29) +#define IntEn_RxBIE (1<<28) +#define IntEn_RxSQIE (1<<27) +#define IntEn_TxLEIE (1<<26) +#define IntEn_ECIE (1<<25) +#define IntEn_TxUHIE (1<<24) +#define IntEn_MOIE (1<<18) +#define IntEn_TxCOIE (1<<17) +#define IntEn_RxROIE (1<<16) +#define IntEn_MIIIE (1<<12) +#define IntEn_PHYSIE (1<<11) +#define IntEn_TIE (1<<10) +#define IntEn_SWIE (1<<8) +#define IntEn_TxSQIE (1<<3) +#define IntEn_RxEOFIE (1<<2) +#define IntEn_RxEOBIE (1<<1) +#define IntEn_RxHDRIE (1<<0) + +#define REG_IntStsP 0x0028 /*offset to Interrupt Status Preserve Reg, R/W */ +#define REG_IntStsC 0x002c /*offset to Interrupt Status Clear Reg, R */ +#define IntSts_RWI (1<<30) +#define IntSts_RxMI (1<<29) +#define IntSts_RxBI (1<<28) +#define IntSts_RxSQI (1<<27) +#define IntSts_TxLEI (1<<26) +#define IntSts_ECI (1<<25) +#define IntSts_TxUHI (1<<24) +#define IntSts_MOI (1<<18) +#define IntSts_TxCOI (1<<17) +#define IntSts_RxROI (1<<16) +#define IntSts_MIII (1<<12) +#define IntSts_PHYSI (1<<11) +#define IntSts_TI (1<<10) +#define IntSts_AHBE (1<<9) +#define IntSts_SWI (1<<8) +#define IntSts_OTHER (1<<4) +#define IntSts_TxSQ (1<<3) +#define IntSts_RxSQ (1<<2) + +#define REG_GT 0x0040 /*offset to General Timer Reg */ +#define GT_GTC (0xffff<<16) +#define GT_GTP (0xffff<<0) + +#define REG_FCT 0x0044 /*offset to Flow Control Timer Reg */ +#define FCT_FCT (0x00ffffff<<0) + +#define REG_FCF 0x0048 /*offset to Flow Control Format Reg */ +#define FCF_MACCT (0xffff<<16) +#define FCF_TPT (0xffff<<0) + +#define REG_AFP 0x004c /*offset to Address Filter Pointer Reg */ +#define AFP_AFP (0x07<<0) /*Address Filter Pointer + (bank control for REG_IndAD) */ +#define AFP_AFP_IA0 0 /*Primary Individual Address (MAC Addr) */ +#define AFP_AFP_IA1 1 /*Individual Address 1 */ +#define AFP_AFP_IA2 2 /*Individual Address 2 */ +#define AFP_AFP_IA3 3 /*Individual Address 3 */ +#define AFP_AFP_DTxP 6 /*Destination Address of Tx Pause Frame */ +#define AFP_AFP_HASH 7 /*Hash Table */ + +#define REG_IndAD 0x0050 /*offset to Individual Address Reg, + n bytes, R/W */ + +#define REG_GIntSts 0x0060 /*offset to Global Interrupt + Status Reg (writing 1 will clear) */ +#define REG_GIntROS 0x0068 /*offset to Global Interrupt + Status Read Only Reg */ +#define GIntSts_INT (1<<15) /*Global Interrupt Request Status */ + +#define REG_GIntMsk 0x0064 /*offset to Global Interrupt Mask Reg */ +#define GIntMsk_IntEn (1<<15) /*Global Interrupt Enable */ + +#define REG_GIntFrc 0x006c /*offset to Global Interrupt Force Reg */ +#define GIntFrc_INT (1<<15) /*Force to set GIntSts */ + +#define REG_TxCollCnt 0x0070 /*Transmit Collision Count Reg, R */ +#define REG_RxMissCnt 0x0074 /*Receive Miss Count Reg, R */ +#define REG_RxRntCnt 0x0078 /*Receive Runt Count Reg, R */ + +#define REG_BMCtl 0x0080 /*offset to Bus Master Control Reg, R/W */ +#define BMCtl_MT (1<<13) +#define BMCtl_TT (1<<12) +#define BMCtl_UnH (1<<11) +#define BMCtl_TxChR (1<<10) +#define BMCtl_TxDis (1<<9) +#define BMCtl_TxEn (1<<8) +#define BMCtl_EH2 (1<<6) +#define BMCtl_EH1 (1<<5) +#define BMCtl_EEOB (1<<4) +#define BMCtl_RxChR (1<<2) +#define BMCtl_RxDis (1<<1) +#define BMCtl_RxEn (1<<0) + +#define REG_BMSts 0x0084 /*offset to Bus Master Status Reg, R */ +#define BMSts_TxAct (1<<7) +#define BMSts_TP (1<<4) +#define BMSts_RxAct (1<<3) +#define BMSts_QID (0x07<<0) +#define BMSts_QID_RxDt (0<<0) +#define BMSts_QID_TxDt (1<<0) +#define BMSts_QID_RxSts (2<<0) +#define BMSts_QID_TxSts (3<<0) +#define BMSts_QID_RxDesc (4<<0) +#define BMSts_QID_TxDesc (5<<0) + +#define REG_RBCA 0x0088 /*offset to Receive Buffer + Current Address Reg, R */ +#define REG_TBCA 0x008c /*offset to Transmit Buffer + Current Address Reg, R */ + +#define REG_RxDBA 0x0090 /*offset to Receive Descriptor Queue + Base Address Reg, R/W */ +#define REG_RxDBL 0x0094 /*offset to Receive Descriptor Queue + Base Length Reg, R/W, 16bits */ +#define REG_RxDCL 0x0096 /*offset to Receive Descriptor Queue + Current Length Reg, R/W, 16bits */ +#define REG_RxDCA 0x0098 /*offset to Receive Descriptor Queue + Current Address Reg, R/W */ + +#define REG_RxDEQ 0x009c /*offset to Receive Descriptor + Enqueue Reg, R/W */ +#define RxDEQ_RDV (0xffff<<16) /*R 16bit; Receive Descriptor Value */ +#define RxDEQ_RDI (0xff<<0) /*W 8bit; Receive Descriptor Increment */ + +#define REG_RxSBA 0x00a0 /*offset to Receive Status Queue + Base Address Reg, R/W */ +#define REG_RxSBL 0x00a4 /*offset to Receive Status Queue + Base Length Reg, R/W, 16bits */ +#define REG_RxSCL 0x00a6 /*offset to Receive Status Queue + Current Length Reg, R/W, 16bits */ +#define REG_RxSCA 0x00a8 /*offset to Receive Status Queue + Current Address Reg, R/W */ + +#define REG_RxSEQ 0x00ac /*offset to Receive Status Queue + Current Address Reg, R/W */ +#define RxSEQ_RSV (0xffff<<16) +#define RxSEQ_RSI (0xff<<0) + +#define REG_TxDBA 0x00b0 /*offset to Transmit Descriptor Queue + Base Address Reg, R/W */ +#define REG_TxDBL 0x00b4 /*offset to Transmit Descriptor Queue + Base Length Reg, R/W, 16bits */ +#define REG_TxDCL 0x00b6 /*offset to Transmit Descriptor Queue + Current Length Reg, R/W, 16bits */ +#define REG_TxDCA 0x00b8 /*offset to Transmit Descriptor Queue + Current Address Reg, R/W */ + +#define REG_TxDEQ 0x00bc /*offset to Transmit Descriptor Queue + Current Address Reg, R/W */ +#define TxDEQ_TDV (0xffff<<16) +#define TxDEQ_TDI (0xff<<0) + +#define REG_TxSBA 0x00c0 /*offset to Transmit Status Queue + Base Address Reg, R/W */ +#define REG_TxSBL 0x00c4 /*offset to Transmit Status Queue + Base Length Reg, R/W, 16bits */ +#define REG_TxSCL 0x00c6 /*offset to Transmit Status Queue + Current Length Reg, R/W, 16bits */ +#define REG_TxSCA 0x00c8 /*offset to Transmit Status Queue + Current Address Reg, R/W */ + +#define REG_RxBTH 0x00d0 /*offset to Receive Buffer + Threshold Reg, R/W */ +#define RxBTH_RDHT (0x03ff<<16) +#define RxBTH_RDST (0x03ff<<0) + +#define REG_TxBTH 0x00d4 /*offset to Transmit Buffer + Threshold Reg, R/W */ +#define TxBTH_TDHT (0x03ff<<16) +#define TxBTH_TDST (0x03ff<<0) + +#define REG_RxSTH 0x00d8 /*offset to Receive Status + Threshold Reg, R/W */ +#define RxSTH_RSHT (0x003f<<16) +#define RxSTH_RSST (0x003f<<0) + +#define REG_TxSTH 0x00dc /*offset to Transmit Status + Threshold Reg, R/W */ +#define TxSTH_TSHT (0x003f<<16) +#define TxSTH_TSST (0x003f<<0) + +#define REG_RxDTH 0x00e0 /*offset to Receive Descriptor + Threshold Reg, R/W */ +#define RxDTH_RDHT (0x003f<<16) +#define RxDTH_RDST (0x003f<<0) + +#define REG_TxDTH 0x00e4 /*offset to Transmit Descriptor + Threshold Reg, R/W */ +#define TxDTH_TDHT (0x003f<<16) +#define TxDTH_TDST (0x003f<<0) + +#define REG_MaxFL 0x00e8 /*offset to Max Frame Length Reg, R/W */ +#define MaxFL_MFL (0x07ff<<16) +#define MaxFL_TST (0x07ff<<0) + +#define REG_RxHL 0x00ec /*offset to Receive Header Length Reg, R/W */ +#define RxHL_RHL2 (0x07ff<<16) +#define RxHL_RHL1 (0x03ff<<0) + +#define REG_MACCFG0 0x0100 /*offset to Test Reg #0, R/W */ +#define MACCFG0_DbgSel (1<<7) +#define MACCFG0_LCKEN (1<<6) +#define MACCFG0_LRATE (1<<5) +#define MACCFG0_RXERR (1<<4) +#define MACCFG0_BIT33 (1<<2) +#define MACCFG0_PMEEN (1<<1) +#define MACCFG0_PMEST (1<<0) + +#define REG_MACCFG1 0x0104 /*offset to Test Reg #1, R/W */ +#define REG_MACCFG2 0x0108 /*offset to Test Reg #2, R */ +#define REG_MACCFG3 0x010c /*offset to Test Reg #3, R */ + +/*--------------------------------------------------------------- + * Definition of Descriptor/Status Queue Entry + *-------------------------------------------------------------*/ +struct rx_dsc { + __be32 ba; + __be16 bl; + __be16 bi; /* let nsof flag be part of bi */ +}; + +#define RXSTS_RFP 0x80000000 +#define RXSTS_RWE 0x40000000 +#define RXSTS_EOF 0x20000000 +#define RXSTS_EOB 0x10000000 +#define RXSTS_AM 0x00C00000 +#define RXSTS_OE 0x00100000 +#define RXSTS_FE 0x00080000 +#define RXSTS_RUNT 0x00040000 +#define RXSTS_EDATA 0x00020000 +#define RXSTS_CRCE 0x00010000 + +#define RXSTS_BI 0x7FFF +struct rx_sts { /* Receive Status Queue Entry */ + __be32 w1; + __be16 fl; + __be16 bi; /* bi and rfp2 */ +}; + +#define TXDSC_BL 0x0FFF +#define TXDSC_AF 0x8000 +#define TXDSC_BI 0x7FFF +#define TXDSC_EOF 0x8000 +struct tx_dsc { /* Transmit Descriptor Queue Entry */ + __be32 ba; /*b31-0: physical Buffer Address */ + __be16 bl_af; /* Buffer Length, Abort Frame */ + __be16 bi_eof; /* Buffer Index, End Of Frame */ +}; + +#define TXSTS_BI 0x7fff +#define TXSTS_TXFP 0x80 +#define TXSTS_TXWE 0x40 +#define TXSTS_LCRS 0x10 +#define TXSTS_TXU 0x02 +#define TXSTS_ECOLL 0x01 + +struct tx_sts { + __be16 bi; + u8 ncoll; + u8 flags; +}; + +/* + * Size of device registers occupied in memory/IO address map + */ +#define DEV_REG_SPACE 0x00010000 + +#endif /* _EP93xx_ETH_H_ */ Index: linux-2.6.16/drivers/mtd/maps/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/mtd/maps/Kconfig +++ linux-2.6.16/drivers/mtd/maps/Kconfig @@ -37,7 +37,7 @@ config MTD_PHYSMAP_START config MTD_PHYSMAP_LEN hex "Physical length of flash mapping" depends on MTD_PHYSMAP - default "0x4000000" + default "0x0" help This is the total length of the mapping of the flash chips on your particular board. If there is space, or aliases, in the Index: linux-2.6.16/drivers/mtd/maps/physmap.c =================================================================== --- linux-2.6.16.orig/drivers/mtd/maps/physmap.c +++ linux-2.6.16/drivers/mtd/maps/physmap.c @@ -51,6 +51,9 @@ static int __init init_physmap(void) static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; const char **type; + if (!physmap_map.size) + return -EIO; + printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys); physmap_map.virt = ioremap(physmap_map.phys, physmap_map.size); Index: linux-2.6.16/drivers/mtd/nand/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/mtd/nand/Kconfig +++ linux-2.6.16/drivers/mtd/nand/Kconfig @@ -55,6 +55,12 @@ config MTD_NAND_TOTO help Support for NAND flash on Texas Instruments Toto platform. +config MTD_NAND_TS7250 + tristate "NAND flash device on TS-7250 board" + depends on MACH_TS72XX && MTD_NAND + help + Support for NAND flash on Technologic Systems TS-7250 platform. + config MTD_NAND_IDS tristate Index: linux-2.6.16/drivers/mtd/nand/Makefile =================================================================== --- linux-2.6.16.orig/drivers/mtd/nand/Makefile +++ linux-2.6.16/drivers/mtd/nand/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP) += dis obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o +obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o nand-objs = nand_base.o nand_bbt.o Index: linux-2.6.16/drivers/mtd/nand/ts7250.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/mtd/nand/ts7250.c @@ -0,0 +1,208 @@ +/* + * drivers/mtd/nand/ts7250.c + * + * Copyright (C) 2004 Technologic Systems (support@embeddedARM.com) + * + * Derived from drivers/mtd/nand/edb7312.c + * Copyright (C) 2004 Marius Gröger (mag@sysgo.de) + * + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff Exp $ + * + * 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. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * TS-7250 board which utilizes a Samsung 32 Mbyte part. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * MTD structure for TS7250 board + */ +static struct mtd_info *ts7250_mtd = NULL; + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL }; + +#define NUM_PARTITIONS 3 + +/* + * Define static partitions for flash device + */ +static struct mtd_partition partition_info32[] = { + { + .name = "TS-BOOTROM", + .offset = 0x00000000, + .size = 0x00004000, + }, { + .name = "Linux", + .offset = 0x00004000, + .size = 0x01d00000, + }, { + .name = "RedBoot", + .offset = 0x01d04000, + .size = 0x002fc000, + }, +}; + +/* + * Define static partitions for flash device + */ +static struct mtd_partition partition_info128[] = { + { + .name = "TS-BOOTROM", + .offset = 0x00000000, + .size = 0x00004000, + }, { + .name = "Linux", + .offset = 0x00004000, + .size = 0x07d00000, + }, { + .name = "RedBoot", + .offset = 0x07d04000, + .size = 0x002fc000, + }, +}; +#endif + + +/* + * hardware specific access to control-lines + */ +static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd) +{ + unsigned long ctrl = TS72XX_NAND_CONTROL_VIRT_BASE; + + switch(cmd) { + case NAND_CTL_SETCLE: + __raw_writeb(__raw_readb(ctrl) | 0x2, ctrl); + break; + case NAND_CTL_CLRCLE: + __raw_writeb(__raw_readb(ctrl) & ~0x2, ctrl); + break; + case NAND_CTL_SETALE: + __raw_writeb(__raw_readb(ctrl) | 0x1, ctrl); + break; + case NAND_CTL_CLRALE: + __raw_writeb(__raw_readb(ctrl) & ~0x1, ctrl); + break; + case NAND_CTL_SETNCE: + __raw_writeb(__raw_readb(ctrl) | 0x4, ctrl); + break; + case NAND_CTL_CLRNCE: + __raw_writeb(__raw_readb(ctrl) & ~0x4, ctrl); + break; + } +} + +/* + * read device ready pin + */ +static int ts7250_device_ready(struct mtd_info *mtd) +{ + return __raw_readb(TS72XX_NAND_BUSY_VIRT_BASE) & 0x20; +} + +/* + * Main initialization routine + */ +static int __init ts7250_init(void) +{ + struct nand_chip *this; + const char *part_type = 0; + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + + if (!machine_is_ts72xx() || board_is_ts7200()) + return -ENXIO; + + /* Allocate memory for MTD device structure and private data */ + ts7250_mtd = kmalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), GFP_KERNEL); + if (!ts7250_mtd) { + printk("Unable to allocate TS7250 NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *)(&ts7250_mtd[1]); + + /* Initialize structures */ + memset(ts7250_mtd, 0, sizeof(struct mtd_info)); + memset(this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + ts7250_mtd->priv = this; + + /* insert callbacks */ + this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE; + this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE; + this->hwcontrol = ts7250_hwcontrol; + this->dev_ready = ts7250_device_ready; + this->chip_delay = 15; + this->eccmode = NAND_ECC_SOFT; + + printk("Searching for NAND flash...\n"); + /* Scan to find existence of the device */ + if (nand_scan(ts7250_mtd, 1)) { + kfree(ts7250_mtd); + return -ENXIO; + } + +#ifdef CONFIG_MTD_PARTITIONS + ts7250_mtd->name = "ts7250-nand"; + mtd_parts_nb = parse_mtd_partitions(ts7250_mtd, part_probes, + &mtd_parts, 0); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (mtd_parts_nb == 0) { + mtd_parts = partition_info32; + if (ts7250_mtd->size >= (128 * 0x100000)) + mtd_parts = partition_info128; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(ts7250_mtd, mtd_parts, mtd_parts_nb); + + /* Return happy */ + return 0; +} +module_init(ts7250_init); + +/* + * Clean up routine + */ +static void __exit ts7250_cleanup(void) +{ + /* Unregister the device */ + del_mtd_device(ts7250_mtd); + + /* Free the MTD device structure */ + kfree(ts7250_mtd); +} +module_exit(ts7250_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jesse Off "); +MODULE_DESCRIPTION("MTD map driver for Technologic Systems TS-7250 board"); Index: linux-2.6.16/drivers/i2c/busses/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/i2c/busses/Kconfig +++ linux-2.6.16/drivers/i2c/busses/Kconfig @@ -511,4 +511,16 @@ config I2C_MV64XXX This driver can also be built as a module. If so, the module will be called i2c-mv64xxx. +config I2C_EP93XX + tristate "Cirrus Logic EP93XX GPIO-based I2C interface" + depends on I2C && ARCH_EP93XX + select I2C_ALGOBIT + help + Say Y here if you have an Cirrus Logic EP93XX based + system and are using GPIO lines for an I2C bus. + + This support is also available as a module. If so, the module + will be called i2c-ep93xx. + + endmenu Index: linux-2.6.16/drivers/i2c/busses/Makefile =================================================================== --- linux-2.6.16.orig/drivers/i2c/busses/Makefile +++ linux-2.6.16/drivers/i2c/busses/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o +obj-$(CONFIG_I2C_EP93XX) += i2c-ep93xx.o ifeq ($(CONFIG_I2C_DEBUG_BUS),y) EXTRA_CFLAGS += -DDEBUG Index: linux-2.6.16/drivers/i2c/busses/i2c-ep93xx.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/i2c/busses/i2c-ep93xx.c @@ -0,0 +1,173 @@ +/* + * Intel's IXP4xx XScale NPU chipsets (IXP420, 421, 422, 425) do not have + * an on board I2C controller but provide 16 GPIO pins that are often + * used to create an I2C bus. This driver provides an i2c_adapter + * interface that plugs in under algo_bit and drives the GPIO pins + * as instructed by the alogorithm driver. + * + * Based on i2x-ixp4xx.c + * Author: Deepak Saxena + * Copyright (c) 2003-2004 MontaVista Software Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * NOTE: Since different platforms will use different GPIO pins for + * I2C, this driver uses an ep93xx specific platform_data + * pointer to pass the GPIO numbers to the driver. This + * allows us to support all the different boards + * w/o having to put #ifdefs in this driver. + * + * See arch/arm/mach-ep93xx/core.c for an example of building a + * device list and filling in the ep93xx_i2c_pins data structure + * that is passed as the platform_data to this driver. + */ + +#include +#include +#include +#include +#include + +#include +#include + +static inline int ep93xx_scl_pin(void *data) +{ + return ((struct ep93xx_i2c_pins *)data)->scl_pin; +} + +static inline int ep93xx_sda_pin(void *data) +{ + return ((struct ep93xx_i2c_pins *)data)->sda_pin; +} + +static void ep93xx_bit_setscl(void *data, int val) +{ + gpio_line_set(ep93xx_scl_pin(data), 0); + gpio_line_config(ep93xx_scl_pin(data), + val ? GPIO_IN : GPIO_OUT); +} + +static void ep93xx_bit_setsda(void *data, int val) +{ + gpio_line_set(ep93xx_sda_pin(data), 0); + gpio_line_config(ep93xx_sda_pin(data), + val ? GPIO_IN : GPIO_OUT); +} + +static int ep93xx_bit_getscl(void *data) +{ + int scl; + + gpio_line_config(ep93xx_scl_pin(data), GPIO_IN); + scl = gpio_line_get(ep93xx_scl_pin(data)); + + return scl; +} + +static int ep93xx_bit_getsda(void *data) +{ + int sda; + + gpio_line_config(ep93xx_sda_pin(data), GPIO_IN); + sda = gpio_line_get(ep93xx_sda_pin(data)); + + return sda; +} + +struct ep93xx_i2c_data { + struct ep93xx_i2c_pins *gpio_pins; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo_data; +}; + +static int ep93xx_i2c_remove(struct platform_device *plat_dev) +{ + struct ep93xx_i2c_data *drv_data = platform_get_drvdata(plat_dev); + + platform_set_drvdata(plat_dev, NULL); + + i2c_bit_del_bus(&drv_data->adapter); + + kfree(drv_data); + + return 0; +} + +static int ep93xx_i2c_probe(struct platform_device *plat_dev) +{ + int err; + struct ep93xx_i2c_pins *gpio = plat_dev->dev.platform_data; + struct ep93xx_i2c_data *drv_data = + kzalloc(sizeof(struct ep93xx_i2c_data), GFP_KERNEL); + + if (!drv_data) + return -ENOMEM; + + drv_data->gpio_pins = gpio; + + /* + * We could make a lot of these structures static, but + * certain platforms may have multiple GPIO-based I2C + * buses for various device domains, so we need per-device + * algo_data->data. + */ + drv_data->algo_data.data = gpio; + drv_data->algo_data.setsda = ep93xx_bit_setsda; + drv_data->algo_data.setscl = ep93xx_bit_setscl; + drv_data->algo_data.getsda = ep93xx_bit_getsda; + drv_data->algo_data.getscl = ep93xx_bit_getscl; + drv_data->algo_data.udelay = 10; + drv_data->algo_data.mdelay = 10; + drv_data->algo_data.timeout = 100; + + strlcpy(drv_data->adapter.name, plat_dev->dev.driver->name, + I2C_NAME_SIZE); + drv_data->adapter.algo_data = &drv_data->algo_data; + + drv_data->adapter.dev.parent = &plat_dev->dev; + + gpio_line_config(gpio->scl_pin, GPIO_IN); + gpio_line_config(gpio->sda_pin, GPIO_IN); + gpio_line_set(gpio->scl_pin, 0); + gpio_line_set(gpio->sda_pin, 0); + + if ((err = i2c_bit_add_bus(&drv_data->adapter) != 0)) { + printk(KERN_ERR "ERROR: Could not install %s\n", plat_dev->dev.bus_id); + + kfree(drv_data); + return err; + } + + platform_set_drvdata(plat_dev, drv_data); + + return 0; +} + +static struct platform_driver ep93xx_i2c_driver = { + .probe = ep93xx_i2c_probe, + .remove = ep93xx_i2c_remove, + .driver = { + .name = "ep93xx-i2c", + .owner = THIS_MODULE, + }, +}; + +static int __init ep93xx_i2c_init(void) +{ + return platform_driver_register(&ep93xx_i2c_driver); +} + +static void __exit ep93xx_i2c_exit(void) +{ + platform_driver_unregister(&ep93xx_i2c_driver); +} + +module_init(ep93xx_i2c_init); +module_exit(ep93xx_i2c_exit); + +MODULE_DESCRIPTION("GPIO-based I2C adapter for EP93XX systems"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alessandro Zummo "); Index: linux-2.6.16/drivers/rtc/rtc-lib.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-lib.c @@ -0,0 +1,101 @@ +/* + * rtc and date/time utility functions + * + * Copyright (C) 2005-06 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c and other bits + * + * 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 + +static const unsigned char rtc_days_in_month[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) + +int rtc_month_days(unsigned int month, unsigned int year) +{ + return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1); +} +EXPORT_SYMBOL(rtc_month_days); + +/* + * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. + */ +void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) +{ + register int days, month, year; + + days = time / 86400; + time -= days * 86400; + + /* day of the week, 1970-01-01 was a Thursday */ + tm->tm_wday = (days + 4) % 7; + + year = 1970 + days / 365; + days -= (year - 1970) * 365 + + LEAPS_THRU_END_OF(year - 1) + - LEAPS_THRU_END_OF(1970 - 1); + if (days < 0) { + year -= 1; + days += 365 + LEAP_YEAR(year); + } + tm->tm_year = year - 1900; + tm->tm_yday = days + 1; + + for (month = 0; month < 11; month++) { + int newdays; + + newdays = days - rtc_month_days(month, year); + if (newdays < 0) + break; + days = newdays; + } + tm->tm_mon = month; + tm->tm_mday = days + 1; + + tm->tm_hour = time / 3600; + time -= tm->tm_hour * 3600; + tm->tm_min = time / 60; + tm->tm_sec = time - tm->tm_min * 60; +} +EXPORT_SYMBOL(rtc_time_to_tm); + +/* + * Does the rtc_time represent a valid date/time? + */ +int rtc_valid_tm(struct rtc_time *tm) +{ + if (tm->tm_year < 70 + || tm->tm_mon >= 12 + || tm->tm_mday < 1 + || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) + || tm->tm_hour >= 24 + || tm->tm_min >= 60 + || tm->tm_sec >= 60) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(rtc_valid_tm); + +/* + * Convert Gregorian date to seconds since 01-01-1970 00:00:00. + */ +int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) +{ + *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + return 0; +} +EXPORT_SYMBOL(rtc_tm_to_time); + +MODULE_LICENSE("GPL") Index: linux-2.6.16/drivers/rtc/Makefile =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for RTC class/drivers. +# + +obj-$(CONFIG_RTC_LIB) += rtc-lib.o +obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o +obj-$(CONFIG_RTC_CLASS) += rtc-core.o +rtc-core-y := class.o interface.o + +obj-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o +obj-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o +obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o + +obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o +obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o +obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o +obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o +obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o +obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o +obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o +obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o Index: linux-2.6.16/drivers/rtc/Kconfig =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/Kconfig @@ -0,0 +1,165 @@ +\# +# RTC class/drivers configuration +# + +menu "Real Time Clock" + +config RTC_LIB + tristate + +config RTC_CLASS + tristate "RTC class" + depends on EXPERIMENTAL + default n + select RTC_LIB + help + Generic RTC class support. If you say yes here, you will + be allowed to plug one or more RTCs to your system. You will + probably want to enable one of more of the interfaces below. + + This driver can also be built as a module. If so, the module + will be called rtc-class. + +config RTC_HCTOSYS + bool "Set system time from RTC on startup" + depends on RTC_CLASS = y + default y + help + If you say yes here, the system time will be set using + the value read from the specified RTC device. This is useful + in order to avoid unnecessary fschk runs. + +config RTC_HCTOSYS_DEVICE + string "The RTC to read the time from" + depends on RTC_HCTOSYS = y + default "rtc0" + help + The RTC device that will be used as the source for + the system time, usually rtc0. + +comment "RTC interfaces" + depends on RTC_CLASS + +config RTC_INTF_SYSFS + tristate "sysfs" + depends on RTC_CLASS && SYSFS + default RTC_CLASS + help + Say yes here if you want to use your RTC using the sysfs + interface, /sys/class/rtc/rtcX . + + This driver can also be built as a module. If so, the module + will be called rtc-sysfs. + +config RTC_INTF_PROC + tristate "proc" + depends on RTC_CLASS && PROC_FS + default RTC_CLASS + help + Say yes here if you want to use your RTC using the proc + interface, /proc/driver/rtc . + + This driver can also be built as a module. If so, the module + will be called rtc-proc. + +config RTC_INTF_DEV + tristate "dev" + depends on RTC_CLASS + default RTC_CLASS + help + Say yes here if you want to use your RTC using the dev + interface, /dev/rtc . + + This driver can also be built as a module. If so, the module + will be called rtc-dev. + +comment "RTC drivers" + depends on RTC_CLASS + +config RTC_DRV_X1205 + tristate "Xicor/Intersil X1205" + depends on RTC_CLASS && I2C + help + If you say yes here you get support for the + Xicor/Intersil X1205 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-x1205. + +config RTC_DRV_DS1672 + tristate "Dallas/Maxim DS1672" + depends on RTC_CLASS && I2C + help + If you say yes here you get support for the + Dallas/Maxim DS1672 timekeeping chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1672. + +config RTC_DRV_PCF8563 + tristate "Philips PCF8563/Epson RTC8564" + depends on RTC_CLASS && I2C + help + If you say yes here you get support for the + Philips PCF8563 RTC chip. The Epson RTC8564 + should work as well. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf8563. + +config RTC_DRV_RS5C372 + tristate "Ricoh RS5C372A/B" + depends on RTC_CLASS && I2C + help + If you say yes here you get support for the + Ricoh RS5C372A and RS5C372B RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rs5c372. + +config RTC_DRV_M48T86 + tristate "ST M48T86/Dallas DS12887" + depends on RTC_CLASS + help + If you say Y here you will get support for the + ST M48T86 and Dallas DS12887 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-m48t86. + +config RTC_DRV_EP93XX + tristate "Cirrus Logic EP93XX" + depends on RTC_CLASS && ARCH_EP93XX + help + If you say yes here you get support for the + RTC embedded in the Cirrus Logic EP93XX processors. + + This driver can also be built as a module. If so, the module + will be called rtc-ep93xx. + +config RTC_DRV_SA1100 + tristate "SA11x0/PXA2xx" + depends on RTC_CLASS && (ARCH_SA1100 || ARCH_PXA) + help + If you say Y here you will get access to the real time clock + built into your SA11x0 or PXA2xx CPU. + + To compile this driver as a module, choose M here: the + module will be called rtc-sa1100. + +config RTC_DRV_TEST + tristate "Test driver/device" + depends on RTC_CLASS + help + If you say yes here you get support for the + RTC test driver. It's a software RTC which can be + used to test the RTC subsystem APIs. It gets + the time from the system clock. + You want this driver only if you are doing development + on the RTC subsystem. Please read the source code + for further details. + + This driver can also be built as a module. If so, the module + will be called rtc-test. + +endmenu Index: linux-2.6.16/drivers/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/Kconfig +++ linux-2.6.16/drivers/Kconfig @@ -70,4 +70,6 @@ source "drivers/sn/Kconfig" source "drivers/edac/Kconfig" +source "drivers/rtc/Kconfig" + endmenu Index: linux-2.6.16/drivers/Makefile =================================================================== --- linux-2.6.16.orig/drivers/Makefile +++ linux-2.6.16/drivers/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_GADGET) += usb/gadget/ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_INPUT) += input/ obj-$(CONFIG_I2O) += message/ +obj-$(CONFIG_RTC_LIB) += rtc/ obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_W1) += w1/ obj-$(CONFIG_HWMON) += hwmon/ Index: linux-2.6.16/include/linux/rtc.h =================================================================== --- linux-2.6.16.orig/include/linux/rtc.h +++ linux-2.6.16/include/linux/rtc.h @@ -93,8 +93,100 @@ struct rtc_pll_info { #define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */ #define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */ +/* interrupt flags */ +#define RTC_IRQF 0x80 /* any of the following is active */ +#define RTC_PF 0x40 +#define RTC_AF 0x20 +#define RTC_UF 0x10 + #ifdef __KERNEL__ +extern int rtc_month_days(unsigned int month, unsigned int year); +extern int rtc_valid_tm(struct rtc_time *tm); +extern int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time); +extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm); + +#include +#include +#include +#include +#include + +extern struct class *rtc_class; + +struct rtc_class_ops { + int (*open)(struct device *); + void (*release)(struct device *); + int (*ioctl)(struct device *, unsigned int, unsigned long); + int (*read_time)(struct device *, struct rtc_time *); + int (*set_time)(struct device *, struct rtc_time *); + int (*read_alarm)(struct device *, struct rtc_wkalrm *); + int (*set_alarm)(struct device *, struct rtc_wkalrm *); + int (*proc)(struct device *, struct seq_file *); + int (*set_mmss)(struct device *, unsigned long secs); + int (*irq_set_state)(struct device *, int enabled); + int (*irq_set_freq)(struct device *, int freq); + int (*read_callback)(struct device *, int data); +}; + +#define RTC_DEVICE_NAME_SIZE 20 +struct rtc_task; + +struct rtc_device +{ + struct class_device class_dev; + struct module *owner; + + int id; + char name[RTC_DEVICE_NAME_SIZE]; + + struct rtc_class_ops *ops; + struct mutex ops_lock; + + struct class_device *rtc_dev; + struct cdev char_dev; + struct mutex char_lock; + + unsigned long irq_data; + spinlock_t irq_lock; + wait_queue_head_t irq_queue; + struct fasync_struct *async_queue; + + struct rtc_task *irq_task; + spinlock_t irq_task_lock; + int irq_freq; +}; +#define to_rtc_device(d) container_of(d, struct rtc_device, class_dev) + +extern struct rtc_device *rtc_device_register(const char *name, + struct device *dev, + struct rtc_class_ops *ops, + struct module *owner); +extern void rtc_device_unregister(struct rtc_device *rdev); +extern int rtc_interface_register(struct class_interface *intf); + +extern int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm); +extern int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm); +extern int rtc_set_mmss(struct class_device *class_dev, unsigned long secs); +extern int rtc_read_alarm(struct class_device *class_dev, + struct rtc_wkalrm *alrm); +extern int rtc_set_alarm(struct class_device *class_dev, + struct rtc_wkalrm *alrm); +extern void rtc_update_irq(struct class_device *class_dev, + unsigned long num, unsigned long events); + +extern struct class_device *rtc_class_open(char *name); +extern void rtc_class_close(struct class_device *class_dev); + +extern int rtc_irq_register(struct class_device *class_dev, + struct rtc_task *task); +extern void rtc_irq_unregister(struct class_device *class_dev, + struct rtc_task *task); +extern int rtc_irq_set_state(struct class_device *class_dev, + struct rtc_task *task, int enabled); +extern int rtc_irq_set_freq(struct class_device *class_dev, + struct rtc_task *task, int freq); + typedef struct rtc_task { void (*func)(void *private_data); void *private_data; Index: linux-2.6.16/arch/arm/common/rtctime.c =================================================================== --- linux-2.6.16.orig/arch/arm/common/rtctime.c +++ linux-2.6.16/arch/arm/common/rtctime.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -42,89 +43,6 @@ static struct rtc_ops *rtc_ops; #define rtc_epoch 1900UL -static const unsigned char days_in_month[] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) -#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) - -static int month_days(unsigned int month, unsigned int year) -{ - return days_in_month[month] + (LEAP_YEAR(year) && month == 1); -} - -/* - * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. - */ -void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) -{ - int days, month, year; - - days = time / 86400; - time -= days * 86400; - - tm->tm_wday = (days + 4) % 7; - - year = 1970 + days / 365; - days -= (year - 1970) * 365 - + LEAPS_THRU_END_OF(year - 1) - - LEAPS_THRU_END_OF(1970 - 1); - if (days < 0) { - year -= 1; - days += 365 + LEAP_YEAR(year); - } - tm->tm_year = year - 1900; - tm->tm_yday = days + 1; - - for (month = 0; month < 11; month++) { - int newdays; - - newdays = days - month_days(month, year); - if (newdays < 0) - break; - days = newdays; - } - tm->tm_mon = month; - tm->tm_mday = days + 1; - - tm->tm_hour = time / 3600; - time -= tm->tm_hour * 3600; - tm->tm_min = time / 60; - tm->tm_sec = time - tm->tm_min * 60; -} -EXPORT_SYMBOL(rtc_time_to_tm); - -/* - * Does the rtc_time represent a valid date/time? - */ -int rtc_valid_tm(struct rtc_time *tm) -{ - if (tm->tm_year < 70 || - tm->tm_mon >= 12 || - tm->tm_mday < 1 || - tm->tm_mday > month_days(tm->tm_mon, tm->tm_year + 1900) || - tm->tm_hour >= 24 || - tm->tm_min >= 60 || - tm->tm_sec >= 60) - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL(rtc_valid_tm); - -/* - * Convert Gregorian date to seconds since 01-01-1970 00:00:00. - */ -int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) -{ - *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); - - return 0; -} -EXPORT_SYMBOL(rtc_tm_to_time); - /* * Calculate the next alarm time given the requested alarm time mask * and the current time. @@ -151,13 +69,13 @@ void rtc_next_alarm_time(struct rtc_time } } -static inline int rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm) +static inline int rtc_arm_read_time(struct rtc_ops *ops, struct rtc_time *tm) { memset(tm, 0, sizeof(struct rtc_time)); return ops->read_time(tm); } -static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm) +static inline int rtc_arm_set_time(struct rtc_ops *ops, struct rtc_time *tm) { int ret; @@ -168,7 +86,7 @@ static inline int rtc_set_time(struct rt return ret; } -static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) +static inline int rtc_arm_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) { int ret = -EINVAL; if (ops->read_alarm) { @@ -178,7 +96,7 @@ static inline int rtc_read_alarm(struct return ret; } -static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) +static inline int rtc_arm_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) { int ret = -EINVAL; if (ops->set_alarm) @@ -266,7 +184,7 @@ static int rtc_ioctl(struct inode *inode switch (cmd) { case RTC_ALM_READ: - ret = rtc_read_alarm(ops, &alrm); + ret = rtc_arm_read_alarm(ops, &alrm); if (ret) break; ret = copy_to_user(uarg, &alrm.time, sizeof(tm)); @@ -288,11 +206,11 @@ static int rtc_ioctl(struct inode *inode alrm.time.tm_wday = -1; alrm.time.tm_yday = -1; alrm.time.tm_isdst = -1; - ret = rtc_set_alarm(ops, &alrm); + ret = rtc_arm_set_alarm(ops, &alrm); break; case RTC_RD_TIME: - ret = rtc_read_time(ops, &tm); + ret = rtc_arm_read_time(ops, &tm); if (ret) break; ret = copy_to_user(uarg, &tm, sizeof(tm)); @@ -310,7 +228,7 @@ static int rtc_ioctl(struct inode *inode ret = -EFAULT; break; } - ret = rtc_set_time(ops, &tm); + ret = rtc_arm_set_time(ops, &tm); break; case RTC_EPOCH_SET: @@ -341,11 +259,11 @@ static int rtc_ioctl(struct inode *inode ret = -EFAULT; break; } - ret = rtc_set_alarm(ops, &alrm); + ret = rtc_arm_set_alarm(ops, &alrm); break; case RTC_WKALM_RD: - ret = rtc_read_alarm(ops, &alrm); + ret = rtc_arm_read_alarm(ops, &alrm); if (ret) break; ret = copy_to_user(uarg, &alrm, sizeof(alrm)); @@ -435,7 +353,7 @@ static int rtc_read_proc(char *page, cha struct rtc_time tm; char *p = page; - if (rtc_read_time(ops, &tm) == 0) { + if (rtc_arm_read_time(ops, &tm) == 0) { p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" "rtc_date\t: %04d-%02d-%02d\n" @@ -445,7 +363,7 @@ static int rtc_read_proc(char *page, cha rtc_epoch); } - if (rtc_read_alarm(ops, &alrm) == 0) { + if (rtc_arm_read_alarm(ops, &alrm) == 0) { p += sprintf(p, "alrm_time\t: "); if ((unsigned int)alrm.time.tm_hour <= 24) p += sprintf(p, "%02d:", alrm.time.tm_hour); Index: linux-2.6.16/include/asm-arm/rtc.h =================================================================== --- linux-2.6.16.orig/include/asm-arm/rtc.h +++ linux-2.6.16/include/asm-arm/rtc.h @@ -25,9 +25,6 @@ struct rtc_ops { int (*proc)(char *buf); }; -void rtc_time_to_tm(unsigned long, struct rtc_time *); -int rtc_tm_to_time(struct rtc_time *, unsigned long *); -int rtc_valid_tm(struct rtc_time *); void rtc_next_alarm_time(struct rtc_time *, struct rtc_time *, struct rtc_time *); void rtc_update(unsigned long, unsigned long); int register_rtc(struct rtc_ops *); Index: linux-2.6.16/drivers/char/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/char/Kconfig +++ linux-2.6.16/drivers/char/Kconfig @@ -695,7 +695,7 @@ config NVRAM config RTC tristate "Enhanced Real Time Clock Support" - depends on !PPC32 && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV + depends on !PPC32 && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM ---help--- If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you Index: linux-2.6.16/arch/arm/mach-integrator/time.c =================================================================== --- linux-2.6.16.orig/arch/arm/mach-integrator/time.c +++ linux-2.6.16/arch/arm/mach-integrator/time.c @@ -40,13 +40,13 @@ static int integrator_set_rtc(void) return 1; } -static int rtc_read_alarm(struct rtc_wkalrm *alrm) +static int integrator_rtc_read_alarm(struct rtc_wkalrm *alrm) { rtc_time_to_tm(readl(rtc_base + RTC_MR), &alrm->time); return 0; } -static inline int rtc_set_alarm(struct rtc_wkalrm *alrm) +static inline int integrator_rtc_set_alarm(struct rtc_wkalrm *alrm) { unsigned long time; int ret; @@ -62,7 +62,7 @@ static inline int rtc_set_alarm(struct r return ret; } -static int rtc_read_time(struct rtc_time *tm) +static int integrator_rtc_read_time(struct rtc_time *tm) { rtc_time_to_tm(readl(rtc_base + RTC_DR), tm); return 0; @@ -76,7 +76,7 @@ static int rtc_read_time(struct rtc_time * edge of the 1Hz clock, we must write the time one second * in advance. */ -static inline int rtc_set_time(struct rtc_time *tm) +static inline int integrator_rtc_set_time(struct rtc_time *tm) { unsigned long time; int ret; @@ -90,10 +90,10 @@ static inline int rtc_set_time(struct rt static struct rtc_ops rtc_ops = { .owner = THIS_MODULE, - .read_time = rtc_read_time, - .set_time = rtc_set_time, - .read_alarm = rtc_read_alarm, - .set_alarm = rtc_set_alarm, + .read_time = integrator_rtc_read_time, + .set_time = integrator_rtc_set_time, + .read_alarm = integrator_rtc_read_alarm, + .set_alarm = integrator_rtc_set_alarm, }; static irqreturn_t arm_rtc_interrupt(int irq, void *dev_id, Index: linux-2.6.16/arch/mips/ddb5xxx/common/rtc_ds1386.c =================================================================== --- linux-2.6.16.orig/arch/mips/ddb5xxx/common/rtc_ds1386.c +++ linux-2.6.16/arch/mips/ddb5xxx/common/rtc_ds1386.c @@ -165,6 +165,6 @@ rtc_ds1386_init(unsigned long base) WRITE_RTC(0xB, byte); /* set the function pointers */ - rtc_get_time = rtc_ds1386_get_time; - rtc_set_time = rtc_ds1386_set_time; + rtc_mips_get_time = rtc_ds1386_get_time; + rtc_mips_set_time = rtc_ds1386_set_time; } Index: linux-2.6.16/arch/mips/dec/time.c =================================================================== --- linux-2.6.16.orig/arch/mips/dec/time.c +++ linux-2.6.16/arch/mips/dec/time.c @@ -193,8 +193,8 @@ static void dec_ioasic_hpt_init(unsigned void __init dec_time_init(void) { - rtc_get_time = dec_rtc_get_time; - rtc_set_mmss = dec_rtc_set_mmss; + rtc_mips_get_time = dec_rtc_get_time; + rtc_mips_set_mmss = dec_rtc_set_mmss; mips_timer_state = dec_timer_state; mips_timer_ack = dec_timer_ack; Index: linux-2.6.16/arch/mips/ite-boards/generic/time.c =================================================================== --- linux-2.6.16.orig/arch/mips/ite-boards/generic/time.c +++ linux-2.6.16/arch/mips/ite-boards/generic/time.c @@ -227,8 +227,8 @@ void __init it8172_time_init(void) local_irq_restore(flags); - rtc_get_time = it8172_rtc_get_time; - rtc_set_time = it8172_rtc_set_time; + rtc_mips_get_time = it8172_rtc_get_time; + rtc_mips_set_time = it8172_rtc_set_time; } #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) Index: linux-2.6.16/arch/mips/jmr3927/common/rtc_ds1742.c =================================================================== --- linux-2.6.16.orig/arch/mips/jmr3927/common/rtc_ds1742.c +++ linux-2.6.16/arch/mips/jmr3927/common/rtc_ds1742.c @@ -159,8 +159,8 @@ rtc_ds1742_init(unsigned long base) db_assert((rtc_base & 0xe0000000) == KSEG1); /* set the function pointers */ - rtc_get_time = rtc_ds1742_get_time; - rtc_set_time = rtc_ds1742_set_time; + rtc_mips_get_time = rtc_ds1742_get_time; + rtc_mips_set_time = rtc_ds1742_set_time; /* clear oscillator stop bit */ CMOS_WRITE(RTC_READ, RTC_CONTROL); Index: linux-2.6.16/arch/mips/kernel/time.c =================================================================== --- linux-2.6.16.orig/arch/mips/kernel/time.c +++ linux-2.6.16/arch/mips/kernel/time.c @@ -65,9 +65,9 @@ static int null_rtc_set_time(unsigned lo return 0; } -unsigned long (*rtc_get_time)(void) = null_rtc_get_time; -int (*rtc_set_time)(unsigned long) = null_rtc_set_time; -int (*rtc_set_mmss)(unsigned long); +unsigned long (*rtc_mips_get_time)(void) = null_rtc_get_time; +int (*rtc_mips_set_time)(unsigned long) = null_rtc_set_time; +int (*rtc_mips_set_mmss)(unsigned long); /* usecs per counter cycle, shifted to left by 32 bits */ @@ -440,14 +440,14 @@ irqreturn_t timer_interrupt(int irq, voi /* * If we have an externally synchronized Linux clock, then update - * CMOS clock accordingly every ~11 minutes. rtc_set_time() has to be + * CMOS clock accordingly every ~11 minutes. rtc_mips_set_time() has to be * called as close as possible to 500 ms before the new second starts. */ if (ntp_synced() && xtime.tv_sec > last_rtc_update + 660 && (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { - if (rtc_set_mmss(xtime.tv_sec) == 0) { + if (rtc_mips_set_mmss(xtime.tv_sec) == 0) { last_rtc_update = xtime.tv_sec; } else { /* do it again in 60 s */ @@ -565,7 +565,7 @@ asmlinkage void ll_local_timer_interrupt * b) (optional) calibrate and set the mips_hpt_frequency * (only needed if you intended to use fixed_rate_gettimeoffset * or use cpu counter as timer interrupt source) - * 2) setup xtime based on rtc_get_time(). + * 2) setup xtime based on rtc_mips_get_time(). * 3) choose a appropriate gettimeoffset routine. * 4) calculate a couple of cached variables for later usage * 5) board_timer_setup() - @@ -633,10 +633,10 @@ void __init time_init(void) if (board_time_init) board_time_init(); - if (!rtc_set_mmss) - rtc_set_mmss = rtc_set_time; + if (!rtc_mips_set_mmss) + rtc_mips_set_mmss = rtc_mips_set_time; - xtime.tv_sec = rtc_get_time(); + xtime.tv_sec = rtc_mips_get_time(); xtime.tv_nsec = 0; set_normalized_timespec(&wall_to_monotonic, @@ -772,8 +772,8 @@ void to_tm(unsigned long tim, struct rtc EXPORT_SYMBOL(rtc_lock); EXPORT_SYMBOL(to_tm); -EXPORT_SYMBOL(rtc_set_time); -EXPORT_SYMBOL(rtc_get_time); +EXPORT_SYMBOL(rtc_mips_set_time); +EXPORT_SYMBOL(rtc_mips_get_time); unsigned long long sched_clock(void) { Index: linux-2.6.16/arch/mips/lasat/setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/lasat/setup.c +++ linux-2.6.16/arch/mips/lasat/setup.c @@ -174,8 +174,8 @@ void __init plat_setup(void) #ifdef CONFIG_DS1603 ds1603 = &ds_defs[mips_machtype]; - rtc_get_time = ds1603_read; - rtc_set_time = ds1603_set; + rtc_mips_get_time = ds1603_read; + rtc_mips_set_time = ds1603_set; #endif #ifdef DYNAMIC_SERIAL_INIT Index: linux-2.6.16/arch/mips/mips-boards/atlas/atlas_setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/mips-boards/atlas/atlas_setup.c +++ linux-2.6.16/arch/mips/mips-boards/atlas/atlas_setup.c @@ -65,7 +65,7 @@ void __init plat_setup(void) board_time_init = mips_time_init; board_timer_setup = mips_timer_setup; - rtc_get_time = mips_rtc_get_time; + rtc_mips_get_time = mips_rtc_get_time; } static void __init serial_init(void) Index: linux-2.6.16/arch/mips/mips-boards/malta/malta_setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/mips-boards/malta/malta_setup.c +++ linux-2.6.16/arch/mips/mips-boards/malta/malta_setup.c @@ -225,5 +225,5 @@ void __init plat_setup(void) board_time_init = mips_time_init; board_timer_setup = mips_timer_setup; - rtc_get_time = mips_rtc_get_time; + rtc_mips_get_time = mips_rtc_get_time; } Index: linux-2.6.16/arch/mips/momentum/jaguar_atx/setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/momentum/jaguar_atx/setup.c +++ linux-2.6.16/arch/mips/momentum/jaguar_atx/setup.c @@ -229,8 +229,8 @@ void momenco_time_init(void) mips_hpt_frequency = cpu_clock / 2; board_timer_setup = momenco_timer_setup; - rtc_get_time = m48t37y_get_time; - rtc_set_time = m48t37y_set_time; + rtc_mips_get_time = m48t37y_get_time; + rtc_mips_set_time = m48t37y_set_time; } static struct resource mv_pci_io_mem0_resource = { Index: linux-2.6.16/arch/mips/momentum/ocelot_3/setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/momentum/ocelot_3/setup.c +++ linux-2.6.16/arch/mips/momentum/ocelot_3/setup.c @@ -215,8 +215,8 @@ void momenco_time_init(void) mips_hpt_frequency = cpu_clock / 2; board_timer_setup = momenco_timer_setup; - rtc_get_time = m48t37y_get_time; - rtc_set_time = m48t37y_set_time; + rtc_mips_get_time = m48t37y_get_time; + rtc_mips_set_time = m48t37y_set_time; } /* Index: linux-2.6.16/arch/mips/momentum/ocelot_c/setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/momentum/ocelot_c/setup.c +++ linux-2.6.16/arch/mips/momentum/ocelot_c/setup.c @@ -227,8 +227,8 @@ void momenco_time_init(void) printk("momenco_time_init cpu_clock=%d\n", cpu_clock); board_timer_setup = momenco_timer_setup; - rtc_get_time = m48t37y_get_time; - rtc_set_time = m48t37y_set_time; + rtc_mips_get_time = m48t37y_get_time; + rtc_mips_set_time = m48t37y_set_time; } void __init plat_setup(void) Index: linux-2.6.16/arch/mips/pmc-sierra/yosemite/setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/pmc-sierra/yosemite/setup.c +++ linux-2.6.16/arch/mips/pmc-sierra/yosemite/setup.c @@ -198,8 +198,8 @@ static void __init py_rtc_setup(void) if (!m48t37_base) printk(KERN_ERR "Mapping the RTC failed\n"); - rtc_get_time = m48t37y_get_time; - rtc_set_time = m48t37y_set_time; + rtc_mips_get_time = m48t37y_get_time; + rtc_mips_set_time = m48t37y_set_time; write_seqlock(&xtime_lock); xtime.tv_sec = m48t37y_get_time(); Index: linux-2.6.16/arch/mips/sgi-ip22/ip22-time.c =================================================================== --- linux-2.6.16.orig/arch/mips/sgi-ip22/ip22-time.c +++ linux-2.6.16/arch/mips/sgi-ip22/ip22-time.c @@ -212,8 +212,8 @@ static void indy_timer_setup(struct irqa void __init ip22_time_init(void) { /* setup hookup functions */ - rtc_get_time = indy_rtc_get_time; - rtc_set_time = indy_rtc_set_time; + rtc_mips_get_time = indy_rtc_get_time; + rtc_mips_set_time = indy_rtc_set_time; board_time_init = indy_time_init; board_timer_setup = indy_timer_setup; Index: linux-2.6.16/arch/mips/sgi-ip32/ip32-setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/sgi-ip32/ip32-setup.c +++ linux-2.6.16/arch/mips/sgi-ip32/ip32-setup.c @@ -91,8 +91,8 @@ void __init plat_setup(void) { board_be_init = ip32_be_init; - rtc_get_time = mc146818_get_cmos_time; - rtc_set_mmss = mc146818_set_rtc_mmss; + rtc_mips_get_time = mc146818_get_cmos_time; + rtc_mips_set_mmss = mc146818_set_rtc_mmss; board_time_init = ip32_time_init; board_timer_setup = ip32_timer_setup; Index: linux-2.6.16/arch/mips/sibyte/swarm/setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/sibyte/swarm/setup.c +++ linux-2.6.16/arch/mips/sibyte/swarm/setup.c @@ -121,14 +121,14 @@ void __init plat_setup(void) if (xicor_probe()) { printk("swarm setup: Xicor 1241 RTC detected.\n"); - rtc_get_time = xicor_get_time; - rtc_set_time = xicor_set_time; + rtc_mips_get_time = xicor_get_time; + rtc_mips_set_time = xicor_set_time; } if (m41t81_probe()) { printk("swarm setup: M41T81 RTC detected.\n"); - rtc_get_time = m41t81_get_time; - rtc_set_time = m41t81_set_time; + rtc_mips_get_time = m41t81_get_time; + rtc_mips_set_time = m41t81_set_time; } printk("This kernel optimized for " Index: linux-2.6.16/arch/mips/sni/setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/sni/setup.c +++ linux-2.6.16/arch/mips/sni/setup.c @@ -164,8 +164,8 @@ static struct pci_controller sni_control static inline void sni_pcimt_time_init(void) { - rtc_get_time = mc146818_get_cmos_time; - rtc_set_time = mc146818_set_rtc_mmss; + rtc_mips_get_time = mc146818_get_cmos_time; + rtc_mips_set_time = mc146818_set_rtc_mmss; } void __init plat_setup(void) Index: linux-2.6.16/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c =================================================================== --- linux-2.6.16.orig/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c +++ linux-2.6.16/arch/mips/tx4927/toshiba_rbtx4927/toshiba_rbtx4927_setup.c @@ -1036,8 +1036,8 @@ toshiba_rbtx4927_time_init(void) #ifdef CONFIG_RTC_DS1742 - rtc_get_time = rtc_ds1742_get_time; - rtc_set_time = rtc_ds1742_set_time; + rtc_mips_get_time = rtc_ds1742_get_time; + rtc_mips_set_time = rtc_ds1742_set_time; TOSHIBA_RBTX4927_SETUP_DPRINTK(TOSHIBA_RBTX4927_SETUP_TIME_INIT, ":rtc_ds1742_init()-\n"); Index: linux-2.6.16/arch/mips/tx4938/common/rtc_rx5c348.c =================================================================== --- linux-2.6.16.orig/arch/mips/tx4938/common/rtc_rx5c348.c +++ linux-2.6.16/arch/mips/tx4938/common/rtc_rx5c348.c @@ -197,6 +197,6 @@ rtc_rx5c348_init(int chipid) srtc_24h = 1; /* set the function pointers */ - rtc_get_time = rtc_rx5c348_get_time; - rtc_set_time = rtc_rx5c348_set_time; + rtc_mips_get_time = rtc_rx5c348_get_time; + rtc_mips_set_time = rtc_rx5c348_set_time; } Index: linux-2.6.16/include/asm-mips/time.h =================================================================== --- linux-2.6.16.orig/include/asm-mips/time.h +++ linux-2.6.16/include/asm-mips/time.h @@ -26,14 +26,14 @@ extern spinlock_t rtc_lock; /* * RTC ops. By default, they point to no-RTC functions. - * rtc_get_time - mktime(year, mon, day, hour, min, sec) in seconds. - * rtc_set_time - reverse the above translation and set time to RTC. - * rtc_set_mmss - similar to rtc_set_time, but only min and sec need + * rtc_mips_get_time - mktime(year, mon, day, hour, min, sec) in seconds. + * rtc_mips_set_time - reverse the above translation and set time to RTC. + * rtc_mips_set_mmss - similar to rtc_set_time, but only min and sec need * to be set. Used by RTC sync-up. */ -extern unsigned long (*rtc_get_time)(void); -extern int (*rtc_set_time)(unsigned long); -extern int (*rtc_set_mmss)(unsigned long); +extern unsigned long (*rtc_mips_get_time)(void); +extern int (*rtc_mips_set_time)(unsigned long); +extern int (*rtc_mips_set_mmss)(unsigned long); /* * Timer interrupt functions. Index: linux-2.6.16/drivers/rtc/class.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/class.c @@ -0,0 +1,145 @@ +/* + * RTC subsystem, base class + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * class skeleton from drivers/hwmon/hwmon.c + * + * 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 + +static DEFINE_IDR(rtc_idr); +static DEFINE_MUTEX(idr_lock); +struct class *rtc_class; + +static void rtc_device_release(struct class_device *class_dev) +{ + struct rtc_device *rtc = to_rtc_device(class_dev); + mutex_lock(&idr_lock); + idr_remove(&rtc_idr, rtc->id); + mutex_unlock(&idr_lock); + kfree(rtc); +} + +/** + * rtc_device_register - register w/ RTC class + * @dev: the device to register + * + * rtc_device_unregister() must be called when the class device is no + * longer needed. + * + * Returns the pointer to the new struct class device. + */ +struct rtc_device *rtc_device_register(const char *name, struct device *dev, + struct rtc_class_ops *ops, + struct module *owner) +{ + struct rtc_device *rtc; + int id, err; + + if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { + err = -ENOMEM; + goto exit; + } + + + mutex_lock(&idr_lock); + err = idr_get_new(&rtc_idr, NULL, &id); + mutex_unlock(&idr_lock); + + if (err < 0) + goto exit; + + id = id & MAX_ID_MASK; + + rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); + if (rtc == NULL) { + err = -ENOMEM; + goto exit_idr; + } + + rtc->id = id; + rtc->ops = ops; + rtc->owner = owner; + rtc->class_dev.dev = dev; + rtc->class_dev.class = rtc_class; + rtc->class_dev.release = rtc_device_release; + + mutex_init(&rtc->ops_lock); + spin_lock_init(&rtc->irq_lock); + spin_lock_init(&rtc->irq_task_lock); + + strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); + snprintf(rtc->class_dev.class_id, BUS_ID_SIZE, "rtc%d", id); + + err = class_device_register(&rtc->class_dev); + if (err) + goto exit_kfree; + + dev_info(dev, "rtc core: registered %s as %s\n", + rtc->name, rtc->class_dev.class_id); + + return rtc; + +exit_kfree: + kfree(rtc); + +exit_idr: + idr_remove(&rtc_idr, id); + +exit: + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(rtc_device_register); + + +/** + * rtc_device_unregister - removes the previously registered RTC class device + * + * @rtc: the RTC class device to destroy + */ +void rtc_device_unregister(struct rtc_device *rtc) +{ + mutex_lock(&rtc->ops_lock); + rtc->ops = NULL; + mutex_unlock(&rtc->ops_lock); + class_device_unregister(&rtc->class_dev); +} +EXPORT_SYMBOL_GPL(rtc_device_unregister); + +int rtc_interface_register(struct class_interface *intf) +{ + intf->class = rtc_class; + return class_interface_register(intf); +} +EXPORT_SYMBOL_GPL(rtc_interface_register); + +static int __init rtc_init(void) +{ + rtc_class = class_create(THIS_MODULE, "rtc"); + if (IS_ERR(rtc_class)) { + printk(KERN_ERR "%s: couldn't create class\n", __FILE__); + return PTR_ERR(rtc_class); + } + return 0; +} + +static void __exit rtc_exit(void) +{ + class_destroy(rtc_class); +} + +module_init(rtc_init); +module_exit(rtc_exit); + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("RTC class support"); +MODULE_LICENSE("GPL"); Index: linux-2.6.16/drivers/rtc/interface.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/interface.c @@ -0,0 +1,277 @@ +/* + * RTC subsystem, interface functions + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c + * + * 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 + +int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm) +{ + int err; + struct rtc_device *rtc = to_rtc_device(class_dev); + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return -EBUSY; + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->read_time) + err = -EINVAL; + else { + memset(tm, 0, sizeof(struct rtc_time)); + err = rtc->ops->read_time(class_dev->dev, tm); + } + + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_read_time); + +int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm) +{ + int err; + struct rtc_device *rtc = to_rtc_device(class_dev); + + err = rtc_valid_tm(tm); + if (err != 0) + return err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return -EBUSY; + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->set_time) + err = -EINVAL; + else + err = rtc->ops->set_time(class_dev->dev, tm); + + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_set_time); + +int rtc_set_mmss(struct class_device *class_dev, unsigned long secs) +{ + int err; + struct rtc_device *rtc = to_rtc_device(class_dev); + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return -EBUSY; + + if (!rtc->ops) + err = -ENODEV; + else if (rtc->ops->set_mmss) + err = rtc->ops->set_mmss(class_dev->dev, secs); + else if (rtc->ops->read_time && rtc->ops->set_time) { + struct rtc_time new, old; + + err = rtc->ops->read_time(class_dev->dev, &old); + if (err == 0) { + rtc_time_to_tm(secs, &new); + + /* + * avoid writing when we're going to change the day of + * the month. We will retry in the next minute. This + * basically means that if the RTC must not drift + * by more than 1 minute in 11 minutes. + */ + if (!((old.tm_hour == 23 && old.tm_min == 59) || + (new.tm_hour == 23 && new.tm_min == 59))) + err = rtc->ops->set_time(class_dev->dev, &new); + } + } + else + err = -EINVAL; + + mutex_unlock(&rtc->ops_lock); + + return err; +} +EXPORT_SYMBOL_GPL(rtc_set_mmss); + +int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm) +{ + int err; + struct rtc_device *rtc = to_rtc_device(class_dev); + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return -EBUSY; + + if (rtc->ops == NULL) + err = -ENODEV; + else if (!rtc->ops->read_alarm) + err = -EINVAL; + else { + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + err = rtc->ops->read_alarm(class_dev->dev, alarm); + } + + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_read_alarm); + +int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm) +{ + int err; + struct rtc_device *rtc = to_rtc_device(class_dev); + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return -EBUSY; + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->set_alarm) + err = -EINVAL; + else + err = rtc->ops->set_alarm(class_dev->dev, alarm); + + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_set_alarm); + +void rtc_update_irq(struct class_device *class_dev, + unsigned long num, unsigned long events) +{ + struct rtc_device *rtc = to_rtc_device(class_dev); + + spin_lock(&rtc->irq_lock); + rtc->irq_data = (rtc->irq_data + (num << 8)) | events; + spin_unlock(&rtc->irq_lock); + + spin_lock(&rtc->irq_task_lock); + if (rtc->irq_task) + rtc->irq_task->func(rtc->irq_task->private_data); + spin_unlock(&rtc->irq_task_lock); + + wake_up_interruptible(&rtc->irq_queue); + kill_fasync(&rtc->async_queue, SIGIO, POLL_IN); +} +EXPORT_SYMBOL_GPL(rtc_update_irq); + +struct class_device *rtc_class_open(char *name) +{ + struct class_device *class_dev = NULL, + *class_dev_tmp; + + down(&rtc_class->sem); + list_for_each_entry(class_dev_tmp, &rtc_class->children, node) { + if (strncmp(class_dev_tmp->class_id, name, BUS_ID_SIZE) == 0) { + class_dev = class_dev_tmp; + break; + } + } + + if (class_dev) { + if (!try_module_get(to_rtc_device(class_dev)->owner)) + class_dev = NULL; + } + up(&rtc_class->sem); + + return class_dev; +} +EXPORT_SYMBOL_GPL(rtc_class_open); + +void rtc_class_close(struct class_device *class_dev) +{ + module_put(to_rtc_device(class_dev)->owner); +} +EXPORT_SYMBOL_GPL(rtc_class_close); + +int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task) +{ + int retval = -EBUSY; + struct rtc_device *rtc = to_rtc_device(class_dev); + + if (task == NULL || task->func == NULL) + return -EINVAL; + + spin_lock(&rtc->irq_task_lock); + if (rtc->irq_task == NULL) { + rtc->irq_task = task; + retval = 0; + } + spin_unlock(&rtc->irq_task_lock); + + return retval; +} +EXPORT_SYMBOL_GPL(rtc_irq_register); + +void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task) +{ + struct rtc_device *rtc = to_rtc_device(class_dev); + + spin_lock(&rtc->irq_task_lock); + if (rtc->irq_task == task) + rtc->irq_task = NULL; + spin_unlock(&rtc->irq_task_lock); +} +EXPORT_SYMBOL_GPL(rtc_irq_unregister); + +int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int enabled) +{ + int err = 0; + unsigned long flags; + struct rtc_device *rtc = to_rtc_device(class_dev); + + spin_lock_irqsave(&rtc->irq_task_lock, flags); + if (rtc->irq_task != task) + err = -ENXIO; + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); + + if (err == 0) + err = rtc->ops->irq_set_state(class_dev->dev, enabled); + + return err; +} +EXPORT_SYMBOL_GPL(rtc_irq_set_state); + +int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq) +{ + int err = 0, tmp = 0; + unsigned long flags; + struct rtc_device *rtc = to_rtc_device(class_dev); + + /* allowed range is 2-8192 */ + if (freq < 2 || freq > 8192) + return -EINVAL; +/* + FIXME: this does not belong here, will move where appropriate + at a later stage. It cannot hurt right now, trust me :) + if ((freq > rtc_max_user_freq) && (!capable(CAP_SYS_RESOURCE))) + return -EACCES; +*/ + /* check if freq is a power of 2 */ + while (freq > (1 << tmp)) + tmp++; + + if (freq != (1 << tmp)) + return -EINVAL; + + spin_lock_irqsave(&rtc->irq_task_lock, flags); + if (rtc->irq_task != task) + err = -ENXIO; + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); + + if (err == 0) { + err = rtc->ops->irq_set_freq(class_dev->dev, freq); + if (err == 0) + rtc->irq_freq = freq; + } + return err; +} Index: linux-2.6.16/drivers/rtc/hctosys.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/hctosys.c @@ -0,0 +1,69 @@ +/* + * RTC subsystem, initialize system time on startup + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 + +/* IMPORTANT: the RTC only stores whole seconds. It is arbitrary + * whether it stores the most close value or the value with partial + * seconds truncated. However, it is important that we use it to store + * the truncated value. This is because otherwise it is necessary, + * in an rtc sync function, to read both xtime.tv_sec and + * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read + * of >32bits is not possible. So storing the most close value would + * slow down the sync API. So here we have the truncated value and + * the best guess is to add 0.5s. + */ + +static int __init rtc_hctosys(void) +{ + int err; + struct rtc_time tm; + struct class_device *class_dev = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); + + if (class_dev == NULL) { + printk("%s: unable to open rtc device (%s)\n", + __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); + return -ENODEV; + } + + err = rtc_read_time(class_dev, &tm); + if (err == 0) { + err = rtc_valid_tm(&tm); + if (err == 0) { + struct timespec tv; + + tv.tv_nsec = NSEC_PER_SEC >> 1; + + rtc_tm_to_time(&tm, &tv.tv_sec); + + do_settimeofday(&tv); + + dev_info(class_dev->dev, + "setting the system clock to " + "%d-%02d-%02d %02d:%02d:%02d (%u)\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + (unsigned int) tv.tv_sec); + } + else + dev_err(class_dev->dev, + "hctosys: invalid date/time\n"); + } + else + dev_err(class_dev->dev, + "hctosys: unable to read the hardware clock\n"); + + rtc_class_close(class_dev); + + return 0; +} + +late_initcall(rtc_hctosys); Index: linux-2.6.16/CREDITS =================================================================== --- linux-2.6.16.orig/CREDITS +++ linux-2.6.16/CREDITS @@ -3738,10 +3738,11 @@ D: Mylex DAC960 PCI RAID driver D: Miscellaneous kernel fixes N: Alessandro Zummo -E: azummo@ita.flashnet.it -W: http://freepage.logicom.it/azummo/ +E: a.zummo@towertech.it D: CMI8330 support is sb_card.c D: ISAPnP fixes in sb_card.c +D: ZyXEL omni.net lcd plus driver +D: RTC subsystem S: Italy N: Marc Zyngier Index: linux-2.6.16/MAINTAINERS =================================================================== --- linux-2.6.16.orig/MAINTAINERS +++ linux-2.6.16/MAINTAINERS @@ -2193,6 +2193,12 @@ M: p_gortmaker@yahoo.com L: linux-kernel@vger.kernel.org S: Maintained +REAL TIME CLOCK (RTC) SUBSYSTEM +P: Alessandro Zummo +M: a.zummo@towertech.it +L: linux-kernel@vger.kernel.org +S: Maintained + REISERFS FILE SYSTEM P: Hans Reiser M: reiserfs-dev@namesys.com Index: linux-2.6.16/drivers/i2c/chips/x1205.c =================================================================== --- linux-2.6.16.orig/drivers/i2c/chips/x1205.c +++ /dev/null @@ -1,698 +0,0 @@ -/* - * x1205.c - An i2c driver for the Xicor X1205 RTC - * Copyright 2004 Karen Spearel - * Copyright 2005 Alessandro Zummo - * - * please send all reports to: - * kas11 at tampabay dot rr dot com - * a dot zummo at towertech dot it - * - * based on the other drivers in this same directory. - * - * 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 - -#define DRV_VERSION "0.9.9" - -/* Addresses to scan: none. This chip is located at - * 0x6f and uses a two bytes register addressing. - * Two bytes need to be written to read a single register, - * while most other chips just require one and take the second - * one as the data to be written. To prevent corrupting - * unknown chips, the user must explicitely set the probe parameter. - */ - -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD; -I2C_CLIENT_MODULE_PARM(hctosys, - "Set the system time from the hardware clock upon initialization"); - -/* offsets into CCR area */ - -#define CCR_SEC 0 -#define CCR_MIN 1 -#define CCR_HOUR 2 -#define CCR_MDAY 3 -#define CCR_MONTH 4 -#define CCR_YEAR 5 -#define CCR_WDAY 6 -#define CCR_Y2K 7 - -#define X1205_REG_SR 0x3F /* status register */ -#define X1205_REG_Y2K 0x37 -#define X1205_REG_DW 0x36 -#define X1205_REG_YR 0x35 -#define X1205_REG_MO 0x34 -#define X1205_REG_DT 0x33 -#define X1205_REG_HR 0x32 -#define X1205_REG_MN 0x31 -#define X1205_REG_SC 0x30 -#define X1205_REG_DTR 0x13 -#define X1205_REG_ATR 0x12 -#define X1205_REG_INT 0x11 -#define X1205_REG_0 0x10 -#define X1205_REG_Y2K1 0x0F -#define X1205_REG_DWA1 0x0E -#define X1205_REG_YRA1 0x0D -#define X1205_REG_MOA1 0x0C -#define X1205_REG_DTA1 0x0B -#define X1205_REG_HRA1 0x0A -#define X1205_REG_MNA1 0x09 -#define X1205_REG_SCA1 0x08 -#define X1205_REG_Y2K0 0x07 -#define X1205_REG_DWA0 0x06 -#define X1205_REG_YRA0 0x05 -#define X1205_REG_MOA0 0x04 -#define X1205_REG_DTA0 0x03 -#define X1205_REG_HRA0 0x02 -#define X1205_REG_MNA0 0x01 -#define X1205_REG_SCA0 0x00 - -#define X1205_CCR_BASE 0x30 /* Base address of CCR */ -#define X1205_ALM0_BASE 0x00 /* Base address of ALARM0 */ - -#define X1205_SR_RTCF 0x01 /* Clock failure */ -#define X1205_SR_WEL 0x02 /* Write Enable Latch */ -#define X1205_SR_RWEL 0x04 /* Register Write Enable */ - -#define X1205_DTR_DTR0 0x01 -#define X1205_DTR_DTR1 0x02 -#define X1205_DTR_DTR2 0x04 - -#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */ - -/* Prototypes */ -static int x1205_attach(struct i2c_adapter *adapter); -static int x1205_detach(struct i2c_client *client); -static int x1205_probe(struct i2c_adapter *adapter, int address, int kind); -static int x1205_command(struct i2c_client *client, unsigned int cmd, - void *arg); - -static struct i2c_driver x1205_driver = { - .driver = { - .name = "x1205", - }, - .attach_adapter = &x1205_attach, - .detach_client = &x1205_detach, -}; - -struct x1205_data { - struct i2c_client client; - struct list_head list; - unsigned int epoch; -}; - -static const unsigned char days_in_mo[] = - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - -static LIST_HEAD(x1205_clients); - -/* Workaround until the I2C subsytem will allow to send - * commands to a specific client. This function will send the command - * to the first client. - */ -int x1205_do_command(unsigned int cmd, void *arg) -{ - struct list_head *walk; - struct list_head *tmp; - struct x1205_data *data; - - list_for_each_safe(walk, tmp, &x1205_clients) { - data = list_entry(walk, struct x1205_data, list); - return x1205_command(&data->client, cmd, arg); - } - - return -ENODEV; -} - -#define is_leap(year) \ - ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) - -/* make sure the rtc_time values are in bounds */ -static int x1205_validate_tm(struct rtc_time *tm) -{ - int year = tm->tm_year + 1900; - - if ((tm->tm_year < 70) || (tm->tm_year > 255)) - return -EINVAL; - - if ((tm->tm_mon > 11) || (tm->tm_mday == 0)) - return -EINVAL; - - if (tm->tm_mday > days_in_mo[tm->tm_mon] - + ((tm->tm_mon == 1) && is_leap(year))) - return -EINVAL; - - if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60)) - return -EINVAL; - - return 0; -} - -/* - * In the routines that deal directly with the x1205 hardware, we use - * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch - * Epoch is initialized as 2000. Time is set to UTC. - */ -static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, - u8 reg_base) -{ - unsigned char dt_addr[2] = { 0, reg_base }; - static unsigned char sr_addr[2] = { 0, X1205_REG_SR }; - - unsigned char buf[8], sr; - - struct i2c_msg msgs[] = { - { client->addr, 0, 2, sr_addr }, /* setup read ptr */ - { client->addr, I2C_M_RD, 1, &sr }, /* read status */ - { client->addr, 0, 2, dt_addr }, /* setup read ptr */ - { client->addr, I2C_M_RD, 8, buf }, /* read date */ - }; - - struct x1205_data *data = i2c_get_clientdata(client); - - /* read status register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { - dev_err(&client->dev, "%s: read error\n", __FUNCTION__); - return -EIO; - } - - /* check for battery failure */ - if (sr & X1205_SR_RTCF) { - dev_warn(&client->dev, - "Clock had a power failure, you must set the date.\n"); - return -EINVAL; - } - - /* read date registers */ - if ((i2c_transfer(client->adapter, &msgs[2], 2)) != 2) { - dev_err(&client->dev, "%s: read error\n", __FUNCTION__); - return -EIO; - } - - dev_dbg(&client->dev, - "%s: raw read data - sec=%02x, min=%02x, hr=%02x, " - "mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n", - __FUNCTION__, - buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6], buf[7]); - - tm->tm_sec = BCD2BIN(buf[CCR_SEC]); - tm->tm_min = BCD2BIN(buf[CCR_MIN]); - tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */ - tm->tm_mday = BCD2BIN(buf[CCR_MDAY]); - tm->tm_mon = BCD2BIN(buf[CCR_MONTH]); - data->epoch = BCD2BIN(buf[CCR_Y2K]) * 100; - tm->tm_year = BCD2BIN(buf[CCR_YEAR]) + data->epoch - 1900; - tm->tm_wday = buf[CCR_WDAY]; - - dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " - "mday=%d, mon=%d, year=%d, wday=%d\n", - __FUNCTION__, - tm->tm_sec, tm->tm_min, tm->tm_hour, - tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); - - return 0; -} - -static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, - int datetoo, u8 reg_base) -{ - int i, err, xfer; - - unsigned char buf[8]; - - static const unsigned char wel[3] = { 0, X1205_REG_SR, - X1205_SR_WEL }; - - static const unsigned char rwel[3] = { 0, X1205_REG_SR, - X1205_SR_WEL | X1205_SR_RWEL }; - - static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 }; - - struct x1205_data *data = i2c_get_clientdata(client); - - /* check if all values in the tm struct are correct */ - if ((err = x1205_validate_tm(tm)) < 0) - return err; - - dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " - "mday=%d, mon=%d, year=%d, wday=%d\n", - __FUNCTION__, - tm->tm_sec, tm->tm_min, tm->tm_hour, - tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); - - buf[CCR_SEC] = BIN2BCD(tm->tm_sec); - buf[CCR_MIN] = BIN2BCD(tm->tm_min); - - /* set hour and 24hr bit */ - buf[CCR_HOUR] = BIN2BCD(tm->tm_hour) | X1205_HR_MIL; - - /* should we also set the date? */ - if (datetoo) { - buf[CCR_MDAY] = BIN2BCD(tm->tm_mday); - - /* month, 0 - 11 */ - buf[CCR_MONTH] = BIN2BCD(tm->tm_mon); - - /* year, since 1900 */ - buf[CCR_YEAR] = BIN2BCD(tm->tm_year + 1900 - data->epoch); - buf[CCR_WDAY] = tm->tm_wday & 0x07; - buf[CCR_Y2K] = BIN2BCD(data->epoch / 100); - } - - /* this sequence is required to unlock the chip */ - xfer = i2c_master_send(client, wel, 3); - if (xfer != 3) { - dev_err(&client->dev, "%s: wel - %d\n", __FUNCTION__, xfer); - return -EIO; - } - - xfer = i2c_master_send(client, rwel, 3); - if (xfer != 3) { - dev_err(&client->dev, "%s: rwel - %d\n", __FUNCTION__, xfer); - return -EIO; - } - - /* write register's data */ - for (i = 0; i < (datetoo ? 8 : 3); i++) { - unsigned char rdata[3] = { 0, reg_base + i, buf[i] }; - - xfer = i2c_master_send(client, rdata, 3); - if (xfer != 3) { - dev_err(&client->dev, - "%s: xfer=%d addr=%02x, data=%02x\n", - __FUNCTION__, - xfer, rdata[1], rdata[2]); - return -EIO; - } - }; - - /* disable further writes */ - xfer = i2c_master_send(client, diswe, 3); - if (xfer != 3) { - dev_err(&client->dev, "%s: diswe - %d\n", __FUNCTION__, xfer); - return -EIO; - } - - return 0; -} - -static int x1205_get_dtrim(struct i2c_client *client, int *trim) -{ - unsigned char dtr; - static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR }; - - struct i2c_msg msgs[] = { - { client->addr, 0, 2, dtr_addr }, /* setup read ptr */ - { client->addr, I2C_M_RD, 1, &dtr }, /* read dtr */ - }; - - /* read dtr register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { - dev_err(&client->dev, "%s: read error\n", __FUNCTION__); - return -EIO; - } - - dev_dbg(&client->dev, "%s: raw dtr=%x\n", __FUNCTION__, dtr); - - *trim = 0; - - if (dtr & X1205_DTR_DTR0) - *trim += 20; - - if (dtr & X1205_DTR_DTR1) - *trim += 10; - - if (dtr & X1205_DTR_DTR2) - *trim = -*trim; - - return 0; -} - -static int x1205_get_atrim(struct i2c_client *client, int *trim) -{ - s8 atr; - static unsigned char atr_addr[2] = { 0, X1205_REG_ATR }; - - struct i2c_msg msgs[] = { - { client->addr, 0, 2, atr_addr }, /* setup read ptr */ - { client->addr, I2C_M_RD, 1, &atr }, /* read atr */ - }; - - /* read atr register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { - dev_err(&client->dev, "%s: read error\n", __FUNCTION__); - return -EIO; - } - - dev_dbg(&client->dev, "%s: raw atr=%x\n", __FUNCTION__, atr); - - /* atr is a two's complement value on 6 bits, - * perform sign extension. The formula is - * Catr = (atr * 0.25pF) + 11.00pF. - */ - if (atr & 0x20) - atr |= 0xC0; - - dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __FUNCTION__, atr, atr); - - *trim = (atr * 250) + 11000; - - dev_dbg(&client->dev, "%s: real=%d\n", __FUNCTION__, *trim); - - return 0; -} - -static int x1205_hctosys(struct i2c_client *client) -{ - int err; - - struct rtc_time tm; - struct timespec tv; - - err = x1205_command(client, X1205_CMD_GETDATETIME, &tm); - - if (err) { - dev_err(&client->dev, - "Unable to set the system clock\n"); - return err; - } - - /* IMPORTANT: the RTC only stores whole seconds. It is arbitrary - * whether it stores the most close value or the value with partial - * seconds truncated. However, it is important that we use it to store - * the truncated value. This is because otherwise it is necessary, - * in an rtc sync function, to read both xtime.tv_sec and - * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read - * of >32bits is not possible. So storing the most close value would - * slow down the sync API. So here we have the truncated value and - * the best guess is to add 0.5s. - */ - - tv.tv_nsec = NSEC_PER_SEC >> 1; - - /* WARNING: this is not the C library 'mktime' call, it is a built in - * inline function from include/linux/time.h. It expects (requires) - * the month to be in the range 1-12 - */ - - tv.tv_sec = mktime(tm.tm_year + 1900, tm.tm_mon + 1, - tm.tm_mday, tm.tm_hour, - tm.tm_min, tm.tm_sec); - - do_settimeofday(&tv); - - dev_info(&client->dev, - "setting the system clock to %d-%d-%d %d:%d:%d\n", - tm.tm_year + 1900, tm.tm_mon + 1, - tm.tm_mday, tm.tm_hour, tm.tm_min, - tm.tm_sec); - - return 0; -} - -struct x1205_limit -{ - unsigned char reg; - unsigned char mask; - unsigned char min; - unsigned char max; -}; - -static int x1205_validate_client(struct i2c_client *client) -{ - int i, xfer; - - /* Probe array. We will read the register at the specified - * address and check if the given bits are zero. - */ - static const unsigned char probe_zero_pattern[] = { - /* register, mask */ - X1205_REG_SR, 0x18, - X1205_REG_DTR, 0xF8, - X1205_REG_ATR, 0xC0, - X1205_REG_INT, 0x18, - X1205_REG_0, 0xFF, - }; - - static const struct x1205_limit probe_limits_pattern[] = { - /* register, mask, min, max */ - { X1205_REG_Y2K, 0xFF, 19, 20 }, - { X1205_REG_DW, 0xFF, 0, 6 }, - { X1205_REG_YR, 0xFF, 0, 99 }, - { X1205_REG_MO, 0xFF, 0, 12 }, - { X1205_REG_DT, 0xFF, 0, 31 }, - { X1205_REG_HR, 0x7F, 0, 23 }, - { X1205_REG_MN, 0xFF, 0, 59 }, - { X1205_REG_SC, 0xFF, 0, 59 }, - { X1205_REG_Y2K1, 0xFF, 19, 20 }, - { X1205_REG_Y2K0, 0xFF, 19, 20 }, - }; - - /* check that registers have bits a 0 where expected */ - for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) { - unsigned char buf; - - unsigned char addr[2] = { 0, probe_zero_pattern[i] }; - - struct i2c_msg msgs[2] = { - { client->addr, 0, 2, addr }, - { client->addr, I2C_M_RD, 1, &buf }, - }; - - xfer = i2c_transfer(client->adapter, msgs, 2); - if (xfer != 2) { - dev_err(&client->adapter->dev, - "%s: could not read register %x\n", - __FUNCTION__, addr[1]); - - return -EIO; - } - - if ((buf & probe_zero_pattern[i+1]) != 0) { - dev_err(&client->adapter->dev, - "%s: register=%02x, zero pattern=%d, value=%x\n", - __FUNCTION__, addr[1], i, buf); - - return -ENODEV; - } - } - - /* check limits (only registers with bcd values) */ - for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) { - unsigned char reg, value; - - unsigned char addr[2] = { 0, probe_limits_pattern[i].reg }; - - struct i2c_msg msgs[2] = { - { client->addr, 0, 2, addr }, - { client->addr, I2C_M_RD, 1, ® }, - }; - - xfer = i2c_transfer(client->adapter, msgs, 2); - - if (xfer != 2) { - dev_err(&client->adapter->dev, - "%s: could not read register %x\n", - __FUNCTION__, addr[1]); - - return -EIO; - } - - value = BCD2BIN(reg & probe_limits_pattern[i].mask); - - if (value > probe_limits_pattern[i].max || - value < probe_limits_pattern[i].min) { - dev_dbg(&client->adapter->dev, - "%s: register=%x, lim pattern=%d, value=%d\n", - __FUNCTION__, addr[1], i, value); - - return -ENODEV; - } - } - - return 0; -} - -static int x1205_attach(struct i2c_adapter *adapter) -{ - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); - - return i2c_probe(adapter, &addr_data, x1205_probe); -} - -int x1205_direct_attach(int adapter_id, - struct i2c_client_address_data *address_data) -{ - int err; - struct i2c_adapter *adapter = i2c_get_adapter(adapter_id); - - if (adapter) { - err = i2c_probe(adapter, - address_data, x1205_probe); - - i2c_put_adapter(adapter); - - return err; - } - - return -ENODEV; -} - -static int x1205_probe(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *client; - struct x1205_data *data; - - int err = 0; - - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); - - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { - err = -ENODEV; - goto exit; - } - - if (!(data = kzalloc(sizeof(struct x1205_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - - /* Initialize our structures */ - data->epoch = 2000; - - client = &data->client; - client->addr = address; - client->driver = &x1205_driver; - client->adapter = adapter; - - strlcpy(client->name, "x1205", I2C_NAME_SIZE); - - i2c_set_clientdata(client, data); - - /* Verify the chip is really an X1205 */ - if (kind < 0) { - if (x1205_validate_client(client) < 0) { - err = -ENODEV; - goto exit_kfree; - } - } - - /* Inform the i2c layer */ - if ((err = i2c_attach_client(client))) - goto exit_kfree; - - list_add(&data->list, &x1205_clients); - - dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); - - /* If requested, set the system time */ - if (hctosys) - x1205_hctosys(client); - - return 0; - -exit_kfree: - kfree(data); - -exit: - return err; -} - -static int x1205_detach(struct i2c_client *client) -{ - int err; - struct x1205_data *data = i2c_get_clientdata(client); - - dev_dbg(&client->dev, "%s\n", __FUNCTION__); - - if ((err = i2c_detach_client(client))) - return err; - - list_del(&data->list); - - kfree(data); - - return 0; -} - -static int x1205_command(struct i2c_client *client, unsigned int cmd, - void *param) -{ - if (param == NULL) - return -EINVAL; - - if (!capable(CAP_SYS_TIME)) - return -EACCES; - - dev_dbg(&client->dev, "%s: cmd=%d\n", __FUNCTION__, cmd); - - switch (cmd) { - case X1205_CMD_GETDATETIME: - return x1205_get_datetime(client, param, X1205_CCR_BASE); - - case X1205_CMD_SETTIME: - return x1205_set_datetime(client, param, 0, - X1205_CCR_BASE); - - case X1205_CMD_SETDATETIME: - return x1205_set_datetime(client, param, 1, - X1205_CCR_BASE); - - case X1205_CMD_GETALARM: - return x1205_get_datetime(client, param, X1205_ALM0_BASE); - - case X1205_CMD_SETALARM: - return x1205_set_datetime(client, param, 1, - X1205_ALM0_BASE); - - case X1205_CMD_GETDTRIM: - return x1205_get_dtrim(client, param); - - case X1205_CMD_GETATRIM: - return x1205_get_atrim(client, param); - - default: - return -EINVAL; - } -} - -static int __init x1205_init(void) -{ - return i2c_add_driver(&x1205_driver); -} - -static void __exit x1205_exit(void) -{ - i2c_del_driver(&x1205_driver); -} - -MODULE_AUTHOR( - "Karen Spearel , " - "Alessandro Zummo "); -MODULE_DESCRIPTION("Xicor X1205 RTC driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - -EXPORT_SYMBOL_GPL(x1205_do_command); -EXPORT_SYMBOL_GPL(x1205_direct_attach); - -module_init(x1205_init); -module_exit(x1205_exit); Index: linux-2.6.16/drivers/i2c/chips/Makefile =================================================================== --- linux-2.6.16.orig/drivers/i2c/chips/Makefile +++ linux-2.6.16/drivers/i2c/chips/Makefile @@ -10,10 +10,8 @@ obj-$(CONFIG_SENSORS_M41T00) += m41t00.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o -obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TPS65010) += tps65010.o -obj-$(CONFIG_RTC_X1205_I2C) += x1205.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG Index: linux-2.6.16/drivers/i2c/chips/rtc8564.c =================================================================== --- linux-2.6.16.orig/drivers/i2c/chips/rtc8564.c +++ /dev/null @@ -1,385 +0,0 @@ -/* - * linux/drivers/i2c/chips/rtc8564.c - * - * Copyright (C) 2002-2004 Stefan Eletzhofer - * - * based on linux/drivers/acron/char/pcf8583.c - * Copyright (C) 2000 Russell King - * - * 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. - * - * Driver for system3's EPSON RTC 8564 chip - */ -#include -#include -#include -#include -#include -#include -#include /* get the user-level API */ -#include - -#include "rtc8564.h" - -#ifdef DEBUG -# define _DBG(x, fmt, args...) do{ if (debug>=x) printk(KERN_DEBUG"%s: " fmt "\n", __FUNCTION__, ##args); } while(0); -#else -# define _DBG(x, fmt, args...) do { } while(0); -#endif - -#define _DBGRTCTM(x, rtctm) if (debug>=x) printk("%s: secs=%d, mins=%d, hours=%d, mday=%d, " \ - "mon=%d, year=%d, wday=%d VL=%d\n", __FUNCTION__, \ - (rtctm).secs, (rtctm).mins, (rtctm).hours, (rtctm).mday, \ - (rtctm).mon, (rtctm).year, (rtctm).wday, (rtctm).vl); - -struct rtc8564_data { - struct i2c_client client; - u16 ctrl; -}; - -static inline u8 _rtc8564_ctrl1(struct i2c_client *client) -{ - struct rtc8564_data *data = i2c_get_clientdata(client); - return data->ctrl & 0xff; -} -static inline u8 _rtc8564_ctrl2(struct i2c_client *client) -{ - struct rtc8564_data *data = i2c_get_clientdata(client); - return (data->ctrl & 0xff00) >> 8; -} - -#define CTRL1(c) _rtc8564_ctrl1(c) -#define CTRL2(c) _rtc8564_ctrl2(c) - -static int debug;; -module_param(debug, int, S_IRUGO | S_IWUSR); - -static struct i2c_driver rtc8564_driver; - -static unsigned short ignore[] = { I2C_CLIENT_END }; -static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END }; - -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_addr, - .probe = ignore, - .ignore = ignore, -}; - -static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem); -static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem); - -static int rtc8564_read(struct i2c_client *client, unsigned char adr, - unsigned char *buf, unsigned char len) -{ - int ret = -EIO; - unsigned char addr[1] = { adr }; - struct i2c_msg msgs[2] = { - {client->addr, 0, 1, addr}, - {client->addr, I2C_M_RD, len, buf} - }; - - _DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, buf, len); - - if (!buf) { - ret = -EINVAL; - goto done; - } - - ret = i2c_transfer(client->adapter, msgs, 2); - if (ret == 2) { - ret = 0; - } - -done: - return ret; -} - -static int rtc8564_write(struct i2c_client *client, unsigned char adr, - unsigned char *data, unsigned char len) -{ - int ret = 0; - unsigned char _data[16]; - struct i2c_msg wr; - int i; - - if (!data || len > 15) { - ret = -EINVAL; - goto done; - } - - _DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, data, len); - - _data[0] = adr; - for (i = 0; i < len; i++) { - _data[i + 1] = data[i]; - _DBG(5, "data[%d] = 0x%02x (%d)", i, data[i], data[i]); - } - - wr.addr = client->addr; - wr.flags = 0; - wr.len = len + 1; - wr.buf = _data; - - ret = i2c_transfer(client->adapter, &wr, 1); - if (ret == 1) { - ret = 0; - } - -done: - return ret; -} - -static int rtc8564_attach(struct i2c_adapter *adap, int addr, int kind) -{ - int ret; - struct i2c_client *new_client; - struct rtc8564_data *d; - unsigned char data[10]; - unsigned char ad[1] = { 0 }; - struct i2c_msg ctrl_wr[1] = { - {addr, 0, 2, data} - }; - struct i2c_msg ctrl_rd[2] = { - {addr, 0, 1, ad}, - {addr, I2C_M_RD, 2, data} - }; - - d = kzalloc(sizeof(struct rtc8564_data), GFP_KERNEL); - if (!d) { - ret = -ENOMEM; - goto done; - } - new_client = &d->client; - - strlcpy(new_client->name, "RTC8564", I2C_NAME_SIZE); - i2c_set_clientdata(new_client, d); - new_client->addr = addr; - new_client->adapter = adap; - new_client->driver = &rtc8564_driver; - - _DBG(1, "client=%p", new_client); - - /* init ctrl1 reg */ - data[0] = 0; - data[1] = 0; - ret = i2c_transfer(new_client->adapter, ctrl_wr, 1); - if (ret != 1) { - printk(KERN_INFO "rtc8564: cant init ctrl1\n"); - ret = -ENODEV; - goto done; - } - - /* read back ctrl1 and ctrl2 */ - ret = i2c_transfer(new_client->adapter, ctrl_rd, 2); - if (ret != 2) { - printk(KERN_INFO "rtc8564: cant read ctrl\n"); - ret = -ENODEV; - goto done; - } - - d->ctrl = data[0] | (data[1] << 8); - - _DBG(1, "RTC8564_REG_CTRL1=%02x, RTC8564_REG_CTRL2=%02x", - data[0], data[1]); - - ret = i2c_attach_client(new_client); -done: - if (ret) { - kfree(d); - } - return ret; -} - -static int rtc8564_probe(struct i2c_adapter *adap) -{ - return i2c_probe(adap, &addr_data, rtc8564_attach); -} - -static int rtc8564_detach(struct i2c_client *client) -{ - i2c_detach_client(client); - kfree(i2c_get_clientdata(client)); - return 0; -} - -static int rtc8564_get_datetime(struct i2c_client *client, struct rtc_tm *dt) -{ - int ret = -EIO; - unsigned char buf[15]; - - _DBG(1, "client=%p, dt=%p", client, dt); - - if (!dt) - return -EINVAL; - - memset(buf, 0, sizeof(buf)); - - ret = rtc8564_read(client, 0, buf, 15); - if (ret) - return ret; - - /* century stored in minute alarm reg */ - dt->year = BCD2BIN(buf[RTC8564_REG_YEAR]); - dt->year += 100 * BCD2BIN(buf[RTC8564_REG_AL_MIN] & 0x3f); - dt->mday = BCD2BIN(buf[RTC8564_REG_DAY] & 0x3f); - dt->wday = BCD2BIN(buf[RTC8564_REG_WDAY] & 7); - dt->mon = BCD2BIN(buf[RTC8564_REG_MON_CENT] & 0x1f); - - dt->secs = BCD2BIN(buf[RTC8564_REG_SEC] & 0x7f); - dt->vl = (buf[RTC8564_REG_SEC] & 0x80) == 0x80; - dt->mins = BCD2BIN(buf[RTC8564_REG_MIN] & 0x7f); - dt->hours = BCD2BIN(buf[RTC8564_REG_HR] & 0x3f); - - _DBGRTCTM(2, *dt); - - return 0; -} - -static int -rtc8564_set_datetime(struct i2c_client *client, struct rtc_tm *dt, int datetoo) -{ - int ret, len = 5; - unsigned char buf[15]; - - _DBG(1, "client=%p, dt=%p", client, dt); - - if (!dt) - return -EINVAL; - - _DBGRTCTM(2, *dt); - - buf[RTC8564_REG_CTRL1] = CTRL1(client) | RTC8564_CTRL1_STOP; - buf[RTC8564_REG_CTRL2] = CTRL2(client); - buf[RTC8564_REG_SEC] = BIN2BCD(dt->secs); - buf[RTC8564_REG_MIN] = BIN2BCD(dt->mins); - buf[RTC8564_REG_HR] = BIN2BCD(dt->hours); - - if (datetoo) { - len += 5; - buf[RTC8564_REG_DAY] = BIN2BCD(dt->mday); - buf[RTC8564_REG_WDAY] = BIN2BCD(dt->wday); - buf[RTC8564_REG_MON_CENT] = BIN2BCD(dt->mon) & 0x1f; - /* century stored in minute alarm reg */ - buf[RTC8564_REG_YEAR] = BIN2BCD(dt->year % 100); - buf[RTC8564_REG_AL_MIN] = BIN2BCD(dt->year / 100); - } - - ret = rtc8564_write(client, 0, buf, len); - if (ret) { - _DBG(1, "error writing data! %d", ret); - } - - buf[RTC8564_REG_CTRL1] = CTRL1(client); - ret = rtc8564_write(client, 0, buf, 1); - if (ret) { - _DBG(1, "error writing data! %d", ret); - } - - return ret; -} - -static int rtc8564_get_ctrl(struct i2c_client *client, unsigned int *ctrl) -{ - struct rtc8564_data *data = i2c_get_clientdata(client); - - if (!ctrl) - return -1; - - *ctrl = data->ctrl; - return 0; -} - -static int rtc8564_set_ctrl(struct i2c_client *client, unsigned int *ctrl) -{ - struct rtc8564_data *data = i2c_get_clientdata(client); - unsigned char buf[2]; - - if (!ctrl) - return -1; - - buf[0] = *ctrl & 0xff; - buf[1] = (*ctrl & 0xff00) >> 8; - data->ctrl = *ctrl; - - return rtc8564_write(client, 0, buf, 2); -} - -static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem) -{ - - if (!mem) - return -EINVAL; - - return rtc8564_read(client, mem->loc, mem->data, mem->nr); -} - -static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem) -{ - - if (!mem) - return -EINVAL; - - return rtc8564_write(client, mem->loc, mem->data, mem->nr); -} - -static int -rtc8564_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - - _DBG(1, "cmd=%d", cmd); - - switch (cmd) { - case RTC_GETDATETIME: - return rtc8564_get_datetime(client, arg); - - case RTC_SETTIME: - return rtc8564_set_datetime(client, arg, 0); - - case RTC_SETDATETIME: - return rtc8564_set_datetime(client, arg, 1); - - case RTC_GETCTRL: - return rtc8564_get_ctrl(client, arg); - - case RTC_SETCTRL: - return rtc8564_set_ctrl(client, arg); - - case MEM_READ: - return rtc8564_read_mem(client, arg); - - case MEM_WRITE: - return rtc8564_write_mem(client, arg); - - default: - return -EINVAL; - } -} - -static struct i2c_driver rtc8564_driver = { - .driver = { - .name = "RTC8564", - }, - .id = I2C_DRIVERID_RTC8564, - .attach_adapter = rtc8564_probe, - .detach_client = rtc8564_detach, - .command = rtc8564_command -}; - -static __init int rtc8564_init(void) -{ - return i2c_add_driver(&rtc8564_driver); -} - -static __exit void rtc8564_exit(void) -{ - i2c_del_driver(&rtc8564_driver); -} - -MODULE_AUTHOR("Stefan Eletzhofer "); -MODULE_DESCRIPTION("EPSON RTC8564 Driver"); -MODULE_LICENSE("GPL"); - -module_init(rtc8564_init); -module_exit(rtc8564_exit); Index: linux-2.6.16/drivers/i2c/chips/rtc8564.h =================================================================== --- linux-2.6.16.orig/drivers/i2c/chips/rtc8564.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * linux/drivers/i2c/chips/rtc8564.h - * - * Copyright (C) 2002-2004 Stefan Eletzhofer - * - * based on linux/drivers/acron/char/pcf8583.h - * Copyright (C) 2000 Russell King - * - * 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. - */ -struct rtc_tm { - unsigned char secs; - unsigned char mins; - unsigned char hours; - unsigned char mday; - unsigned char mon; - unsigned short year; /* xxxx 4 digits :) */ - unsigned char wday; - unsigned char vl; -}; - -struct mem { - unsigned int loc; - unsigned int nr; - unsigned char *data; -}; - -#define RTC_GETDATETIME 0 -#define RTC_SETTIME 1 -#define RTC_SETDATETIME 2 -#define RTC_GETCTRL 3 -#define RTC_SETCTRL 4 -#define MEM_READ 5 -#define MEM_WRITE 6 - -#define RTC8564_REG_CTRL1 0x0 /* T 0 S 0 | T 0 0 0 */ -#define RTC8564_REG_CTRL2 0x1 /* 0 0 0 TI/TP | AF TF AIE TIE */ -#define RTC8564_REG_SEC 0x2 /* VL 4 2 1 | 8 4 2 1 */ -#define RTC8564_REG_MIN 0x3 /* x 4 2 1 | 8 4 2 1 */ -#define RTC8564_REG_HR 0x4 /* x x 2 1 | 8 4 2 1 */ -#define RTC8564_REG_DAY 0x5 /* x x 2 1 | 8 4 2 1 */ -#define RTC8564_REG_WDAY 0x6 /* x x x x | x 4 2 1 */ -#define RTC8564_REG_MON_CENT 0x7 /* C x x 1 | 8 4 2 1 */ -#define RTC8564_REG_YEAR 0x8 /* 8 4 2 1 | 8 4 2 1 */ -#define RTC8564_REG_AL_MIN 0x9 /* AE 4 2 1 | 8 4 2 1 */ -#define RTC8564_REG_AL_HR 0xa /* AE 4 2 1 | 8 4 2 1 */ -#define RTC8564_REG_AL_DAY 0xb /* AE x 2 1 | 8 4 2 1 */ -#define RTC8564_REG_AL_WDAY 0xc /* AE x x x | x 4 2 1 */ -#define RTC8564_REG_CLKOUT 0xd /* FE x x x | x x FD1 FD0 */ -#define RTC8564_REG_TCTL 0xe /* TE x x x | x x FD1 FD0 */ -#define RTC8564_REG_TIMER 0xf /* 8 bit binary */ - -/* Control reg */ -#define RTC8564_CTRL1_TEST1 (1<<3) -#define RTC8564_CTRL1_STOP (1<<5) -#define RTC8564_CTRL1_TEST2 (1<<7) - -#define RTC8564_CTRL2_TIE (1<<0) -#define RTC8564_CTRL2_AIE (1<<1) -#define RTC8564_CTRL2_TF (1<<2) -#define RTC8564_CTRL2_AF (1<<3) -#define RTC8564_CTRL2_TI_TP (1<<4) - -/* CLKOUT frequencies */ -#define RTC8564_FD_32768HZ (0x0) -#define RTC8564_FD_1024HZ (0x1) -#define RTC8564_FD_32 (0x2) -#define RTC8564_FD_1HZ (0x3) - -/* Timer CTRL */ -#define RTC8564_TD_4096HZ (0x0) -#define RTC8564_TD_64HZ (0x1) -#define RTC8564_TD_1HZ (0x2) -#define RTC8564_TD_1_60HZ (0x3) - -#define I2C_DRIVERID_RTC8564 0xf000 Index: linux-2.6.16/include/linux/x1205.h =================================================================== --- linux-2.6.16.orig/include/linux/x1205.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * x1205.h - defines for drivers/i2c/chips/x1205.c - * Copyright 2004 Karen Spearel - * Copyright 2005 Alessandro Zummo - * - * 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. - */ - -#ifndef __LINUX_X1205_H__ -#define __LINUX_X1205_H__ - -/* commands */ - -#define X1205_CMD_GETDATETIME 0 -#define X1205_CMD_SETTIME 1 -#define X1205_CMD_SETDATETIME 2 -#define X1205_CMD_GETALARM 3 -#define X1205_CMD_SETALARM 4 -#define X1205_CMD_GETDTRIM 5 -#define X1205_CMD_SETDTRIM 6 -#define X1205_CMD_GETATRIM 7 -#define X1205_CMD_SETATRIM 8 - -extern int x1205_do_command(unsigned int cmd, void *arg); -extern int x1205_direct_attach(int adapter_id, - struct i2c_client_address_data *address_data); - -#endif /* __LINUX_X1205_H__ */ Index: linux-2.6.16/drivers/i2c/chips/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/i2c/chips/Kconfig +++ linux-2.6.16/drivers/i2c/chips/Kconfig @@ -65,15 +65,6 @@ config SENSORS_PCF8591 This driver can also be built as a module. If so, the module will be called pcf8591. -config SENSORS_RTC8564 - tristate "Epson 8564 RTC chip" - depends on I2C && EXPERIMENTAL - help - If you say yes here you get support for the Epson 8564 RTC chip. - - This driver can also be built as a module. If so, the module - will be called i2c-rtc8564. - config ISP1301_OMAP tristate "Philips ISP1301 with OMAP OTG" depends on I2C && ARCH_OMAP_OTG @@ -126,13 +117,4 @@ config SENSORS_MAX6875 This driver can also be built as a module. If so, the module will be called max6875. -config RTC_X1205_I2C - tristate "Xicor X1205 RTC chip" - depends on I2C && EXPERIMENTAL - help - If you say yes here you get support for the Xicor X1205 RTC chip. - - This driver can also be built as a module. If so, the module - will be called x1205. - endmenu Index: linux-2.6.16/drivers/rtc/rtc-sysfs.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-sysfs.c @@ -0,0 +1,124 @@ +/* + * RTC subsystem, sysfs interface + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 + +/* device attributes */ + +static ssize_t rtc_sysfs_show_name(struct class_device *dev, char *buf) +{ + return sprintf(buf, "%s\n", to_rtc_device(dev)->name); +} +static CLASS_DEVICE_ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL); + +static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(dev, &tm); + if (retval == 0) { + retval = sprintf(buf, "%04d-%02d-%02d\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + } + + return retval; +} +static CLASS_DEVICE_ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL); + +static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(dev, &tm); + if (retval == 0) { + retval = sprintf(buf, "%02d:%02d:%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec); + } + + return retval; +} +static CLASS_DEVICE_ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL); + +static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(dev, &tm); + if (retval == 0) { + unsigned long time; + rtc_tm_to_time(&tm, &time); + retval = sprintf(buf, "%lu\n", time); + } + + return retval; +} +static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL); + +static struct attribute *rtc_attrs[] = { + &class_device_attr_name.attr, + &class_device_attr_date.attr, + &class_device_attr_time.attr, + &class_device_attr_since_epoch.attr, + NULL, +}; + +static struct attribute_group rtc_attr_group = { + .attrs = rtc_attrs, +}; + +static int __devinit rtc_sysfs_add_device(struct class_device *class_dev, + struct class_interface *class_intf) +{ + int err; + + dev_info(class_dev->dev, "rtc intf: sysfs\n"); + + err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group); + if (err) + dev_err(class_dev->dev, + "failed to create sysfs attributes\n"); + + return err; +} + +static void rtc_sysfs_remove_device(struct class_device *class_dev, + struct class_interface *class_intf) +{ + sysfs_remove_group(&class_dev->kobj, &rtc_attr_group); +} + +/* interface registration */ + +static struct class_interface rtc_sysfs_interface = { + .add = &rtc_sysfs_add_device, + .remove = &rtc_sysfs_remove_device, +}; + +static int __init rtc_sysfs_init(void) +{ + return rtc_interface_register(&rtc_sysfs_interface); +} + +static void __exit rtc_sysfs_exit(void) +{ + class_interface_unregister(&rtc_sysfs_interface); +} + +module_init(rtc_sysfs_init); +module_exit(rtc_sysfs_exit); + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("RTC class sysfs interface"); +MODULE_LICENSE("GPL"); Index: linux-2.6.16/drivers/rtc/rtc-proc.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-proc.c @@ -0,0 +1,162 @@ +/* + * RTC subsystem, proc interface + * + * Copyright (C) 2005-06 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c + * + * 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 + +static struct class_device *rtc_dev = NULL; +static DEFINE_MUTEX(rtc_lock); + +static int rtc_proc_show(struct seq_file *seq, void *offset) +{ + int err; + struct class_device *class_dev = seq->private; + struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops; + struct rtc_wkalrm alrm; + struct rtc_time tm; + + err = rtc_read_time(class_dev, &tm); + if (err == 0) { + seq_printf(seq, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + } + + err = rtc_read_alarm(class_dev, &alrm); + if (err == 0) { + seq_printf(seq, "alrm_time\t: "); + if ((unsigned int)alrm.time.tm_hour <= 24) + seq_printf(seq, "%02d:", alrm.time.tm_hour); + else + seq_printf(seq, "**:"); + if ((unsigned int)alrm.time.tm_min <= 59) + seq_printf(seq, "%02d:", alrm.time.tm_min); + else + seq_printf(seq, "**:"); + if ((unsigned int)alrm.time.tm_sec <= 59) + seq_printf(seq, "%02d\n", alrm.time.tm_sec); + else + seq_printf(seq, "**\n"); + + seq_printf(seq, "alrm_date\t: "); + if ((unsigned int)alrm.time.tm_year <= 200) + seq_printf(seq, "%04d-", alrm.time.tm_year + 1900); + else + seq_printf(seq, "****-"); + if ((unsigned int)alrm.time.tm_mon <= 11) + seq_printf(seq, "%02d-", alrm.time.tm_mon + 1); + else + seq_printf(seq, "**-"); + if ((unsigned int)alrm.time.tm_mday <= 31) + seq_printf(seq, "%02d\n", alrm.time.tm_mday); + else + seq_printf(seq, "**\n"); + seq_printf(seq, "alrm_wakeup\t: %s\n", + alrm.enabled ? "yes" : "no"); + seq_printf(seq, "alrm_pending\t: %s\n", + alrm.pending ? "yes" : "no"); + } + + if (ops->proc) + ops->proc(class_dev->dev, seq); + + return 0; +} + +static int rtc_proc_open(struct inode *inode, struct file *file) +{ + struct class_device *class_dev = PDE(inode)->data; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + return single_open(file, rtc_proc_show, class_dev); +} + +static int rtc_proc_release(struct inode *inode, struct file *file) +{ + int res = single_release(inode, file); + module_put(THIS_MODULE); + return res; +} + +static struct file_operations rtc_proc_fops = { + .open = rtc_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = rtc_proc_release, +}; + +static int rtc_proc_add_device(struct class_device *class_dev, + struct class_interface *class_intf) +{ + mutex_lock(&rtc_lock); + if (rtc_dev == NULL) { + struct proc_dir_entry *ent; + + rtc_dev = class_dev; + + ent = create_proc_entry("driver/rtc", 0, NULL); + if (ent) { + struct rtc_device *rtc = to_rtc_device(class_dev); + + ent->proc_fops = &rtc_proc_fops; + ent->owner = rtc->owner; + ent->data = class_dev; + + dev_info(class_dev->dev, "rtc intf: proc\n"); + } + else + rtc_dev = NULL; + } + mutex_unlock(&rtc_lock); + + return 0; +} + +static void rtc_proc_remove_device(struct class_device *class_dev, + struct class_interface *class_intf) +{ + mutex_lock(&rtc_lock); + if (rtc_dev == class_dev) { + remove_proc_entry("driver/rtc", NULL); + rtc_dev = NULL; + } + mutex_unlock(&rtc_lock); +} + +static struct class_interface rtc_proc_interface = { + .add = &rtc_proc_add_device, + .remove = &rtc_proc_remove_device, +}; + +static int __init rtc_proc_init(void) +{ + return rtc_interface_register(&rtc_proc_interface); +} + +static void __exit rtc_proc_exit(void) +{ + class_interface_unregister(&rtc_proc_interface); +} + +module_init(rtc_proc_init); +module_exit(rtc_proc_exit); + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("RTC class proc interface"); +MODULE_LICENSE("GPL"); Index: linux-2.6.16/drivers/rtc/rtc-dev.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-dev.c @@ -0,0 +1,382 @@ +/* + * RTC subsystem, dev interface + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c + * + * 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 + +static struct class *rtc_dev_class; +static dev_t rtc_devt; + +#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ + +static int rtc_dev_open(struct inode *inode, struct file *file) +{ + int err; + struct rtc_device *rtc = container_of(inode->i_cdev, + struct rtc_device, char_dev); + struct rtc_class_ops *ops = rtc->ops; + + /* We keep the lock as long as the device is in use + * and return immediately if busy + */ + if (!(mutex_trylock(&rtc->char_lock))) + return -EBUSY; + + file->private_data = &rtc->class_dev; + + err = ops->open ? ops->open(rtc->class_dev.dev) : 0; + if (err == 0) { + spin_lock_irq(&rtc->irq_lock); + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + + return 0; + } + + /* something has gone wrong, release the lock */ + mutex_unlock(&rtc->char_lock); + return err; +} + + +static ssize_t +rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct rtc_device *rtc = to_rtc_device(file->private_data); + + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t ret; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc->irq_queue, &wait); + do { + __set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irq(&rtc->irq_lock); + data = rtc->irq_data; + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + + if (data != 0) { + ret = 0; + break; + } + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + schedule(); + } while (1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc->irq_queue, &wait); + + if (ret == 0) { + /* Check for any data updates */ + if (rtc->ops->read_callback) + data = rtc->ops->read_callback(rtc->class_dev.dev, data); + + ret = put_user(data, (unsigned long __user *)buf); + if (ret == 0) + ret = sizeof(unsigned long); + } + return ret; +} + +static unsigned int rtc_dev_poll(struct file *file, poll_table *wait) +{ + struct rtc_device *rtc = to_rtc_device(file->private_data); + unsigned long data; + + poll_wait(file, &rtc->irq_queue, wait); + + data = rtc->irq_data; + + return (data != 0) ? (POLLIN | POLLRDNORM) : 0; +} + +static int rtc_dev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct class_device *class_dev = file->private_data; + struct rtc_device *rtc = to_rtc_device(class_dev); + struct rtc_class_ops *ops = rtc->ops; + struct rtc_time tm; + struct rtc_wkalrm alarm; + void __user *uarg = (void __user *) arg; + + /* avoid conflicting IRQ users */ + if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) { + spin_lock(&rtc->irq_task_lock); + if (rtc->irq_task) + err = -EBUSY; + spin_unlock(&rtc->irq_task_lock); + + if (err < 0) + return err; + } + + /* try the driver's ioctl interface */ + if (ops->ioctl) { + err = ops->ioctl(class_dev->dev, cmd, arg); + if (err != -EINVAL) + return err; + } + + /* if the driver does not provide the ioctl interface + * or if that particular ioctl was not implemented + * (-EINVAL), we will try to emulate here. + */ + + switch (cmd) { + case RTC_ALM_READ: + err = rtc_read_alarm(class_dev, &alarm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &alarm.time, sizeof(tm))) + return -EFAULT; + break; + + case RTC_ALM_SET: + if (copy_from_user(&alarm.time, uarg, sizeof(tm))) + return -EFAULT; + + alarm.enabled = 0; + alarm.pending = 0; + alarm.time.tm_mday = -1; + alarm.time.tm_mon = -1; + alarm.time.tm_year = -1; + alarm.time.tm_wday = -1; + alarm.time.tm_yday = -1; + alarm.time.tm_isdst = -1; + err = rtc_set_alarm(class_dev, &alarm); + break; + + case RTC_RD_TIME: + err = rtc_read_time(class_dev, &tm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &tm, sizeof(tm))) + return -EFAULT; + break; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&tm, uarg, sizeof(tm))) + return -EFAULT; + + err = rtc_set_time(class_dev, &tm); + break; +#if 0 + case RTC_EPOCH_SET: +#ifndef rtc_epoch + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) { + err = -EINVAL; + break; + } + if (!capable(CAP_SYS_TIME)) { + err = -EACCES; + break; + } + rtc_epoch = arg; + err = 0; +#endif + break; + + case RTC_EPOCH_READ: + err = put_user(rtc_epoch, (unsigned long __user *)uarg); + break; +#endif + case RTC_WKALM_SET: + if (copy_from_user(&alarm, uarg, sizeof(alarm))) + return -EFAULT; + + err = rtc_set_alarm(class_dev, &alarm); + break; + + case RTC_WKALM_RD: + err = rtc_read_alarm(class_dev, &alarm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &alarm, sizeof(alarm))) + return -EFAULT; + break; + + default: + err = -EINVAL; + break; + } + + return err; +} + +static int rtc_dev_release(struct inode *inode, struct file *file) +{ + struct rtc_device *rtc = to_rtc_device(file->private_data); + + if (rtc->ops->release) + rtc->ops->release(rtc->class_dev.dev); + + mutex_unlock(&rtc->char_lock); + return 0; +} + +static int rtc_dev_fasync(int fd, struct file *file, int on) +{ + struct rtc_device *rtc = to_rtc_device(file->private_data); + return fasync_helper(fd, file, on, &rtc->async_queue); +} + +static struct file_operations rtc_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = rtc_dev_read, + .poll = rtc_dev_poll, + .ioctl = rtc_dev_ioctl, + .open = rtc_dev_open, + .release = rtc_dev_release, + .fasync = rtc_dev_fasync, +}; + +/* insertion/removal hooks */ + +static int rtc_dev_add_device(struct class_device *class_dev, + struct class_interface *class_intf) +{ + int err = 0; + struct rtc_device *rtc = to_rtc_device(class_dev); + + if (rtc->id >= RTC_DEV_MAX) { + dev_err(class_dev->dev, "too many RTCs\n"); + return -EINVAL; + } + + mutex_init(&rtc->char_lock); + spin_lock_init(&rtc->irq_lock); + init_waitqueue_head(&rtc->irq_queue); + + cdev_init(&rtc->char_dev, &rtc_dev_fops); + rtc->char_dev.owner = rtc->owner; + + if (cdev_add(&rtc->char_dev, MKDEV(MAJOR(rtc_devt), rtc->id), 1)) { + cdev_del(&rtc->char_dev); + dev_err(class_dev->dev, + "failed to add char device %d:%d\n", + MAJOR(rtc_devt), rtc->id); + return -ENODEV; + } + + rtc->rtc_dev = class_device_create(rtc_dev_class, NULL, + MKDEV(MAJOR(rtc_devt), rtc->id), + class_dev->dev, "rtc%d", rtc->id); + if (IS_ERR(rtc->rtc_dev)) { + dev_err(class_dev->dev, "cannot create rtc_dev device\n"); + err = PTR_ERR(rtc->rtc_dev); + goto err_cdev_del; + } + + dev_info(class_dev->dev, "rtc intf: dev (%d:%d)\n", + MAJOR(rtc->rtc_dev->devt), + MINOR(rtc->rtc_dev->devt)); + + return 0; + +err_cdev_del: + + cdev_del(&rtc->char_dev); + return err; +} + +static void rtc_dev_remove_device(struct class_device *class_dev, + struct class_interface *class_intf) +{ + struct rtc_device *rtc = to_rtc_device(class_dev); + + if (rtc->rtc_dev) { + dev_dbg(class_dev->dev, "removing char %d:%d\n", + MAJOR(rtc->rtc_dev->devt), + MINOR(rtc->rtc_dev->devt)); + + class_device_unregister(rtc->rtc_dev); + cdev_del(&rtc->char_dev); + } +} + +/* interface registration */ + +static struct class_interface rtc_dev_interface = { + .add = &rtc_dev_add_device, + .remove = &rtc_dev_remove_device, +}; + +static int __init rtc_dev_init(void) +{ + int err; + + rtc_dev_class = class_create(THIS_MODULE, "rtc-dev"); + if (IS_ERR(rtc_dev_class)) + return PTR_ERR(rtc_dev_class); + + err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); + if (err < 0) { + printk(KERN_ERR "%s: failed to allocate char dev region\n", + __FILE__); + goto err_destroy_class; + } + + err = rtc_interface_register(&rtc_dev_interface); + if (err < 0) { + printk(KERN_ERR "%s: failed to register the interface\n", + __FILE__); + goto err_unregister_chrdev; + } + + return 0; + +err_unregister_chrdev: + unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); + +err_destroy_class: + class_destroy(rtc_dev_class); + + return err; +} + +static void __exit rtc_dev_exit(void) +{ + class_interface_unregister(&rtc_dev_interface); + class_destroy(rtc_dev_class); + unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); +} + +module_init(rtc_dev_init); +module_exit(rtc_dev_exit); + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("RTC class dev interface"); +MODULE_LICENSE("GPL"); Index: linux-2.6.16/drivers/rtc/rtc-x1205.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-x1205.c @@ -0,0 +1,618 @@ +/* + * An i2c driver for the Xicor/Intersil X1205 RTC + * Copyright 2004 Karen Spearel + * Copyright 2005 Alessandro Zummo + * + * please send all reports to: + * Karen Spearel + * Alessandro Zummo + * + * based on a lot of other RTC drivers. + * + * 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 + +#define DRV_VERSION "1.0.6" + +/* Addresses to scan: none. This chip is located at + * 0x6f and uses a two bytes register addressing. + * Two bytes need to be written to read a single register, + * while most other chips just require one and take the second + * one as the data to be written. To prevent corrupting + * unknown chips, the user must explicitely set the probe parameter. + */ + +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD; + +/* offsets into CCR area */ + +#define CCR_SEC 0 +#define CCR_MIN 1 +#define CCR_HOUR 2 +#define CCR_MDAY 3 +#define CCR_MONTH 4 +#define CCR_YEAR 5 +#define CCR_WDAY 6 +#define CCR_Y2K 7 + +#define X1205_REG_SR 0x3F /* status register */ +#define X1205_REG_Y2K 0x37 +#define X1205_REG_DW 0x36 +#define X1205_REG_YR 0x35 +#define X1205_REG_MO 0x34 +#define X1205_REG_DT 0x33 +#define X1205_REG_HR 0x32 +#define X1205_REG_MN 0x31 +#define X1205_REG_SC 0x30 +#define X1205_REG_DTR 0x13 +#define X1205_REG_ATR 0x12 +#define X1205_REG_INT 0x11 +#define X1205_REG_0 0x10 +#define X1205_REG_Y2K1 0x0F +#define X1205_REG_DWA1 0x0E +#define X1205_REG_YRA1 0x0D +#define X1205_REG_MOA1 0x0C +#define X1205_REG_DTA1 0x0B +#define X1205_REG_HRA1 0x0A +#define X1205_REG_MNA1 0x09 +#define X1205_REG_SCA1 0x08 +#define X1205_REG_Y2K0 0x07 +#define X1205_REG_DWA0 0x06 +#define X1205_REG_YRA0 0x05 +#define X1205_REG_MOA0 0x04 +#define X1205_REG_DTA0 0x03 +#define X1205_REG_HRA0 0x02 +#define X1205_REG_MNA0 0x01 +#define X1205_REG_SCA0 0x00 + +#define X1205_CCR_BASE 0x30 /* Base address of CCR */ +#define X1205_ALM0_BASE 0x00 /* Base address of ALARM0 */ + +#define X1205_SR_RTCF 0x01 /* Clock failure */ +#define X1205_SR_WEL 0x02 /* Write Enable Latch */ +#define X1205_SR_RWEL 0x04 /* Register Write Enable */ + +#define X1205_DTR_DTR0 0x01 +#define X1205_DTR_DTR1 0x02 +#define X1205_DTR_DTR2 0x04 + +#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */ + +/* Prototypes */ +static int x1205_attach(struct i2c_adapter *adapter); +static int x1205_detach(struct i2c_client *client); +static int x1205_probe(struct i2c_adapter *adapter, int address, int kind); + +static struct i2c_driver x1205_driver = { + .driver = { + .name = "x1205", + }, + .attach_adapter = &x1205_attach, + .detach_client = &x1205_detach, +}; + +/* + * In the routines that deal directly with the x1205 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch + * Epoch is initialized as 2000. Time is set to UTC. + */ +static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, + unsigned char reg_base) +{ + unsigned char dt_addr[2] = { 0, reg_base }; + + unsigned char buf[8]; + + struct i2c_msg msgs[] = { + { client->addr, 0, 2, dt_addr }, /* setup read ptr */ + { client->addr, I2C_M_RD, 8, buf }, /* read date */ + }; + + /* read date registers */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + dev_dbg(&client->dev, + "%s: raw read data - sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n", + __FUNCTION__, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + tm->tm_sec = BCD2BIN(buf[CCR_SEC]); + tm->tm_min = BCD2BIN(buf[CCR_MIN]); + tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */ + tm->tm_mday = BCD2BIN(buf[CCR_MDAY]); + tm->tm_mon = BCD2BIN(buf[CCR_MONTH]) - 1; /* mon is 0-11 */ + tm->tm_year = BCD2BIN(buf[CCR_YEAR]) + + (BCD2BIN(buf[CCR_Y2K]) * 100) - 1900; + tm->tm_wday = buf[CCR_WDAY]; + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int x1205_get_status(struct i2c_client *client, unsigned char *sr) +{ + static unsigned char sr_addr[2] = { 0, X1205_REG_SR }; + + struct i2c_msg msgs[] = { + { client->addr, 0, 2, sr_addr }, /* setup read ptr */ + { client->addr, I2C_M_RD, 1, sr }, /* read status */ + }; + + /* read status register */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + return 0; +} + +static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, + int datetoo, u8 reg_base) +{ + int i, xfer; + unsigned char buf[8]; + + static const unsigned char wel[3] = { 0, X1205_REG_SR, + X1205_SR_WEL }; + + static const unsigned char rwel[3] = { 0, X1205_REG_SR, + X1205_SR_WEL | X1205_SR_RWEL }; + + static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 }; + + dev_dbg(&client->dev, + "%s: secs=%d, mins=%d, hours=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour); + + buf[CCR_SEC] = BIN2BCD(tm->tm_sec); + buf[CCR_MIN] = BIN2BCD(tm->tm_min); + + /* set hour and 24hr bit */ + buf[CCR_HOUR] = BIN2BCD(tm->tm_hour) | X1205_HR_MIL; + + /* should we also set the date? */ + if (datetoo) { + dev_dbg(&client->dev, + "%s: mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + buf[CCR_MDAY] = BIN2BCD(tm->tm_mday); + + /* month, 1 - 12 */ + buf[CCR_MONTH] = BIN2BCD(tm->tm_mon + 1); + + /* year, since the rtc epoch*/ + buf[CCR_YEAR] = BIN2BCD(tm->tm_year % 100); + buf[CCR_WDAY] = tm->tm_wday & 0x07; + buf[CCR_Y2K] = BIN2BCD(tm->tm_year / 100); + } + + /* this sequence is required to unlock the chip */ + if ((xfer = i2c_master_send(client, wel, 3)) != 3) { + dev_err(&client->dev, "%s: wel - %d\n", __FUNCTION__, xfer); + return -EIO; + } + + if ((xfer = i2c_master_send(client, rwel, 3)) != 3) { + dev_err(&client->dev, "%s: rwel - %d\n", __FUNCTION__, xfer); + return -EIO; + } + + /* write register's data */ + for (i = 0; i < (datetoo ? 8 : 3); i++) { + unsigned char rdata[3] = { 0, reg_base + i, buf[i] }; + + xfer = i2c_master_send(client, rdata, 3); + if (xfer != 3) { + dev_err(&client->dev, + "%s: xfer=%d addr=%02x, data=%02x\n", + __FUNCTION__, + xfer, rdata[1], rdata[2]); + return -EIO; + } + }; + + /* disable further writes */ + if ((xfer = i2c_master_send(client, diswe, 3)) != 3) { + dev_err(&client->dev, "%s: diswe - %d\n", __FUNCTION__, xfer); + return -EIO; + } + + return 0; +} + +static int x1205_fix_osc(struct i2c_client *client) +{ + int err; + struct rtc_time tm; + + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + + if ((err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE)) < 0) + dev_err(&client->dev, + "unable to restart the oscillator\n"); + + return err; +} + +static int x1205_get_dtrim(struct i2c_client *client, int *trim) +{ + unsigned char dtr; + static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR }; + + struct i2c_msg msgs[] = { + { client->addr, 0, 2, dtr_addr }, /* setup read ptr */ + { client->addr, I2C_M_RD, 1, &dtr }, /* read dtr */ + }; + + /* read dtr register */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + dev_dbg(&client->dev, "%s: raw dtr=%x\n", __FUNCTION__, dtr); + + *trim = 0; + + if (dtr & X1205_DTR_DTR0) + *trim += 20; + + if (dtr & X1205_DTR_DTR1) + *trim += 10; + + if (dtr & X1205_DTR_DTR2) + *trim = -*trim; + + return 0; +} + +static int x1205_get_atrim(struct i2c_client *client, int *trim) +{ + s8 atr; + static unsigned char atr_addr[2] = { 0, X1205_REG_ATR }; + + struct i2c_msg msgs[] = { + { client->addr, 0, 2, atr_addr }, /* setup read ptr */ + { client->addr, I2C_M_RD, 1, &atr }, /* read atr */ + }; + + /* read atr register */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + dev_dbg(&client->dev, "%s: raw atr=%x\n", __FUNCTION__, atr); + + /* atr is a two's complement value on 6 bits, + * perform sign extension. The formula is + * Catr = (atr * 0.25pF) + 11.00pF. + */ + if (atr & 0x20) + atr |= 0xC0; + + dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __FUNCTION__, atr, atr); + + *trim = (atr * 250) + 11000; + + dev_dbg(&client->dev, "%s: real=%d\n", __FUNCTION__, *trim); + + return 0; +} + +struct x1205_limit +{ + unsigned char reg, mask, min, max; +}; + +static int x1205_validate_client(struct i2c_client *client) +{ + int i, xfer; + + /* Probe array. We will read the register at the specified + * address and check if the given bits are zero. + */ + static const unsigned char probe_zero_pattern[] = { + /* register, mask */ + X1205_REG_SR, 0x18, + X1205_REG_DTR, 0xF8, + X1205_REG_ATR, 0xC0, + X1205_REG_INT, 0x18, + X1205_REG_0, 0xFF, + }; + + static const struct x1205_limit probe_limits_pattern[] = { + /* register, mask, min, max */ + { X1205_REG_Y2K, 0xFF, 19, 20 }, + { X1205_REG_DW, 0xFF, 0, 6 }, + { X1205_REG_YR, 0xFF, 0, 99 }, + { X1205_REG_MO, 0xFF, 0, 12 }, + { X1205_REG_DT, 0xFF, 0, 31 }, + { X1205_REG_HR, 0x7F, 0, 23 }, + { X1205_REG_MN, 0xFF, 0, 59 }, + { X1205_REG_SC, 0xFF, 0, 59 }, + { X1205_REG_Y2K1, 0xFF, 19, 20 }, + { X1205_REG_Y2K0, 0xFF, 19, 20 }, + }; + + /* check that registers have bits a 0 where expected */ + for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) { + unsigned char buf; + + unsigned char addr[2] = { 0, probe_zero_pattern[i] }; + + struct i2c_msg msgs[2] = { + { client->addr, 0, 2, addr }, + { client->addr, I2C_M_RD, 1, &buf }, + }; + + if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) { + dev_err(&client->adapter->dev, + "%s: could not read register %x\n", + __FUNCTION__, probe_zero_pattern[i]); + + return -EIO; + } + + if ((buf & probe_zero_pattern[i+1]) != 0) { + dev_err(&client->adapter->dev, + "%s: register=%02x, zero pattern=%d, value=%x\n", + __FUNCTION__, probe_zero_pattern[i], i, buf); + + return -ENODEV; + } + } + + /* check limits (only registers with bcd values) */ + for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) { + unsigned char reg, value; + + unsigned char addr[2] = { 0, probe_limits_pattern[i].reg }; + + struct i2c_msg msgs[2] = { + { client->addr, 0, 2, addr }, + { client->addr, I2C_M_RD, 1, ® }, + }; + + if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) { + dev_err(&client->adapter->dev, + "%s: could not read register %x\n", + __FUNCTION__, probe_limits_pattern[i].reg); + + return -EIO; + } + + value = BCD2BIN(reg & probe_limits_pattern[i].mask); + + if (value > probe_limits_pattern[i].max || + value < probe_limits_pattern[i].min) { + dev_dbg(&client->adapter->dev, + "%s: register=%x, lim pattern=%d, value=%d\n", + __FUNCTION__, probe_limits_pattern[i].reg, + i, value); + + return -ENODEV; + } + } + + return 0; +} + +static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + return x1205_get_datetime(to_i2c_client(dev), + &alrm->time, X1205_ALM0_BASE); +} + +static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + return x1205_set_datetime(to_i2c_client(dev), + &alrm->time, 1, X1205_ALM0_BASE); +} + +static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return x1205_get_datetime(to_i2c_client(dev), + tm, X1205_CCR_BASE); +} + +static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return x1205_set_datetime(to_i2c_client(dev), + tm, 1, X1205_CCR_BASE); +} + +static int x1205_rtc_proc(struct device *dev, struct seq_file *seq) +{ + int err, dtrim, atrim; + + seq_printf(seq, "24hr\t\t: yes\n"); + + if ((err = x1205_get_dtrim(to_i2c_client(dev), &dtrim)) == 0) + seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim); + + if ((err = x1205_get_atrim(to_i2c_client(dev), &atrim)) == 0) + seq_printf(seq, "analog_trim\t: %d.%02d pF\n", + atrim / 1000, atrim % 1000); + return 0; +} + +static struct rtc_class_ops x1205_rtc_ops = { + .proc = x1205_rtc_proc, + .read_time = x1205_rtc_read_time, + .set_time = x1205_rtc_set_time, + .read_alarm = x1205_rtc_read_alarm, + .set_alarm = x1205_rtc_set_alarm, +}; + +static ssize_t x1205_sysfs_show_atrim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int atrim; + + if (x1205_get_atrim(to_i2c_client(dev), &atrim) == 0) + return sprintf(buf, "%d.%02d pF\n", + atrim / 1000, atrim % 1000); + return 0; +} +static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL); + +static ssize_t x1205_sysfs_show_dtrim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int dtrim; + + if (x1205_get_dtrim(to_i2c_client(dev), &dtrim) == 0) + return sprintf(buf, "%d ppm\n", dtrim); + + return 0; +} +static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL); + +static int x1205_attach(struct i2c_adapter *adapter) +{ + dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + return i2c_probe(adapter, &addr_data, x1205_probe); +} + +static int x1205_probe(struct i2c_adapter *adapter, int address, int kind) +{ + int err = 0; + unsigned char sr; + struct i2c_client *client; + struct rtc_device *rtc; + + dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto exit; + } + + if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + /* I2C client */ + client->addr = address; + client->driver = &x1205_driver; + client->adapter = adapter; + + strlcpy(client->name, x1205_driver.driver.name, I2C_NAME_SIZE); + + /* Verify the chip is really an X1205 */ + if (kind < 0) { + if (x1205_validate_client(client) < 0) { + err = -ENODEV; + goto exit_kfree; + } + } + + /* Inform the i2c layer */ + if ((err = i2c_attach_client(client))) + goto exit_kfree; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + rtc = rtc_device_register(x1205_driver.driver.name, &client->dev, + &x1205_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + err = PTR_ERR(rtc); + dev_err(&client->dev, + "unable to register the class device\n"); + goto exit_detach; + } + + i2c_set_clientdata(client, rtc); + + /* Check for power failures and eventualy enable the osc */ + if ((err = x1205_get_status(client, &sr)) == 0) { + if (sr & X1205_SR_RTCF) { + dev_err(&client->dev, + "power failure detected, " + "please set the clock\n"); + udelay(50); + x1205_fix_osc(client); + } + } + else + dev_err(&client->dev, "couldn't read status\n"); + + device_create_file(&client->dev, &dev_attr_atrim); + device_create_file(&client->dev, &dev_attr_dtrim); + + return 0; + +exit_detach: + i2c_detach_client(client); + +exit_kfree: + kfree(client); + +exit: + return err; +} + +static int x1205_detach(struct i2c_client *client) +{ + int err; + struct rtc_device *rtc = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __FUNCTION__); + + if (rtc) + rtc_device_unregister(rtc); + + if ((err = i2c_detach_client(client))) + return err; + + kfree(client); + + return 0; +} + +static int __init x1205_init(void) +{ + return i2c_add_driver(&x1205_driver); +} + +static void __exit x1205_exit(void) +{ + i2c_del_driver(&x1205_driver); +} + +MODULE_AUTHOR( + "Karen Spearel , " + "Alessandro Zummo "); +MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(x1205_init); +module_exit(x1205_exit); Index: linux-2.6.16/drivers/rtc/rtc-test.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-test.c @@ -0,0 +1,204 @@ +/* + * An RTC test device/driver + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 + +static struct platform_device *test0 = NULL, *test1 = NULL; + +static int test_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + return 0; +} + +static int test_rtc_set_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + return 0; +} + +static int test_rtc_read_time(struct device *dev, + struct rtc_time *tm) +{ + rtc_time_to_tm(get_seconds(), tm); + return 0; +} + +static int test_rtc_set_time(struct device *dev, + struct rtc_time *tm) +{ + return 0; +} + +static int test_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + return 0; +} + +static int test_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct platform_device *plat_dev = to_platform_device(dev); + + seq_printf(seq, "24hr\t\t: yes\n"); + seq_printf(seq, "test\t\t: yes\n"); + seq_printf(seq, "id\t\t: %d\n", plat_dev->id); + + return 0; +} + +static int test_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + /* We do support interrupts, they're generated + * using the sysfs interface. + */ + switch (cmd) { + case RTC_PIE_ON: + case RTC_PIE_OFF: + case RTC_UIE_ON: + case RTC_UIE_OFF: + case RTC_AIE_ON: + case RTC_AIE_OFF: + return 0; + + default: + return -EINVAL; + } +} + +static struct rtc_class_ops test_rtc_ops = { + .proc = test_rtc_proc, + .read_time = test_rtc_read_time, + .set_time = test_rtc_set_time, + .read_alarm = test_rtc_read_alarm, + .set_alarm = test_rtc_set_alarm, + .set_mmss = test_rtc_set_mmss, + .ioctl = test_rtc_ioctl, +}; + +static ssize_t test_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", 42); +} +static ssize_t test_irq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + struct platform_device *plat_dev = to_platform_device(dev); + struct rtc_device *rtc = platform_get_drvdata(plat_dev); + + retval = count; + if (strncmp(buf, "tick", 4) == 0) + rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF); + else if (strncmp(buf, "alarm", 5) == 0) + rtc_update_irq(&rtc->class_dev, 1, RTC_AF | RTC_IRQF); + else if (strncmp(buf, "update", 6) == 0) + rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF); + else + retval = -EINVAL; + + return retval; +} +static DEVICE_ATTR(irq, S_IRUGO | S_IWUSR, test_irq_show, test_irq_store); + +static int test_probe(struct platform_device *plat_dev) +{ + int err; + struct rtc_device *rtc = rtc_device_register("test", &plat_dev->dev, + &test_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + err = PTR_ERR(rtc); + dev_err(&plat_dev->dev, + "unable to register the class device\n"); + return err; + } + device_create_file(&plat_dev->dev, &dev_attr_irq); + + platform_set_drvdata(plat_dev, rtc); + + return 0; +} + +static int __devexit test_remove(struct platform_device *plat_dev) +{ + struct rtc_device *rtc = platform_get_drvdata(plat_dev); + + rtc_device_unregister(rtc); + device_remove_file(&plat_dev->dev, &dev_attr_irq); + + return 0; +} + +static struct platform_driver test_drv = { + .probe = test_probe, + .remove = __devexit_p(test_remove), + .driver = { + .name = "rtc-test", + .owner = THIS_MODULE, + }, +}; + +static int __init test_init(void) +{ + int err; + + if ((err = platform_driver_register(&test_drv))) + return err; + + if ((test0 = platform_device_alloc("rtc-test", 0)) == NULL) { + err = -ENOMEM; + goto exit_driver_unregister; + } + + if ((test1 = platform_device_alloc("rtc-test", 1)) == NULL) { + err = -ENOMEM; + goto exit_free_test0; + } + + if ((err = platform_device_add(test0))) + goto exit_free_test1; + + if ((err = platform_device_add(test1))) + goto exit_device_unregister; + + return 0; + +exit_device_unregister: + platform_device_unregister(test0); + +exit_free_test1: + platform_device_put(test1); + +exit_free_test0: + platform_device_put(test0); + +exit_driver_unregister: + platform_driver_unregister(&test_drv); + return err; +} + +static void __exit test_exit(void) +{ + platform_device_unregister(test0); + platform_device_unregister(test1); + platform_driver_unregister(&test_drv); +} + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("RTC test driver/device"); +MODULE_LICENSE("GPL"); + +module_init(test_init); +module_exit(test_exit); Index: linux-2.6.16/drivers/rtc/rtc-ds1672.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-ds1672.c @@ -0,0 +1,232 @@ +/* + * An rtc/i2c driver for the Dallas DS1672 + * Copyright 2005 Alessandro Zummo + * + * 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 + +#define DRV_VERSION "0.2" + +/* Addresses to scan: none. This chip cannot be detected. */ +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD; + +/* Registers */ + +#define DS1672_REG_CNT_BASE 0 +#define DS1672_REG_CONTROL 4 +#define DS1672_REG_TRICKLE 5 + + +/* Prototypes */ +static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind); + +/* + * In the routines that deal directly with the ds1672 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch + * Epoch is initialized as 2000. Time is set to UTC. + */ +static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned long time; + unsigned char addr = DS1672_REG_CNT_BASE; + unsigned char buf[4]; + + struct i2c_msg msgs[] = { + { client->addr, 0, 1, &addr }, /* setup read ptr */ + { client->addr, I2C_M_RD, 4, buf }, /* read date */ + }; + + /* read date registers */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + dev_dbg(&client->dev, + "%s: raw read data - counters=%02x,%02x,%02x,%02x\n" + __FUNCTION__, + buf[0], buf[1], buf[2], buf[3]); + + time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + rtc_time_to_tm(time, tm); + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs) +{ + int xfer; + unsigned char buf[5]; + + buf[0] = DS1672_REG_CNT_BASE; + buf[1] = secs & 0x000000FF; + buf[2] = (secs & 0x0000FF00) >> 8; + buf[3] = (secs & 0x00FF0000) >> 16; + buf[4] = (secs & 0xFF000000) >> 24; + + xfer = i2c_master_send(client, buf, 5); + if (xfer != 5) { + dev_err(&client->dev, "%s: send: %d\n", __FUNCTION__, xfer); + return -EIO; + } + + return 0; +} + +static int ds1672_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned long secs; + + dev_dbg(&client->dev, + "%s: secs=%d, mins=%d, hours=%d, ", + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + rtc_tm_to_time(tm, &secs); + + return ds1672_set_mmss(client, secs); +} + +static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return ds1672_get_datetime(to_i2c_client(dev), tm); +} + +static int ds1672_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return ds1672_set_datetime(to_i2c_client(dev), tm); +} + +static int ds1672_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + return ds1672_set_mmss(to_i2c_client(dev), secs); +} + +static struct rtc_class_ops ds1672_rtc_ops = { + .read_time = ds1672_rtc_read_time, + .set_time = ds1672_rtc_set_time, + .set_mmss = ds1672_rtc_set_mmss, +}; + +static int ds1672_attach(struct i2c_adapter *adapter) +{ + dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + return i2c_probe(adapter, &addr_data, ds1672_probe); +} + +static int ds1672_detach(struct i2c_client *client) +{ + int err; + struct rtc_device *rtc = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __FUNCTION__); + + if (rtc) + rtc_device_unregister(rtc); + + if ((err = i2c_detach_client(client))) + return err; + + kfree(client); + + return 0; +} + +static struct i2c_driver ds1672_driver = { + .driver = { + .name = "ds1672", + }, + .attach_adapter = &ds1672_attach, + .detach_client = &ds1672_detach, +}; + +static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind) +{ + int err = 0; + struct i2c_client *client; + struct rtc_device *rtc; + + dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto exit; + } + + if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + /* I2C client */ + client->addr = address; + client->driver = &ds1672_driver; + client->adapter = adapter; + + strlcpy(client->name, ds1672_driver.driver.name, I2C_NAME_SIZE); + + /* Inform the i2c layer */ + if ((err = i2c_attach_client(client))) + goto exit_kfree; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + rtc = rtc_device_register(ds1672_driver.driver.name, &client->dev, + &ds1672_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + err = PTR_ERR(rtc); + dev_err(&client->dev, + "unable to register the class device\n"); + goto exit_detach; + } + + i2c_set_clientdata(client, rtc); + + return 0; + +exit_detach: + i2c_detach_client(client); + +exit_kfree: + kfree(client); + +exit: + return err; +} + +static int __init ds1672_init(void) +{ + return i2c_add_driver(&ds1672_driver); +} + +static void __exit ds1672_exit(void) +{ + i2c_del_driver(&ds1672_driver); +} + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(ds1672_init); +module_exit(ds1672_exit); Index: linux-2.6.16/drivers/rtc/rtc-pcf8563.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-pcf8563.c @@ -0,0 +1,354 @@ +/* + * An I2C driver for the Philips PCF8563 RTC + * Copyright 2005-06 Tower Technologies + * + * Author: Alessandro Zummo + * Maintainers: http://www.nslu2-linux.org/ + * + * based on the other drivers in this same directory. + * + * http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf + * + * 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 + +#define DRV_VERSION "0.4.2" + +/* Addresses to scan: none + * This chip cannot be reliably autodetected. An empty eeprom + * located at 0x51 will pass the validation routine due to + * the way the registers are implemented. + */ +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Module parameters */ +I2C_CLIENT_INSMOD; + +#define PCF8563_REG_ST1 0x00 /* status */ +#define PCF8563_REG_ST2 0x01 + +#define PCF8563_REG_SC 0x02 /* datetime */ +#define PCF8563_REG_MN 0x03 +#define PCF8563_REG_HR 0x04 +#define PCF8563_REG_DM 0x05 +#define PCF8563_REG_DW 0x06 +#define PCF8563_REG_MO 0x07 +#define PCF8563_REG_YR 0x08 + +#define PCF8563_REG_AMN 0x09 /* alarm */ +#define PCF8563_REG_AHR 0x0A +#define PCF8563_REG_ADM 0x0B +#define PCF8563_REG_ADW 0x0C + +#define PCF8563_REG_CLKO 0x0D /* clock out */ +#define PCF8563_REG_TMRC 0x0E /* timer control */ +#define PCF8563_REG_TMR 0x0F /* timer */ + +#define PCF8563_SC_LV 0x80 /* low voltage */ +#define PCF8563_MO_C 0x80 /* century */ + +/* Prototypes */ +static int pcf8563_attach(struct i2c_adapter *adapter); +static int pcf8563_detach(struct i2c_client *client); +static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind); + +static struct i2c_driver pcf8563_driver = { + .driver = { + .name = "pcf8563", + }, + .attach_adapter = &pcf8563_attach, + .detach_client = &pcf8563_detach, +}; + +/* + * In the routines that deal directly with the pcf8563 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned char buf[13] = { PCF8563_REG_ST1 }; + + struct i2c_msg msgs[] = { + { client->addr, 0, 1, buf }, /* setup read ptr */ + { client->addr, I2C_M_RD, 13, buf }, /* read status + date */ + }; + + /* read registers */ + if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) + dev_info(&client->dev, + "low voltage detected, date/time is not reliable.\n"); + + dev_dbg(&client->dev, + "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", + __FUNCTION__, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8]); + + + tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F); + tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F); + tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F); + tm->tm_wday = buf[PCF8563_REG_DW] & 0x07; + tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR]) + + (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 100 : 0); + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* the clock can give out invalid datetime, but we cannot return + * -EINVAL otherwise hwclock will refuse to set the time on bootup. + */ + if (rtc_valid_tm(tm) < 0) + dev_err(&client->dev, "retrieved date/time is not valid.\n"); + + return 0; +} + +static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + int i, err; + unsigned char buf[9]; + + dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* hours, minutes and seconds */ + buf[PCF8563_REG_SC] = BIN2BCD(tm->tm_sec); + buf[PCF8563_REG_MN] = BIN2BCD(tm->tm_min); + buf[PCF8563_REG_HR] = BIN2BCD(tm->tm_hour); + + buf[PCF8563_REG_DM] = BIN2BCD(tm->tm_mday); + + /* month, 1 - 12 */ + buf[PCF8563_REG_MO] = BIN2BCD(tm->tm_mon + 1); + + /* year and century */ + buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100); + if (tm->tm_year / 100) + buf[PCF8563_REG_MO] |= PCF8563_MO_C; + + buf[PCF8563_REG_DW] = tm->tm_wday & 0x07; + + /* write register's data */ + for (i = 0; i < 7; i++) { + unsigned char data[2] = { PCF8563_REG_SC + i, + buf[PCF8563_REG_SC + i] }; + + err = i2c_master_send(client, data, sizeof(data)); + if (err != sizeof(data)) { + dev_err(&client->dev, + "%s: err=%d addr=%02x, data=%02x\n", + __FUNCTION__, err, data[0], data[1]); + return -EIO; + } + }; + + return 0; +} + +struct pcf8563_limit +{ + unsigned char reg; + unsigned char mask; + unsigned char min; + unsigned char max; +}; + +static int pcf8563_validate_client(struct i2c_client *client) +{ + int i; + + static const struct pcf8563_limit pattern[] = { + /* register, mask, min, max */ + { PCF8563_REG_SC, 0x7F, 0, 59 }, + { PCF8563_REG_MN, 0x7F, 0, 59 }, + { PCF8563_REG_HR, 0x3F, 0, 23 }, + { PCF8563_REG_DM, 0x3F, 0, 31 }, + { PCF8563_REG_MO, 0x1F, 0, 12 }, + }; + + /* check limits (only registers with bcd values) */ + for (i = 0; i < ARRAY_SIZE(pattern); i++) { + int xfer; + unsigned char value; + unsigned char buf = pattern[i].reg; + + struct i2c_msg msgs[] = { + { client->addr, 0, 1, &buf }, + { client->addr, I2C_M_RD, 1, &buf }, + }; + + xfer = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + + if (xfer != ARRAY_SIZE(msgs)) { + dev_err(&client->adapter->dev, + "%s: could not read register 0x%02X\n", + __FUNCTION__, pattern[i].reg); + + return -EIO; + } + + value = BCD2BIN(buf & pattern[i].mask); + + if (value > pattern[i].max || + value < pattern[i].min) { + dev_dbg(&client->adapter->dev, + "%s: pattern=%d, reg=%x, mask=0x%02x, min=%d, " + "max=%d, value=%d, raw=0x%02X\n", + __FUNCTION__, i, pattern[i].reg, pattern[i].mask, + pattern[i].min, pattern[i].max, + value, buf); + + return -ENODEV; + } + } + + return 0; +} + +static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return pcf8563_get_datetime(to_i2c_client(dev), tm); +} + +static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return pcf8563_set_datetime(to_i2c_client(dev), tm); +} + +static int pcf8563_rtc_proc(struct device *dev, struct seq_file *seq) +{ + seq_printf(seq, "24hr\t\t: yes\n"); + return 0; +} + +static struct rtc_class_ops pcf8563_rtc_ops = { + .proc = pcf8563_rtc_proc, + .read_time = pcf8563_rtc_read_time, + .set_time = pcf8563_rtc_set_time, +}; + +static int pcf8563_attach(struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, pcf8563_probe); +} + +static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct rtc_device *rtc; + + int err = 0; + + dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto exit; + } + + if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + client->addr = address; + client->driver = &pcf8563_driver; + client->adapter = adapter; + + strlcpy(client->name, pcf8563_driver.driver.name, I2C_NAME_SIZE); + + /* Verify the chip is really an PCF8563 */ + if (kind < 0) { + if (pcf8563_validate_client(client) < 0) { + err = -ENODEV; + goto exit_kfree; + } + } + + /* Inform the i2c layer */ + if ((err = i2c_attach_client(client))) + goto exit_kfree; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + rtc = rtc_device_register(pcf8563_driver.driver.name, &client->dev, + &pcf8563_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + err = PTR_ERR(rtc); + dev_err(&client->dev, + "unable to register the class device\n"); + goto exit_detach; + } + + i2c_set_clientdata(client, rtc); + + return 0; + +exit_detach: + i2c_detach_client(client); + +exit_kfree: + kfree(client); + +exit: + return err; +} + +static int pcf8563_detach(struct i2c_client *client) +{ + int err; + struct rtc_device *rtc = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __FUNCTION__); + + if (rtc) + rtc_device_unregister(rtc); + + if ((err = i2c_detach_client(client))) + return err; + + kfree(client); + + return 0; +} + +static int __init pcf8563_init(void) +{ + return i2c_add_driver(&pcf8563_driver); +} + +static void __exit pcf8563_exit(void) +{ + i2c_del_driver(&pcf8563_driver); +} + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(pcf8563_init); +module_exit(pcf8563_exit); Index: linux-2.6.16/drivers/rtc/rtc-rs5c372.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-rs5c372.c @@ -0,0 +1,294 @@ +/* + * An I2C driver for the Ricoh RS5C372 RTC + * + * Copyright (C) 2005 Pavel Mironchik + * Copyright (C) 2006 Tower Technologies + * + * 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 + +#define DRV_VERSION "0.2" + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { /* 0x32,*/ I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD; + +#define RS5C372_REG_SECS 0 +#define RS5C372_REG_MINS 1 +#define RS5C372_REG_HOURS 2 +#define RS5C372_REG_WDAY 3 +#define RS5C372_REG_DAY 4 +#define RS5C372_REG_MONTH 5 +#define RS5C372_REG_YEAR 6 +#define RS5C372_REG_TRIM 7 + +#define RS5C372_TRIM_XSL 0x80 +#define RS5C372_TRIM_MASK 0x7F + +#define RS5C372_REG_BASE 0 + +static int rs5c372_attach(struct i2c_adapter *adapter); +static int rs5c372_detach(struct i2c_client *client); +static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind); + +static struct i2c_driver rs5c372_driver = { + .driver = { + .name = "rs5c372", + }, + .attach_adapter = &rs5c372_attach, + .detach_client = &rs5c372_detach, +}; + +static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned char buf[7] = { RS5C372_REG_BASE }; + + /* this implements the 1st reading method, according + * to the datasheet. buf[0] is initialized with + * address ptr and transmission format register. + */ + struct i2c_msg msgs[] = { + { client->addr, 0, 1, buf }, + { client->addr, I2C_M_RD, 7, buf }, + }; + + if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + tm->tm_sec = BCD2BIN(buf[RS5C372_REG_SECS] & 0x7f); + tm->tm_min = BCD2BIN(buf[RS5C372_REG_MINS] & 0x7f); + tm->tm_hour = BCD2BIN(buf[RS5C372_REG_HOURS] & 0x3f); + tm->tm_wday = BCD2BIN(buf[RS5C372_REG_WDAY] & 0x07); + tm->tm_mday = BCD2BIN(buf[RS5C372_REG_DAY] & 0x3f); + + /* tm->tm_mon is zero-based */ + tm->tm_mon = BCD2BIN(buf[RS5C372_REG_MONTH] & 0x1f) - 1; + + /* year is 1900 + tm->tm_year */ + tm->tm_year = BCD2BIN(buf[RS5C372_REG_YEAR]) + 100; + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned char buf[8] = { RS5C372_REG_BASE }; + + dev_dbg(&client->dev, + "%s: secs=%d, mins=%d, hours=%d ", + "mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + buf[1] = BIN2BCD(tm->tm_sec); + buf[2] = BIN2BCD(tm->tm_min); + buf[3] = BIN2BCD(tm->tm_hour); + buf[4] = BIN2BCD(tm->tm_wday); + buf[5] = BIN2BCD(tm->tm_mday); + buf[6] = BIN2BCD(tm->tm_mon + 1); + buf[7] = BIN2BCD(tm->tm_year - 100); + + if ((i2c_master_send(client, buf, 8)) != 8) { + dev_err(&client->dev, "%s: write error\n", __FUNCTION__); + return -EIO; + } + + return 0; +} + +static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim) +{ + unsigned char buf = RS5C372_REG_TRIM; + + struct i2c_msg msgs[] = { + { client->addr, 0, 1, &buf }, + { client->addr, I2C_M_RD, 1, &buf }, + }; + + if ((i2c_transfer(client->adapter, msgs, 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, trim); + + if (osc) + *osc = (buf & RS5C372_TRIM_XSL) ? 32000 : 32768; + + if (trim) + *trim = buf & RS5C372_TRIM_MASK; + + return 0; +} + +static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return rs5c372_get_datetime(to_i2c_client(dev), tm); +} + +static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return rs5c372_set_datetime(to_i2c_client(dev), tm); +} + +static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq) +{ + int err, osc, trim; + + seq_printf(seq, "24hr\t\t: yes\n"); + + if ((err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim)) == 0) { + seq_printf(seq, "%d.%03d KHz\n", osc / 1000, osc % 1000); + seq_printf(seq, "trim\t: %d\n", trim); + } + + return 0; +} + +static struct rtc_class_ops rs5c372_rtc_ops = { + .proc = rs5c372_rtc_proc, + .read_time = rs5c372_rtc_read_time, + .set_time = rs5c372_rtc_set_time, +}; + +static ssize_t rs5c372_sysfs_show_trim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int trim; + + if (rs5c372_get_trim(to_i2c_client(dev), NULL, &trim) == 0) + return sprintf(buf, "0x%2x\n", trim); + + return 0; +} +static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL); + +static ssize_t rs5c372_sysfs_show_osc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int osc; + + if (rs5c372_get_trim(to_i2c_client(dev), &osc, NULL) == 0) + return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000); + + return 0; +} +static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL); + +static int rs5c372_attach(struct i2c_adapter *adapter) +{ + dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + return i2c_probe(adapter, &addr_data, rs5c372_probe); +} + +static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) +{ + int err = 0; + struct i2c_client *client; + struct rtc_device *rtc; + + dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto exit; + } + + if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + /* I2C client */ + client->addr = address; + client->driver = &rs5c372_driver; + client->adapter = adapter; + + strlcpy(client->name, rs5c372_driver.driver.name, I2C_NAME_SIZE); + + /* Inform the i2c layer */ + if ((err = i2c_attach_client(client))) + goto exit_kfree; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + rtc = rtc_device_register(rs5c372_driver.driver.name, &client->dev, + &rs5c372_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + err = PTR_ERR(rtc); + dev_err(&client->dev, + "unable to register the class device\n"); + goto exit_detach; + } + + i2c_set_clientdata(client, rtc); + + device_create_file(&client->dev, &dev_attr_trim); + device_create_file(&client->dev, &dev_attr_osc); + + return 0; + +exit_detach: + i2c_detach_client(client); + +exit_kfree: + kfree(client); + +exit: + return err; +} + +static int rs5c372_detach(struct i2c_client *client) +{ + int err; + struct rtc_device *rtc = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __FUNCTION__); + + if (rtc) + rtc_device_unregister(rtc); + + if ((err = i2c_detach_client(client))) + return err; + + kfree(client); + + return 0; +} + +static __init int rs5c372_init(void) +{ + return i2c_add_driver(&rs5c372_driver); +} + +static __exit void rs5c372_exit(void) +{ + i2c_del_driver(&rs5c372_driver); +} + +module_init(rs5c372_init); +module_exit(rs5c372_exit); + +MODULE_AUTHOR( + "Pavel Mironchik , " + "Alessandro Zummo "); +MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); Index: linux-2.6.16/drivers/rtc/rtc-ep93xx.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-ep93xx.c @@ -0,0 +1,162 @@ +/* + * A driver for the RTC embedded in the Cirrus Logic EP93XX processors + * Copyright (c) 2006 Tower Technologies + * + * Author: Alessandro Zummo + * + * 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 + +#define EP93XX_RTC_REG(x) (EP93XX_RTC_BASE + (x)) +#define EP93XX_RTC_DATA EP93XX_RTC_REG(0x0000) +#define EP93XX_RTC_LOAD EP93XX_RTC_REG(0x000C) +#define EP93XX_RTC_SWCOMP EP93XX_RTC_REG(0x0108) + +#define DRV_VERSION "0.2" + +static int ep93xx_get_swcomp(struct device *dev, unsigned short *preload, + unsigned short *delete) +{ + unsigned short comp = __raw_readl(EP93XX_RTC_SWCOMP); + + if (preload) + *preload = comp & 0xffff; + + if (delete) + *delete = (comp >> 16) & 0x1f; + + return 0; +} + +static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long time = __raw_readl(EP93XX_RTC_DATA); + + rtc_time_to_tm(time, tm); + return 0; +} + +static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + __raw_writel(secs + 1, EP93XX_RTC_LOAD); + return 0; +} + +static int ep93xx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + int err; + unsigned long secs; + + err = rtc_tm_to_time(tm, &secs); + if (err != 0) + return err; + + return ep93xx_rtc_set_mmss(dev, secs); +} + +static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq) +{ + unsigned short preload, delete; + + ep93xx_get_swcomp(dev, &preload, &delete); + + seq_printf(seq, "24hr\t\t: yes\n"); + seq_printf(seq, "preload\t\t: %d\n", preload); + seq_printf(seq, "delete\t\t: %d\n", delete); + + return 0; +} + +static struct rtc_class_ops ep93xx_rtc_ops = { + .read_time = ep93xx_rtc_read_time, + .set_time = ep93xx_rtc_set_time, + .set_mmss = ep93xx_rtc_set_mmss, + .proc = ep93xx_rtc_proc, +}; + +static ssize_t ep93xx_sysfs_show_comp_preload(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned short preload; + + ep93xx_get_swcomp(dev, &preload, NULL); + + return sprintf(buf, "%d\n", preload); +} +static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_sysfs_show_comp_preload, NULL); + +static ssize_t ep93xx_sysfs_show_comp_delete(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned short delete; + + ep93xx_get_swcomp(dev, NULL, &delete); + + return sprintf(buf, "%d\n", delete); +} +static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_sysfs_show_comp_delete, NULL); + + +static int __devinit ep93xx_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc = rtc_device_register("ep93xx", + &dev->dev, &ep93xx_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + dev_err(&dev->dev, "unable to register\n"); + return PTR_ERR(rtc); + } + + platform_set_drvdata(dev, rtc); + + device_create_file(&dev->dev, &dev_attr_comp_preload); + device_create_file(&dev->dev, &dev_attr_comp_delete); + + return 0; +} + +static int __devexit ep93xx_rtc_remove(struct platform_device *dev) +{ + struct rtc_device *rtc = platform_get_drvdata(dev); + + if (rtc) + rtc_device_unregister(rtc); + + platform_set_drvdata(dev, NULL); + + return 0; +} + +static struct platform_driver ep93xx_rtc_platform_driver = { + .driver = { + .name = "ep93xx-rtc", + .owner = THIS_MODULE, + }, + .probe = ep93xx_rtc_probe, + .remove = __devexit_p(ep93xx_rtc_remove), +}; + +static int __init ep93xx_rtc_init(void) +{ + return platform_driver_register(&ep93xx_rtc_platform_driver); +} + +static void __exit ep93xx_rtc_exit(void) +{ + platform_driver_unregister(&ep93xx_rtc_platform_driver); +} + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("EP93XX RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(ep93xx_rtc_init); +module_exit(ep93xx_rtc_exit); Index: linux-2.6.16/drivers/rtc/rtc-sa1100.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-sa1100.c @@ -0,0 +1,388 @@ +/* + * Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * + * Original Driver by Nils Faerber + * + * Modifications from: + * CIH + * Nicolas Pitre + * Andrew Christian + * + * Converted to the RTC subsystem and Driver Model + * by Richard Purdie + * + * 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 + +#ifdef CONFIG_ARCH_PXA +#include +#endif + +#define TIMER_FREQ CLOCK_TICK_RATE +#define RTC_DEF_DIVIDER 32768 - 1 +#define RTC_DEF_TRIM 0 + +static unsigned long rtc_freq = 1024; +static struct rtc_time rtc_alarm; +static spinlock_t sa1100_rtc_lock = SPIN_LOCK_UNLOCKED; + +static int rtc_update_alarm(struct rtc_time *alrm) +{ + struct rtc_time alarm_tm, now_tm; + unsigned long now, time; + int ret; + + do { + now = RCNR; + rtc_time_to_tm(now, &now_tm); + rtc_next_alarm_time(&alarm_tm, &now_tm, alrm); + ret = rtc_tm_to_time(&alarm_tm, &time); + if (ret != 0) + break; + + RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); + RTAR = time; + } while (now != RCNR); + + return ret; +} + +static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct platform_device *pdev = to_platform_device(dev_id); + struct rtc_device *rtc = platform_get_drvdata(pdev); + unsigned int rtsr; + unsigned long events = 0; + + spin_lock(&sa1100_rtc_lock); + + rtsr = RTSR; + /* clear interrupt sources */ + RTSR = 0; + RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); + + /* clear alarm interrupt if it has occurred */ + if (rtsr & RTSR_AL) + rtsr &= ~RTSR_ALE; + RTSR = rtsr & (RTSR_ALE | RTSR_HZE); + + /* update irq data & counter */ + if (rtsr & RTSR_AL) + events |= RTC_AF | RTC_IRQF; + if (rtsr & RTSR_HZ) + events |= RTC_UF | RTC_IRQF; + + rtc_update_irq(&rtc->class_dev, 1, events); + + if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm)) + rtc_update_alarm(&rtc_alarm); + + spin_unlock(&sa1100_rtc_lock); + + return IRQ_HANDLED; +} + +static int rtc_timer1_count; + +static irqreturn_t timer1_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct platform_device *pdev = to_platform_device(dev_id); + struct rtc_device *rtc = platform_get_drvdata(pdev); + + /* + * If we match for the first time, rtc_timer1_count will be 1. + * Otherwise, we wrapped around (very unlikely but + * still possible) so compute the amount of missed periods. + * The match reg is updated only when the data is actually retrieved + * to avoid unnecessary interrupts. + */ + OSSR = OSSR_M1; /* clear match on timer1 */ + + rtc_update_irq(&rtc->class_dev, rtc_timer1_count, RTC_PF | RTC_IRQF); + + if (rtc_timer1_count == 1) + rtc_timer1_count = (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))); + + return IRQ_HANDLED; +} + +static int sa1100_rtc_read_callback(struct device *dev, int data) +{ + if (data & RTC_PF) { + /* interpolate missed periods and set match for the next */ + unsigned long period = TIMER_FREQ/rtc_freq; + unsigned long oscr = OSCR; + unsigned long osmr1 = OSMR1; + unsigned long missed = (oscr - osmr1)/period; + data += missed << 8; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + (missed + 1)*period; + /* Ensure we didn't miss another match in the mean time. + * Here we compare (match - OSCR) 8 instead of 0 -- + * see comment in pxa_timer_interrupt() for explanation. + */ + while( (signed long)((osmr1 = OSMR1) - OSCR) <= 8 ) { + data += 0x100; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + period; + } + } + return data; +} + +static int sa1100_rtc_open(struct device *dev) +{ + int ret; + + ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, SA_INTERRUPT, + "rtc 1Hz", dev); + if (ret) { + printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTC1Hz); + goto fail_ui; + } + ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, SA_INTERRUPT, + "rtc Alrm", dev); + if (ret) { + printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTCAlrm); + goto fail_ai; + } + ret = request_irq(IRQ_OST1, timer1_interrupt, SA_INTERRUPT, + "rtc timer", dev); + if (ret) { + printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_OST1); + goto fail_pi; + } + return 0; + + fail_pi: + free_irq(IRQ_RTCAlrm, NULL); + fail_ai: + free_irq(IRQ_RTC1Hz, NULL); + fail_ui: + return ret; +} + +static void sa1100_rtc_release(struct device *dev) +{ + spin_lock_irq(&sa1100_rtc_lock); + RTSR = 0; + OIER &= ~OIER_E1; + OSSR = OSSR_M1; + spin_unlock_irq(&sa1100_rtc_lock); + + free_irq(IRQ_OST1, dev); + free_irq(IRQ_RTCAlrm, dev); + free_irq(IRQ_RTC1Hz, dev); +} + + +static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + switch(cmd) { + case RTC_AIE_OFF: + spin_lock_irq(&sa1100_rtc_lock); + RTSR &= ~RTSR_ALE; + spin_unlock_irq(&sa1100_rtc_lock); + return 0; + case RTC_AIE_ON: + spin_lock_irq(&sa1100_rtc_lock); + RTSR |= RTSR_ALE; + spin_unlock_irq(&sa1100_rtc_lock); + return 0; + case RTC_UIE_OFF: + spin_lock_irq(&sa1100_rtc_lock); + RTSR &= ~RTSR_HZE; + spin_unlock_irq(&sa1100_rtc_lock); + return 0; + case RTC_UIE_ON: + spin_lock_irq(&sa1100_rtc_lock); + RTSR |= RTSR_HZE; + spin_unlock_irq(&sa1100_rtc_lock); + return 0; + case RTC_PIE_OFF: + spin_lock_irq(&sa1100_rtc_lock); + OIER &= ~OIER_E1; + spin_unlock_irq(&sa1100_rtc_lock); + return 0; + case RTC_PIE_ON: + if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE)) + return -EACCES; + spin_lock_irq(&sa1100_rtc_lock); + OSMR1 = TIMER_FREQ/rtc_freq + OSCR; + OIER |= OIER_E1; + rtc_timer1_count = 1; + spin_unlock_irq(&sa1100_rtc_lock); + return 0; + case RTC_IRQP_READ: + return put_user(rtc_freq, (unsigned long *)arg); + case RTC_IRQP_SET: + if (arg < 1 || arg > TIMER_FREQ) + return -EINVAL; + if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + rtc_freq = arg; + return 0; + } + return -EINVAL; +} + +static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + rtc_time_to_tm(RCNR, tm); + return 0; +} + +static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long time; + int ret; + + ret = rtc_tm_to_time(tm, &time); + if (ret == 0) + RCNR = time; + return ret; +} + +static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time)); + alrm->pending = RTSR & RTSR_AL ? 1 : 0; + return 0; +} + +static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + + spin_lock_irq(&sa1100_rtc_lock); + ret = rtc_update_alarm(&alrm->time); + if (ret == 0) { + memcpy(&rtc_alarm, &alrm->time, sizeof(struct rtc_time)); + + if (alrm->enabled) + enable_irq_wake(IRQ_RTCAlrm); + else + disable_irq_wake(IRQ_RTCAlrm); + } + spin_unlock_irq(&sa1100_rtc_lock); + + return ret; +} + +static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) +{ + seq_printf(seq, "trim/divider\t: 0x%08x\n", RTTR); + seq_printf(seq, "alarm_IRQ\t: %s\n", + (RTSR & RTSR_ALE) ? "yes" : "no" ); + seq_printf(seq, "update_IRQ\t: %s\n", + (RTSR & RTSR_HZE) ? "yes" : "no"); + seq_printf(seq, "periodic_IRQ\t: %s\n", + (OIER & OIER_E1) ? "yes" : "no"); + seq_printf(seq, "periodic_freq\t: %ld\n", rtc_freq); + + return 0; +} + +static struct rtc_class_ops sa1100_rtc_ops = { + .open = sa1100_rtc_open, + .read_callback = sa1100_rtc_read_callback, + .release = sa1100_rtc_release, + .ioctl = sa1100_rtc_ioctl, + .read_time = sa1100_rtc_read_time, + .set_time = sa1100_rtc_set_time, + .read_alarm = sa1100_rtc_read_alarm, + .set_alarm = sa1100_rtc_set_alarm, + .proc = sa1100_rtc_proc, +}; + +static int sa1100_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + /* + * According to the manual we should be able to let RTTR be zero + * and then a default diviser for a 32.768KHz clock is used. + * Apparently this doesn't work, at least for my SA1110 rev 5. + * If the clock divider is uninitialized then reset it to the + * default value to get the 1Hz clock. + */ + if (RTTR == 0) { + RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + printk(KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n"); + /* The current RTC value probably doesn't make sense either */ + RCNR = 0; + } + + rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, + THIS_MODULE); + + if (IS_ERR(rtc)) { + dev_err(&pdev->dev, "Unable to register the RTC device\n"); + return PTR_ERR(rtc); + } + + platform_set_drvdata(pdev, rtc); + + dev_info(&pdev->dev, "SA11xx/PXA2xx RTC Registered\n"); + + return 0; +} + +static int sa1100_rtc_remove(struct platform_device *pdev) +{ + struct rtc_device *rtc = platform_get_drvdata(pdev); + + if (rtc) + rtc_device_unregister(rtc); + + return 0; +} + +static struct platform_driver sa1100_rtc_driver = { + .probe = sa1100_rtc_probe, + .remove = sa1100_rtc_remove, + .driver = { + .name = "sa1100-rtc", + }, +}; + +static int __init sa1100_rtc_init(void) +{ + return platform_driver_register(&sa1100_rtc_driver); +} + +static void __exit sa1100_rtc_exit(void) +{ + platform_driver_unregister(&sa1100_rtc_driver); +} + +module_init(sa1100_rtc_init); +module_exit(sa1100_rtc_exit); + +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); Index: linux-2.6.16/arch/arm/mach-pxa/generic.c =================================================================== --- linux-2.6.16.orig/arch/arm/mach-pxa/generic.c +++ linux-2.6.16/arch/arm/mach-pxa/generic.c @@ -319,6 +319,11 @@ void __init pxa_set_ficp_info(struct pxa pxaficp_device.dev.platform_data = info; } +static struct platform_device pxartc_device = { + .name = "sa1100-rtc", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { &pxamci_device, &udc_device, @@ -329,6 +334,7 @@ static struct platform_device *devices[] &pxaficp_device, &i2c_device, &i2s_device, + &pxartc_device, }; static int __init pxa_init(void) Index: linux-2.6.16/arch/arm/mach-sa1100/generic.c =================================================================== --- linux-2.6.16.orig/arch/arm/mach-sa1100/generic.c +++ linux-2.6.16/arch/arm/mach-sa1100/generic.c @@ -324,6 +324,11 @@ void sa11x0_set_irda_data(struct irda_pl sa11x0ir_device.dev.platform_data = irda; } +static struct platform_device sa11x0rtc_device = { + .name = "sa1100-rtc", + .id = -1, +}; + static struct platform_device *sa11x0_devices[] __initdata = { &sa11x0udc_device, &sa11x0uart1_device, @@ -333,6 +338,7 @@ static struct platform_device *sa11x0_de &sa11x0pcmcia_device, &sa11x0fb_device, &sa11x0mtd_device, + &sa11x0rtc_device, }; static int __init sa1100_init(void) Index: linux-2.6.16/drivers/rtc/rtc-m48t86.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/rtc/rtc-m48t86.c @@ -0,0 +1,209 @@ +/* + * ST M48T86 / Dallas DS12887 RTC driver + * Copyright (c) 2006 Tower Technologies + * + * Author: Alessandro Zummo + * + * 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. + * + * This drivers only supports the clock running in BCD and 24H mode. + * If it will be ever adapted to binary and 12H mode, care must be taken + * to not introduce bugs. + */ + +#include +#include +#include +#include +#include + +#define M48T86_REG_SEC 0x00 +#define M48T86_REG_SECALRM 0x01 +#define M48T86_REG_MIN 0x02 +#define M48T86_REG_MINALRM 0x03 +#define M48T86_REG_HOUR 0x04 +#define M48T86_REG_HOURALRM 0x05 +#define M48T86_REG_DOW 0x06 /* 1 = sunday */ +#define M48T86_REG_DOM 0x07 +#define M48T86_REG_MONTH 0x08 /* 1 - 12 */ +#define M48T86_REG_YEAR 0x09 /* 0 - 99 */ +#define M48T86_REG_A 0x0A +#define M48T86_REG_B 0x0B +#define M48T86_REG_C 0x0C +#define M48T86_REG_D 0x0D + +#define M48T86_REG_B_H24 (1 << 1) +#define M48T86_REG_B_DM (1 << 2) +#define M48T86_REG_B_SET (1 << 7) +#define M48T86_REG_D_VRT (1 << 7) + +#define DRV_VERSION "0.1" + + +static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char reg; + struct platform_device *pdev = to_platform_device(dev); + struct m48t86_ops *ops = pdev->dev.platform_data; + + reg = ops->readb(M48T86_REG_B); + + if (reg & M48T86_REG_B_DM) { + /* data (binary) mode */ + tm->tm_sec = ops->readb(M48T86_REG_SEC); + tm->tm_min = ops->readb(M48T86_REG_MIN); + tm->tm_hour = ops->readb(M48T86_REG_HOUR) & 0x3F; + tm->tm_mday = ops->readb(M48T86_REG_DOM); + /* tm_mon is 0-11 */ + tm->tm_mon = ops->readb(M48T86_REG_MONTH) - 1; + tm->tm_year = ops->readb(M48T86_REG_YEAR) + 100; + tm->tm_wday = ops->readb(M48T86_REG_DOW); + } else { + /* bcd mode */ + tm->tm_sec = BCD2BIN(ops->readb(M48T86_REG_SEC)); + tm->tm_min = BCD2BIN(ops->readb(M48T86_REG_MIN)); + tm->tm_hour = BCD2BIN(ops->readb(M48T86_REG_HOUR) & 0x3F); + tm->tm_mday = BCD2BIN(ops->readb(M48T86_REG_DOM)); + /* tm_mon is 0-11 */ + tm->tm_mon = BCD2BIN(ops->readb(M48T86_REG_MONTH)) - 1; + tm->tm_year = BCD2BIN(ops->readb(M48T86_REG_YEAR)) + 100; + tm->tm_wday = BCD2BIN(ops->readb(M48T86_REG_DOW)); + } + + /* correct the hour if the clock is in 12h mode */ + if (!(reg & M48T86_REG_B_H24)) + if (ops->readb(M48T86_REG_HOUR) & 0x80) + tm->tm_hour += 12; + + return 0; +} + +static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char reg; + struct platform_device *pdev = to_platform_device(dev); + struct m48t86_ops *ops = pdev->dev.platform_data; + + reg = ops->readb(M48T86_REG_B); + + /* update flag and 24h mode */ + reg |= M48T86_REG_B_SET | M48T86_REG_B_H24; + ops->writeb(reg, M48T86_REG_B); + + if (reg & M48T86_REG_B_DM) { + /* data (binary) mode */ + ops->writeb(tm->tm_sec, M48T86_REG_SEC); + ops->writeb(tm->tm_min, M48T86_REG_MIN); + ops->writeb(tm->tm_hour, M48T86_REG_HOUR); + ops->writeb(tm->tm_mday, M48T86_REG_DOM); + ops->writeb(tm->tm_mon + 1, M48T86_REG_MONTH); + ops->writeb(tm->tm_year % 100, M48T86_REG_YEAR); + ops->writeb(tm->tm_wday, M48T86_REG_DOW); + } else { + /* bcd mode */ + ops->writeb(BIN2BCD(tm->tm_sec), M48T86_REG_SEC); + ops->writeb(BIN2BCD(tm->tm_min), M48T86_REG_MIN); + ops->writeb(BIN2BCD(tm->tm_hour), M48T86_REG_HOUR); + ops->writeb(BIN2BCD(tm->tm_mday), M48T86_REG_DOM); + ops->writeb(BIN2BCD(tm->tm_mon + 1), M48T86_REG_MONTH); + ops->writeb(BIN2BCD(tm->tm_year % 100), M48T86_REG_YEAR); + ops->writeb(BIN2BCD(tm->tm_wday), M48T86_REG_DOW); + } + + /* update ended */ + reg &= ~M48T86_REG_B_SET; + ops->writeb(reg, M48T86_REG_B); + + return 0; +} + +static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) +{ + unsigned char reg; + struct platform_device *pdev = to_platform_device(dev); + struct m48t86_ops *ops = pdev->dev.platform_data; + + reg = ops->readb(M48T86_REG_B); + + seq_printf(seq, "24hr\t\t: %s\n", + (reg & M48T86_REG_B_H24) ? "yes" : "no"); + + seq_printf(seq, "mode\t\t: %s\n", + (reg & M48T86_REG_B_DM) ? "binary" : "bcd"); + + reg = ops->readb(M48T86_REG_D); + + seq_printf(seq, "battery\t\t: %s\n", + (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); + + return 0; +} + +static struct rtc_class_ops m48t86_rtc_ops = { + .read_time = m48t86_rtc_read_time, + .set_time = m48t86_rtc_set_time, + .proc = m48t86_rtc_proc, +}; + +static int __devinit m48t86_rtc_probe(struct platform_device *dev) +{ + unsigned char reg; + struct m48t86_ops *ops = dev->dev.platform_data; + struct rtc_device *rtc = rtc_device_register("m48t86", + &dev->dev, &m48t86_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + dev_err(&dev->dev, "unable to register\n"); + return PTR_ERR(rtc); + } + + platform_set_drvdata(dev, rtc); + + /* read battery status */ + reg = ops->readb(M48T86_REG_D); + dev_info(&dev->dev, "battery %s\n", + (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); + + return 0; +} + +static int __devexit m48t86_rtc_remove(struct platform_device *dev) +{ + struct rtc_device *rtc = platform_get_drvdata(dev); + + if (rtc) + rtc_device_unregister(rtc); + + platform_set_drvdata(dev, NULL); + + return 0; +} + +static struct platform_driver m48t86_rtc_platform_driver = { + .driver = { + .name = "rtc-m48t86", + .owner = THIS_MODULE, + }, + .probe = m48t86_rtc_probe, + .remove = __devexit_p(m48t86_rtc_remove), +}; + +static int __init m48t86_rtc_init(void) +{ + return platform_driver_register(&m48t86_rtc_platform_driver); +} + +static void __exit m48t86_rtc_exit(void) +{ + platform_driver_unregister(&m48t86_rtc_platform_driver); +} + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("M48T86 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(m48t86_rtc_init); +module_exit(m48t86_rtc_exit); Index: linux-2.6.16/include/linux/m48t86.h =================================================================== --- /dev/null +++ linux-2.6.16/include/linux/m48t86.h @@ -0,0 +1,16 @@ +/* + * ST M48T86 / Dallas DS12887 RTC driver + * Copyright (c) 2006 Tower Technologies + * + * Author: Alessandro Zummo + * + * 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. +*/ + +struct m48t86_ops +{ + void (*writeb)(unsigned char value, unsigned long addr); + unsigned char (*readb)(unsigned long addr); +}; Index: linux-2.6.16/drivers/char/watchdog/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/char/watchdog/Kconfig +++ linux-2.6.16/drivers/char/watchdog/Kconfig @@ -148,6 +148,16 @@ config MPCORE_WATCHDOG To compile this driver as a module, choose M here: the module will be called mpcore_wdt. +config EP93XX_WATCHDOG + tristate "EP93xx Watchdog" + depends on WATCHDOG && ARCH_EP93XX + help + Say Y here if to include support for the watchdog timer + embedded in the Cirrus Logic EP93xx family of devices. + + To compile this driver as a module, choose M here: the + module will be called ep93xx_wdt. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT Index: linux-2.6.16/drivers/char/watchdog/Makefile =================================================================== --- linux-2.6.16.orig/drivers/char/watchdog/Makefile +++ linux-2.6.16/drivers/char/watchdog/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_ obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o +obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o Index: linux-2.6.16/drivers/char/watchdog/ep93xx_wdt.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/char/watchdog/ep93xx_wdt.c @@ -0,0 +1,264 @@ +/* + * Watchdog driver for Cirrus Logic EP93xx family of devices. + * + * Copyright (c) 2004 Ray Lehtiniemi + * Copyright (c) 2006 Tower Technologies + * Based on ep93xx driver, bits from alim7101_wdt.c + * + * Authors: Ray Lehtiniemi , + * Alessandro Zummo + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * This watchdog fires after 250msec, which is a too short interval + * for us to rely on the user space daemon alone. So we ping the + * wdt each ~200msec and eventually stop doing it if the user space + * daemon dies. + * + * TODO: + * + * - Test last reset from watchdog status + * - Add a few missing ioctls + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define WDT_VERSION "0.3" +#define PFX "ep93xx_wdt: " + +/* default timeout (secs) */ +#define WDT_TIMEOUT 30 + +static int nowayout = WATCHDOG_NOWAYOUT; +static int timeout = WDT_TIMEOUT; + +static struct timer_list timer; +static unsigned long next_heartbeat; +static unsigned long wdt_status; +static unsigned long boot_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +#define EP93XX_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x)) +#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00) +#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04) + +/* reset the wdt every ~200ms */ +#define WDT_INTERVAL (HZ/5) + +static void wdt_enable(void) +{ + __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG); +} + +static void wdt_disable(void) +{ + __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG); +} + +static inline void wdt_ping(void) +{ + __raw_writew(0x5555, EP93XX_WDT_WATCHDOG); +} + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + (timeout * HZ); + + wdt_enable(); + + timer.expires = jiffies + WDT_INTERVAL; + add_timer(&timer); +} + +static void wdt_shutdown(void) +{ + del_timer_sync(&timer); + wdt_disable(); +} + +static void wdt_keepalive(void) +{ + /* user land ping */ + next_heartbeat = jiffies + (timeout * HZ); +} + +static int ep93xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_startup(); + + return nonseekable_open(inode, file); +} + +static ssize_t +ep93xx_wdt_write(struct file *file, const char __user * data, size_t len, + loff_t * ppos) +{ + /* Can't seek (pwrite) on this device */ + if (*ppos != file->f_pos) + return -ESPIPE; + + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + else + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_keepalive(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE, + .identity = "EP93xx Watchdog", +}; + +static int +ep93xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOIOCTLCMD; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int __user *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int __user *)arg); + break; + + case WDIOC_GETTIMEOUT: + /* actually, it is 0.250 seconds.... */ + ret = put_user(1, (int __user *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_keepalive(); + ret = 0; + break; + } + return ret; +} + +static int ep93xx_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + wdt_shutdown(); + else + printk(KERN_CRIT PFX "Device closed unexpectedly - " + "timer will not stop\n"); + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + +static struct file_operations ep93xx_wdt_fops = { + .owner = THIS_MODULE, + .write = ep93xx_wdt_write, + .ioctl = ep93xx_wdt_ioctl, + .open = ep93xx_wdt_open, + .release = ep93xx_wdt_release, +}; + +static struct miscdevice ep93xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ep93xx_wdt_fops, +}; + +static void ep93xx_timer_ping(unsigned long data) +{ + if (time_before(jiffies, next_heartbeat)) + wdt_ping(); + + /* Re-set the timer interval */ + timer.expires = jiffies + WDT_INTERVAL; + add_timer(&timer); +} + + +static int __init ep93xx_wdt_init(void) +{ + int err; + + err = misc_register(&ep93xx_wdt_miscdev); + + boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0; + + printk(KERN_INFO PFX "EP93XX watchdog, driver version " + WDT_VERSION "%s\n", + (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08) + ? " (nCS1 disable detected)" : ""); + + if (timeout < 1 || timeout > 3600) { + timeout = WDT_TIMEOUT; + printk(KERN_INFO PFX + "timeout value must be 1<=x<=3600, using %d\n", + timeout); + } + + init_timer(&timer); + timer.function = ep93xx_timer_ping; + timer.data = 1; + + return err; +} + +static void __exit ep93xx_wdt_exit(void) +{ + wdt_shutdown(); + misc_deregister(&ep93xx_wdt_miscdev); +} + +module_init(ep93xx_wdt_init); +module_exit(ep93xx_wdt_exit); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +MODULE_AUTHOR("Ray Lehtiniemi ," + "Alessandro Zummo "); +MODULE_DESCRIPTION("EP93xx Watchdog"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(WDT_VERSION); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); Index: linux-2.6.16/arch/i386/pci/fixup.c =================================================================== --- linux-2.6.16.orig/arch/i386/pci/fixup.c +++ linux-2.6.16/arch/i386/pci/fixup.c @@ -74,52 +74,6 @@ static void __devinit pci_fixup_ncr53c8 } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, pci_fixup_ncr53c810); -static void __devinit pci_fixup_ide_bases(struct pci_dev *d) -{ - int i; - - /* - * PCI IDE controllers use non-standard I/O port decoding, respect it. - */ - if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE) - return; - DBG("PCI: IDE base address fixup for %s\n", pci_name(d)); - for(i=0; i<4; i++) { - struct resource *r = &d->resource[i]; - if ((r->start & ~0x80) == 0x374) { - r->start |= 2; - r->end = r->start; - } - } -} -DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases); - -static void __devinit pci_fixup_ide_trash(struct pci_dev *d) -{ - int i; - - /* - * Runs the fixup only for the first IDE controller - * (Shai Fultheim - shai@ftcon.com) - */ - static int called = 0; - if (called) - return; - called = 1; - - /* - * There exist PCI IDE controllers which have utter garbage - * in first four base registers. Ignore that. - */ - DBG("PCI: IDE base address trash cleared for %s\n", pci_name(d)); - for(i=0; i<4; i++) - d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0; -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, pci_fixup_ide_trash); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_11, pci_fixup_ide_trash); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_9, pci_fixup_ide_trash); - static void __devinit pci_fixup_latency(struct pci_dev *d) { /* Index: linux-2.6.16/drivers/pci/probe.c =================================================================== --- linux-2.6.16.orig/drivers/pci/probe.c +++ linux-2.6.16/drivers/pci/probe.c @@ -627,6 +627,7 @@ static void pci_read_irq(struct pci_dev static int pci_setup_device(struct pci_dev * dev) { u32 class; + u16 cmd; sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); @@ -654,6 +655,31 @@ static int pci_setup_device(struct pci_d pci_read_bases(dev, 6, PCI_ROM_ADDRESS); pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); + + /* + * Do the ugly legacy mode stuff here rather than broken chip + * quirk code. Legacy mode ATA controllers have fixed + * addresses. These are not always echoed in BAR0-3, and + * BAR0-3 in a few cases contain junk! + */ + if (class == PCI_CLASS_STORAGE_IDE) { + u8 progif; + pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); + if ((progif & 5) != 5) { + dev->resource[0].start = 0x1F0; + dev->resource[0].end = 0x1F7; + dev->resource[0].flags = IORESOURCE_IO; + dev->resource[1].start = 0x3F6; + dev->resource[1].end = 0x3F6; + dev->resource[1].flags = IORESOURCE_IO; + dev->resource[2].start = 0x170; + dev->resource[2].end = 0x177; + dev->resource[2].flags = IORESOURCE_IO; + dev->resource[3].start = 0x376; + dev->resource[3].end = 0x376; + dev->resource[3].flags = IORESOURCE_IO; + } + } break; case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ Index: linux-2.6.16/drivers/scsi/Kconfig =================================================================== --- linux-2.6.16.orig/drivers/scsi/Kconfig +++ linux-2.6.16/drivers/scsi/Kconfig @@ -599,6 +599,323 @@ config SCSI_SATA_INTEL_COMBINED depends on IDE=y && !BLK_DEV_IDE_SATA && (SCSI_SATA_AHCI || SCSI_ATA_PIIX) default y +config SCSI_PATA_ALI + tristate "ALi PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the ALi ATA interfaces + found on the many ALi chipsets. + + If unsure, say N. + +config SCSI_PATA_AMD + tristate "AMD/NVidia PATA support" + depends on SCSI_SATA && PCI + help + This option enables support for the AMD and NVidia PATA + interfaces found on the chipsets for Athlon/Athlon64. + + If unsure, say N. + +config SCSI_PATA_ARTOP + tristate "ARTOP 6210/6260 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for ARTOP PATA controllers. + + If unsure, say N. + +config SCSI_PATA_ATIIXP + tristate "ATI PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI + help + This option enables support for the ATI ATA interfaces + found on the many ATI chipsets. + + If unsure, say N. + +config SCSI_PATA_CMD64X + tristate "CMD64x PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI + help + This option enables support for the CMD64x series chips + except for the CMD640. + + If unsure, say N. + +config SCSI_PATA_CS5520 + tristate "CS5510/5520 PATA support" + depends on SCSI_SATA && PCI + help + This option enables support for the Cyrix 5510/5520 + companion chip used with the MediaGX/Geode processor family. + + If unsure, say N. + +config SCSI_PATA_CS5530 + tristate "CS5530 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the Cyrix/NatSemi/AMD CS5530 + companion chip used with the MediaGX/Geode processor family. + + If unsure, say N. + +config SCSI_PATA_CS5535 + tristate "CS5535 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && X86 && !X86_64 && EXPERIMENTAL + help + This option enables support for the NatSemi/AMD CS5535 + companion chip used with the Geode processor family. + + If unsure, say N. + +config SCSI_PATA_CYPRESS + tristate "Cypress CY82C693 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the Cypress/Contaq CY82C693 + chipset found in some Alpha systems + + If unsure, say N. + +config SCSI_PATA_EFAR + tristate "EFAR SLC90E66 support" + depends on SCSI_SATA && PCI + help + This option enables support for the EFAR SLC90E66 + IDE controller found on some older machines. + + If unsure, say N. + +config SCSI_ATA_GENERIC + tristate "Generic PATA support" + depends on SCSI_SATA && PCI + help + This option enables support for generic BIOS configured + PATA controllers via the new ATA layer + + If unsure, say N. + +config SCSI_PATA_HPT37X + tristate "HPT 370/370A/371/372/374/302 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the majority of the later HPT + PATA controllers via the new ATA layer. + + If unsure, say N. + +config SCSI_PATA_HPT3X2N + tristate "HPT 372N/302N PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the N variant HPT PATA + controllers via the new ATA layer + + If unsure, say N. + +config SCSI_PATA_HPT3X3 + tristate "HPT 343/363 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI + help + This option enables support for the HPT 343/363 + PATA controllers via the new ATA layer + + If unsure, say N. + +config SCSI_PATA_ISAPNP + tristate "ISA Plug and Play PATA support (Raving Lunatic)" + depends on SCSI_SATA && EXPERIMENTAL + help + This option enables support for ISA plug & play ATA + controllers such as those found on old soundcards. + + If unsure, say N. + +config SCSI_PATA_IT8172 + tristate "IT8172 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the ITE 8172 PATA controller + via the new ATA layer. + + If unsure, say N. + +config SCSI_PATA_IT821X + tristate "IT821x PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the ITE 8211 and 8212 + PATA controllers via the new ATA layer, including RAID + mode. + + If unsure, say N. + +config SCSI_PATA_LEGACY + tristate "Legacy ISA PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for ISA bus legacy PATA + interfaces on ide2-5 and allows them to be accessed via + the new ATA layer. + + If unsure, say N. + +config SCSI_PATA_MPIIX + tristate "Intel PATA MPIIX support" + depends on SCSI_SATA && PCI + help + This option enables support for MPIIX PATA support. + + If unsure, say N. + +config SCSI_PATA_NETCELL + tristate "NETCELL Revolution RAID support" + depends on SCSI_SATA && PCI + help + This option enables support for the Netcell Revolution RAID + PATA controller. + + If unsure, say N. + +config SCSI_PATA_NS87410 + tristate "Nat Semi NS87410 PATA support (Experimental)" + depends on SCSI_SATA && PCI + help + This option enables support for the National Semiconductor + NS87410 PCI-IDE controller. + + If unsure, say N. + +config SCSI_PATA_OLDPIIX + tristate "Intel PATA old PIIX support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for early PIIX PATA interfaces. + + If unsure, say N. + +config SCSI_PATA_OPTI + tristate "OPTI621/6215 PATA support" + depends on SCSI_SATA && PCI + help + This option enables full PIO support for the early Opti ATA + controllers found on some old motherboards. + + If unsure, say N. + +config SCSI_PATA_PCMCIA + tristate "PCMCIA PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCMCIA + help + This option enables support for PCMCIA ATA interfaces, including + compact flash card adapters via the new ATA layer. + + If unsure, say N. + +config SCSI_PATA_PDC_OLD + tristate "Older Promise PATA controller support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the Promise 20246, 20262, 20263, + 20265 and 20267 adapters. + + If unsure, say N. + +config SCSI_PATA_QDI + tristate "QDI VLB PATA support" + depends on SCSI_SATA + help + Support for QDI 6500 and 6580 PATA controllers on VESA local bus. + +config SCSI_PATA_RADISYS + tristate "RADISYS 82600 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the RADISYS 82600 + PATA controllers via the new ATA layer + + If unsure, say N. + +config SCSI_PATA_RZ1000 + tristate "PC Tech RZ1000 PATA support" + depends on SCSI_SATA && PCI + help + This option enables basic support for the PC Tech RZ1000/1 + PATA controllers via the new ATA layer + + If unsure, say N. + +config SCSI_PATA_SC1200 + tristate "SC1200 PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the NatSemi/AMD SC1200 SoC + companion chip used with the Geode processor family. + + If unsure, say N. + +config SCSI_PATA_SERVERWORKS + tristate "SERVERWORKS OSB4/CSB5/CSB6 PATA support (Experimental)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the Serverworks OSB4/CSB5 and + CSB6 IDE controllers, via the new ATA layer. + + If unsure, say N. + +config SCSI_PATA_SIL680 + tristate "CMD / Silicon Image 680 PATA support" + depends on SCSI_SATA && PCI + help + This option enables support for CMD / Silicon Image 680 PATA. + + If unsure, say N. + +config SCSI_PATA_SIS + tristate "SiS PATA support (Experimental)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for SiS PATA controllers + + If unsure, say N. + +config SCSI_PATA_TRIFLEX + tristate "Compaq Triflex PATA support (Raving Lunatic)" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + Enable support for the Compaq 'Triflex' IDE controller as found + on many Compaq Pentium-Pro systems, via the new ATA layer. + + If unsure, say N. + +config SCSI_PATA_VIA + tristate "VIA PATA support" + depends on SCSI_SATA && PCI + help + This option enables support for the VIA PATA interfaces + found on the many VIA chipsets. + + If unsure, say N. + +config SCSI_PATA_WINBOND + tristate "Winbond SL82C105 PATA support" + depends on SCSI_SATA && PCI + help + This option enables support for SL82C105 PATA devices found in the + Netwinder and some other systems + + If unsure, say N. + +config SCSI_PATA_TS7200 + tristate "TS7200 Compact Flash support" + depends on MACH_TS72XX + +config SCSI_PATA_EP93XX + tristate "Cirrus Logic EP93XX PATA support" + depends on ARCH_EP93XX + config SCSI_BUSLOGIC tristate "BusLogic SCSI support" depends on (PCI || ISA || MCA) && SCSI && ISA_DMA_API Index: linux-2.6.16/drivers/scsi/Makefile =================================================================== --- linux-2.6.16.orig/drivers/scsi/Makefile +++ linux-2.6.16/drivers/scsi/Makefile @@ -139,6 +139,44 @@ obj-$(CONFIG_SCSI_SATA_ULI) += libata.o obj-$(CONFIG_SCSI_SATA_MV) += libata.o sata_mv.o obj-$(CONFIG_SCSI_PDC_ADMA) += libata.o pdc_adma.o +obj-$(CONFIG_SCSI_PATA_ALI) += libata.o pata_ali.o +obj-$(CONFIG_SCSI_PATA_AMD) += libata.o pata_amd.o +obj-$(CONFIG_SCSI_PATA_ARTOP) += libata.o pata_artop.o +obj-$(CONFIG_SCSI_PATA_ATIIXP) += libata.o pata_atiixp.o +obj-$(CONFIG_SCSI_PATA_CMD64X) += libata.o pata_cmd64x.o +obj-$(CONFIG_SCSI_PATA_CS5520) += libata.o pata_cs5520.o +obj-$(CONFIG_SCSI_PATA_CS5530) += libata.o pata_cs5530.o +obj-$(CONFIG_SCSI_PATA_CS5535) += libata.o pata_cs5535.o +obj-$(CONFIG_SCSI_PATA_CYPRESS) += libata.o pata_cypress.o +obj-$(CONFIG_SCSI_PATA_EFAR) += libata.o pata_efar.o +obj-$(CONFIG_SCSI_PATA_ISAPNP) += libata.o pata_isapnp.o +obj-$(CONFIG_SCSI_PATA_HPT37X) += libata.o pata_hpt37x.o +obj-$(CONFIG_SCSI_PATA_HPT3X2N) += libata.o pata_hpt3x2n.o +obj-$(CONFIG_SCSI_PATA_HPT3X3) += libata.o pata_hpt34x.o +obj-$(CONFIG_SCSI_PATA_IT8172) += libata.o pata_it8172.o +obj-$(CONFIG_SCSI_PATA_IT821X) += libata.o pata_it821x.o +obj-$(CONFIG_SCSI_PATA_MPIIX) += libata.o pata_mpiix.o +obj-$(CONFIG_SCSI_PATA_NETCELL) += libata.o pata_netcell.o +obj-$(CONFIG_SCSI_PATA_NS87410) += libata.o pata_ns87410.o +obj-$(CONFIG_SCSI_PATA_OLDPIIX) += libata.o pata_oldpiix.o +obj-$(CONFIG_SCSI_PATA_OPTI) += libata.o pata_opti.o +obj-$(CONFIG_SCSI_PATA_PCMCIA) += libata.o pata_pcmcia.o +obj-$(CONFIG_SCSI_PATA_PDC_OLD) += libata.o pata_pdc202xx_old.o +obj-$(CONFIG_SCSI_PATA_QDI) += libata.o pata_qdi.o +obj-$(CONFIG_SCSI_PATA_RADISYS) += libata.o pata_radisys.o +obj-$(CONFIG_SCSI_PATA_RZ1000) += libata.o pata_rz1000.o +obj-$(CONFIG_SCSI_PATA_SERVERWORKS) += libata.o pata_serverworks.o +obj-$(CONFIG_SCSI_PATA_SC1200) += libata.o pata_sc1200.o +obj-$(CONFIG_SCSI_PATA_SIL680) += libata.o pata_sil680.o +obj-$(CONFIG_SCSI_PATA_SIS) += libata.o pata_sis.o +obj-$(CONFIG_SCSI_PATA_TRIFLEX) += libata.o pata_triflex.o +obj-$(CONFIG_SCSI_PATA_VIA) += libata.o pata_via.o +obj-$(CONFIG_SCSI_PATA_WINBOND) += libata.o pata_sl82c105.o +obj-$(CONFIG_SCSI_ATA_GENERIC) += libata.o ata_generic.o +obj-$(CONFIG_SCSI_PATA_LEGACY) += libata.o pata_legacy.o +obj-$(CONFIG_SCSI_PATA_TS7200) += libata.o pata_ts7200.o +obj-$(CONFIG_SCSI_PATA_EP93XX) += libata.o pata_ep93xx.o + obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_CHR_DEV_ST) += st.o Index: linux-2.6.16/drivers/scsi/ata_generic.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/ata_generic.c @@ -0,0 +1,241 @@ +/* + * ata_generic.c - Generic PATA/SATA controller driver. + * Copyright 2005 Red Hat Inc , all rights reserved. + * + * Elements from ide/pci/generic.c + * Copyright (C) 2001-2002 Andre Hedrick + * Portions (C) Copyright 2002 Red Hat Inc + * + * May be copied or modified under the terms of the GNU General Public License + * + * Driver for PCI IDE interfaces implementing the standard bus mastering + * interface functionality. This assumes the BIOS did the drive set up and + * tuning for us. By default we do not grab all IDE class devices as they + * may have other drivers or need fixups to avoid problems. Instead we keep + * a default list of stuff without documentation/driver that appears to + * work. + */ + +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include + +#define DRV_NAME "generic" +#define DRV_VERSION "0.1" + +/* + * A generic parallel ATA driver using libata + */ + +static void genpata_phy_reset(struct ata_port *ap) +{ + /* We know the BIOS already did the mode work. Don't tempt any + one else to "fix" things */ + ap->cbl = ATA_CBL_PATA80; + ata_port_probe(ap); + ata_bus_reset(ap); +} + +/** + * genpata_set_mode - mode setting + * @ap: interface to set up + * + * Use a non standard set_mode function. We don't want to be tuned. + * The BIOS configured everything. Our job is not to fiddle. We + * read the dma enabled bits from the PCI configuration of the device + * and respect them. + */ + +static void genpata_set_mode(struct ata_port *ap) +{ + int dma_enabled; + int i; + + /* Bits 5 and 6 indicate if DMA is active on master/slave */ + dma_enabled = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + + for (i = 0; i < ATA_MAX_DEVICES; i++) { + struct ata_device *dev = &ap->device[i]; + if (ata_dev_present(dev)) { + /* We don't really care */ + dev->pio_mode = XFER_PIO_0; + dev->dma_mode = XFER_MW_DMA_0; + /* We do need the right mode information for DMA or PIO + and this comes from the current configuration flags */ + /* FIXME: at some point in the future this should become + a library helper which reads the disk modes from the + disk as well */ + if (dma_enabled & (1 << (5 + i))) { + dev->xfer_mode = XFER_MW_DMA_0; + dev->xfer_shift = ATA_SHIFT_MWDMA; + dev->flags &= ~ATA_DFLAG_PIO; + } else { + dev->xfer_mode = XFER_PIO_0; + dev->xfer_shift = ATA_SHIFT_PIO; + dev->flags |= ATA_DFLAG_PIO; + } + } + } +} + +static struct scsi_host_template genpata_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations genpata_port_ops = { + .set_mode = genpata_set_mode, + + .port_disable = ata_port_disable, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = genpata_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .data_xfer = ata_pio_data_xfer, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static int ide_generic_all; /* Set to claim all devices */ + +static int __init ide_generic_all_on(char *unused) +{ + ide_generic_all = 1; + printk(KERN_INFO "ATA generic will claim all unknown PCI IDE class storage controllers.\n"); + return 1; +} + +__setup("all-generic-ide", ide_generic_all_on); + +/** + * pata_generic_init - attach generic IDE + * @dev: PCI device found + * @id: match entry + * + * Called each time a matching IDE interface is found. We check if the + * interface is one we wish to claim and if so we perform any chip + * specific hacks then let the ATA layer do the heavy lifting. + */ + +static int pata_generic_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + u16 command; + static struct ata_port_info info = { + .sht = &genpata_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_IRQ_MASK, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x3f, + .port_ops = &genpata_port_ops + }; + static struct ata_port_info *port_info[2] = { &info, &info }; + + /* Don't use the generic entry unless instructed to do so */ + if (id->driver_data == 1 && ide_generic_all == 0) + return -ENODEV; + + /* Devices that need care */ + if (dev->vendor == PCI_VENDOR_ID_UMC && + dev->device == PCI_DEVICE_ID_UMC_UM8886A && + (!(PCI_FUNC(dev->devfn) & 1))) + return -ENODEV; + + if (dev->vendor == PCI_VENDOR_ID_OPTI && + dev->device == PCI_DEVICE_ID_OPTI_82C558 && + (!(PCI_FUNC(dev->devfn) & 1))) + return -ENODEV; + + /* Don't re-enable devices in generic mode or we will break some + motherboards with disabled and unused IDE controllers */ + pci_read_config_word(dev, PCI_COMMAND, &command); + if (!(command & PCI_COMMAND_IO)) + return -ENODEV; + + if (dev->vendor == PCI_VENDOR_ID_AL) + ata_pci_clear_simplex(dev); + + return ata_pci_init_one(dev, port_info, 2); +} + +static struct pci_device_id pata_generic[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE), }, + { PCI_DEVICE(PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565), }, + { PCI_DEVICE(PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F), }, + { PCI_DEVICE(PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A), }, + { PCI_DEVICE(PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF), }, + { PCI_DEVICE(PCI_VENDOR_ID_HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE), }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561), }, + { PCI_DEVICE(PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558), }, + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO), }, + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), }, + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), }, + /* Must come last. If you add entries adjust this table appropriately */ + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xFFFFFF00UL, 1}, + { 0, }, +}; + +static struct pci_driver pata_generic_pci_driver = { + .name = DRV_NAME, + .id_table = pata_generic, + .probe = pata_generic_init_one, + .remove = ata_pci_remove_one +}; + +static int __init pata_generic_init(void) +{ + return pci_module_init(&pata_generic_pci_driver); +} + + +static void __exit pata_generic_exit(void) +{ + pci_unregister_driver(&pata_generic_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for generic ATA"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, pata_generic); +MODULE_VERSION(DRV_VERSION); + +module_init(pata_generic_init); +module_exit(pata_generic_exit); Index: linux-2.6.16/drivers/scsi/ata_piix.c =================================================================== --- linux-2.6.16.orig/drivers/scsi/ata_piix.c +++ linux-2.6.16/drivers/scsi/ata_piix.c @@ -91,9 +91,10 @@ #include #include #include +#include #define DRV_NAME "ata_piix" -#define DRV_VERSION "1.05" +#define DRV_VERSION "1.05-ac7" enum { PIIX_IOCFG = 0x54, /* IDE I/O configuration register */ @@ -122,6 +123,17 @@ enum { piix4_pata = 2, ich6_sata = 3, ich6_sata_ahci = 4, + ich0_pata = 5, + ich2_pata = 6, + ich3_pata = 7, + ich4_pata = 8, + cich_pata = 9, + piix3_pata = 10, + esb_pata = 11, + ich_pata = 12, + ich6_pata = 13, + ich7_pata = 14, + esb2_pata = 15, PIIX_AHCI_DEVICE = 6, }; @@ -130,20 +142,69 @@ static int piix_init_one (struct pci_dev const struct pci_device_id *ent); static void piix_pata_phy_reset(struct ata_port *ap); +static void ich_pata_phy_reset(struct ata_port *ap); static void piix_sata_phy_reset(struct ata_port *ap); static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev); static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev); +static void ich_set_dmamode (struct ata_port *ap, struct ata_device *adev); static unsigned int in_module_init = 1; static const struct pci_device_id piix_pci_tbl[] = { #ifdef ATA_ENABLE_PATA +#if 0 + /* Neptune and earlier are simple PIO */ + /* 430HX and friends. MWDMA */ + { 0x8086, 0x122e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix3_pata }, + { 0x8086, 0x1230, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix3_pata }, + /* Intel PIIX3 */ + { 0x8086, 0x7010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix3_pata }, +#endif + /* Intel PIIX4 for the 430TX/440BX/MX chipset: UDMA 33 */ + /* Also PIIX4E (fn3 rev 2) and PIIX4M (fn3 rev 3) */ { 0x8086, 0x7111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata }, { 0x8086, 0x24db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, { 0x8086, 0x25a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, + /* Intel PIIX4 */ + { 0x8086, 0x7199, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata }, + /* Intel PIIX4 */ + { 0x8086, 0x7601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata }, + /* Intel PIIX */ + { 0x8086, 0x84CA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata }, + /* Intel ICH (i810, i815, i840) UDMA 66*/ + { 0x8086, 0x2411, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata }, + /* Intel ICH0 : UDMA 33*/ + { 0x8086, 0x2421, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich0_pata }, + /* Intel ICH2M */ + { 0x8086, 0x244A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich2_pata }, + /* Intel ICH2 (i810E2, i845, 850, 860) UDMA 100 */ + { 0x8086, 0x244B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich2_pata }, + /* Intel ICH3M */ + { 0x8086, 0x248A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich3_pata }, + /* Intel ICH3 (E7500/1) UDMA 100 */ + { 0x8086, 0x248B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich3_pata }, +#if 0 + { 0x8086, 0x24C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, dunno_pata }, +#endif + /* Intel ICH4 (i845GV, i845E, i852, i855) UDMA 100 */ + { 0x8086, 0x24CA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich4_pata }, + { 0x8086, 0x24CB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich4_pata }, + /* Intel ICH5 */ + { 0x8086, 0x24DB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, + /* C-ICH (i810E2) */ + { 0x8086, 0x245B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, cich_pata }, + /* ESB (855GME/875P + 6300ESB) UDMA 100 */ + { 0x8086, 0x25A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, esb_pata }, + /* ICH6 (and 6) (i915) UDMA 100 */ + { 0x8086, 0x266F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich6_pata }, + /* ICH7/7-R (i945, i975) UDMA 100*/ + { 0x8086, 0x27DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich7_pata }, + { 0x8086, 0x269E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, esb2_pata }, #endif - - /* NOTE: The following PCI ids must be kept in sync with the + /* + * SATA ports + * + * NOTE: The following PCI ids must be kept in sync with the * list in drivers/pci/quirks.c. */ @@ -213,6 +274,40 @@ static const struct ata_port_operations .bmdma_status = ata_bmdma_status, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, + + .data_xfer = ata_pio_data_xfer, + + .eng_timeout = ata_eng_timeout, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop, +}; + +static const struct ata_port_operations ich_pata_ops = { + .port_disable = ata_port_disable, + .set_piomode = piix_set_piomode, + .set_dmamode = ich_set_dmamode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = ich_pata_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + + .data_xfer = ata_pio_data_xfer, .eng_timeout = ata_eng_timeout, @@ -242,6 +337,8 @@ static const struct ata_port_operations .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, + .data_xfer = ata_pio_data_xfer, + .eng_timeout = ata_eng_timeout, .irq_handler = ata_interrupt, @@ -253,47 +350,39 @@ static const struct ata_port_operations }; static struct ata_port_info piix_port_info[] = { - /* ich5_pata */ + /* ich5_pata: 0*/ { .sht = &piix_sht, .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | PIIX_FLAG_CHECKINTR, .pio_mask = 0x1f, /* pio0-4 */ -#if 0 .mwdma_mask = 0x06, /* mwdma1-2 */ -#else - .mwdma_mask = 0x00, /* mwdma broken */ -#endif - .udma_mask = 0x3f, /* udma0-5 */ - .port_ops = &piix_pata_ops, + .udma_mask = ATA_UDMA6, + .port_ops = &ich_pata_ops, }, - /* ich5_sata */ + /* ich5_sata: 1 */ { .sht = &piix_sht, .host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST | PIIX_FLAG_COMBINED | PIIX_FLAG_CHECKINTR, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ + .udma_mask = ATA_UDMA6, .port_ops = &piix_sata_ops, }, - /* piix4_pata */ + /* piix4_pata: 2 */ { .sht = &piix_sht, .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, .pio_mask = 0x1f, /* pio0-4 */ -#if 0 .mwdma_mask = 0x06, /* mwdma1-2 */ -#else - .mwdma_mask = 0x00, /* mwdma broken */ -#endif .udma_mask = ATA_UDMA_MASK_40C, .port_ops = &piix_pata_ops, }, - /* ich6_sata */ + /* ich6_sata: 3 */ { .sht = &piix_sht, .host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST | @@ -301,11 +390,11 @@ static struct ata_port_info piix_port_in ATA_FLAG_SLAVE_POSS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ + .udma_mask = ATA_UDMA6, .port_ops = &piix_sata_ops, }, - /* ich6_sata_ahci */ + /* ich6_sata_ahci: 4 */ { .sht = &piix_sht, .host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST | @@ -313,9 +402,119 @@ static struct ata_port_info piix_port_in ATA_FLAG_SLAVE_POSS | PIIX_FLAG_AHCI, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ - .udma_mask = 0x7f, /* udma0-6 */ + .udma_mask = ATA_UDMA6, .port_ops = &piix_sata_ops, }, + + /* ich0_pata: 5 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* MWDMA0 is broken on chip */ + .udma_mask = ATA_UDMA4, + .port_ops = &ich_pata_ops, + }, + + /* ich2_pata: 6 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* MWDMA0 is broken on chip */ + .udma_mask = ATA_UDMA5, + .port_ops = &ich_pata_ops, + }, + + /* ich3_pata: 7 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* MWDMA0 is broken on chip */ + .udma_mask = ATA_UDMA5, + .port_ops = &ich_pata_ops, + }, + + /* ich4_pata: 8 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* Check: maybe 0x07 */ + .udma_mask = ATA_UDMA5, + .port_ops = &ich_pata_ops, + }, + + /* cich_pata: 9 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* Check: maybe 0x07 */ + .udma_mask = ATA_UDMA5, + .port_ops = &ich_pata_ops, + }, + + /* piix3_pata: 10 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma1-2 */ + .udma_mask = ATA_UDMA_MASK_40C, + .port_ops = &piix_pata_ops, + }, + + /* esb_pata: 11 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* Check: maybe 0x07 */ + .udma_mask = ATA_UDMA5, + .port_ops = &piix_pata_ops, + }, + + /* ich_pata: 12 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* Check: maybe 0x07 */ + .udma_mask = ATA_UDMA4, /* UDMA66 */ + .port_ops = &ich_pata_ops, + }, + + /* ich6_pata: 13 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* Check: maybe 0x07 */ + .udma_mask = ATA_UDMA6, /* UDMA133 */ + .port_ops = &ich_pata_ops, + }, + + /* ich7_pata: 14 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* Check: maybe 0x07 */ + .udma_mask = ATA_UDMA6, + .port_ops = &ich_pata_ops, + }, + + /* esb2_pata: 15 */ + { + .sht = &piix_sht, + .host_flags = ATA_FLAG_SRST | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio 0-4 */ + .mwdma_mask = 0x06, /* Check: maybe 0x07 */ + .udma_mask = ATA_UDMA5, + .port_ops = &ich_pata_ops, + }, }; static struct pci_bits piix_enable_bits[] = { @@ -339,7 +538,7 @@ MODULE_VERSION(DRV_VERSION); * LOCKING: * None (inherited from caller). */ -static void piix_pata_cbl_detect(struct ata_port *ap) +static void ich_pata_cbl_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); u8 tmp, mask; @@ -366,8 +565,9 @@ cbl40: * piix_pata_phy_reset - Probe specified port on PATA host controller * @ap: Port to probe * - * Probe PATA phy. - * + * Probe PATA phy. Unlike the ICH we have no IOCFG register and + * don't do UDMA66+ anyway. + * LOCKING: * None (inherited from caller). */ @@ -381,11 +581,34 @@ static void piix_pata_phy_reset(struct a printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); return; } + ap->cbl = ATA_CBL_PATA40; + ata_port_probe(ap); + ata_bus_reset(ap); +} - piix_pata_cbl_detect(ap); - ata_port_probe(ap); +/** + * ich_pata_phy_reset - Probe specified port on PATA host controller + * @ap: Port to probe + * + * Probe PATA phy. + * + * LOCKING: + * None (inherited from caller). + */ + +static void ich_pata_phy_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->hard_port_no])) { + ata_port_disable(ap); + printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); + return; + } + + ich_pata_cbl_detect(ap); + ata_port_probe(ap); ata_bus_reset(ap); } @@ -481,6 +704,13 @@ static void piix_set_piomode (struct ata unsigned int slave_port = 0x44; u16 master_data; u8 slave_data; + u8 udma_enable; + int control = 0; + + /* + * See Intel Document 298600-004 for the timing programing rules + * for ICH controllers. + */ static const /* ISP RTC */ u8 timings[][2] = { { 0, 0 }, @@ -489,20 +719,30 @@ static void piix_set_piomode (struct ata { 2, 1 }, { 2, 3 }, }; + if (pio > 2) + control |= 1; /* TIME1 enable */ + if (ata_pio_need_iordy(adev)) + control |= 2; /* IE enable */ + + /* Intel specifies that the PPE functionality is for disk only */ + if (adev->class == ATA_DEV_ATA) + control |= 4; /* PPE enable */ + pci_read_config_word(dev, master_port, &master_data); if (is_slave) { + /* Enable SITRE (seperate slave timing register) */ master_data |= 0x4000; - /* enable PPE, IE and TIME */ - master_data |= 0x0070; + /* enable PPE1, IE1 and TIME1 as needed */ + master_data |= (control << 4); pci_read_config_byte(dev, slave_port, &slave_data); slave_data &= (ap->hard_port_no ? 0x0f : 0xf0); - slave_data |= - (timings[pio][0] << 2) | - (timings[pio][1] << (ap->hard_port_no ? 4 : 0)); + /* Load the timing nibble for this slave */ + slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << (ap->hard_port_no ? 4 : 0); } else { + /* Master keeps the bits in a different format */ master_data &= 0xccf8; - /* enable PPE, IE and TIME */ - master_data |= 0x0007; + /* Enable PPE, IE and TIME as appropriate */ + master_data |= control; master_data |= (timings[pio][0] << 12) | (timings[pio][1] << 8); @@ -510,84 +750,165 @@ static void piix_set_piomode (struct ata pci_write_config_word(dev, master_port, master_data); if (is_slave) pci_write_config_byte(dev, slave_port, slave_data); + + /* Ensure the UDMA bit is off - it will be turned back on if + UDMA is selected */ + + if (ap->udma_mask) { + pci_read_config_byte(dev, 0x48, &udma_enable); + udma_enable &= ~(1 << (2 * ap->hard_port_no + adev->devno)); + pci_write_config_byte(dev, 0x48, udma_enable); + } } /** - * piix_set_dmamode - Initialize host controller PATA PIO timings + * do_piix_set_dmamode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring - * @adev: um - * @udma: udma mode, 0 - 6 + * @adev: device to configure + * @isich: True if the device is an ICH and has IOCFG registers * - * Set UDMA mode for device, in host controller PCI config space. + * Set MW/UDMA mode for device, in host controller PCI config space. + * Note: We know the caller has already set the PIO mode. In doing + * so it has correctly set PPE, SITRE, IORDY and TIME1. We rely on that. * * LOCKING: * None (inherited from caller). */ -static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev) +static void do_pata_set_dmamode (struct ata_port *ap, struct ata_device *adev, int isich) { - unsigned int udma = adev->dma_mode; /* FIXME: MWDMA too */ struct pci_dev *dev = to_pci_dev(ap->host_set->dev); - u8 maslave = ap->hard_port_no ? 0x42 : 0x40; - u8 speed = udma; - unsigned int drive_dn = (ap->hard_port_no ? 2 : 0) + adev->devno; - int a_speed = 3 << (drive_dn * 4); - int u_flag = 1 << drive_dn; - int v_flag = 0x01 << drive_dn; - int w_flag = 0x10 << drive_dn; - int u_speed = 0; - int sitre; - u16 reg4042, reg4a; - u8 reg48, reg54, reg55; - - pci_read_config_word(dev, maslave, ®4042); - DPRINTK("reg4042 = 0x%04x\n", reg4042); - sitre = (reg4042 & 0x4000) ? 1 : 0; - pci_read_config_byte(dev, 0x48, ®48); - pci_read_config_word(dev, 0x4a, ®4a); - pci_read_config_byte(dev, 0x54, ®54); - pci_read_config_byte(dev, 0x55, ®55); - - switch(speed) { - case XFER_UDMA_4: - case XFER_UDMA_2: u_speed = 2 << (drive_dn * 4); break; - case XFER_UDMA_6: - case XFER_UDMA_5: - case XFER_UDMA_3: - case XFER_UDMA_1: u_speed = 1 << (drive_dn * 4); break; - case XFER_UDMA_0: u_speed = 0 << (drive_dn * 4); break; - case XFER_MW_DMA_2: - case XFER_MW_DMA_1: break; - default: - BUG(); - return; - } + u8 master_port = ap->hard_port_no ? 0x42 : 0x40; + u16 master_data; + u8 speed = adev->dma_mode; + int devid = adev->devno + 2 * ap->hard_port_no; + u8 udma_enable; + + static const /* ISP RTC */ + u8 timings[][2] = { { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + pci_read_config_word(dev, master_port, &master_data); + pci_read_config_byte(dev, 0x48, &udma_enable); + if (speed >= XFER_UDMA_0) { - if (!(reg48 & u_flag)) - pci_write_config_byte(dev, 0x48, reg48 | u_flag); - if (speed == XFER_UDMA_5) { - pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag); - } else { - pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag); + unsigned int udma = adev->dma_mode - XFER_UDMA_0; + u16 udma_timing; + u16 ideconf; + int u_clock, u_speed; + + /* + * UDMA is handled by a combination of clock switching and + * selection of dividers + * + * Handy rule: Odd modes are UDMATIMx 01, even are 02 + * except UDMA0 which is 00 + */ + u_speed = min(2 - (udma & 1), udma); + if (udma == 5) + u_clock = 0x1000; /* 100Mhz */ + else if (udma > 2) + u_clock = 1; /* 66Mhz */ + else + u_clock = 0; /* 33Mhz */ + + udma_enable |= (1 << devid); + + /* Load the CT/RP selection */ + pci_read_config_word(dev, 0x4A, &udma_timing); + udma_timing &= ~(3 << (4 * devid)); + udma_timing |= u_speed << (4 * devid); + pci_write_config_word(dev, 0x4A, udma_timing); + + if (isich) { + /* Select a 33/66/100Mhz clock */ + pci_read_config_word(dev, 0x54, &ideconf); + ideconf &= ~(0x1001 << devid); + ideconf |= u_clock << devid; + /* For ICH or later we should set bit 10 for better + performance (WR_PingPong_En) */ + pci_write_config_word(dev, 0x54, ideconf); } - if ((reg4a & a_speed) != u_speed) - pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed); - if (speed > XFER_UDMA_2) { - if (!(reg54 & v_flag)) - pci_write_config_byte(dev, 0x54, reg54 | v_flag); - } else - pci_write_config_byte(dev, 0x54, reg54 & ~v_flag); } else { - if (reg48 & u_flag) - pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); - if (reg4a & a_speed) - pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); - if (reg54 & v_flag) - pci_write_config_byte(dev, 0x54, reg54 & ~v_flag); - if (reg55 & w_flag) - pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag); + /* + * MWDMA is driven by the PIO timings. We must also enable + * IORDY unconditionally along with TIME1. PPE has already + * been set when the PIO timing was set. + */ + unsigned int mwdma = adev->dma_mode - XFER_MW_DMA_0; + unsigned int control; + u8 slave_data; + const unsigned int needed_pio[3] = { + XFER_PIO_0, XFER_PIO_3, XFER_PIO_4 + }; + int pio = needed_pio[mwdma] - XFER_PIO_0; + + control = 3; /* IORDY|TIME1 */ + + /* If the drive MWDMA is faster than it can do PIO then + we must force PIO into PIO0 */ + + if (adev->pio_mode < needed_pio[mwdma]) + /* Enable DMA timing only */ + control |= 8; /* PIO cycles in PIO0 */ + + if (adev->devno) { /* Slave */ + master_data &= 0xFF4F; /* Mask out IORDY|TIME1|DMAONLY */ + master_data |= control << 4; + pci_read_config_byte(dev, 0x44, &slave_data); + slave_data &= (0x0F + 0xE1 * ap->hard_port_no); + /* Load the matching timing */ + slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << (ap->hard_port_no ? 4 : 0); + pci_write_config_byte(dev, 0x44, slave_data); + } else { /* Master */ + master_data &= 0xCCF4; /* Mask out IORDY|TIME1|DMAONLY + and master timing bits */ + master_data |= control; + master_data |= + (timings[pio][0] << 12) | + (timings[pio][1] << 8); + } + udma_enable &= ~(1 << devid); + pci_write_config_word(dev, master_port, master_data); } + /* Don't scribble on 0x48 if the controller does not support UDMA */ + if (ap->udma_mask) + pci_write_config_byte(dev, 0x48, udma_enable); +} + +/** + * piix_set_dmamode - Initialize host controller PATA DMA timings + * @ap: Port whose timings we are configuring + * @adev: um + * + * Set MW/UDMA mode for device, in host controller PCI config space. + * + * LOCKING: + * None (inherited from caller). + */ + +static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev) +{ + do_pata_set_dmamode(ap, adev, 0); +} + +/** + * ich_set_dmamode - Initialize host controller PATA DMA timings + * @ap: Port whose timings we are configuring + * @adev: um + * + * Set MW/UDMA mode for device, in host controller PCI config space. + * + * LOCKING: + * None (inherited from caller). + */ + +static void ich_set_dmamode (struct ata_port *ap, struct ata_device *adev) +{ + do_pata_set_dmamode(ap, adev, 1); } #define AHCI_PCI_BAR 5 @@ -646,15 +967,15 @@ static int __devinit piix_check_450nx_er pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); pci_read_config_word(pdev, 0x41, &cfg); /* Only on the original revision: IDE DMA can hang */ - if(rev == 0x00) + if (rev == 0x00) no_piix_dma = 1; /* On all revisions below 5 PXB bus lock must be disabled for IDE */ - else if(cfg & (1<<14) && rev < 5) + else if (cfg & (1<<14) && rev < 5) no_piix_dma = 2; } - if(no_piix_dma) + if (no_piix_dma) dev_printk(KERN_WARNING, &ata_dev->dev, "450NX errata present, disabling IDE DMA.\n"); - if(no_piix_dma == 2) + if (no_piix_dma == 2) dev_printk(KERN_WARNING, &ata_dev->dev, "A BIOS update may resolve this.\n"); return no_piix_dma; } Index: linux-2.6.16/drivers/scsi/libata-core.c =================================================================== --- linux-2.6.16.orig/drivers/scsi/libata-core.c +++ linux-2.6.16/drivers/scsi/libata-core.c @@ -61,6 +61,13 @@ #include "libata.h" +#undef readb +#undef writeb + +#define readb(x) printk(KERN_CRIT "readb() at line %d\n", __LINE__) +#define writeb(d,a) printk(KERN_CRIT "writeb() at line %d\n", __LINE__) + + static unsigned int ata_busy_sleep (struct ata_port *ap, unsigned long tmout_pat, unsigned long tmout); @@ -68,9 +75,10 @@ static void ata_dev_reread_id(struct ata static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev); static void ata_set_mode(struct ata_port *ap); static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev); -static unsigned int ata_get_mode_mask(const struct ata_port *ap, int shift); +static unsigned int ata_get_mode_mask(const struct ata_port *ap, struct ata_device *adev, int shift); static int fgb(u32 bitmap); static int ata_choose_xfer_mode(const struct ata_port *ap, + struct ata_device *adev, u8 *xfer_mode_out, unsigned int *xfer_shift_out); static void __ata_qc_complete(struct ata_queued_cmd *qc); @@ -78,7 +86,7 @@ static void __ata_qc_complete(struct ata static unsigned int ata_unique_id = 1; static struct workqueue_struct *ata_wq; -int atapi_enabled = 0; +int atapi_enabled = 1; module_param(atapi_enabled, int, 0444); MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on)"); @@ -334,7 +342,7 @@ static void ata_tf_read_pio(struct ata_p { struct ata_ioports *ioaddr = &ap->ioaddr; - tf->command = ata_check_status(ap); + tf->command = ap->ops->check_status(ap); tf->feature = inb(ioaddr->error_addr); tf->nsect = inb(ioaddr->nsect_addr); tf->lbal = inb(ioaddr->lbal_addr); @@ -368,7 +376,7 @@ static void ata_tf_read_mmio(struct ata_ { struct ata_ioports *ioaddr = &ap->ioaddr; - tf->command = ata_check_status(ap); + tf->command = ap->ops->check_status(ap); tf->feature = readb((void __iomem *)ioaddr->error_addr); tf->nsect = readb((void __iomem *)ioaddr->nsect_addr); tf->lbal = readb((void __iomem *)ioaddr->lbal_addr); @@ -790,6 +798,9 @@ static unsigned int ata_mmio_devchk(stru static unsigned int ata_devchk(struct ata_port *ap, unsigned int device) { + if (ap->ops->dev_check) + return ap->ops->dev_check(ap, device); + if (ap->flags & ATA_FLAG_MMIO) return ata_mmio_devchk(ap, device); return ata_pio_devchk(ap, device); @@ -1209,6 +1220,48 @@ unsigned int ata_pio_need_iordy(const st return 0; } +static void ata_dev_check_hpa(struct ata_port *ap, struct ata_device *dev) +{ + struct ata_taskfile tf; + unsigned long long true_size; + unsigned int err_mask; + + if (!ata_id_has_hpa(dev->id) || !ata_id_hpa_enabled(dev->id)) + return; + + /* Issue a query for HPA */ + ata_dev_select(ap, dev->devno, 1, 1); + ata_tf_init(ap, &tf, dev->devno); + + if (dev->flags & ATA_DFLAG_LBA48) { + tf.command = ATA_CMD_READ_NATIVE_MAX_EXT; + tf.device |= 0x40; + err_mask = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0); + if (err_mask) + return; + /* Ok HPA is live */ + true_size = (tf.hob_lbah << 16) | (tf.hob_lbam << 8) | (tf.hob_lbal); + true_size <<= 24; + true_size |= (tf.lbah << 16) | (tf.lbam << 8) | tf.lbal; + } else { + tf.command = ATA_CMD_READ_NATIVE_MAX; + tf.device |= 0x40; + err_mask = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0); + if (err_mask) + return; + /* Ok HPA is live */ + true_size = ((tf.device & 0x0F) << 24) | + (tf.lbah << 16) | + (tf.lbam << 8) | + tf.lbal; + } + dev->flags |= ATA_DFLAG_HPA; + /* Should save the HPA value and expose it for dmraid then + remove the clipping */ + printk(KERN_INFO "HPA present: true size %lld sectors.\n", + true_size + 1); +} + /** * ata_dev_identify - obtain IDENTIFY x DEVICE page * @ap: port on which device we wish to probe resides @@ -1332,7 +1385,7 @@ retry: /* ATA-specific feature tests */ if (dev->class == ATA_DEV_ATA) { - if (!ata_id_is_ata(dev->id)) /* sanity check */ + if (!ata_id_is_ata(dev->id) && !ata_id_is_cfa(dev->id)) /* sanity check */ goto err_out_nosup; /* get major version */ @@ -1404,6 +1457,13 @@ retry: } ap->host->max_cmd_len = 16; + + /* + * See if we have the HPA misfeature on the drive + */ +#if 0 /* TESTING */ + ata_dev_check_hpa(ap, dev); +#endif } /* ATAPI-specific feature tests */ @@ -1489,10 +1549,24 @@ static int ata_bus_probe(struct ata_port ap->ops->phy_reset(ap); if (ap->flags & ATA_FLAG_PORT_DISABLED) goto err_out; + + /* The reset means we are in PIO 0, but the controller may not + yet be correctly set up and may have old BIOS settings, or just + no settings at all. Set all the devices to PIO 0 */ + + for (i = 0; i < ATA_MAX_DEVICES; i++) + ap->device[i].pio_mode = XFER_PIO_0; for (i = 0; i < ATA_MAX_DEVICES; i++) { + struct ata_device *adev = & ap->device[i]; + + /* Set up the controller on this port for PIO 0. We must not + send the drive speed setting commands at this point */ + if (ap->ops->set_piomode) + ap->ops->set_piomode(ap, adev); + ata_dev_identify(ap, i); - if (ata_dev_present(&ap->device[i])) { + if (ata_dev_present(adev)) { found = 1; ata_dev_config(ap,i); } @@ -1501,7 +1575,11 @@ static int ata_bus_probe(struct ata_port if ((!found) || (ap->flags & ATA_FLAG_PORT_DISABLED)) goto err_out_disable; - ata_set_mode(ap); + if(ap->ops->set_mode) + ap->ops->set_mode(ap); + else + ata_set_mode(ap); + if (ap->flags & ATA_FLAG_PORT_DISABLED) goto err_out_disable; @@ -1616,6 +1694,23 @@ void sata_phy_reset(struct ata_port *ap) } /** + * ata_dev_pair - return other device on cable + * @ap: port + * @adev: device + * + * Obtain the other device on the same cable, or if none is + * present NULL is returned + */ + +struct ata_device *ata_dev_pair(struct ata_port *ap, struct ata_device *adev) +{ + struct ata_device *pair = &ap->device[1 - adev->devno]; + if (!ata_dev_present(pair)) + return NULL; + return pair; +} + +/** * ata_port_disable - Disable port. * @ap: Port to be disabled. * @@ -1828,16 +1923,19 @@ static void ata_dev_set_mode(struct ata_ ap->id, dev->devno, xfer_mode_str[idx]); } -static int ata_host_set_pio(struct ata_port *ap) +static int ata_host_set_pio(struct ata_port *ap, struct ata_device *adev) { unsigned int mask; - int x, i; + int x; u8 base, xfer_mode; - mask = ata_get_mode_mask(ap, ATA_SHIFT_PIO); + if (!ata_dev_present(adev)) + return 0; + + mask = ata_get_mode_mask(ap, adev, ATA_SHIFT_PIO); x = fgb(mask); if (x < 0) { - printk(KERN_WARNING "ata%u: no PIO support\n", ap->id); + printk(KERN_WARNING "ata%u: no PIO support for device %d.\n", ap->id, adev->devno); return -1; } @@ -1847,34 +1945,24 @@ static int ata_host_set_pio(struct ata_p DPRINTK("base 0x%x xfer_mode 0x%x mask 0x%x x %d\n", (int)base, (int)xfer_mode, mask, x); - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - if (ata_dev_present(dev)) { - dev->pio_mode = xfer_mode; - dev->xfer_mode = xfer_mode; - dev->xfer_shift = ATA_SHIFT_PIO; - if (ap->ops->set_piomode) - ap->ops->set_piomode(ap, dev); - } - } + adev->pio_mode = xfer_mode; + adev->xfer_mode = xfer_mode; + adev->xfer_shift = ATA_SHIFT_PIO; + if (ap->ops->set_piomode) + ap->ops->set_piomode(ap, adev); return 0; } -static void ata_host_set_dma(struct ata_port *ap, u8 xfer_mode, - unsigned int xfer_shift) +static void ata_host_set_dma(struct ata_port *ap, struct ata_device *adev, + u8 xfer_mode, unsigned int xfer_shift) { - int i; - - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - if (ata_dev_present(dev)) { - dev->dma_mode = xfer_mode; - dev->xfer_mode = xfer_mode; - dev->xfer_shift = xfer_shift; - if (ap->ops->set_dmamode) - ap->ops->set_dmamode(ap, dev); - } + if (ata_dev_present(adev)) { + adev->dma_mode = xfer_mode; + adev->xfer_mode = xfer_mode; + adev->xfer_shift = xfer_shift; + if (ap->ops->set_dmamode) + ap->ops->set_dmamode(ap, adev); } } @@ -1890,32 +1978,64 @@ static void ata_host_set_dma(struct ata_ */ static void ata_set_mode(struct ata_port *ap) { - unsigned int xfer_shift; - u8 xfer_mode; + unsigned int xfer_shift[ATA_MAX_DEVICES]; + u8 xfer_mode[ATA_MAX_DEVICES]; int rc; + int i; + int used_dma = 0; /* Track if DMA was used for this setup */ - /* step 1: always set host PIO timings */ - rc = ata_host_set_pio(ap); - if (rc) - goto err_out; + /* We need to set timings individually for each device */ - /* step 2: choose the best data xfer mode */ - xfer_mode = xfer_shift = 0; - rc = ata_choose_xfer_mode(ap, &xfer_mode, &xfer_shift); - if (rc) - goto err_out; + /* Compute the timings first so that when we ask the device to do + speed configuration it can see all the intended device state in + full */ - /* step 3: if that xfer mode isn't PIO, set host DMA timings */ - if (xfer_shift != ATA_SHIFT_PIO) - ata_host_set_dma(ap, xfer_mode, xfer_shift); - - /* step 4: update devices' xfer mode */ - ata_dev_set_mode(ap, &ap->device[0]); - ata_dev_set_mode(ap, &ap->device[1]); + for (i = 0; i < ATA_MAX_DEVICES; i++) { + struct ata_device *adev = &ap->device[i]; + /* Choose the best data xfer mode */ + xfer_mode[i] = xfer_shift[i] = 0; + rc = ata_choose_xfer_mode(ap, adev, &xfer_mode[i], &xfer_shift[i]); + if (rc) + goto err_out; + + } + + /* Now set the mode tables we have computed */ + for (i = 0; i < ATA_MAX_DEVICES; i++) { + struct ata_device *adev = &ap->device[i]; + /* step 1: always set host PIO timings */ + rc = ata_host_set_pio(ap, adev); + if (rc) + goto err_out; + + /* step 2: if that xfer mode isn't PIO, set host DMA timings */ + if (xfer_shift[i] != ATA_SHIFT_PIO) { + ata_host_set_dma(ap, adev, xfer_mode[i], xfer_shift[i]); + used_dma = 1; + } + + /* In some cases the DMA mode will cause the driver to + update the pio mode to match chip limits. */ + + /* step 3: update devices' xfer mode */ + ata_dev_set_mode(ap, adev); + } if (ap->flags & ATA_FLAG_PORT_DISABLED) return; + /* + * Record simplex status. If we selected DMA then the other + * host channels are not permitted to do so. + */ + + if (used_dma && (ap->host_set->host_set_flags & ATA_HOST_SIMPLEX)) + ap->host_set->simplex_claimed = 1; + + /* + * Chip specific finalisation + */ + if (ap->ops->post_set_mode) ap->ops->post_set_mode(ap); @@ -2146,10 +2266,10 @@ void ata_bus_reset(struct ata_port *ap) rc = ata_bus_softreset(ap, devmask); else if ((ap->flags & ATA_FLAG_SATA_RESET) == 0) { /* set up device control */ - if (ap->flags & ATA_FLAG_MMIO) - writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr); - else - outb(ap->ctl, ioaddr->ctl_addr); +// if (ap->flags & ATA_FLAG_MMIO) +// writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr); +// else +// outb(ap->ctl, ioaddr->ctl_addr); rc = ata_bus_edd(ap); } @@ -2204,132 +2324,126 @@ static void ata_pr_blacklisted(const str } static const char * const ata_dma_blacklist [] = { - "WDC AC11000H", - "WDC AC22100H", - "WDC AC32500H", - "WDC AC33100H", - "WDC AC31600H", - "WDC AC32100H", - "WDC AC23200L", - "Compaq CRD-8241B", - "CRD-8400B", - "CRD-8480B", - "CRD-8482B", - "CRD-84", - "SanDisk SDP3B", - "SanDisk SDP3B-64", - "SANYO CD-ROM CRD", - "HITACHI CDR-8", - "HITACHI CDR-8335", - "HITACHI CDR-8435", - "Toshiba CD-ROM XM-6202B", - "TOSHIBA CD-ROM XM-1702BC", - "CD-532E-A", - "E-IDE CD-ROM CR-840", - "CD-ROM Drive/F5A", - "WPI CDD-820", - "SAMSUNG CD-ROM SC-148C", - "SAMSUNG CD-ROM SC", - "SanDisk SDP3B-64", - "ATAPI CD-ROM DRIVE 40X MAXIMUM", - "_NEC DV5800A", + "WDC AC11000H", NULL, + "WDC AC22100H", NULL, + "WDC AC32500H", NULL, + "WDC AC33100H", NULL, + "WDC AC31600H", NULL, + "WDC AC32100H", "24.09P07", + "WDC AC23200L", "21.10N21", + "Compaq CRD-8241B", NULL, + "CRD-8400B", NULL, + "CRD-8480B", NULL, + "CRD-8482B", NULL, + "CRD-84", NULL, + "SanDisk SDP3B", NULL, + "SanDisk SDP3B-64", NULL, + "SANYO CD-ROM CRD", NULL, + "HITACHI CDR-8", NULL, + "HITACHI CDR-8335", NULL, + "HITACHI CDR-8435", NULL, + "Toshiba CD-ROM XM-6202B", NULL, + "TOSHIBA CD-ROM XM-1702BC", NULL, + "CD-532E-A", NULL, + "E-IDE CD-ROM CR-840", NULL, + "CD-ROM Drive/F5A", NULL, + "WPI CDD-820", NULL, + "SAMSUNG CD-ROM SC-148C", NULL, + "SAMSUNG CD-ROM SC", NULL, + "SanDisk SDP3B-64", NULL, + "ATAPI CD-ROM DRIVE 40X MAXIMUM",NULL, + "_NEC DV5800A", NULL, + "SAMSUNG CD-ROM SN-124", "N001" }; -static int ata_dma_blacklisted(const struct ata_device *dev) +static int ata_strim(char *s, size_t len) { - unsigned char model_num[40]; - char *s; - unsigned int len; - int i; - - ata_dev_id_string(dev->id, model_num, ATA_ID_PROD_OFS, - sizeof(model_num)); - s = &model_num[0]; - len = strnlen(s, sizeof(model_num)); + len = strnlen(s, len); /* ATAPI specifies that empty space is blank-filled; remove blanks */ while ((len > 0) && (s[len - 1] == ' ')) { len--; s[len] = 0; } + return len; +} - for (i = 0; i < ARRAY_SIZE(ata_dma_blacklist); i++) - if (!strncmp(ata_dma_blacklist[i], s, len)) - return 1; +static int ata_dma_blacklisted(const struct ata_device *dev) +{ + unsigned char model_num[40]; + unsigned char model_rev[16]; + unsigned int nlen, rlen; + int i; + ata_dev_id_string(dev->id, model_num, ATA_ID_PROD_OFS, + sizeof(model_num)); + ata_dev_id_string(dev->id, model_rev, ATA_ID_FW_REV_OFS, + sizeof(model_rev)); + nlen = ata_strim(model_num, sizeof(model_num)); + rlen = ata_strim(model_rev, sizeof(model_rev)); + + for (i = 0; i < ARRAY_SIZE(ata_dma_blacklist); i += 2) { + if (!strncmp(ata_dma_blacklist[i], model_num, nlen)) { + if (ata_dma_blacklist[i+1] == NULL) + return 1; + if (!strncmp(ata_dma_blacklist[i], model_rev, rlen)) + return 1; + } + } return 0; } -static unsigned int ata_get_mode_mask(const struct ata_port *ap, int shift) +static unsigned int ata_get_mode_mask(const struct ata_port *ap, struct ata_device *adev, int shift) { - const struct ata_device *master, *slave; unsigned int mask; + struct ata_host_set *hs = ap->host_set; - master = &ap->device[0]; - slave = &ap->device[1]; + if (!ata_dev_present(adev)) + return 0xFF; /* Drive isn't limiting anything */ + + if (shift == ATA_SHIFT_PIO) { + u16 tmp_mode = ata_pio_modes(adev); + mask = ap->pio_mask; + mask &= tmp_mode; + } - assert (ata_dev_present(master) || ata_dev_present(slave)); + /* + * Enforce simplex rules if host is simplex + */ + + if (hs->host_set_flags & ATA_HOST_SIMPLEX) { + if (hs->simplex_claimed) { + if (shift != ATA_SHIFT_PIO) + return 0; + } + } if (shift == ATA_SHIFT_UDMA) { mask = ap->udma_mask; - if (ata_dev_present(master)) { - mask &= (master->id[ATA_ID_UDMA_MODES] & 0xff); - if (ata_dma_blacklisted(master)) { - mask = 0; - ata_pr_blacklisted(ap, master); - } - } - if (ata_dev_present(slave)) { - mask &= (slave->id[ATA_ID_UDMA_MODES] & 0xff); - if (ata_dma_blacklisted(slave)) { - mask = 0; - ata_pr_blacklisted(ap, slave); - } + mask &= (adev->id[ATA_ID_UDMA_MODES] & 0xff); + if (ata_dma_blacklisted(adev)) { + mask = 0; + ata_pr_blacklisted(ap, adev); } + /* 40 pin cable enforcement */ + if (ap->cbl == ATA_CBL_PATA40) + mask &= ATA_UDMA_MASK_40C; } else if (shift == ATA_SHIFT_MWDMA) { mask = ap->mwdma_mask; - if (ata_dev_present(master)) { - mask &= (master->id[ATA_ID_MWDMA_MODES] & 0x07); - if (ata_dma_blacklisted(master)) { - mask = 0; - ata_pr_blacklisted(ap, master); - } - } - if (ata_dev_present(slave)) { - mask &= (slave->id[ATA_ID_MWDMA_MODES] & 0x07); - if (ata_dma_blacklisted(slave)) { - mask = 0; - ata_pr_blacklisted(ap, slave); - } - } - } - else if (shift == ATA_SHIFT_PIO) { - mask = ap->pio_mask; - if (ata_dev_present(master)) { - /* spec doesn't return explicit support for - * PIO0-2, so we fake it - */ - u16 tmp_mode = master->id[ATA_ID_PIO_MODES] & 0x03; - tmp_mode <<= 3; - tmp_mode |= 0x7; - mask &= tmp_mode; + mask &= (adev->id[ATA_ID_MWDMA_MODES] & 0x07); + if (ata_dma_blacklisted(adev)) { + mask = 0; + ata_pr_blacklisted(ap, adev); } - if (ata_dev_present(slave)) { - /* spec doesn't return explicit support for - * PIO0-2, so we fake it - */ - u16 tmp_mode = slave->id[ATA_ID_PIO_MODES] & 0x03; - tmp_mode <<= 3; - tmp_mode |= 0x7; - mask &= tmp_mode; - } - } - else { - mask = 0xffffffff; /* shut up compiler warning */ - BUG(); - } - + } else if (shift != ATA_SHIFT_PIO) + panic("gmm:bad shift"); /* BUG confuses the compiler */ + /* + * Allow the controller to see the proposed mode and + * device data to do any custom filtering rules. + */ + if(ap->ops->mode_filter) + mask = ap->ops->mode_filter(ap, adev, mask, shift); return mask; } @@ -2349,6 +2463,7 @@ static int fgb(u32 bitmap) /** * ata_choose_xfer_mode - attempt to find best transfer mode * @ap: Port for which an xfer mode will be selected + * @adev: ATA device for which xfer mode is being selected * @xfer_mode_out: (output) SET FEATURES - XFER MODE code * @xfer_shift_out: (output) bit shift that selects this mode * @@ -2363,6 +2478,7 @@ static int fgb(u32 bitmap) */ static int ata_choose_xfer_mode(const struct ata_port *ap, + struct ata_device *adev, u8 *xfer_mode_out, unsigned int *xfer_shift_out) { @@ -2371,7 +2487,7 @@ static int ata_choose_xfer_mode(const st for (i = 0; i < ARRAY_SIZE(xfer_mode_classes); i++) { shift = xfer_mode_classes[i].shift; - mask = ata_get_mode_mask(ap, shift); + mask = ata_get_mode_mask(ap, adev, shift); x = fgb(mask); if (x >= 0) { @@ -3007,6 +3123,7 @@ void swap_buf_le16(u16 *buf, unsigned in /** * ata_mmio_data_xfer - Transfer data by MMIO * @ap: port to read/write + * @adev: device to target * @buf: data buffer * @buflen: buffer length * @write_data: read/write @@ -3017,8 +3134,8 @@ void swap_buf_le16(u16 *buf, unsigned in * Inherited from caller. */ -static void ata_mmio_data_xfer(struct ata_port *ap, unsigned char *buf, - unsigned int buflen, int write_data) +void ata_mmio_data_xfer(struct ata_port *ap, struct ata_device *adev, + unsigned char *buf, unsigned int buflen, int write_data) { unsigned int i; unsigned int words = buflen >> 1; @@ -3052,6 +3169,7 @@ static void ata_mmio_data_xfer(struct at /** * ata_pio_data_xfer - Transfer data by PIO * @ap: port to read/write + * @adev: device to target * @buf: data buffer * @buflen: buffer length * @write_data: read/write @@ -3062,11 +3180,11 @@ static void ata_mmio_data_xfer(struct at * Inherited from caller. */ -static void ata_pio_data_xfer(struct ata_port *ap, unsigned char *buf, - unsigned int buflen, int write_data) +void ata_pio_data_xfer(struct ata_port *ap, struct ata_device *adev, + unsigned char *buf, unsigned int buflen, int write_data) { unsigned int words = buflen >> 1; - + /* Transfer multiple of 2 bytes */ if (write_data) outsw(ap->ioaddr.data_addr, buf, words); @@ -3089,36 +3207,55 @@ static void ata_pio_data_xfer(struct ata } /** - * ata_data_xfer - Transfer data from/to the data register. + * ata_pio_data_xfer_noirq - Transfer data from/to the data register. * @ap: port to read/write + * @adev: device to target * @buf: data buffer * @buflen: buffer length * @do_write: read/write * - * Transfer data from/to the device data register. + * Transfer data from/to the device data register. This variant + * ensures local IRQs do not interrupt the data stream and this + * is needed for some controllers. * * LOCKING: * Inherited from caller. */ -static void ata_data_xfer(struct ata_port *ap, unsigned char *buf, - unsigned int buflen, int do_write) +void ata_pio_data_xfer_noirq(struct ata_port *ap, struct ata_device *adev, + unsigned char *buf, unsigned int buflen, int do_write) { /* Make the crap hardware pay the costs not the good stuff */ - if (unlikely(ap->flags & ATA_FLAG_IRQ_MASK)) { - unsigned long flags; - local_irq_save(flags); - if (ap->flags & ATA_FLAG_MMIO) - ata_mmio_data_xfer(ap, buf, buflen, do_write); - else - ata_pio_data_xfer(ap, buf, buflen, do_write); - local_irq_restore(flags); - } else { - if (ap->flags & ATA_FLAG_MMIO) - ata_mmio_data_xfer(ap, buf, buflen, do_write); - else - ata_pio_data_xfer(ap, buf, buflen, do_write); - } + unsigned long flags; + local_irq_save(flags); + ata_pio_data_xfer(ap, adev, buf, buflen, do_write); + local_irq_restore(flags); +} + +/** + * ata_mmio_data_xfer_noirq - Transfer data from/to the data register. + * @ap: address to read/write + * @adev: device to target + * @buf: data buffer + * @buflen: buffer length + * @do_write: read/write + * + * Transfer data from/to the device data register. This variant + * ensures local IRQs do not interrupt the data stream and this + * is needed for some controllers. + * + * LOCKING: + * Inherited from caller. + */ + +void ata_mmio_data_xfer_noirq(struct ata_port *ap, struct ata_device *adev, + unsigned char *buf, unsigned int buflen, int do_write) +{ + /* Make the crap hardware pay the costs not the good stuff */ + unsigned long flags; + local_irq_save(flags); + ata_mmio_data_xfer(ap, adev, buf, buflen, do_write); + local_irq_restore(flags); } /** @@ -3164,7 +3301,7 @@ static void ata_pio_sector(struct ata_qu /* do the actual data transfer */ do_write = (qc->tf.flags & ATA_TFLAG_WRITE); - ata_data_xfer(ap, buf, ATA_SECT_SIZE, do_write); + ap->ops->data_xfer(ap, qc->dev, buf, ATA_SECT_SIZE, do_write); kunmap(page); } @@ -3211,7 +3348,7 @@ next_sg: ap->id, bytes); for (i = 0; i < words; i++) - ata_data_xfer(ap, (unsigned char*)pad_buf, 2, do_write); + ap->ops->data_xfer(ap, qc->dev, (unsigned char*)pad_buf, 2, do_write); ap->hsm_task_state = HSM_ST_LAST; return; @@ -3246,7 +3383,7 @@ next_sg: DPRINTK("data %s\n", qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read"); /* do the actual data transfer */ - ata_data_xfer(ap, buf, count, do_write); + ap->ops->data_xfer(ap, qc->dev, buf, count, do_write); kunmap(page); @@ -3955,14 +4092,15 @@ void ata_bmdma_setup(struct ata_queued_c void ata_bmdma_irq_clear(struct ata_port *ap) { - if (ap->flags & ATA_FLAG_MMIO) { - void __iomem *mmio = ((void __iomem *) ap->ioaddr.bmdma_addr) + ATA_DMA_STATUS; - writeb(readb(mmio), mmio); - } else { - unsigned long addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS; - outb(inb(addr), addr); - } - + if (ap->ioaddr.bmdma_addr) { + if (ap->flags & ATA_FLAG_MMIO) { + void __iomem *mmio = ((void __iomem *) ap->ioaddr.bmdma_addr) + ATA_DMA_STATUS; + writeb(readb(mmio), mmio); + } else { + unsigned long addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS; + outb(inb(addr), addr); + } + } } @@ -4201,12 +4339,12 @@ static void atapi_packet_task(void *_dat */ spin_lock_irqsave(&ap->host_set->lock, flags); ap->flags &= ~ATA_FLAG_NOINTR; - ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1); + ap->ops->data_xfer(ap, qc->dev, qc->cdb, ap->cdb_len, 1); if (qc->tf.protocol == ATA_PROT_ATAPI_DMA) ap->ops->bmdma_start(qc); /* initiate bmdma */ spin_unlock_irqrestore(&ap->host_set->lock, flags); } else { - ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1); + ap->ops->data_xfer(ap, qc->dev, qc->cdb, ap->cdb_len, 1); /* PIO commands are handled by polling */ ap->hsm_task_state = HSM_ST; @@ -4512,7 +4650,7 @@ err_out: * Number of ports registered. Zero on error (no ports registered). */ -int ata_device_add(const struct ata_probe_ent *ent) +int ata_device_add(struct ata_probe_ent *ent) { unsigned int count = 0, i; struct device *dev = ent->dev; @@ -4532,6 +4670,9 @@ int ata_device_add(const struct ata_prob host_set->mmio_base = ent->mmio_base; host_set->private_data = ent->private_data; host_set->ops = ent->port_ops; + host_set->host_set_flags = ent->host_set_flags; + + ent->host_set = host_set; /* register each port bound to this device */ for (i = 0; i < ent->n_ports; i++) { @@ -4543,6 +4684,14 @@ int ata_device_add(const struct ata_prob goto err_out; host_set->ports[i] = ap; + + /* Tidy up if we have no bus master base + Not sure this is the right spot to do it */ + if (ap->ioaddr.bmdma_addr == 0) { + ap->udma_mask = 0; + ap->mwdma_mask = 0; + } + xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) | (ap->mwdma_mask << ATA_SHIFT_MWDMA) | (ap->pio_mask << ATA_SHIFT_PIO); @@ -4558,7 +4707,11 @@ int ata_device_add(const struct ata_prob ap->ioaddr.bmdma_addr, ent->irq); + if (!ent->irq) + printk(KERN_INFO "ata%u: polling mode\n", ap->id); + ata_chk_status(ap); + /* This last call probably should be conditional on bmdma */ host_set->ops->irq_clear(ap); count++; } @@ -4567,7 +4720,8 @@ int ata_device_add(const struct ata_prob goto err_free_ret; /* obtain irq, that is shared between channels */ - if (request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags, + if (ent->irq) + if (request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags, DRV_NAME, host_set)) goto err_out; @@ -4612,7 +4766,8 @@ int ata_device_add(const struct ata_prob ata_scsi_scan_host(ap); } - dev_set_drvdata(dev, host_set); + if(dev) + dev_set_drvdata(dev, host_set); VPRINTK("EXIT, returning %u\n", ent->n_ports); return ent->n_ports; /* success */ @@ -4649,7 +4804,8 @@ void ata_host_set_remove(struct ata_host scsi_remove_host(ap->host); } - free_irq(host_set->irq, host_set); + if (host_set->irq) + free_irq(host_set->irq, host_set); for (i = 0; i < host_set->n_ports; i++) { ap = host_set->ports[i]; @@ -4786,6 +4942,7 @@ ata_pci_init_native_mode(struct pci_dev { struct ata_probe_ent *probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); + unsigned long bmdma; int p = 0; if (!probe_ent) @@ -4800,7 +4957,13 @@ ata_pci_init_native_mode(struct pci_dev probe_ent->port[p].altstatus_addr = probe_ent->port[p].ctl_addr = pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS; - probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4); + bmdma = pci_resource_start(pdev, 4); + + if (bmdma) { + if (inb(bmdma + 2) & 0x80) + probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; + probe_ent->port[p].bmdma_addr = bmdma; + } ata_std_ports(&probe_ent->port[p]); p++; } @@ -4810,7 +4973,14 @@ ata_pci_init_native_mode(struct pci_dev probe_ent->port[p].altstatus_addr = probe_ent->port[p].ctl_addr = pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS; - probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4) + 8; + bmdma = pci_resource_start(pdev, 4); + + if (bmdma) { + bmdma += 8; + if(inb(bmdma + 2) & 0x80) + probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; + probe_ent->port[p].bmdma_addr = bmdma; + } ata_std_ports(&probe_ent->port[p]); p++; } @@ -4822,6 +4992,7 @@ ata_pci_init_native_mode(struct pci_dev static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, struct ata_port_info *port, int port_num) { struct ata_probe_ent *probe_ent; + unsigned long bmdma; probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port); if (!probe_ent) @@ -4847,7 +5018,13 @@ static struct ata_probe_ent *ata_pci_ini probe_ent->port[0].ctl_addr = 0x376; break; } - probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4) + 8 * port_num; + bmdma = pci_resource_start(pdev, 4); + if(bmdma != 0) { + bmdma += 8 * port_num; + probe_ent->port[0].bmdma_addr = bmdma; + if (inb(bmdma + 2) & 0x80) + probe_ent->host_set_flags |= ATA_HOST_SIMPLEX; + } ata_std_ports(&probe_ent->port[0]); return probe_ent; } @@ -5088,6 +5265,33 @@ int ata_pci_device_resume(struct pci_dev pci_set_master(pdev); return 0; } + +/** + * ata_pci_clear_simplex - attempt to kick device out of simplex + * @pdev: PCI device + * + * Some PCI ATA devices report simplex mode but in fact can be told to + * enter non simplex mode. This implements the neccessary logic to + * perform the task on such devices. Calling it on other devices will + * have -undefined- behaviour. + */ + +int ata_pci_clear_simplex(struct pci_dev *pdev) +{ + unsigned long bmdma = pci_resource_start(pdev, 4); + u8 simplex; + + if (bmdma == 0) + return -ENOENT; + + simplex = inb(bmdma + 0x02); + outb(simplex & 0x60, bmdma + 0x02); + simplex = inb(bmdma + 0x02); + if (simplex & 0x80) + return -EOPNOTSUPP; + return 0; +} + #endif /* CONFIG_PCI */ @@ -5165,6 +5369,10 @@ EXPORT_SYMBOL_GPL(ata_bmdma_start); EXPORT_SYMBOL_GPL(ata_bmdma_irq_clear); EXPORT_SYMBOL_GPL(ata_bmdma_status); EXPORT_SYMBOL_GPL(ata_bmdma_stop); +EXPORT_SYMBOL_GPL(ata_mmio_data_xfer); +EXPORT_SYMBOL_GPL(ata_pio_data_xfer); +EXPORT_SYMBOL_GPL(ata_mmio_data_xfer_noirq); +EXPORT_SYMBOL_GPL(ata_pio_data_xfer_noirq); EXPORT_SYMBOL_GPL(ata_port_probe); EXPORT_SYMBOL_GPL(sata_phy_reset); EXPORT_SYMBOL_GPL(__sata_phy_reset); @@ -5182,6 +5390,7 @@ EXPORT_SYMBOL_GPL(ata_dev_id_string); EXPORT_SYMBOL_GPL(ata_dev_config); EXPORT_SYMBOL_GPL(ata_scsi_simulate); +EXPORT_SYMBOL_GPL(ata_dev_pair); EXPORT_SYMBOL_GPL(ata_pio_need_iordy); EXPORT_SYMBOL_GPL(ata_timing_compute); EXPORT_SYMBOL_GPL(ata_timing_merge); @@ -5194,6 +5403,8 @@ EXPORT_SYMBOL_GPL(ata_pci_init_one); EXPORT_SYMBOL_GPL(ata_pci_remove_one); EXPORT_SYMBOL_GPL(ata_pci_device_suspend); EXPORT_SYMBOL_GPL(ata_pci_device_resume); +EXPORT_SYMBOL_GPL(ata_pci_clear_simplex); + #endif /* CONFIG_PCI */ EXPORT_SYMBOL_GPL(ata_device_suspend); Index: linux-2.6.16/drivers/scsi/libata-scsi.c =================================================================== --- linux-2.6.16.orig/drivers/scsi/libata-scsi.c +++ linux-2.6.16/drivers/scsi/libata-scsi.c @@ -511,13 +511,11 @@ void ata_to_sense_error(unsigned id, u8 printk(KERN_WARNING "ata%u: no sense translation for status: 0x%02x\n", id, drv_stat); - /* For our last chance pick, use medium read error because - * it's much more common than an ATA drive telling you a write - * has failed. - */ - *sk = MEDIUM_ERROR; - *asc = 0x11; /* "unrecovered read error" */ - *ascq = 0x04; /* "auto-reallocation failed" */ + /* We need a sensible error return here, which is tricky, and one + that won't cause people to do things like return a disk wrongly */ + *sk = ABORTED_COMMAND; + *asc = 0x00; + *ascq = 0x00; translate_done: printk(KERN_ERR "ata%u: translated ATA stat/err 0x%02x/%02x to " Index: linux-2.6.16/drivers/scsi/pata_ali.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_ali.c @@ -0,0 +1,624 @@ +/* + * ata-ali.c - ALI 15x3 PATA for new ATA layer + * (C) 2005 Red Hat Inc + * Alan Cox + * + * based in part upon + * linux/drivers/ide/pci/alim15x3.c Version 0.17 2003/01/02 + * + * Copyright (C) 1998-2000 Michel Aubry, Maintainer + * Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer + * Copyright (C) 1999-2000 CJ, cjtsai@ali.com.tw, Maintainer + * + * Copyright (C) 1998-2000 Andre Hedrick (andre@linux-ide.org) + * May be copied or modified under the terms of the GNU General Public License + * Copyright (C) 2002 Alan Cox + * ALi (now ULi M5228) support by Clear Zhang + * + * Documentation + * Chipset documentation available under NDA only + * + * TODO/CHECK + * Cannot have ATAPI on both master & slave for rev < c2 (???) but + * otherwise should do atapi DMA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "ali" +#define DRV_VERSION "0.3" + +/** + * ali_c2_cable_detect - cable detection + * @ap: ATA port + * + * Perform cable detection for C2 and later revisions + */ + +static int ali_c2_cable_detect(struct ata_port *ap) { + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 ata66; + + /* Certain laptops use short but suitable cables and don't + implement the detect logic */ + + /* Fujitsu P2000 */ + if (pdev->subsystem_vendor == 0x10CF && pdev->subsystem_device == 0x10AF) + return ATA_CBL_PATA80; + + /* Host view cable detect 0x4A bit 0 primary bit 1 secondary + Bit set for 40 pin */ + pci_read_config_byte(pdev, 0x4A, &ata66); + if (ata66 & (1 << ap->hard_port_no)) + return ATA_CBL_PATA40; + else + return ATA_CBL_PATA80; +} + +/** + * ali_early_phy_reset - reset for eary chip + * @ap: ATA port + * + * Handle the reset callback for the later chips with cable detect + */ + +static void ali_c2_phy_reset(struct ata_port *ap) +{ + ap->cbl = ali_c2_cable_detect(ap); + ata_bus_reset(ap); + ata_port_probe(ap); +} + +/** + * ali_early_cable_detect - cable detection + * @ap: ATA port + * + * Perform cable detection for older chipsets. This turns out to be + * rather easy to implement + */ + +static int ali_early_cable_detect(struct ata_port *ap) { + return ATA_CBL_PATA40; +} + +/** + * ali_early_phy_reset - reset for eary chip + * @ap: ATA port + * + * Handle the reset callback for the early (pre cable detect) chips. + */ + +static void ali_early_phy_reset(struct ata_port *ap) +{ + ap->cbl = ali_early_cable_detect(ap); + ata_bus_reset(ap); + ata_port_probe(ap); +} + +/** + * ali_20_filter - filter for earlier ALI DMA + * @ap: ALi ATA port + * @adev: attached device + * + * Ensure that we do not do DMA on CD devices. We may be able to + * fix that later on. Also ensure we do not do UDMA on WDC drives + */ + +static unsigned int ali_20_filter(const struct ata_port *ap, struct ata_device *adev, unsigned int mask, int shift) +{ + char model_num[40]; + /* No DMA on CD for now */ + if (adev->class != ATA_DEV_ATA && shift != ATA_SHIFT_PIO) + return 0; + + if (shift != ATA_SHIFT_UDMA) + return mask; + + ata_dev_id_string(adev->id, model_num, ATA_ID_PROD_OFS, sizeof(model_num)); + if (strstr(model_num, "WDC")) + return 0; + return mask; +} + +/** + * ali_fifo_control - FIFO manager + * @ap: ALi channel to control + * @adev: device for FIFO control + * @on: 0 for off 1 for on + * + * Enable or disable the FIFO on a given device. Because of the way the + * ALi FIFO works it provides a boost on ATA disk but can be confused by + * ATAPI and we must therefore manage it. + */ + +static void ali_fifo_control(struct ata_port *ap, struct ata_device *adev, int on) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int pio_fifo = 0x54 + ap->hard_port_no; + u8 fifo; + int shift = 4 * adev->devno; + + /* Bits 3:2 (7:6 for slave) control the PIO. 00 is off 01 + is on. The FIFO must not be used for ATAPI. We preserve + BIOS set thresholds */ + pci_read_config_byte(pdev, pio_fifo, &fifo); + fifo &= ~(0x0C << shift); + if (on) + fifo |= (on << shift); + pci_write_config_byte(pdev, pio_fifo, fifo); +} + +/** + * ali_program_modes - load mode registers + * @ap: ALi channel to load + * @adev: Device the timing is for + * @cmd: Command timing + * @data: Data timing + * @udma: UDMA timing or zero for off + * + * Loads the timing registers for cmd/data and disable UDMA if + * udma is zero. If udma is set then load and enable the UDMA + * timing but do not touch the command/data timing. + */ + +static void ali_program_modes(struct ata_port *ap, struct ata_device *adev, struct ata_timing *t, u8 ultra) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int cas = 0x58 + 4 * ap->hard_port_no; /* Command timing */ + int cbt = 0x59 + 4 * ap->hard_port_no; /* Command timing */ + int drwt = 0x5A + 4 * ap->hard_port_no + adev->devno; /* R/W timing */ + int udmat = 0x56 + ap->hard_port_no; /* UDMA timing */ + int shift = 4 * adev->devno; + u8 udma; + + if (t != NULL) { + t->setup = FIT(t->setup, 1, 8) & 7; + t->act8b = FIT(t->act8b, 1, 8) & 7; + t->rec8b = FIT(t->rec8b, 1, 16) & 15; + t->active = FIT(t->active, 1, 8) & 7; + t->recover = FIT(t->recover, 1, 16) & 15; + + pci_write_config_byte(pdev, cas, t->setup); + pci_write_config_byte(pdev, cbt, (t->act8b << 4) | t->rec8b); + pci_write_config_byte(pdev, drwt, (t->active << 4) | t->recover); + } + + /* Set up the UDMA enable */ + pci_read_config_byte(pdev, udmat, &udma); + udma &= ~(0x0F << shift); + udma |= ultra << shift; + pci_write_config_byte(pdev, udmat, udma); +} + +/** + * ali_set_piomode - set initial PIO mode data + * @ap: ATA interface + * @adev: ATA device + * + * Program the ALi registers for PIO mode. FIXME: add timings for + * PIO5. + */ + +static void ali_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct ata_device *pair = ata_dev_pair(ap, adev); + struct ata_timing t; + unsigned long T = 1000000000 / 33333; /* PCI clock based */ + + ata_timing_compute(adev, adev->pio_mode, &t, T, 1); + if (pair) { + struct ata_timing p; + ata_timing_compute(pair, pair->pio_mode, &p, T, 1); + ata_timing_merge(&p, &t, &t, ATA_TIMING_SETUP|ATA_TIMING_8BIT); + if (pair->dma_mode) { + ata_timing_compute(pair, pair->dma_mode, &p, T, 1); + ata_timing_merge(&p, &t, &t, ATA_TIMING_SETUP|ATA_TIMING_8BIT); + } + } + + /* PIO FIFO is only permitted on ATA disk */ + if (adev->class != ATA_DEV_ATA) + ali_fifo_control(ap, adev, 0); + ali_program_modes(ap, adev, &t, 0); + if (adev->class == ATA_DEV_ATA) + ali_fifo_control(ap, adev, 0x04); + +} + +/** + * ali_set_dmamode - set initial DMA mode data + * @ap: ATA interface + * @adev: ATA device + * + * FIXME: MWDMA timings + */ + +static void ali_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + static u8 udma_timing[7] = { 0xC, 0xB, 0xA, 0x9, 0x8, 0xF, 0xD }; + struct ata_device *pair = ata_dev_pair(ap, adev); + struct ata_timing t; + unsigned long T = 1000000000 / 33333; /* PCI clock based */ + + + if (adev->class == ATA_DEV_ATA) + ali_fifo_control(ap, adev, 0x08); + + if (adev->dma_mode >= XFER_UDMA_0) { + ali_program_modes(ap, adev, NULL, udma_timing[adev->dma_mode - XFER_UDMA_0]); + } else { + ata_timing_compute(adev, adev->dma_mode, &t, T, 1); + if (pair) { + struct ata_timing p; + ata_timing_compute(pair, pair->pio_mode, &p, T, 1); + ata_timing_merge(&p, &t, &t, ATA_TIMING_SETUP|ATA_TIMING_8BIT); + if (pair->dma_mode) { + ata_timing_compute(pair, pair->dma_mode, &p, T, 1); + ata_timing_merge(&p, &t, &t, ATA_TIMING_SETUP|ATA_TIMING_8BIT); + } + } + ali_program_modes(ap, adev, &t, 0); + } +} + +/** + * ali_lock_sectors - Keep older devices to 255 sector mode + * @ap: ATA port + * @adev: Device + * + * Called during the bus probe for each device that is found. We use + * this call to lock the sector count of the device to 255 or less on + * older ALi controllers. If we didn't do this then large I/O's would + * require LBA48 commands which the older ALi requires are issued by + * slower PIO methods + */ + +static void ali_lock_sectors(struct ata_port *ap, struct ata_device *adev) +{ + if(ap->host->max_sectors > 255) { + ap->host->max_sectors = 255; + ap->host->hostt->max_sectors = 255; + } + adev->flags |= ATA_DFLAG_LOCK_SECTORS; +} + +static struct scsi_host_template ali_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + /* Keep LBA28 counts so large I/O's don't turn LBA48 and PIO + with older controllers. Not locked so will grow on C5 or later */ + .max_sectors = 255, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +/* + * Port operations for PIO only ALi + */ + +static struct ata_port_operations ali_early_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = ali_set_piomode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = ali_early_phy_reset, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +/* + * Port operations for DMA capable ALi without cable + * detect + */ +static struct ata_port_operations ali_20_port_ops = { + .port_disable = ata_port_disable, + + .set_piomode = ali_set_piomode, + .set_dmamode = ali_set_dmamode, + .mode_filter = ali_20_filter, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + .dev_config = ali_lock_sectors, + + .phy_reset = ali_early_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +/* + * Port operations for DMA capable ALi with cable detect + */ +static struct ata_port_operations ali_c2_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = ali_set_piomode, + .set_dmamode = ali_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + .dev_config = ali_lock_sectors, + + .phy_reset = ali_c2_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +/* + * Port operations for DMA capable ALi with cable detect and LBA48 + */ +static struct ata_port_operations ali_c5_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = ali_set_piomode, + .set_dmamode = ali_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = ali_c2_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +/** + * ali_init_one - discovery callback + * @pdev: PCI device ID + * @id: PCI table info + * + * An ALi IDE interface has been discovered. Figure out what revision + * and perform configuration work before handing it to the ATA layer + */ + +static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + static struct ata_port_info info_early = { + .sht = &ali_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .port_ops = &ali_early_port_ops + }; + /* Revision 0x20 added DMA */ + static struct ata_port_info info_20 = { + .sht = &ali_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_PIO_LBA48, + .pio_mask = 0x1f, + /*.mwdma_mask = 0x07,*/ + .port_ops = &ali_20_port_ops + }; + /* Revision 0x20 with support logic added UDMA */ + static struct ata_port_info info_20_udma = { + .sht = &ali_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_PIO_LBA48, + .pio_mask = 0x1f, + /*.mwdma_mask = 0x07, */ + .udma_mask = 0x07, /* UDMA33 */ + .port_ops = &ali_20_port_ops + }; + /* Revision 0xC2 adds UDMA66 */ + static struct ata_port_info info_c2 = { + .sht = &ali_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_PIO_LBA48, + .pio_mask = 0x1f, + /*.mwdma_mask = 0x07, */ + .udma_mask = 0x1f, + .port_ops = &ali_c2_port_ops + }; + /* Revision 0xC3 is UDMA100 */ + static struct ata_port_info info_c3 = { + .sht = &ali_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_PIO_LBA48, + .pio_mask = 0x1f, + /* .mwdma_mask = 0x07, */ + .udma_mask = 0x3f, + .port_ops = &ali_c2_port_ops + }; + /* Revision 0xC4 is UDMA133 */ + static struct ata_port_info info_c4 = { + .sht = &ali_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_PIO_LBA48, + .pio_mask = 0x1f, + /* .mwdma_mask = 0x07, */ + .udma_mask = 0x7f, + .port_ops = &ali_c2_port_ops + }; + /* Revision 0xC5 is UDMA133 with LBA48 DMA */ + static struct ata_port_info info_c5 = { + .sht = &ali_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + /* .mwdma_mask = 0x07, */ + .udma_mask = 0x7f, + .port_ops = &ali_c5_port_ops + }; + + static struct ata_port_info *port_info[2]; + u8 rev, tmp; + struct pci_dev *north, *isa_bridge; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); + + /* + * The chipset revision selects the driver operations and + * mode data. + */ + + if (rev < 0x20) { + port_info[0] = port_info[1] = &info_early; + } else if (rev < 0xC2) { + /* 1543-E/F, 1543C-C, 1543C-D, 1543C-E */ + pci_read_config_byte(pdev, 0x4B, &tmp); + /* Clear CD-ROM DMA write bit */ + tmp &= 0x7F; + pci_write_config_byte(pdev, 0x4B, tmp); + port_info[0] = port_info[1] = &info_20; + } else if (rev == 0xC2) { + port_info[0] = port_info[1] = &info_c2; + } else if (rev == 0xC3) { + port_info[0] = port_info[1] = &info_c3; + } else if (rev == 0xC4) { + port_info[0] = port_info[1] = &info_c4; + } else + port_info[0] = port_info[1] = &info_c5; + + if (rev >= 0xC2) { + /* Enable cable detection logic */ + pci_read_config_byte(pdev, 0x4B, &tmp); + pci_write_config_byte(pdev, 0x4B, tmp | 0x08); + } + + north = pci_get_slot(pdev->bus, PCI_DEVFN(0,0)); + isa_bridge = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); + + if (north && north->vendor == PCI_VENDOR_ID_AL) { + /* Configure the ALi bridge logic. For non ALi rely on BIOS. + Set the south bridge enable bit */ + pci_read_config_byte(isa_bridge, 0x79, &tmp); + if (rev == 0xC2) + pci_write_config_byte(isa_bridge, 0x79, tmp | 0x04); + else if (rev > 0xC2) + pci_write_config_byte(isa_bridge, 0x79, tmp | 0x02); + } + + if (rev >= 0x20) { + if (rev < 0xC2) { + /* Are we paired with a UDMA capable chip */ + pci_read_config_byte(isa_bridge, 0x5E, &tmp); + if ((tmp & 0x1E) == 0x12) + port_info[0] = port_info[1] = &info_20_udma; + } + /* + * CD_ROM DMA on (0x53 bit 0). Enable this even if we want + * to use PIO. 0x53 bit 1 (rev 20 only) - enable FIFO control + * via 0x54/55. + */ + pci_read_config_byte(pdev, 0x53, &tmp); + if (rev == 0x20) + tmp &= ~0x02; + tmp |= 0x01; /* CD_ROM enable for DMA */ + pci_write_config_byte(pdev, 0x53, tmp); + } + + pci_dev_put(isa_bridge); + pci_dev_put(north); + + ata_pci_clear_simplex(pdev); + return ata_pci_init_one(pdev, port_info, 2); +} + +static struct pci_device_id ali[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5228), }, + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229), }, + { 0, }, +}; + +static struct pci_driver ali_pci_driver = { + .name = DRV_NAME, + .id_table = ali, + .probe = ali_init_one, + .remove = ata_pci_remove_one +}; + +static int __init ali_init(void) +{ + return pci_register_driver(&ali_pci_driver); +} + + +static void __exit ali_exit(void) +{ + pci_unregister_driver(&ali_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for ALi PATA"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, ali); +MODULE_VERSION(DRV_VERSION); + +module_init(ali_init); +module_exit(ali_exit); Index: linux-2.6.16/drivers/scsi/pata_amd.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_amd.c @@ -0,0 +1,650 @@ +/* + * pata_amd.c - AMD PATA for new ATA layer + * (C) 2005 Red Hat Inc + * Alan Cox + * + * Based on pata-sil680. Errata information is taken from data sheets + * and the amd74xx.c driver by Vojtech Pavlik. Nvidia SATA devices are + * claimed by sata-nv.c. + * + * TODO: + * Nvidia support here or seperated ? + * Debug cable detect + * Variable system clock when/if it makes sense + * Power management on ports + * + * + * Documentation publically available. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pata_amd" +#define DRV_VERSION "0.1.3" + +/** + * timing_setup - shared timing computation and load + * @ap: ATA port being set up + * @adev: drive being configured + * @offset: port offset + * @speed: target speed + * @clock: clock multiplier (number of times 33MHz for this part) + * + * Perform the actual timing set up for Nvidia or AMD PATA devices. + * The actual devices vary so they all call into this helper function + * providing the clock multipler and offset (because AMD and Nvidia put + * the ports at different locations). + */ + +static void timing_setup(struct ata_port *ap, struct ata_device *adev, int offset, int speed, int clock) +{ + static const unsigned char amd_cyc2udma[] = { + 6, 6, 5, 4, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 7 + }; + + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + struct ata_device *peer = ata_dev_pair(ap, adev); + int dn = ap->hard_port_no * 2 + adev->devno; + struct ata_timing at, apeer; + int T, UT; + const int amd_clock = 33333; /* KHz. */ + u8 t; + + T = 1000000000 / amd_clock; + UT = T / min_t(int, max_t(int, clock, 1), 2); + + if (ata_timing_compute(adev, speed, &at, T, UT) < 0) { + dev_printk(KERN_ERR, &pdev->dev, "unknown mode %d.\n", speed); + return; + } + + if (peer) { + /* This may be over conservative */ + if (peer->dma_mode) { + ata_timing_compute(peer, peer->dma_mode, &apeer, T, UT); + ata_timing_merge(&apeer, &at, &at, ATA_TIMING_8BIT); + } + ata_timing_compute(peer, peer->pio_mode, &apeer, T, UT); + ata_timing_merge(&apeer, &at, &at, ATA_TIMING_8BIT); + } + + if (speed == XFER_UDMA_5 && amd_clock <= 33333) at.udma = 1; + if (speed == XFER_UDMA_6 && amd_clock <= 33333) at.udma = 15; + + /* + * Now do the setup work + */ + + /* Configure the address set up timing */ + pci_read_config_byte(pdev, offset + 0x0C, &t); + t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(at.setup, 1, 4) - 1) << ((3 - dn) << 1)); + pci_write_config_byte(pdev, offset + 0x0C , t); + + /* Configure the 8bit I/O timing */ + pci_write_config_byte(pdev, offset + 0x0E + (1 - (dn >> 1)), + ((FIT(at.act8b, 1, 16) - 1) << 4) | (FIT(at.rec8b, 1, 16) - 1)); + + /* Drive timing */ + pci_write_config_byte(pdev, offset + 0x08 + (3 - dn), + ((FIT(at.active, 1, 16) - 1) << 4) | (FIT(at.recover, 1, 16) - 1)); + + switch (clock) { + case 1: + t = at.udma ? (0xc0 | (FIT(at.udma, 2, 5) - 2)) : 0x03; + break; + + case 2: + t = at.udma ? (0xc0 | amd_cyc2udma[FIT(at.udma, 2, 10)]) : 0x03; + break; + + case 3: + t = at.udma ? (0xc0 | amd_cyc2udma[FIT(at.udma, 1, 10)]) : 0x03; + break; + + case 4: + t = at.udma ? (0xc0 | amd_cyc2udma[FIT(at.udma, 1, 15)]) : 0x03; + break; + + default: + return; + } + + /* UDMA timing */ + pci_write_config_byte(pdev, offset + 0x10 + (3 - dn), t); +} + +/** + * amd_cable_detect - cable detection + * @ap: ATA port + * + * Perform cable detection. The BIOS stores this in PCI config + * space for us. + */ + +static int amd_cable_detect(struct ata_port *ap) { + static u32 bitmask[2] = {0x00030000, 0x00C00000}; + u32 ata66; + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + + pci_read_config_dword(pdev, 0x42, &ata66); + if (ata66 & bitmask[ap->hard_port_no]) + return ATA_CBL_PATA80; + else + return ATA_CBL_PATA40; + +} + +static void amd_phy_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + static struct pci_bits amd_enable_bits[] = { + { 0x40, 1, 0x02, 0x02 }, + { 0x40, 1, 0x01, 0x01 } + }; + + if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->hard_port_no])) { + ata_port_disable(ap); + printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); + return; + } + ap->cbl = amd_cable_detect(ap); + ata_bus_reset(ap); + ata_port_probe(ap); +} + +/** + * amd33_set_piomode - set initial PIO mode data + * @ap: ATA interface + * @adev: ATA device + * + * Program the AMD registers for PIO mode. + */ + +static void amd33_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x40, adev->pio_mode, 1); +} + +static void amd66_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x40, adev->pio_mode, 2); +} + +static void amd100_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x40, adev->pio_mode, 3); +} + +static void amd133_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x40, adev->pio_mode, 4); +} + +/** + * amd33_set_dmamode - set initial DMA mode data + * @ap: ATA interface + * @adev: ATA device + * + * Program the MWDMA/UDMA modes for the AMD and Nvidia + * chipset. + */ + +static void amd33_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x40, adev->dma_mode, 1); +} + +static void amd66_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x40, adev->dma_mode, 2); +} + +static void amd100_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x40, adev->dma_mode, 3); +} + +static void amd133_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x40, adev->dma_mode, 4); +} + + +/** + * nv_cable_detect - cable detection + * @ap: ATA port + * + * Perform cable detection. The BIOS stores this in PCI config + * space for us. + */ + +static int nv_cable_detect(struct ata_port *ap) { + static u32 bitmask[2] = {0x00030000, 0x00C00000}; + u32 ata66; + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + + pci_read_config_dword(pdev, 0x52, &ata66); + if (ata66 & bitmask[ap->hard_port_no]) + return ATA_CBL_PATA80; + else + return ATA_CBL_PATA40; + +} + +static void nv_phy_reset(struct ata_port *ap) +{ + ap->cbl = nv_cable_detect(ap); + ata_bus_reset(ap); + ata_port_probe(ap); +} + +/** + * nv100_set_piomode - set initial PIO mode data + * @ap: ATA interface + * @adev: ATA device + * + * Program the AMD registers for PIO mode. + */ + +static void nv100_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x50, adev->pio_mode, 3); +} + +static void nv133_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x50, adev->pio_mode, 4); +} + +/** + * nv100_set_dmamode - set initial DMA mode data + * @ap: ATA interface + * @adev: ATA device + * + * Program the MWDMA/UDMA modes for the AMD and Nvidia + * chipset. + */ + +static void nv100_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x50, adev->dma_mode, 3); +} + +static void nv133_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + timing_setup(ap, adev, 0x50, adev->dma_mode, 4); +} + +static struct scsi_host_template amd_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations amd33_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = amd33_set_piomode, + .set_dmamode = amd33_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = amd_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static struct ata_port_operations amd66_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = amd66_set_piomode, + .set_dmamode = amd66_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = amd_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static struct ata_port_operations amd100_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = amd100_set_piomode, + .set_dmamode = amd100_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = amd_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static struct ata_port_operations amd133_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = amd133_set_piomode, + .set_dmamode = amd133_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = amd_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static struct ata_port_operations nv100_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = nv100_set_piomode, + .set_dmamode = nv100_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = nv_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static struct ata_port_operations nv133_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = nv133_set_piomode, + .set_dmamode = nv133_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = nv_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + static struct ata_port_info info[10] = { + { /* 0: AMD 7401 */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, /* No SWDMA */ + .udma_mask = 0x07, /* UDMA 33 */ + .port_ops = &amd33_port_ops + }, + { /* 1: Early AMD7409 - no swdma */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x1f, /* UDMA 66 */ + .port_ops = &amd66_port_ops + }, + { /* 2: AMD 7409, no swdma errata */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x1f, /* UDMA 66 */ + .port_ops = &amd66_port_ops + }, + { /* 3: AMD 7411 */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x3f, /* UDMA 100 */ + .port_ops = &amd100_port_ops + }, + { /* 4: AMD 7441 */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x3f, /* UDMA 100 */ + .port_ops = &amd100_port_ops + }, + { /* 5: AMD 8111*/ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, /* UDMA 133, no swdma */ + .port_ops = &amd133_port_ops + }, + { /* 6: AMD 8111 UDMA 100 (Serenade) */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x3f, /* UDMA 100, no swdma */ + .port_ops = &amd133_port_ops + }, + { /* 7: Nvidia Nforce */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x3f, /* UDMA 100 */ + .port_ops = &nv100_port_ops + }, + { /* 8: Nvidia Nforce2 and later */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, /* UDMA 133, no swdma */ + .port_ops = &nv133_port_ops + }, + { /* 9: AMD CS5536 (Geode companion) */ + .sht = &amd_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x3f, /* UDMA 100 */ + .port_ops = &amd100_port_ops + } + }; + static struct ata_port_info *port_info[2]; + static int printed_version; + int type = id->driver_data; + u8 rev; + u8 fifo; + + if (!printed_version++) + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + + pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); + pci_read_config_byte(pdev, 0x41, &fifo); + + /* Check for AMD7409 without swdma errata and if found adjust type */ + if (type == 1 && rev > 0x7) + type = 2; + + /* Check for AMD7411 */ + if (type == 3) + /* FIFO is broken */ + pci_write_config_byte(pdev, 0x41, fifo & 0x0F); + else + pci_write_config_byte(pdev, 0x41, fifo | 0xF0); + + /* Serenade ? */ + if (type == 5 && pdev->subsystem_vendor == PCI_VENDOR_ID_AMD && + pdev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE) + type = 6; /* UDMA 100 only */ + + if (type < 3) + ata_pci_clear_simplex(pdev); + + /* And fire it up */ + + port_info[0] = port_info[1] = &info[type]; + return ata_pci_init_one(pdev, port_info, 2); +} + +static const struct pci_device_id amd[] = { + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, + { 0, }, +}; + +static struct pci_driver amd_pci_driver = { + .name = DRV_NAME, + .id_table = amd, + .probe = amd_init_one, + .remove = ata_pci_remove_one +}; + +static int __init amd_init(void) +{ + return pci_register_driver(&amd_pci_driver); +} + +static void __exit amd_exit(void) +{ + pci_unregister_driver(&amd_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for AMD PATA IDE"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, amd); +MODULE_VERSION(DRV_VERSION); + +module_init(amd_init); +module_exit(amd_exit); Index: linux-2.6.16/drivers/scsi/pata_artop.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_artop.c @@ -0,0 +1,506 @@ +/* + * pata_artop.c - ARTOP ATA controller driver + * + * (C) 2006 Red Hat + * + * Based in part on drivers/ide/pci/aec62xx.c + * Copyright (C) 1999-2002 Andre Hedrick + * 865/865R fixes for Macintosh card version from a patch to the old + * driver by Thibaut VARENE + * When setting the PCI latency we must set 0x80 or higher for burst + * performance Alessandro Zummo + * + * TODO + * 850 serialization once the core supports it + * Investigate no_dsc on 850R + * Clock detect + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pata_artop" +#define DRV_VERSION "0.2.1" + +/* + * The ARTOP has 33 Mhz and "over clocked" timing tables. Until we + * get PCI bus speed functionality we leave this as 0. Its a variable + * for when we get the functionality and also for folks wanting to + * test stuff. + */ + +static int clock = 0; + +/** + * artop6210_phy_reset - Probe specified port on PATA host controller + * @ap: Port to probe + * + * LOCKING: + * None (inherited from caller). + */ + +static void artop6210_phy_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + const struct pci_bits artop_enable_bits[] = { + { 0x4AU, 1U, 0x02UL, 0x02UL }, /* port 0 */ + { 0x4AU, 1U, 0x04UL, 0x04UL }, /* port 1 */ + }; + + if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->hard_port_no])) { + ata_port_disable(ap); + printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); + return; + } + ap->cbl = ATA_CBL_PATA40; + ata_port_probe(ap); + ata_bus_reset(ap); +} + +/** + * artop6260_cable_detect - check for 40/80 pin + * @ap: Port + * + * The ARTOP hardware reports the cable detect bits in register 0x49. + * Nothing complicated needed here. + */ + +static int artop6260_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 tmp; + + pci_read_config_byte(pdev, 0x49, &tmp); + if (tmp & (1 >> ap->hard_port_no)) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + +/** + * artop6260_phy_reset - Probe specified port on PATA host controller + * @ap: Port to probe + * + * LOCKING: + * None (inherited from caller). + */ + +static void artop6260_phy_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + const struct pci_bits artop_enable_bits[] = { + { 0x4AU, 1U, 0x02UL, 0x02UL }, /* port 0 */ + { 0x4AU, 1U, 0x04UL, 0x04UL }, /* port 1 */ + }; + + /* Odd numbered device ids are the units with enable bits (the -R cards) */ + if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->hard_port_no])) { + ata_port_disable(ap); + printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); + return; + } + ap->cbl = artop6260_cable_detect(ap); + ata_port_probe(ap); + ata_bus_reset(ap); +} + +/** + * artop6210_load_piomode - Load a set of PATA PIO timings + * @ap: Port whose timings we are configuring + * @adev: Device + * @pio: PIO mode + * + * Set PIO mode for device, in host controller PCI config space. This + * is used both to set PIO timings in PIO mode and also to set the + * matching PIO clocking for UDMA, as well as the MWDMA timings. + * + * LOCKING: + * None (inherited from caller). + */ + +static void artop6210_load_piomode(struct ata_port *ap, struct ata_device *adev, unsigned int pio) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int dn = adev->devno + 2 * ap->hard_port_no; + const u16 timing[2][5] = { + { 0x0000, 0x000A, 0x0008, 0x0303, 0x0301 }, + { 0x0700, 0x070A, 0x0708, 0x0403, 0x0401 } + + }; + /* Load the PIO timing active/recovery bits */ + pci_write_config_word(pdev, 0x40 + 2 * dn, timing[clock][pio]); +} + +/** + * artop6210_set_piomode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @adev: Device we are configuring + * + * Set PIO mode for device, in host controller PCI config space. For + * ARTOP we must also clear the UDMA bits if we are not doing UDMA. In + * the event UDMA is used the later call to set_dmamode will set the + * bits as required. + * + * LOCKING: + * None (inherited from caller). + */ + +static void artop6210_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int dn = adev->devno + 2 * ap->hard_port_no; + u8 ultra; + + artop6210_load_piomode(ap, adev, adev->pio_mode - XFER_PIO_0); + + /* Clear the UDMA mode bits (set_dmamode will redo this if needed) */ + pci_read_config_byte(pdev, 0x54, &ultra); + ultra &= ~(3 << (2 * dn)); + pci_write_config_byte(pdev, 0x54, ultra); +} + +/** + * artop6260_load_piomode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @adev: Device we are configuring + * @pio: PIO mode + * + * Set PIO mode for device, in host controller PCI config space. The + * ARTOP6260 and relatives store the timing data differently. + * + * LOCKING: + * None (inherited from caller). + */ + +static void artop6260_load_piomode (struct ata_port *ap, struct ata_device *adev, unsigned int pio) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int dn = adev->devno + 2 * ap->hard_port_no; + const u8 timing[2][5] = { + { 0x00, 0x0A, 0x08, 0x33, 0x31 }, + { 0x70, 0x7A, 0x78, 0x43, 0x41 } + + }; + /* Load the PIO timing active/recovery bits */ + pci_write_config_byte(pdev, 0x40 + dn, timing[clock][pio]); +} + +/** + * artop6260_set_piomode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @adev: Device we are configuring + * + * Set PIO mode for device, in host controller PCI config space. For + * ARTOP we must also clear the UDMA bits if we are not doing UDMA. In + * the event UDMA is used the later call to set_dmamode will set the + * bits as required. + * + * LOCKING: + * None (inherited from caller). + */ + +static void artop6260_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 ultra; + + artop6260_load_piomode(ap, adev, adev->pio_mode - XFER_PIO_0); + + /* Clear the UDMA mode bits (set_dmamode will redo this if needed) */ + pci_read_config_byte(pdev, 0x44 + ap->hard_port_no, &ultra); + ultra &= ~(7 << (4 * adev->devno)); /* One nibble per drive */ + pci_write_config_byte(pdev, 0x44 + ap->hard_port_no, ultra); +} + +/** + * artop6210_set_dmamode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @adev: um + * + * Set DMA mode for device, in host controller PCI config space. + * + * LOCKING: + * None (inherited from caller). + */ + +static void artop6210_set_dmamode (struct ata_port *ap, struct ata_device *adev) +{ + unsigned int pio; + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int dn = adev->devno + 2 * ap->hard_port_no; + u8 ultra; + + if (adev->dma_mode == XFER_MW_DMA_0) + pio = 1; + else + pio = 4; + + /* Load the PIO timing active/recovery bits */ + artop6210_load_piomode(ap, adev, pio); + + pci_read_config_byte(pdev, 0x54, &ultra); + ultra &= ~(3 << (2 * dn)); + + /* Add ultra DMA bits if in UDMA mode */ + if (adev->dma_mode >= XFER_UDMA_0) { + u8 mode = (adev->dma_mode - XFER_UDMA_0) + 1 - clock; + if (mode == 0) + mode = 1; + ultra |= (mode << (2 * dn)); + } + pci_write_config_byte(pdev, 0x54, ultra); +} + +/** + * artop6260_set_dmamode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @adev: Device we are configuring + * + * Set DMA mode for device, in host controller PCI config space. The + * ARTOP6260 and relatives store the timing data differently. + * + * LOCKING: + * None (inherited from caller). + */ + +static void artop6260_set_dmamode (struct ata_port *ap, struct ata_device *adev) +{ + unsigned int pio = adev->pio_mode - XFER_PIO_0; + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 ultra; + + if (adev->dma_mode == XFER_MW_DMA_0) + pio = 1; + else + pio = 4; + + /* Load the PIO timing active/recovery bits */ + artop6260_load_piomode(ap, adev, pio); + + /* Add ultra DMA bits if in UDMA mode */ + pci_read_config_byte(pdev, 0x44 + ap->hard_port_no, &ultra); + ultra &= ~(7 << (4 * adev->devno)); /* One nibble per drive */ + if (adev->dma_mode >= XFER_UDMA_0) { + u8 mode = adev->dma_mode - XFER_UDMA_0 + 1 - clock; + if (mode == 0) + mode = 1; + ultra |= (mode << (4 * adev->devno)); + } + pci_write_config_byte(pdev, 0x44 + ap->hard_port_no, ultra); +} + +static struct scsi_host_template artop_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static const struct ata_port_operations artop6210_ops = { + .port_disable = ata_port_disable, + .set_piomode = artop6210_set_piomode, + .set_dmamode = artop6210_set_dmamode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = artop6210_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop, +}; + +static const struct ata_port_operations artop6260_ops = { + .port_disable = ata_port_disable, + .set_piomode = artop6260_set_piomode, + .set_dmamode = artop6260_set_dmamode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = artop6260_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .data_xfer = ata_pio_data_xfer, + + .eng_timeout = ata_eng_timeout, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop, +}; + + +/** + * artop_init_one - Register ARTOP ATA PCI device with kernel services + * @pdev: PCI device to register + * @ent: Entry in artop_pci_tbl matching with @pdev + * + * Called from kernel PCI layer. + * + * LOCKING: + * Inherited from PCI layer (may sleep). + * + * RETURNS: + * Zero on success, or -ERRNO value. + */ + +static int artop_init_one (struct pci_dev *pdev, const struct pci_device_id *id) +{ + static int printed_version; + static struct ata_port_info info_6210 = { + .sht = &artop_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = ATA_UDMA2, + .port_ops = &artop6210_ops, + }; + static struct ata_port_info info_626x = { + .sht = &artop_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = ATA_UDMA4, + .port_ops = &artop6260_ops, + }; + static struct ata_port_info info_626x_fast = { + .sht = &artop_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = ATA_UDMA5, + .port_ops = &artop6260_ops, + }; + struct ata_port_info *port_info[2]; + struct ata_port_info *info; + int ports = 2; + + if (!printed_version++) + dev_printk(KERN_DEBUG, &pdev->dev, + "version " DRV_VERSION "\n"); + + if (id->driver_data == 0) { /* 6210 variant */ + info = &info_6210; + /* BIOS may have left us in UDMA, clear it before libata probe */ + pci_write_config_byte(pdev, 0x54, 0); + /* For the moment (also lacks dsc) */ + printk(KERN_WARNING "ARTOP 6210 requires serialize functionality not yet supported by libata.\n"); + printk(KERN_WARNING "Secondary ATA ports will not be activated.\n"); + ports = 1; + } + else if (id->driver_data == 1) /* 6260 */ + info = &info_626x; + else if (id->driver_data == 2) { /* 6260 or 6260 + fast */ + unsigned long io = pci_resource_start(pdev, 4); + u8 reg; + + info = &info_626x; + if (inb(io) & 0x10) + info = &info_626x_fast; + /* Mac systems come up with some registers not set as we + will need them */ + + /* Clear reset & test bits */ + pci_read_config_byte(pdev, 0x49, ®); + pci_write_config_byte(pdev, 0x49, reg & ~ 0x30); + + /* PCI latency must be > 0x80 for burst mode, tweak it + * if required. + */ + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, ®); + if (reg <= 0x80) + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x90); + + /* Enable IRQ output and burst mode */ + pci_read_config_byte(pdev, 0x4a, ®); + pci_write_config_byte(pdev, 0x4a, (reg & ~0x01) | 0x80); + + } + port_info[0] = port_info[1] = info; + return ata_pci_init_one(pdev, port_info, ports); +} + +static const struct pci_device_id artop_pci_tbl[] = { + { 0x1191, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0x1191, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { 0x1191, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { 0x1191, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { 0x1191, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { } /* terminate list */ +}; + +static struct pci_driver artop_pci_driver = { + .name = DRV_NAME, + .id_table = artop_pci_tbl, + .probe = artop_init_one, + .remove = ata_pci_remove_one, +}; + +static int __init artop_init(void) +{ + return pci_register_driver(&artop_pci_driver); +} + +static void __exit artop_exit(void) +{ + pci_unregister_driver(&artop_pci_driver); +} + + +module_init(artop_init); +module_exit(artop_exit); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("SCSI low-level driver for ARTOP PATA"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, artop_pci_tbl); +MODULE_VERSION(DRV_VERSION); + Index: linux-2.6.16/drivers/scsi/pata_atiixp.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_atiixp.c @@ -0,0 +1,299 @@ +/* + * pata_atiixp.c - ATI PATA for new ATA layer + * (C) 2005 Red Hat Inc + * Alan Cox + * + * Based on + * + * linux/drivers/ide/pci/atiixp.c Version 0.01-bart2 Feb. 26, 2004 + * + * Copyright (C) 2003 ATI Inc. + * Copyright (C) 2004 Bartlomiej Zolnierkiewicz + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pata_atiixp" +#define DRV_VERSION "0.1.1" + +enum { + ATIIXP_IDE_PIO_TIMING = 0x40, + ATIIXP_IDE_MWDMA_TIMING = 0x44, + ATIIXP_IDE_PIO_CONTROL = 0x48, + ATIIXP_IDE_PIO_MODE = 0x4a, + ATIIXP_IDE_UDMA_CONTROL = 0x54, + ATIIXP_IDE_UDMA_MODE = 0x56 +}; + +static void atiixp_phy_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + static struct pci_bits atiixp_enable_bits[] = { + { 0x48, 1, 0x01, 0x00 }, + { 0x48, 1, 0x08, 0x00 } + }; + + if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->hard_port_no])) { + ata_port_disable(ap); + printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); + return; + } + ap->cbl = ATA_CBL_PATA80; + ata_port_probe(ap); + ata_bus_reset(ap); +} + +/** + * atiixp_set_pio_timing - set initial PIO mode data + * @ap: ATA interface + * @adev: ATA device + * + * Called by both the pio and dma setup functions to set the controller + * timings for PIO transfers. We must load both the mode number and + * timing values into the controller. + */ + +static void atiixp_set_pio_timing(struct ata_port *ap, struct ata_device *adev, int pio) +{ + static u8 pio_timings[5] = { 0x5D, 0x47, 0x34, 0x22, 0x20 }; + + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int dn = 2 * ap->hard_port_no + adev->devno; + + /* Check this is correct - the order is odd in both drivers */ + int timing_shift = (16 * ap->hard_port_no) + 8 * (adev->devno ^ 1); + u16 pio_mode_data, pio_timing_data; + + pci_read_config_word(pdev, ATIIXP_IDE_PIO_MODE, &pio_mode_data); + pio_mode_data &= ~(0x7 << (4 * dn)); + pio_mode_data |= pio << (4 * dn); + pci_write_config_word(pdev, ATIIXP_IDE_PIO_MODE, pio_mode_data); + + pci_read_config_word(pdev, ATIIXP_IDE_PIO_TIMING, &pio_timing_data); + pio_mode_data &= ~(0xFF << timing_shift); + pio_mode_data |= (pio_timings[pio] << timing_shift); + pci_write_config_word(pdev, ATIIXP_IDE_PIO_TIMING, pio_timing_data); +} + +/** + * atiixp_set_piomode - set initial PIO mode data + * @ap: ATA interface + * @adev: ATA device + * + * Called to do the PIO mode setup. We use a shared helper for this + * as the DMA setup must also adjust the PIO timing information. + */ + +static void atiixp_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + atiixp_set_pio_timing(ap, adev, adev->pio_mode - XFER_PIO_0); +} + +/** + * atiixp_set_dmamode - set initial DMA mode data + * @ap: ATA interface + * @adev: ATA device + * + * Called to do the DMA mode setup. We use timing tables for most + * modes but must tune an appropriate PIO mode to match. + */ + +static void atiixp_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + static u8 mwdma_timings[5] = { 0x77, 0x21, 0x20 }; + + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int dma = adev->dma_mode; + int dn = 2 * ap->hard_port_no + adev->devno; + int wanted_pio; + + if (adev->dma_mode >= XFER_UDMA_0) { + u16 udma_mode_data; + + dma -= XFER_UDMA_0; + + pci_read_config_word(pdev, ATIIXP_IDE_UDMA_MODE, &udma_mode_data); + udma_mode_data &= ~(0x7 << (4 * dn)); + udma_mode_data |= dma << (4 * dn); + pci_write_config_word(pdev, ATIIXP_IDE_UDMA_MODE, udma_mode_data); + } else { + u16 mwdma_timing_data; + /* Check this is correct - the order is odd in both drivers */ + int timing_shift = (16 * ap->hard_port_no) + 8 * (adev->devno ^ 1); + + dma -= XFER_MW_DMA_0; + + pci_read_config_word(pdev, ATIIXP_IDE_MWDMA_TIMING, &mwdma_timing_data); + mwdma_timing_data &= ~(0xFF << timing_shift); + mwdma_timing_data |= (mwdma_timings[dma] << timing_shift); + pci_write_config_word(pdev, ATIIXP_IDE_MWDMA_TIMING, mwdma_timing_data); + } + /* + * We must now look at the PIO mode situation. We may need to + * adjust the PIO mode to keep the timings acceptable + */ + if (adev->dma_mode >= XFER_MW_DMA_2) + wanted_pio = 4; + else if (adev->dma_mode == XFER_MW_DMA_1) + wanted_pio = 3; + else if (adev->dma_mode == XFER_MW_DMA_0) + wanted_pio = 0; + else BUG(); + + if (adev->pio_mode != wanted_pio) + atiixp_set_pio_timing(ap, adev, wanted_pio); +} + +/** + * atiixp_bmdma_start - DMA start callback + * @qc: Command in progress + * + * When DMA begins we need to ensure that the UDMA control + * register for the channel is correctly set. + */ + +static void atiixp_bmdma_start(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_device *adev = qc->dev; + + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int dn = (2 * ap->hard_port_no) + adev->devno; + u16 tmp16; + + pci_read_config_word(pdev, ATIIXP_IDE_UDMA_CONTROL, &tmp16); + if (adev->dma_mode >= XFER_UDMA_0) + tmp16 |= (1 << dn); + else + tmp16 &= ~(1 << dn); + pci_write_config_word(pdev, ATIIXP_IDE_UDMA_CONTROL, tmp16); + ata_bmdma_start(qc); +} + +/** + * atiixp_dma_stop - DMA stop callback + * @qc: Command in progress + * + * DMA has completed. Clear the UDMA flag as the next operations will + * be PIO ones not UDMA data transfer. + */ + +static void atiixp_bmdma_stop(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int dn = (2 * ap->hard_port_no) + qc->dev->devno; + u16 tmp16; + + pci_read_config_word(pdev, ATIIXP_IDE_UDMA_CONTROL, &tmp16); + tmp16 &= ~(1 << dn); + pci_write_config_word(pdev, ATIIXP_IDE_UDMA_CONTROL, tmp16); + ata_bmdma_stop(qc); +} + +static struct scsi_host_template atiixp_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations atiixp_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = atiixp_set_piomode, + .set_dmamode = atiixp_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = atiixp_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = atiixp_bmdma_start, + .bmdma_stop = atiixp_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static int atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + static struct ata_port_info info = { + .sht = &atiixp_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x06, /* No MWDMA0 support */ + .udma_mask = 0x3F, + .port_ops = &atiixp_port_ops + }; + static struct ata_port_info *port_info[2] = { &info, &info }; + return ata_pci_init_one(dev, port_info, 2); +} + +static struct pci_device_id atiixp[] = { + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; + +static struct pci_driver atiixp_pci_driver = { + .name = DRV_NAME, + .id_table = atiixp, + .probe = atiixp_init_one, + .remove = ata_pci_remove_one +}; + +static int __init atiixp_init(void) +{ + return pci_register_driver(&atiixp_pci_driver); +} + + +static void __exit atiixp_exit(void) +{ + pci_unregister_driver(&atiixp_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for ATI IXP200/300/400"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, atiixp); +MODULE_VERSION(DRV_VERSION); + +module_init(atiixp_init); +module_exit(atiixp_exit); Index: linux-2.6.16/drivers/scsi/pata_cmd64x.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_cmd64x.c @@ -0,0 +1,486 @@ +/* + * pata_cmd64x.c - ATI PATA for new ATA layer + * (C) 2005 Red Hat Inc + * Alan Cox + * + * Based upon + * linux/drivers/ide/pci/cmd64x.c Version 1.30 Sept 10, 2002 + * + * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines. + * Note, this driver is not used at all on other systems because + * there the "BIOS" has done all of the following already. + * Due to massive hardware bugs, UltraDMA is only supported + * on the 646U2 and not on the 646U. + * + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) + * + * Copyright (C) 1999-2002 Andre Hedrick + * + * TODO + * Testing work + * Non x86 needs PIO 0 loading before we commence ident + * - but this belongs in libata-core + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pata_cmd64x" +#define DRV_VERSION "0.1.1" + +/* + * CMD64x specific registers definition. + */ + +enum { + CFR = 0x50, + CFR_INTR_CH0 = 0x02, + CNTRL = 0x51, + CNTRL_DIS_RA0 = 0x40, + CNTRL_DIS_RA1 = 0x80, + CNTRL_ENA_2ND = 0x08, + CMDTIM = 0x52, + ARTTIM0 = 0x53, + DRWTIM0 = 0x54, + ARTTIM1 = 0x55, + DRWTIM1 = 0x56, + ARTTIM23 = 0x57, + ARTTIM23_DIS_RA2 = 0x04, + ARTTIM23_DIS_RA3 = 0x08, + ARTTIM23_INTR_CH1 = 0x10, + ARTTIM2 = 0x57, + ARTTIM3 = 0x57, + DRWTIM23 = 0x58, + DRWTIM2 = 0x58, + BRST = 0x59, + DRWTIM3 = 0x5b, + BMIDECR0 = 0x70, + MRDMODE = 0x71, + MRDMODE_INTR_CH0 = 0x04, + MRDMODE_INTR_CH1 = 0x08, + MRDMODE_BLK_CH0 = 0x10, + MRDMODE_BLK_CH1 = 0x20, + BMIDESR0 = 0x72, + UDIDETCR0 = 0x73, + DTPR0 = 0x74, + BMIDECR1 = 0x78, + BMIDECSR = 0x79, + BMIDESR1 = 0x7A, + UDIDETCR1 = 0x7B, + DTPR1 = 0x7C +}; + +/* Phee Phy Pho Phum */ + +static void cmd64x_phy_reset(struct ata_port *ap) +{ + ap->cbl = ATA_CBL_PATA40; + ata_port_probe(ap); + ata_bus_reset(ap); +} + +static void cmd648_phy_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 r; + + /* Check cable detect bits */ + pci_read_config_byte(pdev, BMIDECSR, &r); + if (r & (1 << ap->hard_port_no)) + ap->cbl = ATA_CBL_PATA80; + else + ap->cbl = ATA_CBL_PATA40; + + ata_port_probe(ap); + ata_bus_reset(ap); +} + +/** + * cmd64x_set_piomode - set initial PIO mode data + * @ap: ATA interface + * @adev: ATA device + * + * Called to do the PIO mode setup. + */ + +static void cmd64x_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + struct ata_timing t; + const unsigned long T = 1000000 / 33; + const u8 setup_data[] = { 0x40, 0x40, 0x40, 0x80, 0x00 }; + + u8 reg; + + /* Port layout is not logical so use a table */ + const u8 arttim_port[2][2] = { + { ARTTIM0, ARTTIM1 }, + { ARTTIM23, ARTTIM23 } + }; + const u8 drwtim_port[2][2] = { + { DRWTIM0, DRWTIM1 }, + { DRWTIM2, DRWTIM3 } + }; + + int arttim = arttim_port[ap->hard_port_no][adev->devno]; + int drwtim = drwtim_port[ap->hard_port_no][adev->devno]; + + + if (ata_timing_compute(adev, adev->pio_mode, &t, T, 0) < 0) { + printk(KERN_ERR DRV_NAME ": mome computation failed.\n"); + return; + } + if (ap->hard_port_no) { + /* Slave has shared address setup */ + struct ata_device *pair = ata_dev_pair(ap, adev); + + if (pair) { + struct ata_timing tp; + ata_timing_compute(pair, pair->pio_mode, &tp, T, 0); + ata_timing_merge(&t, &tp, &t, ATA_TIMING_SETUP); + } + } + + printk(KERN_DEBUG DRV_NAME ": active %d recovery %d setup %d.\n", + t.active, t.recover, t.setup); + if (t.recover > 16) { + t.active += t.recover - 16; + t.recover = 16; + } + if (t.active > 16) + t.active = 16; + + /* Now convert the clocks into values we can actually stuff into + the chip */ + + if (t.recover > 1) + t.recover--; + else + t.recover = 15; + + if (t.setup > 4) + t.setup = 0xC0; + else + t.setup = setup_data[t.setup]; + + t.active &= 0x0F; /* 0 = 16 */ + + /* Load setup timing */ + pci_read_config_byte(pdev, arttim, ®); + reg &= 0x3F; + reg |= t.setup; + pci_write_config_byte(pdev, arttim, reg); + + /* Load active/recovery */ + pci_write_config_byte(pdev, drwtim, (t.active << 4) | t.recover); +} + +/** + * cmd64x_set_dmamode - set initial DMA mode data + * @ap: ATA interface + * @adev: ATA device + * + * Called to do the DMA mode setup. + */ + +static void cmd64x_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 regU, regD; + + int pciU = UDIDETCR0 + 8 * ap->hard_port_no; + int pciD = BMIDESR0 + 8 * ap->hard_port_no; + int shift = 2 * adev->devno; + const u8 udma_data[] = { 0x31, 0x21, 0x11, 0x25, 0x15, 0x05 }; + const u8 mwdma_data[] = { 0x30, 0x20, 0x10 }; + + pci_read_config_byte(pdev, pciD, ®D); + pci_read_config_byte(pdev, pciU, ®U); + + regD &= ~(0x20 << shift); + regU &= ~(0x35 << shift); + + if (adev->dma_mode >= XFER_UDMA_0) + regU |= udma_data[adev->dma_mode - XFER_UDMA_0] << shift; + else + regD |= mwdma_data[adev->dma_mode - XFER_MW_DMA_0] << shift; + + regD |= 0x20 << adev->devno; + + pci_write_config_byte(pdev, pciU, regU); + pci_write_config_byte(pdev, pciD, regD); +} + +/** + * cmd648_dma_stop - DMA stop callback + * @qc: Command in progress + * + * DMA has completed. + */ + +static void cmd648_bmdma_stop(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 dma_intr; + int dma_reg = ap->hard_port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; + int dma_mask = ap->hard_port_no ? ARTTIM2 : CFR; + + ata_bmdma_stop(qc); + + pci_read_config_byte(pdev, dma_reg, &dma_intr); + pci_write_config_byte(pdev, dma_reg, dma_intr | dma_mask); +} + +/** + * cmd646r1_dma_stop - DMA stop callback + * @qc: Command in progress + * + * Stub for now while investigating the r1 quirk in the old driver. + */ + +static void cmd646r1_bmdma_stop(struct ata_queued_cmd *qc) +{ + ata_bmdma_stop(qc); +} + +static struct scsi_host_template cmd64x_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations cmd64x_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cmd64x_set_piomode, + .set_dmamode = cmd64x_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = cmd64x_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static struct ata_port_operations cmd646r1_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cmd64x_set_piomode, + .set_dmamode = cmd64x_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = cmd64x_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = cmd646r1_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static struct ata_port_operations cmd648_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cmd64x_set_piomode, + .set_dmamode = cmd64x_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = cmd648_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = cmd648_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + u32 class_rev; + + static struct ata_port_info cmd_info[6] = { + { /* CMD 643 - no UDMA */ + .sht = &cmd64x_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .port_ops = &cmd64x_port_ops + }, + { /* CMD 646 with broken UDMA */ + .sht = &cmd64x_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .port_ops = &cmd64x_port_ops + }, + { /* CMD 646 with working UDMA */ + .sht = &cmd64x_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = ATA_UDMA1, + .port_ops = &cmd64x_port_ops + }, + { /* CMD 646 rev 1 */ + .sht = &cmd64x_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .port_ops = &cmd646r1_port_ops + }, + { /* CMD 648 */ + .sht = &cmd64x_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = ATA_UDMA2, + .port_ops = &cmd648_port_ops + }, + { /* CMD 649 */ + .sht = &cmd64x_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = ATA_UDMA3, + .port_ops = &cmd648_port_ops + } + }; + static struct ata_port_info *port_info[2], *info; + u8 mrdmode; + + info = &cmd_info[id->driver_data]; + + pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xFF; + + if (id->driver_data == 0) /* 643 */ + ata_pci_clear_simplex(pdev); + + if (pdev->device == PCI_DEVICE_ID_CMD_646) { + /* Does UDMA work ? */ + if (class_rev > 4) + info = &cmd_info[2]; + /* Early rev with other problems ? */ + else if (class_rev == 1) + info = &cmd_info[3]; + } + + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); + pci_read_config_byte(pdev, MRDMODE, &mrdmode); + mrdmode &= ~ 0x30; /* IRQ set up */ + mrdmode |= 0x02; /* Memory read line enable */ + pci_write_config_byte(pdev, MRDMODE, mrdmode); + + /* Force PIO 0 here.. */ + + /* PPC specific fixup copied from old driver */ +#ifdef CONFIG_PPC + pci_write_config_byte(pdev, UDIDETCR0, 0xF0); +#endif + + port_info[0] = port_info[1] = info; + return ata_pci_init_one(pdev, port_info, 2); +} + +static struct pci_device_id cmd64x[] = { + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, + { 0, }, +}; + +static struct pci_driver cmd64x_pci_driver = { + .name = DRV_NAME, + .id_table = cmd64x, + .probe = cmd64x_init_one, + .remove = ata_pci_remove_one +}; + +static int __init cmd64x_init(void) +{ + return pci_register_driver(&cmd64x_pci_driver); +} + + +static void __exit cmd64x_exit(void) +{ + pci_unregister_driver(&cmd64x_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for CMD64x series PATA controllers"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, cmd64x); +MODULE_VERSION(DRV_VERSION); + +module_init(cmd64x_init); +module_exit(cmd64x_exit); Index: linux-2.6.16/drivers/scsi/pata_cs5520.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_cs5520.c @@ -0,0 +1,322 @@ +/* + * IDE tuning and bus mastering support for the CS5510/CS5520 + * chipsets + * + * The CS5510/CS5520 are slightly unusual devices. Unlike the + * typical IDE controllers they do bus mastering with the drive in + * PIO mode and smarter silicon. + * + * The practical upshot of this is that we must always tune the + * drive for the right PIO mode. We must also ignore all the blacklists + * and the drive bus mastering DMA information. Also to confuse matters + * further we can do DMA on PIO only drives. + * + * DMA on the 5510 also requires we disable_hlt() during DMA on early + * revisions. + * + * *** This driver is strictly experimental *** + * + * (c) Copyright Red Hat Inc 2002 + * + * 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, 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. + * + * Documentation: + * Not publically available. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cs5520" +#define DRV_VERSION "0.4" + +struct pio_clocks +{ + int address; + int assert; + int recovery; +}; + +static struct pio_clocks cs5520_pio_clocks[]={ + {3, 6, 11}, + {2, 5, 6}, + {1, 4, 3}, + {1, 3, 2}, + {1, 2, 1} +}; + +/** + * cs5520_set_timings - program PIO timings + * @ap: ATA port + * @adev: ATA device + * + * Program the PIO mode timings for the controller according to the pio + * clocking table. + */ + +static void cs5520_set_timings(struct ata_port *ap, struct ata_device *adev, int pio) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + int slave = adev->devno; + + pio -= XFER_PIO_0; + + /* Channel command timing */ + pci_write_config_byte(pdev, 0x62 + ap->hard_port_no, + (cs5520_pio_clocks[pio].recovery << 4) | + (cs5520_pio_clocks[pio].assert)); + /* FIXME: should these use address ? */ + /* Read command timing */ + pci_write_config_byte(pdev, 0x64 + 4*ap->hard_port_no + slave, + (cs5520_pio_clocks[pio].recovery << 4) | + (cs5520_pio_clocks[pio].assert)); + /* Write command timing */ + pci_write_config_byte(pdev, 0x66 + 4*ap->hard_port_no + slave, + (cs5520_pio_clocks[pio].recovery << 4) | + (cs5520_pio_clocks[pio].assert)); +} + +/** + * cs5520_enable_dma - turn on DMA bits + * + * Turn on the DMA bits for this disk. Needed because the BIOS probably + * has not done the work for us. Belongs in the core SATA code. + */ + +static int cs5520_enable_dma(struct ata_port *ap, struct ata_device *adev) +{ + /* Set the DMA enable/disable flag */ + u8 reg = inb(ap->ioaddr.bmdma_addr + 0x02); + reg |= 1<<(adev->devno + 5); + outb(reg, ap->ioaddr.bmdma_addr + 0x02); +} + +/** + * cs5520_set_dmamode - program DMA timings + * @ap: ATA port + * @adev: ATA device + * + * Program the DMA mode timings for the controller according to the pio + * clocking table. Note that this device sets the DMA timings to PIO + * mode values. This may seem bizarre but the 5520 architecture talks + * PIO mode to the disk and DMA mode to the controller so the underlying + * transfers are PIO timed. + */ + +static void cs5520_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + static const int dma_xlate[3] = { XFER_PIO_0, XFER_PIO_3, XFER_PIO_4 }; + cs5520_set_timings(ap, adev, dma_xlate[adev->dma_mode]); + cs5520_enable_dma(ap, adev); +} + +/** + * cs5520_set_piomode - program PIO timings + * @ap: ATA port + * @adev: ATA device + * + * Program the PIO mode timings for the controller according to the pio + * clocking table. We know pio_mode will equal dma_mode because of the + * CS5520 architecture. At least once we turned DMA on and wrote a + * mode setter. + */ + +static void cs5520_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + cs5520_set_timings(ap, adev, adev->pio_mode); +} + +static struct scsi_host_template cs5520_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations cs5520_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cs5520_set_piomode, + .set_dmamode = cs5520_set_dmamode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .data_xfer = ata_pio_data_xfer, + + .eng_timeout = ata_eng_timeout, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop, +}; + +static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + u8 pcicfg; + static struct ata_probe_ent probe[2]; + int ports = 0; + + pci_read_config_byte(dev, 0x60, &pcicfg); + + /* Check if the ATA ports are enabled */ + if ((pcicfg & 3) == 0) + return -ENODEV; + + if ((pcicfg & 0x40) == 0) { + printk(KERN_WARNING DRV_NAME ": DMA mode disabled. Enabling.\n"); + pci_write_config_byte(dev, 0x60, pcicfg | 0x40); + } + + /* Perform set up for DMA */ + if (pci_enable_device_bars(dev, 1<<2)) { + printk(KERN_ERR DRV_NAME ": unable to configure BAR2.\n"); + return -ENODEV; + } + pci_set_master(dev); + if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) { + printk(KERN_ERR DRV_NAME ": unable to configure DMA mask.\n"); + return -ENODEV; + } + if (pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK)) { + printk(KERN_ERR DRV_NAME ": unable to configure consistent DMA mask.\n"); + return -ENODEV; + } + + /* We have to do our own plumbing as the PCI setup for this + chipset is non-standard so we can't punt to the libata code */ + + INIT_LIST_HEAD(&probe[0].node); + probe[0].dev = pci_dev_to_dev(dev); + probe[0].port_ops = &cs5520_port_ops; + probe[0].sht = &cs5520_sht; + probe[0].pio_mask = 0x1F; + probe[0].mwdma_mask = id->driver_data; + probe[0].irq = 14; + probe[0].irq_flags = SA_SHIRQ; + probe[0].host_flags = ATA_FLAG_SLAVE_POSS; + probe[0].legacy_mode = 1; + probe[0].n_ports = 1; + probe[0].port[0].cmd_addr = 0x1F0; + probe[0].port[0].ctl_addr = 0x3F6; + probe[0].port[0].altstatus_addr = 0x3F6; + probe[0].port[0].bmdma_addr = pci_resource_start(dev, 2); + + /* The secondary lurks at different addresses but is otherwise + the same beastie */ + + probe[1] = probe[0]; + INIT_LIST_HEAD(&probe[1].node); + probe[1].irq = 15; + probe[1].hard_port_no = 1; + probe[1].port[0].cmd_addr = 0x170; + probe[1].port[0].ctl_addr = 0x376; + probe[1].port[0].altstatus_addr = 0x376; + probe[1].port[0].bmdma_addr = pci_resource_start(dev, 2) + 8; + + /* Let libata fill in the port details */ + ata_std_ports(&probe[0].port[0]); + ata_std_ports(&probe[1].port[0]); + + /* Now add the ports that are active */ + if (pcicfg & 1) + ports += ata_device_add(&probe[0]); + if (pcicfg & 2) + ports += ata_device_add(&probe[1]); + if (ports) + return 0; + return -ENODEV; +} + +/** + * cs5520_remove_one - device unload + * @pdev: PCI device being removed + * + * Handle an unplug/unload event for a PCI device. Unload the + * PCI driver but do not use the default handler as we manage + * resources ourself and *MUST NOT* disable the device as it has + * other functions. + */ + +static void __devexit cs5520_remove_one(struct pci_dev *pdev) +{ + struct device *dev = pci_dev_to_dev(pdev); + struct ata_host_set *host_set = dev_get_drvdata(dev); + + ata_host_set_remove(host_set); + dev_set_drvdata(dev, NULL); +} + +/* For now keep DMA off. We can set it for all but A rev CS5510 once the + core ATA code can handle it */ + +static struct pci_device_id pata_cs5520[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510), }, + { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520), }, + { 0, }, +}; + +static struct pci_driver cs5520_pci_driver = { + .name = DRV_NAME, + .id_table = pata_cs5520, + .probe = cs5520_init_one, + .remove = cs5520_remove_one +}; + + +static int __init cs5520_init(void) +{ + return pci_register_driver(&cs5520_pci_driver); +} + +static void __exit cs5520_exit(void) +{ + pci_unregister_driver(&cs5520_pci_driver); +} + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for Cyrix CS5510/5520"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, pata_cs5520); +MODULE_VERSION(DRV_VERSION); + +module_init(cs5520_init); +module_exit(cs5520_exit); + Index: linux-2.6.16/drivers/scsi/pata_cs5530.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_cs5530.c @@ -0,0 +1,371 @@ +/* + * pata-cs5530.c - CS5530 PATA for new ATA layer + * (C) 2005 Red Hat Inc + * Alan Cox + * + * based upon cs5530.c by Mark Lord. + * + * 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. + * + * 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 + * + * Loosely based on the piix & svwks drivers. + * + * Documentation: + * Available from AMD web site. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cs5530" +#define DRV_VERSION "0.4" + +/** + * cs5530_set_piomode - PIO setup + * @ap: ATA interface + * @adev: device on the interface + * + * Set our PIO requirements. This is fairly simple on the CS5530 + * chips. + */ + +static void cs5530_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + static unsigned int cs5530_pio_timings[2][5] = { + {0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010}, + {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010} + }; + unsigned long base = ( ap->ioaddr.bmdma_addr & ~0x0F) + 0x20 + 0x10 * ap->hard_port_no; + u32 tuning; + int format; + + /* Find out which table to use */ + tuning = inl(base + 0x04); + format = (tuning & 0x80000000UL) ? 1 : 0; + + /* Now load the right timing register */ + if (adev->devno) + base += 0x08; + + outl(cs5530_pio_timings[format][adev->pio_mode - XFER_PIO_0], base); +} + +/** + * cs5530_set_dmamode - DMA timing setup + * @ap: ATA interface + * @adev: Device being configured + * + * We cannot mix MWDMA and UDMA without reloading timings each switch + * master to slave. We track the last DMA setup in order to minimise + * reloads. + */ + +static void cs5530_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + unsigned long base = ( ap->ioaddr.bmdma_addr & ~0x0F) + 0x20 + 0x10 * ap->hard_port_no; + u32 tuning, timing = 0; + u8 reg; + + /* Find out which table to use */ + tuning = inl(base + 0x04); + + switch(adev->dma_mode) { + case XFER_UDMA_0: + timing = 0x00921250;break; + case XFER_UDMA_1: + timing = 0x00911140;break; + case XFER_UDMA_2: + timing = 0x00911030;break; + case XFER_MW_DMA_0: + timing = 0x00077771;break; + case XFER_MW_DMA_1: + timing = 0x00012121;break; + case XFER_MW_DMA_2: + timing = 0x00002020;break; + default: + BUG(); + } + /* Merge in the PIO format bit */ + timing |= (tuning & 0x80000000UL); + if (adev->devno == 0) /* Master */ + outl(timing, base + 0x04); + else { + if (timing & 0x00100000) + tuning |= 0x00100000; /* UDMA for both */ + else + tuning &= ~0x00100000; /* MWDMA for both */ + outl(tuning, base + 0x04); + outl(timing, base + 0x0C); + } + + /* Set the DMA capable bit in the BMDMA area */ + reg = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + reg |= (1 << (5 + adev->devno)); + outb(reg, ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + + /* Remember the last DMA setup we did */ + + ap->private_data = adev; +} + +/** + * cs5530_qc_issue_prot - command issue + * @qc: command pending + * + * Called when the libata layer is about to issue a command. We wrap + * this interface so that we can load the correct ATA timings if + * neccessary. Specifically we have a problem that there is only + * one MWDMA/UDMA bit. + */ + +static int cs5530_qc_issue_prot(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_device *adev = qc->dev; + struct ata_device *prev = ap->private_data; + + /* See if the DMA settings could be wrong */ + if (adev->dma_mode != 0 && adev != prev && prev != NULL) { + /* Maybe, but do the channels match MWDMA/UDMA ? */ + if ((adev->dma_mode >= XFER_UDMA_0 && prev->dma_mode < XFER_UDMA_0) || + (adev->dma_mode < XFER_UDMA_0 && prev->dma_mode >= XFER_UDMA_0)) + /* Switch the mode bits */ + cs5530_set_dmamode(ap, adev); + } + + return ata_qc_issue_prot(qc); +} + +static struct scsi_host_template cs5530_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations cs5530_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cs5530_set_piomode, + .set_dmamode = cs5530_set_dmamode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = cs5530_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static struct dmi_system_id __initdata palmax_dmi_table[] = { + { + .ident = "Palmax PD1100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Cyrix"), + DMI_MATCH(DMI_PRODUCT_NAME, "Caddis"), + }, + }, + { } +}; + +static int cs5530_is_palmax(void) +{ + if (dmi_check_system(palmax_dmi_table)) { + printk(KERN_INFO "Palmax PD1100: Disabling DMA on docking port.\n"); + return 1; + } + return 0; +} + +/** + * cs5530_init_one - Initialise a CS5530 + * @dev: PCI device + * @id: Entry in match table + * + * Install a driver for the newly found CS5530 companion chip. Most of + * this is just housekeeping. We have to set the chip up correctly and + * turn off various bits of emulation magic. + */ + +static int cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + int compiler_warning_pointless_fix; + struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; + static struct ata_port_info info = { + .sht = &cs5530_sht, + .host_flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x07, + .port_ops = &cs5530_port_ops + }; + /* The docking connector doesn't do UDMA, and it seems not MWDMA */ + static struct ata_port_info info_palmax_secondary = { + .sht = &cs5530_sht, + .host_flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, + .port_ops = &cs5530_port_ops + }; + static struct ata_port_info *port_info[2] = { &info, &info }; + + dev = NULL; + while ((dev = pci_get_device(PCI_VENDOR_ID_CYRIX, PCI_ANY_ID, dev)) != NULL) { + switch (dev->device) { + case PCI_DEVICE_ID_CYRIX_PCI_MASTER: + master_0 = pci_dev_get(dev); + break; + case PCI_DEVICE_ID_CYRIX_5530_LEGACY: + cs5530_0 = pci_dev_get(dev); + break; + } + } + if (!master_0) { + printk(KERN_ERR DRV_NAME ": unable to locate PCI MASTER function\n"); + goto fail_put; + } + if (!cs5530_0) { + printk(KERN_ERR DRV_NAME ": unable to locate CS5530 LEGACY function\n"); + goto fail_put; + } + + pci_set_master(cs5530_0); + compiler_warning_pointless_fix = pci_set_mwi(cs5530_0); + + /* + * Set PCI CacheLineSize to 16-bytes: + * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530 + * + * Note: This value is constant because the 5530 is only a Geode companion + */ + + pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04); + + /* + * Disable trapping of UDMA register accesses (Win98 hack): + * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530 + */ + + pci_write_config_word(cs5530_0, 0xd0, 0x5006); + + /* + * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus: + * The other settings are what is necessary to get the register + * into a sane state for IDE DMA operation. + */ + + pci_write_config_byte(master_0, 0x40, 0x1e); + + /* + * Set max PCI burst size (16-bytes seems to work best): + * 16bytes: set bit-1 at 0x41 (reg value of 0x16) + * all others: clear bit-1 at 0x41, and do: + * 128bytes: OR 0x00 at 0x41 + * 256bytes: OR 0x04 at 0x41 + * 512bytes: OR 0x08 at 0x41 + * 1024bytes: OR 0x0c at 0x41 + */ + + pci_write_config_byte(master_0, 0x41, 0x14); + + /* + * These settings are necessary to get the chip + * into a sane state for IDE DMA operation. + */ + + pci_write_config_byte(master_0, 0x42, 0x00); + pci_write_config_byte(master_0, 0x43, 0xc1); + + pci_dev_put(master_0); + pci_dev_put(cs5530_0); + + if (cs5530_is_palmax()) + port_info[1] = &info_palmax_secondary; + + /* Now kick off ATA set up */ + return ata_pci_init_one(dev, port_info, 2); + +fail_put: + if (master_0) + pci_dev_put(master_0); + if (cs5530_0) + pci_dev_put(cs5530_0); + return -ENODEV; +} + +static struct pci_device_id cs5530[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), }, + { 0, }, +}; + +static struct pci_driver cs5530_pci_driver = { + .name = DRV_NAME, + .id_table = cs5530, + .probe = cs5530_init_one, + .remove = ata_pci_remove_one +}; + +static int __init cs5530_init(void) +{ + return pci_register_driver(&cs5530_pci_driver); +} + + +static void __exit cs5530_exit(void) +{ + pci_unregister_driver(&cs5530_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for the Cyrix/NS/AMD 5530"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, cs5530); +MODULE_VERSION(DRV_VERSION); + +module_init(cs5530_init); +module_exit(cs5530_exit); Index: linux-2.6.16/drivers/scsi/pata_cs5535.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_cs5535.c @@ -0,0 +1,286 @@ +/* + * pata-cs5535.c - CS5535 PATA for new ATA layer + * (C) 2005 Red Hat Inc + * Alan Cox + * + * based upon cs5535.c from AMD as cleaned up and + * made readable and Linux style by Wolfgang Zuleger + * + * 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. + * + * 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 + * + * Loosely based on the piix & svwks drivers. + * + * Documentation: + * Available from AMD web site. + * TODO + * Review errata to see if serializing is neccessary + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cs5535" +#define DRV_VERSION "0.2.1" + +/* + * The Geode (Aka Athlon GX now) uses an internal MSR based + * bus system for control. Demented but there you go. + */ + +#define MSR_ATAC_BASE 0x51300000 +#define ATAC_GLD_MSR_CAP (MSR_ATAC_BASE+0) +#define ATAC_GLD_MSR_CONFIG (MSR_ATAC_BASE+0x01) +#define ATAC_GLD_MSR_SMI (MSR_ATAC_BASE+0x02) +#define ATAC_GLD_MSR_ERROR (MSR_ATAC_BASE+0x03) +#define ATAC_GLD_MSR_PM (MSR_ATAC_BASE+0x04) +#define ATAC_GLD_MSR_DIAG (MSR_ATAC_BASE+0x05) +#define ATAC_IO_BAR (MSR_ATAC_BASE+0x08) +#define ATAC_RESET (MSR_ATAC_BASE+0x10) +#define ATAC_CH0D0_PIO (MSR_ATAC_BASE+0x20) +#define ATAC_CH0D0_DMA (MSR_ATAC_BASE+0x21) +#define ATAC_CH0D1_PIO (MSR_ATAC_BASE+0x22) +#define ATAC_CH0D1_DMA (MSR_ATAC_BASE+0x23) +#define ATAC_PCI_ABRTERR (MSR_ATAC_BASE+0x24) + +#define ATAC_BM0_CMD_PRIM 0x00 +#define ATAC_BM0_STS_PRIM 0x02 +#define ATAC_BM0_PRD 0x04 + +#define CS5535_CABLE_DETECT 0x48 + +#define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL)==0x00009172 ) + +/** + * cs5535_cable_detect - detect cable type + * @ap: Port to detect on + * + * Perform cable detection for ATA66 capable cable. Return a libata + * cable type. + */ + +static int cs5535_cable_detect(struct ata_port *ap) +{ + u8 cable; + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + + pci_read_config_byte(pdev, CS5535_CABLE_DETECT, &cable); + if (cable & 1) + return ATA_CBL_PATA80; + else + return ATA_CBL_PATA40; +} + +/** + * cs5535_phy_reset - reset/probe + * @ap: Port to reset + * + * Reset and configure a port + */ + +static void cs5535_phy_reset(struct ata_port *ap) +{ + ap->cbl = cs5535_cable_detect(ap); + ata_bus_reset(ap); + ata_port_probe(ap); +} + +/** + * cs5535_set_piomode - PIO setup + * @ap: ATA interface + * @adev: device on the interface + * + * Set our PIO requirements. The CS5535 is pretty clean about all this + */ + +static void cs5535_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + static u16 pio_timings[5] = { + 0xF7F4, 0x53F3, 0x13F1, 0x5131, 0x1131 + }; + static u16 pio_cmd_timings[5] = { + 0xF7F4, 0x53F3, 0x13F1, 0x5131, 0x1131 + }; + u32 reg, dummy; + struct ata_device *pair = ata_dev_pair(ap, adev); + + int mode = adev->pio_mode - XFER_PIO_0; + int cmdmode = mode; + + /* Command timing has to be for the lowest of the pair of devices */ + if (pair) { + int pairmode = pair->pio_mode - XFER_PIO_0; + cmdmode = min(mode, pairmode); + /* Write the other drive timing register if it changed */ + if (cmdmode < pairmode) + wrmsr(ATAC_CH0D0_PIO + 2 * pair->devno, + pio_cmd_timings[cmdmode] << 16 | pio_timings[pairmode], 0); + } + /* Write the drive timing register */ + wrmsr(ATAC_CH0D0_PIO + 2 * adev->devno, + pio_cmd_timings[cmdmode] << 16 | pio_timings[mode], 0); + + /* Set the PIO "format 1" bit in the DMA timing register */ + rdmsr(ATAC_CH0D0_DMA + 2 * adev->devno, reg, dummy); + wrmsr(ATAC_CH0D0_DMA + 2 * adev->devno, reg | 0x80000000UL, 0); +} + +/** + * cs5535_set_dmamode - DMA timing setup + * @ap: ATA interface + * @adev: Device being configured + * + */ + +static void cs5535_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + static u32 udma_timings[5] = { 0x7F7436A1, 0x7F733481, 0x7F723261, 0x7F713161, 0x7F703061 }; + static u32 mwdma_timings[3] = { 0x7F0FFFF3, 0x7F035352, 0x7F024241 }; + u32 reg, dummy; + int mode = adev->dma_mode; + + rdmsr(ATAC_CH0D0_DMA + 2 * adev->devno, reg, dummy); + reg &= 0x80000000UL; + if (mode >= XFER_UDMA_0) + reg |= udma_timings[mode - XFER_UDMA_0]; + else + reg |= mwdma_timings[mode - XFER_MW_DMA_0]; + wrmsr(ATAC_CH0D0_DMA + 2 * adev->devno, reg, 0); +} + +static struct scsi_host_template cs5535_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations cs5535_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cs5535_set_piomode, + .set_dmamode = cs5535_set_dmamode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = cs5535_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +/** + * cs5535_init_one - Initialise a CS5530 + * @dev: PCI device + * @id: Entry in match table + * + * Install a driver for the newly found CS5530 companion chip. Most of + * this is just housekeeping. We have to set the chip up correctly and + * turn off various bits of emulation magic. + */ + +static int cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + static struct ata_port_info info = { + .sht = &cs5535_sht, + .host_flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x1f, + .port_ops = &cs5535_port_ops + }; + struct ata_port_info *ports[1] = { &info }; + + u32 timings, dummy; + + /* Check the BIOS set the initial timing clock. If not set the + timings for PIO0 */ + rdmsr(ATAC_CH0D0_PIO, timings, dummy); + if (CS5535_BAD_PIO(timings)) + wrmsr(ATAC_CH0D0_PIO, 0xF7F4F7F4UL, 0); + rdmsr(ATAC_CH0D1_PIO, timings, dummy); + if (CS5535_BAD_PIO(timings)) + wrmsr(ATAC_CH0D1_PIO, 0xF7F4F7F4UL, 0); + return ata_pci_init_one(dev, ports, 1); +} + +static struct pci_device_id cs5535[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, 0x002D), }, + { 0, }, +}; + +static struct pci_driver cs5535_pci_driver = { + .name = DRV_NAME, + .id_table = cs5535, + .probe = cs5535_init_one, + .remove = ata_pci_remove_one +}; + +static int __init cs5535_init(void) +{ + return pci_register_driver(&cs5535_pci_driver); +} + + +static void __exit cs5535_exit(void) +{ + pci_unregister_driver(&cs5535_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox, Jens Altmann, Wolfgan Zuleger, Alexander Kiausch"); +MODULE_DESCRIPTION("low-level driver for the NS/AMD 5530"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, cs5535); +MODULE_VERSION(DRV_VERSION); + +module_init(cs5535_init); +module_exit(cs5535_exit); Index: linux-2.6.16/drivers/scsi/pata_cypress.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_cypress.c @@ -0,0 +1,215 @@ +/* + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pata_cypress" +#define DRV_VERSION "0.1" + +/* here are the offset definitions for the registers */ + +enum { + CY82_IDE_CMDREG = 0x04, + CY82_IDE_ADDRSETUP = 0x48, + CY82_IDE_MASTER_IOR = 0x4C, + CY82_IDE_MASTER_IOW = 0x4D, + CY82_IDE_SLAVE_IOR = 0x4E, + CY82_IDE_SLAVE_IOW = 0x4F, + CY82_IDE_MASTER_8BIT = 0x50, + CY82_IDE_SLAVE_8BIT = 0x51, + + CY82_INDEX_PORT = 0x22, + CY82_DATA_PORT = 0x23, + + CY82_INDEX_CTRLREG1 = 0x01, + CY82_INDEX_CHANNEL0 = 0x30, + CY82_INDEX_CHANNEL1 = 0x31, + CY82_INDEX_TIMEOUT = 0x32 +}; + +/* Phee Phy Pho Phum */ + +static void cy82c693_phy_reset(struct ata_port *ap) +{ + ap->cbl = ATA_CBL_PATA40; + ata_port_probe(ap); + ata_bus_reset(ap); +} + +/** + * cy82c693_set_piomode - set initial PIO mode data + * @ap: ATA interface + * @adev: ATA device + * + * Called to do the PIO mode setup. + */ + +static void cy82c693_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + struct ata_timing t; + const unsigned long T = 1000000 / 33; + short time_16, time_8; + u32 addr; + + if (ata_timing_compute(adev, adev->pio_mode, &t, T, 1) < 0) { + printk(KERN_ERR DRV_NAME ": mome computation failed.\n"); + return; + } + + time_16 = FIT(t.recover, 0, 15) | (FIT(t.active, 0, 15) << 4); + time_8 = FIT(t.act8b, 0, 15) | (FIT(t.rec8b, 0, 15) << 4); + + if (adev->devno == 0) { + pci_read_config_dword(pdev, CY82_IDE_ADDRSETUP, &addr); + + addr &= ~0x0F; /* Mask bits */ + addr |= FIT(t.setup, 0, 15); + + pci_write_config_dword(pdev, CY82_IDE_ADDRSETUP, addr); + pci_write_config_byte(pdev, CY82_IDE_MASTER_IOR, time_16); + pci_write_config_byte(pdev, CY82_IDE_MASTER_IOW, time_16); + pci_write_config_byte(pdev, CY82_IDE_MASTER_8BIT, time_8); + } else { + pci_read_config_dword(pdev, CY82_IDE_ADDRSETUP, &addr); + + addr &= ~0xF0; /* Mask bits */ + addr |= (FIT(t.setup, 0, 15) << 4); + + pci_write_config_dword(pdev, CY82_IDE_ADDRSETUP, addr); + pci_write_config_byte(pdev, CY82_IDE_SLAVE_IOR, time_16); + pci_write_config_byte(pdev, CY82_IDE_SLAVE_IOW, time_16); + pci_write_config_byte(pdev, CY82_IDE_SLAVE_8BIT, time_8); + } +} + +/** + * cy82c693_set_dmamode - set initial DMA mode data + * @ap: ATA interface + * @adev: ATA device + * + * Called to do the DMA mode setup. + */ + +static void cy82c693_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + int reg = CY82_INDEX_CHANNEL0 + ap->hard_port_no; + + /* Be afraid, be very afraid. Magic registers in low I/O space */ + outb(reg, 0x22); + outb(adev->dma_mode - XFER_MW_DMA_0, 0x23); + + /* 0x50 gives the best behaviour on the Alpha's using this chip */ + outb(CY82_INDEX_TIMEOUT, 0x22); + outb(0x50, 0x23); +} + +static struct scsi_host_template cy82c693_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations cy82c693_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cy82c693_set_piomode, + .set_dmamode = cy82c693_set_dmamode, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = cy82c693_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +static int cy82c693_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + static struct ata_port_info info = { + .sht = &cy82c693_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .port_ops = &cy82c693_port_ops + }; + static struct ata_port_info *port_info[1] = { &info }; + + /* Devfn 1 is the ATA primary. The secondary is magic and on devfn2. For the + moment we don't handle the secondary. FIXME */ + + if (PCI_FUNC(pdev->devfn) != 1) + return -ENODEV; + + return ata_pci_init_one(pdev, port_info, 1); +} + +static struct pci_device_id cy82c693[] = { + { PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; + +static struct pci_driver cy82c693_pci_driver = { + .name = DRV_NAME, + .id_table = cy82c693, + .probe = cy82c693_init_one, + .remove = ata_pci_remove_one +}; + +static int __init cy82c693_init(void) +{ + return pci_register_driver(&cy82c693_pci_driver); +} + + +static void __exit cy82c693_exit(void) +{ + pci_unregister_driver(&cy82c693_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for the CY82C693 PATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, cy82c693); +MODULE_VERSION(DRV_VERSION); + +module_init(cy82c693_init); +module_exit(cy82c693_exit); Index: linux-2.6.16/drivers/scsi/pata_efar.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_efar.c @@ -0,0 +1,341 @@ +/* + * pata_efar.c - EFAR PIIX clone controller driver + * + * (C) 2005 Red Hat + * + * Some parts based on ata_piix.c by Jeff Garzik and others. + * + * The EFAR is a PIIX4 clone with UDMA66 support. Unlike the later + * Intel ICH controllers the EFAR widened the UDMA mode register bits + * and doesn't require the funky clock selection. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pata_efar" +#define DRV_VERSION "0.1" + +/** + * efar_cable_detect - check for 40/80 pin + * @ap: Port + * + * Perform cable detection for the EFAR ATA interface. This is + * different to the PIIX arrangement + */ + +static int efar_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 tmp; + + pci_read_config_byte(pdev, 0x47, &tmp); + if (tmp & (2 >> ap->hard_port_no)) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + +/** + * efar_phy_reset - Probe specified port on PATA host controller + * @ap: Port to probe + * + * LOCKING: + * None (inherited from caller). + */ + +static void efar_phy_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + const struct pci_bits efar_enable_bits[] = { + { 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */ + { 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */ + }; + + if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->hard_port_no])) { + ata_port_disable(ap); + printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); + return; + } + ap->cbl = efar_cable_detect(ap); + ata_port_probe(ap); + ata_bus_reset(ap); +} + +/** + * efar_set_piomode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @adev: um + * + * Set PIO mode for device, in host controller PCI config space. + * + * LOCKING: + * None (inherited from caller). + */ + +static void efar_set_piomode (struct ata_port *ap, struct ata_device *adev) +{ + unsigned int pio = adev->pio_mode - XFER_PIO_0; + struct pci_dev *dev = to_pci_dev(ap->host_set->dev); + unsigned int idetm_port= ap->hard_port_no ? 0x42 : 0x40; + u16 idetm_data; + int control = 0; + + /* + * See Intel Document 298600-004 for the timing programing rules + * for PIIX/ICH. The EFAR is a clone so very similar + */ + + static const /* ISP RTC */ + u8 timings[][2] = { { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + if (pio > 2) + control |= 1; /* TIME1 enable */ + if (ata_pio_need_iordy(adev)) /* PIO 3/4 require IORDY */ + control |= 2; /* IE enable */ + /* Intel specifies that the PPE functionality is for disk only */ + if (adev->class == ATA_DEV_ATA) + control |= 4; /* PPE enable */ + + pci_read_config_word(dev, idetm_port, &idetm_data); + + /* Enable PPE, IE and TIME as appropriate */ + + if (adev->devno == 0) { + idetm_data &= 0xCCF0; + idetm_data |= control; + idetm_data |= (timings[pio][0] << 12) | + (timings[pio][1] << 8); + } else { + int shift = 4 * ap->hard_port_no; + u8 slave_data; + + idetm_data &= 0xCC0F; + idetm_data |= (control << 4); + + /* Slave timing in seperate register */ + pci_read_config_byte(dev, 0x44, &slave_data); + slave_data &= 0x0F << shift; + slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << shift; + pci_write_config_byte(dev, 0x44, slave_data); + } + + idetm_data |= 0x4000; /* Ensure SITRE is enabled */ + pci_write_config_word(dev, idetm_port, idetm_data); +} + +/** + * efar_set_dmamode - Initialize host controller PATA DMA timings + * @ap: Port whose timings we are configuring + * @adev: Device to program + * + * Set UDMA/MWDMA mode for device, in host controller PCI config space. + * + * LOCKING: + * None (inherited from caller). + */ + +static void efar_set_dmamode (struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *dev = to_pci_dev(ap->host_set->dev); + u8 master_port = ap->hard_port_no ? 0x42 : 0x40; + u16 master_data; + u8 speed = adev->dma_mode; + int devid = adev->devno + 2 * ap->hard_port_no; + u8 udma_enable; + + static const /* ISP RTC */ + u8 timings[][2] = { { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + pci_read_config_word(dev, master_port, &master_data); + pci_read_config_byte(dev, 0x48, &udma_enable); + + if (speed >= XFER_UDMA_0) { + unsigned int udma = adev->dma_mode - XFER_UDMA_0; + u16 udma_timing; + + udma_enable |= (1 << devid); + + /* Load the UDMA mode number */ + pci_read_config_word(dev, 0x4A, &udma_timing); + udma_timing &= ~(7 << (4 * devid)); + udma_timing |= udma << (4 * devid); + pci_write_config_word(dev, 0x4A, udma_timing); + } else { + /* + * MWDMA is driven by the PIO timings. We must also enable + * IORDY unconditionally along with TIME1. PPE has already + * been set when the PIO timing was set. + */ + unsigned int mwdma = adev->dma_mode - XFER_MW_DMA_0; + unsigned int control; + u8 slave_data; + const unsigned int needed_pio[3] = { + XFER_PIO_0, XFER_PIO_3, XFER_PIO_4 + }; + int pio = needed_pio[mwdma] - XFER_PIO_0; + + control = 3; /* IORDY|TIME1 */ + + /* If the drive MWDMA is faster than it can do PIO then + we must force PIO into PIO0 */ + + if (adev->pio_mode < needed_pio[mwdma]) + /* Enable DMA timing only */ + control |= 8; /* PIO cycles in PIO0 */ + + if (adev->devno) { /* Slave */ + master_data &= 0xFF4F; /* Mask out IORDY|TIME1|DMAONLY */ + master_data |= control << 4; + pci_read_config_byte(dev, 0x44, &slave_data); + slave_data &= (0x0F + 0xE1 * ap->hard_port_no); + /* Load the matching timing */ + slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << (ap->hard_port_no ? 4 : 0); + pci_write_config_byte(dev, 0x44, slave_data); + } else { /* Master */ + master_data &= 0xCCF4; /* Mask out IORDY|TIME1|DMAONLY + and master timing bits */ + master_data |= control; + master_data |= + (timings[pio][0] << 12) | + (timings[pio][1] << 8); + } + udma_enable &= ~(1 << devid); + pci_write_config_word(dev, master_port, master_data); + } + pci_write_config_byte(dev, 0x48, udma_enable); +} + +static struct scsi_host_template efar_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static const struct ata_port_operations efar_ops = { + .port_disable = ata_port_disable, + .set_piomode = efar_set_piomode, + .set_dmamode = efar_set_dmamode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = efar_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .data_xfer = ata_pio_data_xfer, + + .eng_timeout = ata_eng_timeout, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop, +}; + + +/** + * efar_init_one - Register EFAR ATA PCI device with kernel services + * @pdev: PCI device to register + * @ent: Entry in efar_pci_tbl matching with @pdev + * + * Called from kernel PCI layer. + * + * LOCKING: + * Inherited from PCI layer (may sleep). + * + * RETURNS: + * Zero on success, or -ERRNO value. + */ + +static int efar_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + static struct ata_port_info info = { + .sht = &efar_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma1-2 */ + .udma_mask = 0x0f, /* UDMA 66 */ + .port_ops = &efar_ops, + }; + static struct ata_port_info *port_info[2] = { &info, &info }; + + if (!printed_version++) + dev_printk(KERN_DEBUG, &pdev->dev, + "version " DRV_VERSION "\n"); + + return ata_pci_init_one(pdev, port_info, 2); +} + +static const struct pci_device_id efar_pci_tbl[] = { + { 0x1055, 0x9130, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* terminate list */ +}; + +static struct pci_driver efar_pci_driver = { + .name = DRV_NAME, + .id_table = efar_pci_tbl, + .probe = efar_init_one, + .remove = ata_pci_remove_one, +}; + +static int __init efar_init(void) +{ + return pci_register_driver(&efar_pci_driver); +} + +static void __exit efar_exit(void) +{ + pci_unregister_driver(&efar_pci_driver); +} + + +module_init(efar_init); +module_exit(efar_exit); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("SCSI low-level driver for EFAR PIIX clones"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, efar_pci_tbl); +MODULE_VERSION(DRV_VERSION); + Index: linux-2.6.16/drivers/scsi/pata_hpt34x.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_hpt34x.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "hpt34x" +#define DRV_VERSION "0.1" + +/** + * hpt34x_bus_reset - reset the hpt34x bus + * @ap: ATA port to reset + * + * Perform the housekeeping when doing an ATA bus reeset. We just + * need to force the cable type. + */ + +static void hpt34x_phy_reset(struct ata_port *ap) +{ + ap->cbl = ATA_CBL_PATA40; + ata_bus_reset(ap); + ata_port_probe(ap); +} + +/** + * hpt34x_set_piomode - PIO setup + * @ap: ATA interface + * @adev: device on the interface + * + * Set our PIO requirements. This is fairly simple on the HPT34x as + * all we have to do is clear the MWDMA and UDMA bits then load the + * mode number. + */ + +static void hpt34x_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u32 r1, r2; + int dn = 2 * ap->hard_port_no + adev->devno; + + pci_read_config_dword(pdev, 0x44, &r1); + pci_read_config_dword(pdev, 0x48, &r2); + /* Load the PIO timing number */ + r1 &= ~(7 << (3 * dn)); + r1 |= (adev->pio_mode - XFER_PIO_0) << (3 * dn); + r2 &= ~(0x11 << dn); /* Clear MWDMA and UDMA bits */ + + pci_write_config_dword(pdev, 0x44, r1); + pci_write_config_dword(pdev, 0x48, r2); +} + +/** + * hpt34x_set_dmamode - DMA timing setup + * @ap: ATA interface + * @adev: Device being configured + * + * Set up the channel for MWDMA or UDMA modes. Much the same as with + * PIO, load the mode number and then set MWDMA or UDMA flag. + */ + +static void hpt34x_set_dmamode(struct ata_port *ap, struct ata_device *adev) +{ + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u32 r1, r2; + int dn = 2 * ap->hard_port_no + adev->devno; + int mode_num = adev->dma_mode & 0x0F; + + pci_read_config_dword(pdev, 0x44, &r1); + pci_read_config_dword(pdev, 0x48, &r2); + /* Load the timing number */ + r1 &= ~(7 << (3 * dn)); + r1 |= (mode_num << (3 * dn)); + r2 &= ~(0x11 << dn); /* Clear MWDMA and UDMA bits */ + + if (adev->dma_mode >= XFER_UDMA_0) + r2 |= 0x01 << dn; /* Ultra mode */ + else + r2 |= 0x10 << dn; /* MWDMA */ + + pci_write_config_dword(pdev, 0x44, r1); + pci_write_config_dword(pdev, 0x48, r2); +} + +static struct scsi_host_template hpt34x_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +// .ordered_flush = 1, +}; + +static struct ata_port_operations hpt34x_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = hpt34x_set_piomode, + .set_dmamode = hpt34x_set_dmamode, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = hpt34x_phy_reset, + + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .eng_timeout = ata_eng_timeout, + .data_xfer = ata_pio_data_xfer, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop +}; + +/** + * hpt34x_init_one - Initialise an HPT343/363 + * @dev: PCI device + * @id: Entry in match table + * + * Perform basic initialisation. The chip has a quirk that it won't + * function unless it is at XX00. The old ATA driver touched this up + * but we leave it for pci quirks to do properly. + */ + +static int hpt34x_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + static struct ata_port_info info = { + .sht = &hpt34x_sht, + .host_flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x07, + .port_ops = &hpt34x_port_ops + }; + static struct ata_port_info *port_info[2] = { &info, &info }; + u16 cmd; + + /* Initialize the board */ + pci_write_config_word(dev, 0x80, 0x00); + /* Check if it is a 343 or a 363. 363 has COMMAND_MEMORY set */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_MEMORY) + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0); + else + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); + + /* Now kick off ATA set up */ + return ata_pci_init_one(dev, port_info, 2); +} + +static struct pci_device_id hpt34x[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343), }, + { 0, }, +}; + +static struct pci_driver hpt34x_pci_driver = { + .name = DRV_NAME, + .id_table = hpt34x, + .probe = hpt34x_init_one, + .remove = ata_pci_remove_one +}; + +static int __init hpt34x_init(void) +{ + return pci_register_driver(&hpt34x_pci_driver); +} + + +static void __exit hpt34x_exit(void) +{ + pci_unregister_driver(&hpt34x_pci_driver); +} + + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for the Highpoint HPT343/363"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, hpt34x); +MODULE_VERSION(DRV_VERSION); + +module_init(hpt34x_init); +module_exit(hpt34x_exit); Index: linux-2.6.16/drivers/scsi/pata_hpt366.c =================================================================== --- /dev/null +++ linux-2.6.16/drivers/scsi/pata_hpt366.c @@ -0,0 +1,479 @@ +/* + * Libata driver for the highpoint 366 and 368 UDMA66 ATA controllers. + * + * This driver is heavily based upon: + * + * linux/drivers/ide/pci/hpt366.c Version 0.36 April 25, 2003 + * + * Copyright (C) 1999-2003 Andre Hedrick + * Portions Copyright (C) 2001 Sun Microsystems, Inc. + * Portions Copyright (C) 2003 Red Hat Inc + * + * + * TODO + * Maybe PLL mode + * Look into engine reset on timeout errors. Should not be + * required. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "hpt36x" +#define DRV_VERSION "0.2" + +struct hpt_clock { + u8 xfer_speed; + u32 timing; +}; + +/* key for bus clock timings + * bit + * 0:3 data_high_time. inactive time of DIOW_/DIOR_ for PIO and MW + * DMA. cycles = value + 1 + * 4:8 data_low_time. active time of DIOW_/DIOR_ for PIO and MW + * DMA. cycles = value + 1 + * 9:12 cmd_high_time