--- linux-2.6.5/kernel/printk.c~heh 2004-04-03 22:38:24.000000000 -0500 +++ linux-2.6.5/kernel/printk.c 2004-04-30 20:57:36.000000000 -0400 @@ -832,3 +832,25 @@ printk_ratelimit_burst); } EXPORT_SYMBOL(printk_ratelimit); + +#include + +static void +show_msg_info(int key, struct pt_regs *regs, struct tty_struct *tty) +{ + call_console_drivers(log_end - logged_chars, log_end); +} + +static struct sysrq_key_op msg_info_op = { + .handler = show_msg_info, + .help_msg = "Dumpmsgs", + .action_msg = "Kernel Messages", +}; + +static int __init dbg_init(void) +{ + register_sysrq_key('d', &msg_info_op); + return 0; +} + +__initcall(dbg_init); --- linux-2.6.5/kernel/resource.c~heh 2004-04-03 22:37:36.000000000 -0500 +++ linux-2.6.5/kernel/resource.c 2004-04-30 20:57:36.000000000 -0400 @@ -179,6 +179,8 @@ { struct resource *tmp, **p; + BUG_ON(old->child); + p = &old->parent->child; for (;;) { tmp = *p; @@ -409,6 +411,47 @@ EXPORT_SYMBOL(adjust_resource); /* + * Given an existing resource, change its start and size to match the + * arguments. Returns -EBUSY if it can't fit. Existing children of + * the resource are assumed to be immutable. + */ +int reallocate_resource(struct resource *res, unsigned long start, unsigned long size) +{ + struct resource *tmp, *parent = res->parent; + unsigned long end = start + size - 1; + int result = -EBUSY; + + write_lock(&resource_lock); + + if ((start < parent->start) || (end > parent->end)) + goto out; + + for (tmp = res->child; tmp; tmp = tmp->sibling) { + if ((tmp->start < start) || (tmp->end > end)) + goto out; + } + + if (res->sibling && (res->sibling->start <= end)) + goto out; + + tmp = parent->child; + if (tmp != res) { + while (tmp->sibling != res) + tmp = tmp->sibling; + if (start <= tmp->end) + goto out; + } + + res->start = start; + res->end = end; + result = 0; + + out: + write_unlock(&resource_lock); + return result; +} + +/* * This is compatibility stuff for IO resources. * * Note how this, unlike the above, knows about --- linux-2.6.5/include/asm-arm/mach/irq.h~heh 2004-04-03 22:36:54.000000000 -0500 +++ linux-2.6.5/include/asm-arm/mach/irq.h 2004-04-30 20:57:36.000000000 -0400 @@ -14,6 +14,19 @@ struct pt_regs; struct seq_file; +/* + * Architectures are expected to define NR_IRQ_DEVICES and + * NR_IRQ_DEVICE_SHIFT if they wish to use dynamic IRQs. + */ +#ifndef NR_IRQ_DEVICES +#define NR_IRQ_DEVICES 1 +#endif +#define NR_IRQ_PER_DEVICE (1 << (NR_IRQ_DEVICE_SHIFT - 1)) + +#define IRQ_DEVICE(i) ((i) >> NR_IRQ_DEVICE_SHIFT) +#define IRQ_INDEX(i) ((i) & (NR_IRQ_PER_GROUP - 1)) +#define TO_IRQ(g,i) (((g) << NR_IRQ_DEVICE_SHIFT) + (i)) + typedef void (*irq_handler_t)(unsigned int, struct irqdesc *, struct pt_regs *); typedef void (*irq_control_t)(unsigned int); --- linux-2.6.5/include/asm-arm/arch-pxa/uncompress.h~heh 2004-04-03 22:36:17.000000000 -0500 +++ linux-2.6.5/include/asm-arm/arch-pxa/uncompress.h 2004-04-30 20:57:36.000000000 -0400 @@ -12,6 +12,7 @@ #define FFUART ((volatile unsigned long *)0x40100000) #define BTUART ((volatile unsigned long *)0x40200000) #define STUART ((volatile unsigned long *)0x40700000) +#define HWUART ((volatile unsigned long *)0x41600000) #define UART FFUART --- linux-2.6.5/include/asm-arm/arch-pxa/dma.h~heh 2004-04-03 22:38:18.000000000 -0500 +++ linux-2.6.5/include/asm-arm/arch-pxa/dma.h 2004-04-30 20:57:36.000000000 -0400 @@ -22,11 +22,11 @@ * Note: this structure must always be aligned to a 16-byte boundary. */ -typedef struct { - volatile u32 ddadr; /* Points to the next descriptor + flags */ - volatile u32 dsadr; /* DSADR value for the current transfer */ - volatile u32 dtadr; /* DTADR value for the current transfer */ - volatile u32 dcmd; /* DCMD value for the current transfer */ +typedef struct pxa_dma_desc { + u32 ddadr; /* Points to the next descriptor + flags */ + u32 dsadr; /* DSADR value for the current transfer */ + u32 dtadr; /* DTADR value for the current transfer */ + u32 dcmd; /* DCMD value for the current transfer */ } pxa_dma_desc; /* --- linux-2.6.5/include/asm-arm/arch-pxa/serial.h~heh 2004-04-03 22:37:06.000000000 -0500 +++ linux-2.6.5/include/asm-arm/arch-pxa/serial.h 2004-04-30 20:57:36.000000000 -0400 @@ -43,6 +43,15 @@ io_type: SERIAL_IO_MEM, \ irq: IRQ_BTUART, \ flags: STD_COM_FLAGS, \ + }, { \ + type: PORT_PXA, \ + xmit_fifo_size: 64, \ + baud_base: BAUD_BASE, \ + iomem_base: &HWUART, \ + iomem_reg_shift: 2, \ + io_type: SERIAL_IO_MEM, \ + irq: IRQ_HWUART, \ + flags: STD_COM_FLAGS, \ } #define EXTRA_SERIAL_PORT_DEFNS --- linux-2.6.5/include/asm-arm/arch-pxa/pxa-regs.h~heh 2004-04-03 22:37:36.000000000 -0500 +++ linux-2.6.5/include/asm-arm/arch-pxa/pxa-regs.h 2004-04-30 20:57:36.000000000 -0400 @@ -124,26 +124,26 @@ #define DRCMR12 __REG(0x40000130) /* Request to Channel Map Register for AC97 audio transmit Request */ #define DRCMR13 __REG(0x40000134) /* Request to Channel Map Register for SSP receive Request */ #define DRCMR14 __REG(0x40000138) /* Request to Channel Map Register for SSP transmit Request */ -#define DRCMR15 __REG(0x4000013c) /* Reserved */ -#define DRCMR16 __REG(0x40000140) /* Reserved */ +#define DRCMR15 __REG(0x4000013c) /* Request to Channel Map Register for NSSP receive Request */ +#define DRCMR16 __REG(0x40000140) /* Request to Channel Map Register for NSSP transmit Request */ #define DRCMR17 __REG(0x40000144) /* Request to Channel Map Register for ICP receive Request */ #define DRCMR18 __REG(0x40000148) /* Request to Channel Map Register for ICP transmit Request */ #define DRCMR19 __REG(0x4000014c) /* Request to Channel Map Register for STUART receive Request */ #define DRCMR20 __REG(0x40000150) /* Request to Channel Map Register for STUART transmit Request */ #define DRCMR21 __REG(0x40000154) /* Request to Channel Map Register for MMC receive Request */ #define DRCMR22 __REG(0x40000158) /* Request to Channel Map Register for MMC transmit Request */ -#define DRCMR23 __REG(0x4000015c) /* Reserved */ -#define DRCMR24 __REG(0x40000160) /* Reserved */ +#define DRCMR23 __REG(0x4000015c) /* Request to Channel Map Register for ASSP receive Request */ +#define DRCMR24 __REG(0x40000160) /* Request to Channel Map Register for ASSP transmit Request */ #define DRCMR25 __REG(0x40000164) /* Request to Channel Map Register for USB endpoint 1 Request */ #define DRCMR26 __REG(0x40000168) /* Request to Channel Map Register for USB endpoint 2 Request */ #define DRCMR27 __REG(0x4000016C) /* Request to Channel Map Register for USB endpoint 3 Request */ #define DRCMR28 __REG(0x40000170) /* Request to Channel Map Register for USB endpoint 4 Request */ -#define DRCMR29 __REG(0x40000174) /* Reserved */ +#define DRCMR29 __REG(0x40000174) /* Request to Channel Map Register for HWUART receive Request */ #define DRCMR30 __REG(0x40000178) /* Request to Channel Map Register for USB endpoint 6 Request */ #define DRCMR31 __REG(0x4000017C) /* Request to Channel Map Register for USB endpoint 7 Request */ #define DRCMR32 __REG(0x40000180) /* Request to Channel Map Register for USB endpoint 8 Request */ #define DRCMR33 __REG(0x40000184) /* Request to Channel Map Register for USB endpoint 9 Request */ -#define DRCMR34 __REG(0x40000188) /* Reserved */ +#define DRCMR34 __REG(0x40000188) /* Request to Channel Map Register for HWUART transmit Request */ #define DRCMR35 __REG(0x4000018C) /* Request to Channel Map Register for USB endpoint 11 Request */ #define DRCMR36 __REG(0x40000190) /* Request to Channel Map Register for USB endpoint 12 Request */ #define DRCMR37 __REG(0x40000194) /* Request to Channel Map Register for USB endpoint 13 Request */ @@ -163,12 +163,16 @@ #define DRCMRTXPCDR DRCMR12 #define DRCMRRXSSDR DRCMR13 #define DRCMRTXSSDR DRCMR14 +#define DRCMRRXNSSPDR DRCMR15 +#define DRCMRTXNSSPDR DRCMR16 #define DRCMRRXICDR DRCMR17 #define DRCMRTXICDR DRCMR18 #define DRCMRRXSTRBR DRCMR19 #define DRCMRTXSTTHR DRCMR20 #define DRCMRRXMMC DRCMR21 #define DRCMRTXMMC DRCMR22 +#define DRCMRRXASSPDR DRCMR23 +#define DRCMRTXASSPDR DRCMR24 #define DRCMR_MAPVLD (1 << 7) /* Map Valid (read / write) */ #define DRCMR_CHLNUM 0x0f /* mask for Channel Number (read / write) */ @@ -303,6 +307,22 @@ #define BTDLL __REG(0x40200000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */ #define BTDLH __REG(0x40200004) /* Divisor Latch High Register (DLAB = 1) (read/write) */ +/* Hardware UART (HWUART) */ +#define HWUART HWRBR +#define HWRBR __REG(0x41600000) /* Receive Buffer Register (read only) */ +#define HWTHR __REG(0x41600000) /* Transmit Holding Register (write only) */ +#define HWIER __REG(0x41600004) /* Interrupt Enable Register (read/write) */ +#define HWIIR __REG(0x41600008) /* Interrupt ID Register (read only) */ +#define HWFCR __REG(0x41600008) /* FIFO Control Register (write only) */ +#define HWLCR __REG(0x4160000C) /* Line Control Register (read/write) */ +#define HWMCR __REG(0x41600010) /* Modem Control Register (read/write) */ +#define HWLSR __REG(0x41600014) /* Line Status Register (read only) */ +#define HWMSR __REG(0x41600018) /* Reserved */ +#define HWSPR __REG(0x4160001C) /* Scratch Pad Register (read/write) */ +#define HWISR __REG(0x41600020) /* Infrared Selection Register (read/write) */ +#define HWDLL __REG(0x41600000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */ +#define HWDLH __REG(0x41600004) /* Divisor Latch High Register (DLAB = 1) (read/write) */ + /* Standard UART (STUART) */ #define STUART STRBR #define STRBR __REG(0x40700000) /* Receive Buffer Register (read only) */ @@ -1078,6 +1098,111 @@ /* + * NSSP Serial Port Registers (Network SSP) + */ + +#define NSSCR0 __REG(0x41400000) /* NSSP Control Register 0 */ +#define NSSCR1 __REG(0x41400004) /* NSSP Control Register 1 */ +#define NSSSR __REG(0x41400008) /* NSSP Status Register */ +#define NSSITR __REG(0x4140000C) /* NSSP Interrupt Test Register */ +#define NSSDR __REG(0x41400010) /* (Write / Read) NSSP Data Write Register/NSSP Data Read Register */ +#define NSSTO __REG(0x41400028) /* NSSP Time Out Register */ +#define NSSPSP __REG(0x4140002C) /* NSSP Programable Serial Port Register*/ + + +/* + * ASSP Serial Port Registers (Audio SSP) + */ + +#define ASSCR0 __REG(0x41500000) /* ASSP Control Register 0 */ +#define ASSCR1 __REG(0x41500004) /* ASSP Control Register 1 */ +#define ASSSR __REG(0x41500008) /* ASSP Status Register */ +#define ASSITR __REG(0x4150000C) /* ASSP Interrupt Test Register */ +#define ASSDR __REG(0x41500010) /* (Write / Read) ASSP Data Write Register/ASSP Data Read Register */ +#define ASSTO __REG(0x41500028) /* ASSP Time Out Register */ +#define ASSPSP __REG(0x4150002C) /* ASSP Programable Serial Port Register*/ + + +/* + * Bit definitions for SSP, NSSP and ASSP registers + * - note that some bits are only available on the NSSP and ASSP + */ + +#define SSCR0_EDSS (1 << 20) /* ext. data size select */ +#define SSCR0_SCR_MASK 0x000fff00 /* [19:8] secrial clock rate */ +#define SSCR0_SCR(x) (((x)<<8) & XSSCR0_SCR_MASK) +#define SSCR0_SSE (1 << 7) /* sync ser port enable */ +#define SSCR0_FRF_MASK 0x00000030 /* [5:4] frame format */ +#define SSCR0_FRF(x) (((x)<<4) & XSSCR0_FRF_MASK) +#define SSCR0_FRF_SPI 0x00000000 /* ser peripheral i/f */ +#define SSCR0_FRF_TISSP 0x00000010 /* TI sync ser port */ +#define SSCR0_FRF_MICROWAVE 0x00000020 /* microwire */ +#define SSCR0_FRF_PSP 0x00000030 /* prog ser protocol */ +#define SSCR0_DSS_MASK 0x0000000f /* data size select */ +#define SSCR0_DSS(x) ((x) & XSSCR0_DSS_MASK) + +#define SSCR1_TTELP (1 << 31) /* tx hi-z later phase */ +#define SSCR1_TTE (1 << 30) /* tx hi-z enable */ +#define SSCR1_EBCEI (1 << 29) /* bit count error int mask */ +#define SSCR1_SCFR (1 << 28) /* slave clock free running */ +#define SSCR1_SCLKDIR (1 << 25) /* ssp clock direction */ +#define SSCR1_SFRMDIR (1 << 24) /* ssp frame direction */ +#define SSCR1_RWOT (1 << 23) /* rx without transmit */ +#define SSCR1_TSRE (1 << 21) /* tx req enable */ +#define SSCR1_RSRE (1 << 20) /* rx req enable */ +#define SSCR1_TINTE (1 << 19) /* timeout int enable */ +#define SSCR1_STRF (1 << 15) /* select fifo for efwr */ +#define SSCR1_EFWR (1 << 14) /* fifo write/read enable */ +#define SSCR1_RFT_MASK 0x00003c00 /* [13:10] rx fifo threshold */ +#define SSCR1_RFT(x) (((x)<<10) & XSSCR1_RFT_MASK) +#define SSCR1_TFT_MASK 0x000003c0 /* [9:6] tx fifo threshold */ +#define SSCR1_TFT(x) (((x)<<6) & XSSCR1_TFT_MASK) +#define SSCR1_MWDS (1 << 5) /* microwire tx data size */ +#define SSCR1_SPH (1 << 4) /* SPI SSPSCLK phase */ +#define SSCR1_SPO (1 << 3) /* motorolla SPI polarity */ +#define SSCR1_LBM (1 << 2) /* loop-back mode */ +#define SSCR1_TIE (1 << 1) /* tx fifo int enable */ +#define SSCR1_RIE (1 << 0) /* rx fifo int enable */ + +#define SSPSP_DMYSTOP_MASK 0x01800000 /* [24:23] dummy stop */ +#define SSPSP_DMYSTOP(x) (((x)<<23) & XSSPSP_DMYSTOP_MASK) +#define SSPSP_SFRMWDTH_MASK 0x007f0000 /* [22:16] serial frame width */ +#define SSPSP_SFRMWDTH(x) (((x)<<16) & XSSPSP_SFRMWDTH_MASK) +#define SSPSP_SFRMDLY_MASK 0x0000fe00 /* [15:9] serial frame delay */ +#define SSPSP_SFRMDLY(x) (((x)<<9) & XSSPSP_SFRMDLY_MASK) +#define SSPSP_DMYSTRT_MASK 0x00000180 /* [8:7] dummy start */ +#define SSPSP_DMYSTRT(x) (((x)<<7) & XSSPSP_DMYSTRT_MASK) +#define SSPSP_STRTDLY_MASK 0x00000070 /* [6:4] three-bit start delay */ +#define SSPSP_STRTDLY(x) (((x)<<4) & XSSPSP_STRTDLY_MASK) +#define SSPSP_ETDS (1 << 3) /* end of tx data state */ +#define SSPSP_SFRMP (1 << 2) /* serial frame polarity */ +#define SSPSP_SCMODE_MASK 0x00000003 /* bit-rate clock mode */ +#define SSPSP_SCMODE(x) ((x) & XSSPSP_SCMODE_MASK) + +#define SSTO_TIMEOUT_MASK 0x00ffffff /* [23:0] timeout */ +#define SSTO_TIMEOUT(x) ((x) & XSSTO_TIMEOUT_MASK) + +#define SSITR_TROR (1 << 7) /* test rx fifo overrun */ +#define SSITR_TRFS (1 << 6) /* test rx fifo serv req */ +#define SSITR_TTFS (1 << 5) /* test tx fifo serv req */ + +#define SSSR_BCE (1 << 23) /* bit count error */ +#define SSSR_CSS (1 << 22) /* clock sync stat */ +#define SSSR_TUR (1 << 21) /* tx fifo underrun */ +#define SSSR_TINT (1 << 19) /* rx timeout int */ +#define SSSR_RFL_MASK 0x0000f000 /* rx fifo level */ +#define SSSR_RFL(x) (((x)<<16) & XSSSR_RFL_MASK) +#define SSSR_TFL_MASK 0x00000f00 /* tx fifo level */ +#define SSSR_TFL(x) (((x)<<8) & XSSSR_TFL_MASK) +#define SSSR_ROR (1 << 7) /* rx fifo overrun */ +#define SSSR_RFS (1 << 6) /* rx fifo serv request */ +#define SSSR_TFS (1 << 5) /* tx fifo serv req */ +#define SSSR_BSY (1 << 4) /* SSP busy */ +#define SSSR_RNE (1 << 3) /* rx fifo not empty */ +#define SSSR_TNF (1 << 2) /* tx fifo not full */ + + +/* * MultiMediaCard (MMC) controller */ @@ -1122,6 +1247,7 @@ #define CKEN7_BTUART (1 << 7) /* BTUART Unit Clock Enable */ #define CKEN6_FFUART (1 << 6) /* FFUART Unit Clock Enable */ #define CKEN5_STUART (1 << 5) /* STUART Unit Clock Enable */ +#define CKEN4_HWUART (1 << 4) /* HWUART Unit Clock Enable */ #define CKEN3_SSP (1 << 3) /* SSP Unit Clock Enable */ #define CKEN2_AC97 (1 << 2) /* AC97 Unit Clock Enable */ #define CKEN1_PWM1 (1 << 1) /* PWM1 Clock Enable */ --- linux-2.6.5/include/asm-arm/page.h~heh 2004-04-03 22:36:25.000000000 -0500 +++ linux-2.6.5/include/asm-arm/page.h 2004-04-30 20:57:36.000000000 -0400 @@ -92,6 +92,14 @@ # endif #endif +#ifdef CONFIG_CPU_COPY_V6 +# ifdef _USER +# define MULTI_USER 1 +# else +# define _USER v6 +# endif +#endif + #ifndef _USER #error Unknown user operations model #endif --- linux-2.6.5/include/asm-arm/thread_info.h~heh 2004-04-03 22:37:06.000000000 -0500 +++ linux-2.6.5/include/asm-arm/thread_info.h 2004-04-30 20:57:36.000000000 -0400 @@ -108,8 +108,8 @@ #define TI_CPU 20 #define TI_CPU_DOMAIN 24 #define TI_CPU_SAVE 28 -#define TI_USED_MATH 76 -#define TI_FPSTATE (TI_USED_MATH+16) +#define TI_USED_CP 76 +#define TI_FPSTATE (TI_USED_CP+16) #endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/asm-arm/rtc.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,45 @@ +/* + * linux/include/asm-arm/rtc.h + * + * Copyright (C) 2003 Deep Blue Solutions Ltd. + * + * 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. + */ +#ifndef ASMARM_RTC_H +#define ASMARM_RTC_H + +struct module; + +struct rtc_ops { + struct module *owner; + int (*open)(void); + void (*release)(void); + int (*ioctl)(unsigned int, unsigned long); + + void (*read_time)(struct rtc_time *); + int (*set_time)(struct rtc_time *); + void (*read_alarm)(struct rtc_wkalrm *); + int (*set_alarm)(struct rtc_wkalrm *); + int (*proc)(char *buf); +}; + +void rtc_time_to_tm(unsigned long, struct rtc_time *); +int rtc_tm_to_time(struct rtc_time *, unsigned long *); +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 *); +void unregister_rtc(struct rtc_ops *); + +static inline int rtc_periodic_alarm(struct rtc_time *tm) +{ + return (tm->tm_year == -1) || + ((unsigned)tm->tm_mon >= 12) || + ((unsigned)(tm->tm_mday - 1) >= 31) || + ((unsigned)tm->tm_hour > 23) || + ((unsigned)tm->tm_min > 59) || + ((unsigned)tm->tm_sec > 59); +} + +#endif --- linux-2.6.5/include/linux/serial.h~heh 2004-04-03 22:36:26.000000000 -0500 +++ linux-2.6.5/include/linux/serial.h 2004-04-30 20:57:36.000000000 -0400 @@ -81,17 +81,6 @@ #define SERIAL_IO_HUB6 1 #define SERIAL_IO_MEM 2 -struct serial_uart_config { - char *name; - int dfl_xmit_fifo_size; - int flags; -}; - -#define UART_CLEAR_FIFO 0x01 -#define UART_USE_FIFO 0x02 -#define UART_STARTECH 0x04 -#define UART_NATSEMI 0x08 - /* * Definitions for async_struct (and serial_struct) flags field */ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/i2c-pxa.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,76 @@ +/* + * i2c_pxa.h + * + * Copyright (C) 2002 Intrinsyc 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. + * + */ +#ifndef _I2C_PXA_H_ +#define _I2C_PXA_H_ + +struct i2c_algo_pxa_data +{ + void (*write_byte) (u8 value); + u8 (*read_byte) (void); + void (*start) (void); + void (*repeat_start) (void); + void (*stop) (void); + void (*abort) (void); + int (*wait_bus_not_busy) (void); + int (*wait_for_interrupt) (int wait_type); + void (*transfer) (int lastbyte, int receive, int midbyte); + void (*reset) (void); + + int udelay; + int timeout; +}; + +#define DEF_TIMEOUT 3 +#define BUS_ERROR (-EREMOTEIO) +#define ACK_DELAY 0 /* time to delay before checking bus error */ +#define MAX_MESSAGES 65536 /* maximum number of messages to send */ + +#define I2C_SLEEP_TIMEOUT 2 /* time to sleep for on i2c transactions */ +#define I2C_RETRY (-2000) /* an error has occurred retry transmit */ +#define I2C_TRANSMIT 1 +#define I2C_RECEIVE 0 +#define I2C_PXA_SLAVE_ADDR 0x1 /* slave pxa unit address */ +#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) /* ICR initialization value */ +/* ICR initialize bit values +* +* 15. FM 0 (100 Khz operation) +* 14. UR 0 (No unit reset) +* 13. SADIE 0 (Disables the unit from interrupting on slave addresses +* matching its slave address) +* 12. ALDIE 0 (Disables the unit from interrupt when it loses arbitration +* in master mode) +* 11. SSDIE 0 (Disables interrupts from a slave stop detected, in slave mode) +* 10. BEIE 1 (Enable interrupts from detected bus errors, no ACK sent) +* 9. IRFIE 1 (Enable interrupts from full buffer received) +* 8. ITEIE 1 (Enables the I2C unit to interrupt when transmit buffer empty) +* 7. GCD 1 (Disables i2c unit response to general call messages as a slave) +* 6. IUE 0 (Disable unit until we change settings) +* 5. SCLE 1 (Enables the i2c clock output for master mode (drives SCL) +* 4. MA 0 (Only send stop with the ICR stop bit) +* 3. TB 0 (We are not transmitting a byte initially) +* 2. ACKNAK 0 (Send an ACK after the unit receives a byte) +* 1. STOP 0 (Do not send a STOP) +* 0. START 0 (Do not send a START) +* +*/ + +#define I2C_ISR_INIT 0x7FF /* status register init */ +/* I2C status register init values + * + * 10. BED 1 (Clear bus error detected) + * 9. SAD 1 (Clear slave address detected) + * 7. IRF 1 (Clear IDBR Receive Full) + * 6. ITE 1 (Clear IDBR Transmit Empty) + * 5. ALD 1 (Clear Arbitration Loss Detected) + * 4. SSD 1 (Clear Slave Stop Detected) + */ + +#endif --- linux-2.6.5/include/linux/ioport.h~heh 2004-04-03 22:36:26.000000000 -0500 +++ linux-2.6.5/include/linux/ioport.h 2004-04-30 20:57:36.000000000 -0400 @@ -99,6 +99,7 @@ void (*alignf)(void *, struct resource *, unsigned long, unsigned long), void *alignf_data); +extern int reallocate_resource(struct resource *res, unsigned long start, unsigned long size); int adjust_resource(struct resource *res, unsigned long start, unsigned long size); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/switches.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,74 @@ +/* + * linux/include/linux/switches.h + * + * Copyright (C) 2000 John Dorsey + * + * 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. + * + * 23 October 2000 - created. + */ + +#if !defined(_LINUX_SWITCHES_H) +#define _LINUX_SWITCHES_H + +#define SWITCHES_MASK_SIZE (128) + +typedef unsigned long switches_bitfield; + +#define SWITCHES_BITS (sizeof(switches_bitfield) * 8) +#define SWITCHES_NUM_FIELDS (SWITCHES_MASK_SIZE / SWITCHES_BITS) +#define SWITCHES_FIELD_SELECT(i) ((i) / SWITCHES_BITS) +#define SWITCHES_FIELD_MASK(i) ((switches_bitfield)(1 << (i) % \ + SWITCHES_BITS)) + +typedef struct switches_mask_t { + unsigned int count; + switches_bitfield events[SWITCHES_NUM_FIELDS]; + switches_bitfield states[SWITCHES_NUM_FIELDS]; +} switches_mask_t; + +#define SWITCHES_ZERO(m) \ +do { \ + unsigned int sz_i; \ + (m)->count = 0; \ + for(sz_i = 0; sz_i < SWITCHES_NUM_FIELDS; ++sz_i) \ + (m)->events[sz_i] = (m)->states[sz_i] = 0; \ +} while (0) + +/* `s' is the state of the switch, either 0 or non-zero: */ +#define SWITCHES_SET(m, i, s) \ +do { \ + ((m)->events[SWITCHES_FIELD_SELECT((i))] |= \ + SWITCHES_FIELD_MASK((i))); \ + if(s) \ + ((m)->states[SWITCHES_FIELD_SELECT((i))] |= \ + SWITCHES_FIELD_MASK((i))); \ + else \ + ((m)->states[SWITCHES_FIELD_SELECT((i))] &= \ + ~SWITCHES_FIELD_MASK((i))); \ + ++((m)->count); \ +} while (0) + +/* Should only use to clear an event set by SWITCHES_SET(): */ +#define SWITCHES_CLEAR(m, i) \ +do { \ + ((m)->events[SWITCHES_FIELD_SELECT((i))] &= \ + ~SWITCHES_FIELD_MASK((i))); \ + ((m)->states[SWITCHES_FIELD_SELECT((i))] &= \ + ~SWITCHES_FIELD_MASK((i))); \ + --((m)->count); \ +} + +#define SWITCHES_COUNT(m) ((m)->count) + +/* Returns 0 or non-zero: */ +#define SWITCHES_EVENT(m, i) \ +((m)->events[SWITCHES_FIELD_SELECT((i))] & SWITCHES_FIELD_MASK((i))) + +/* Returns 0 or non-zero: */ +#define SWITCHES_STATE(m, i) \ +((m)->states[SWITCHES_FIELD_SELECT((i))] & SWITCHES_FIELD_MASK((i))) + +#endif /* !defined(_LINUX_SWITCHES_H) */ --- linux-2.6.5/include/linux/serial_reg.h~heh 2004-04-03 22:37:38.000000000 -0500 +++ linux-2.6.5/include/linux/serial_reg.h 2004-04-30 20:57:36.000000000 -0400 @@ -121,6 +121,7 @@ /* * These are the definitions for the Modem Control Register */ +#define UART_MCR_AFE 0x20 /* Enable auto-RTS/CTS (TI16C750) */ #define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ #define UART_MCR_OUT2 0x08 /* Out2 complement */ #define UART_MCR_OUT1 0x04 /* Out1 complement */ @@ -156,6 +157,21 @@ #define UART_FCR_PXAR32 0xc0 /* receive FIFO treshold = 32 */ /* + * The Intel PXA2xx chip defines those bits + */ +#define UART_IER_DMAE 0x80 /* DMA Requests Enable */ +#define UART_IER_UUE 0x40 /* UART Unit Enable */ +#define UART_IER_NRZE 0x20 /* NRZ coding Enable */ +#define UART_IER_RTOIE 0x10 /* Receiver Time Out Interrupt Enable */ + +#define UART_IIR_TOD 0x08 /* Character Timeout Indication Detected */ + +#define UART_FCR_PXAR1 0x00 /* receive FIFO treshold = 1 */ +#define UART_FCR_PXAR8 0x40 /* receive FIFO treshold = 8 */ +#define UART_FCR_PXAR16 0x80 /* receive FIFO treshold = 16 */ +#define UART_FCR_PXAR32 0xc0 /* receive FIFO treshold = 32 */ + +/* * These are the definitions for the Extended Features Register * (StarTech 16C660 only, when DLAB=1) */ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/mmc/card.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,83 @@ +/* + * linux/include/linux/mmc/card.h + * + * 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. + * + * Card driver specific definitions. + */ +#ifndef LINUX_MMC_CARD_H +#define LINUX_MMC_CARD_H + +#include + +struct mmc_cid { + unsigned int manfid; + unsigned int serial; + char prod_name[8]; + unsigned char hwrev; + unsigned char fwrev; + unsigned char month; + unsigned char year; +}; + +struct mmc_csd { + unsigned char mmc_prot; + unsigned short cmdclass; + unsigned short tacc_clks; + unsigned int tacc_ns; + unsigned int max_dtr; + unsigned int read_blkbits; + unsigned int capacity; +}; + +struct mmc_host; + +/* + * MMC device + */ +struct mmc_card { + struct list_head node; /* node in hosts devices list */ + struct mmc_host *host; /* the host this device belongs to */ + struct device dev; /* the device */ + unsigned int rca; /* relative card address of device */ + unsigned int state; /* (our) card state */ +#define MMC_STATE_PRESENT (1<<0) +#define MMC_STATE_DEAD (1<<1) + struct mmc_cid cid; /* card identification */ + struct mmc_csd csd; /* card specific */ +}; + +#define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD) +#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) + +#define mmc_card_name(c) ((c)->cid.prod_name) +#define mmc_card_id(c) ((c)->dev.bus_id) + +#define mmc_list_to_card(l) container_of(l, struct mmc_card, node) +#define mmc_get_drvdata(c) dev_get_drvdata(&(c)->dev) +#define mmc_set_drvdata(c,d) dev_set_drvdata(&(c)->dev, d) + +/* + * MMC device driver (e.g., Flash card, I/O card...) + */ +struct mmc_driver { + struct device_driver drv; + int (*probe)(struct mmc_card *); + void (*remove)(struct mmc_card *); + int (*suspend)(struct mmc_card *, u32); + int (*resume)(struct mmc_card *); +}; + +extern int mmc_register_driver(struct mmc_driver *); +extern void mmc_unregister_driver(struct mmc_driver *); + +static inline int mmc_card_claim_host(struct mmc_card *card) +{ + return __mmc_claim_host(card->host, card); +} + +#define mmc_card_release_host(c) mmc_release_host((c)->host) + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/mmc/mmc.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,88 @@ +/* + * linux/include/linux/mmc/mmc.h + * + * 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. + */ +#ifndef MMC_H +#define MMC_H + +#include +#include +#include + +struct request; +struct mmc_data; +struct mmc_request; + +struct mmc_command { + u32 opcode; + u32 arg; + u32 resp[4]; + unsigned int flags; /* expected response type */ +#define MMC_RSP_NONE (0 << 0) +#define MMC_RSP_SHORT (1 << 0) +#define MMC_RSP_LONG (2 << 0) +#define MMC_RSP_MASK (3 << 0) +#define MMC_RSP_CRC (1 << 3) /* expect valid crc */ +#define MMC_RSP_BUSY (1 << 4) /* card may send busy */ + + unsigned int retries; /* max number of retries */ + unsigned int error; /* command error */ + +#define MMC_ERR_NONE 0 +#define MMC_ERR_TIMEOUT 1 +#define MMC_ERR_BADCRC 2 +#define MMC_ERR_FIFO 3 +#define MMC_ERR_FAILED 4 +#define MMC_ERR_INVALID 5 + + struct mmc_data *data; /* data segment associated with cmd */ + struct mmc_request *req; /* assoicated request */ +}; + +struct mmc_data { + unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ + unsigned int timeout_clks; /* data timeout (in clocks) */ + unsigned int blksz_bits; /* data block size */ + unsigned int blocks; /* number of blocks */ + struct request *rq; /* request structure */ + unsigned int error; /* data error */ + unsigned int flags; + +#define MMC_DATA_WRITE (1 << 8) +#define MMC_DATA_READ (1 << 9) +#define MMC_DATA_STREAM (1 << 10) + + unsigned int bytes_xfered; + + struct mmc_command *stop; /* stop command */ + struct mmc_request *req; /* assoicated request */ +}; + +struct mmc_request { + struct mmc_command *cmd; + struct mmc_data *data; + struct mmc_command *stop; + + void *done_data; /* completion data */ + void (*done)(struct mmc_request *);/* completion function */ +}; + +struct mmc_host; +struct mmc_card; + +extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *); +extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); + +extern int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card); + +static inline void mmc_claim_host(struct mmc_host *host) +{ + __mmc_claim_host(host, (struct mmc_card *)-1); +} + +extern void mmc_release_host(struct mmc_host *host); + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/mmc/host.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,67 @@ +/* + * linux/include/linux/mmc/host.h + * + * 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. + * + * Host driver specific definitions. + */ +#ifndef LINUX_MMC_HOST_H +#define LINUX_MMC_HOST_H + +#include + +struct mmc_ios { + unsigned int clock; /* clock rate */ + unsigned short vdd; /* supply (units of 10mV) */ + unsigned char bus_mode; /* command output mode */ + +#define MMC_BUSMODE_OPENDRAIN 1 +#define MMC_BUSMODE_PUSHPULL 2 + + unsigned char power_mode; /* power supply mode */ + +#define MMC_POWER_OFF 0 +#define MMC_POWER_UP 1 +#define MMC_POWER_ON 2 +}; + +struct mmc_host_ops { + void (*request)(struct mmc_host *host, struct mmc_request *req); + void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); +}; + +struct mmc_card; + +struct mmc_host { + struct device *dev; + struct mmc_host_ops *ops; + unsigned int f_min; + unsigned int f_max; + u32 ocr_avail; + + /* private data */ + unsigned int host_num; /* host number */ + struct mmc_ios ios; /* current io bus settings */ + u32 ocr; /* the current OCR setting */ + + struct list_head cards; /* devices attached to this host */ + + wait_queue_head_t wq; + spinlock_t lock; /* card_busy lock */ + struct mmc_card *card_busy; /* the MMC card claiming host */ + struct mmc_card *card_selected; /* the selected MMC card */ +}; + +extern int mmc_init_host(struct mmc_host *); +extern int mmc_add_host(struct mmc_host *); +extern void mmc_remove_host(struct mmc_host *); +extern int mmc_suspend_host(struct mmc_host *, u32); +extern int mmc_resume_host(struct mmc_host *); + +extern void mmc_detect_change(struct mmc_host *); +extern void mmc_request_done(struct mmc_host *, struct mmc_request *); + +#endif + --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/mmc/protocol.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,203 @@ +/* + * Header for MultiMediaCard (MMC) + * + * Copyright 2002 Hewlett-Packard Company + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Many thanks to Alessandro Rubini and Jonathan Corbet! + * + * Based strongly on code by: + * + * Author: Yong-iL Joh + * Date : $Date: 2002/06/18 12:37:30 $ + * + * Author: Andrew Christian + * 15 May 2002 + */ + +#ifndef MMC_MMC_PROTOCOL_H +#define MMC_MMC_PROTOCOL_H + +/* Standard MMC commands (3.1) type argument response */ + /* class 1 */ +#define MMC_GO_IDLE_STATE 0 /* bc */ +#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ +#define MMC_ALL_SEND_CID 2 /* bcr R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ +#define MMC_SET_DSR 4 /* bc [31:16] RCA */ +#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ +#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */ +#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ +#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ +#define MMC_STOP_TRANSMISSION 12 /* ac R1b */ +#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ +#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ + + /* class 2 */ +#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ +#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ +#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ + + /* class 3 */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + + /* class 4 */ +#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */ +#define MMC_PROGRAM_CID 26 /* adtc R1 */ +#define MMC_PROGRAM_CSD 27 /* adtc R1 */ + + /* class 6 */ +#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */ +#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */ +#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */ + + /* class 5 */ +#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */ +#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */ +#define MMC_ERASE 37 /* ac R1b */ + + /* class 9 */ +#define MMC_FAST_IO 39 /* ac R4 */ +#define MMC_GO_IRQ_STATE 40 /* bcr R5 */ + + /* class 7 */ +#define MMC_LOCK_UNLOCK 42 /* adtc R1b */ + + /* class 8 */ +#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ +#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1b */ + +/* + MMC status in R1 + Type + e : error bit + s : status bit + r : detected and set for the actual command response + x : detected and set during command execution. the host must poll + the card by sending status command in order to read these bits. + Clear condition + a : according to the card state + b : always related to the previous command. Reception of + a valid command will clear it (with a delay of one command) + c : clear by read + */ + +#define R1_OUT_OF_RANGE (1 << 31) /* er, c */ +#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ +#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define R1_ERASE_PARAM (1 << 27) /* ex, c */ +#define R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ +#define R1_CC_ERROR (1 << 20) /* erx, c */ +#define R1_ERROR (1 << 19) /* erx, c */ +#define R1_UNDERRUN (1 << 18) /* ex, c */ +#define R1_OVERRUN (1 << 17) /* ex, c */ +#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ +#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define R1_ERASE_RESET (1 << 13) /* sr, c */ +#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define R1_APP_CMD (1 << 7) /* sr, c */ + +/* These are unpacked versions of the actual responses */ + +struct _mmc_csd { + u8 csd_structure; + u8 spec_vers; + u8 taac; + u8 nsac; + u8 tran_speed; + u16 ccc; + u8 read_bl_len; + u8 read_bl_partial; + u8 write_blk_misalign; + u8 read_blk_misalign; + u8 dsr_imp; + u16 c_size; + u8 vdd_r_curr_min; + u8 vdd_r_curr_max; + u8 vdd_w_curr_min; + u8 vdd_w_curr_max; + u8 c_size_mult; + union { + struct { /* MMC system specification version 3.1 */ + u8 erase_grp_size; + u8 erase_grp_mult; + } v31; + struct { /* MMC system specification version 2.2 */ + u8 sector_size; + u8 erase_grp_size; + } v22; + } erase; + u8 wp_grp_size; + u8 wp_grp_enable; + u8 default_ecc; + u8 r2w_factor; + u8 write_bl_len; + u8 write_bl_partial; + u8 file_format_grp; + u8 copy; + u8 perm_write_protect; + u8 tmp_write_protect; + u8 file_format; + u8 ecc; +}; + +#define MMC_VDD_145_150 0x00000001 /* VDD voltage 1.45 - 1.50 */ +#define MMC_VDD_150_155 0x00000002 /* VDD voltage 1.50 - 1.55 */ +#define MMC_VDD_155_160 0x00000004 /* VDD voltage 1.55 - 1.60 */ +#define MMC_VDD_160_165 0x00000008 /* VDD voltage 1.60 - 1.65 */ +#define MMC_VDD_165_170 0x00000010 /* VDD voltage 1.65 - 1.70 */ +#define MMC_VDD_17_18 0x00000020 /* VDD voltage 1.7 - 1.8 */ +#define MMC_VDD_18_19 0x00000040 /* VDD voltage 1.8 - 1.9 */ +#define MMC_VDD_19_20 0x00000080 /* VDD voltage 1.9 - 2.0 */ +#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ +#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ +#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */ +#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */ +#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */ +#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */ +#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */ +#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */ +#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */ +#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */ +#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */ +#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */ +#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */ +#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */ +#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */ +#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ +#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ + + +/* + * CSD field definitions + */ + +#define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */ +#define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */ +#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 */ + +#define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */ +#define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */ +#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */ +#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 */ + +#endif /* MMC_MMC_PROTOCOL_H */ + --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/l3/algo-bit.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,39 @@ +/* + * linux/include/linux/l3/algo-bit.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * L3 Bus bit-banging algorithm. Derived from i2c-algo-bit.h by + * Simon G. Vogl. + */ +#ifndef L3_ALGO_BIT_H +#define L3_ALGO_BIT_H 1 + +#include + +struct l3_algo_bit_data { + void (*setdat) (void *data, int state); + void (*setclk) (void *data, int state); + void (*setmode)(void *data, int state); + void (*setdir) (void *data, int in); /* set data direction */ + int (*getdat) (void *data); + + void *data; + + /* bus timings (us) */ + int data_hold; + int data_setup; + int clock_high; + int mode_hold; + int mode_setup; + int mode; +}; + +int l3_bit_add_bus(struct l3_adapter *); +int l3_bit_del_bus(struct l3_adapter *); + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/l3/l3.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,95 @@ +/* + * linux/include/linux/l3/l3.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * Derived from i2c.h by Simon G. Vogl + */ +#ifndef L3_H +#define L3_H + +struct l3_msg { + unsigned char addr; /* slave address */ + unsigned char flags; +#define L3_M_RD 0x01 +#define L3_M_NOADDR 0x02 + unsigned short len; /* msg length */ + unsigned char *buf; /* pointer to msg data */ +}; + +#ifdef __KERNEL__ + +#include +#include + +struct l3_adapter; + +struct l3_algorithm { + /* textual description */ + char name[32]; + + /* perform bus transactions */ + int (*xfer)(struct l3_adapter *, struct l3_msg msgs[], int num); +}; + +struct semaphore; + +/* + * l3_adapter is the structure used to identify a physical L3 bus along + * with the access algorithms necessary to access it. + */ +struct l3_adapter { + /* + * This name is used to uniquely identify the adapter. + * It should be the same as the module name. + */ + char name[32]; + + /* + * the algorithm to access the bus + */ + struct l3_algorithm *algo; + + /* + * Algorithm specific data + */ + void *algo_data; + + /* + * This may be NULL, or should point to the module struct + */ + struct module *owner; + + /* + * private data for the adapter + */ + void *data; + + /* + * Our lock. Unlike the i2c layer, we allow this to be used for + * other stuff, like the i2c layer lock. Some people implement + * i2c stuff using the same signals as the l3 bus. + */ + struct semaphore *lock; + + /* + * List of all adapters. + */ + struct list_head adapters; +}; + +extern int l3_add_adapter(struct l3_adapter *); +extern int l3_del_adapter(struct l3_adapter *); +extern void l3_put_adapter(struct l3_adapter *); +extern struct l3_adapter *l3_get_adapter(const char *name); + +extern int l3_write(struct l3_adapter *, int, const char *, int); +extern int l3_read(struct l3_adapter *, int, char *, int); + +#endif + +#endif /* L3_H */ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/include/linux/l3/uda1341.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,61 @@ +/* + * linux/include/linux/l3/uda1341.h + * + * Philips UDA1341 mixer device driver + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + */ + +#define UDA1341_NAME "uda1341" + +struct uda1341_cfg { + unsigned int fs:16; + unsigned int format:3; +}; + +#define FMT_I2S 0 +#define FMT_LSB16 1 +#define FMT_LSB18 2 +#define FMT_LSB20 3 +#define FMT_MSB 4 +#define FMT_LSB16MSB 5 +#define FMT_LSB18MSB 6 +#define FMT_LSB20MSB 7 + +#define L3_UDA1341_CONFIGURE 0x13410001 + +struct l3_gain { + unsigned int left:8; + unsigned int right:8; + unsigned int unused:8; + unsigned int channel:8; +}; + +#define L3_SET_VOLUME 0x13410002 +#define L3_SET_TREBLE 0x13410003 +#define L3_SET_BASS 0x13410004 +#define L3_SET_GAIN 0x13410005 + +struct l3_agc { + unsigned int level:8; + unsigned int enable:1; + unsigned int attack:7; + unsigned int decay:8; + unsigned int channel:8; +}; + +#define L3_INPUT_AGC 0x13410006 + +struct uda1341; + +int uda1341_configure(struct uda1341 *uda, struct uda1341_cfg *conf); +int uda1341_mixer_ctl(struct uda1341 *uda, int cmd, void *arg); +int uda1341_open(struct uda1341 *uda); +void uda1341_close(struct uda1341 *uda); + +struct uda1341 *uda1341_attach(const char *adapter); +void uda1341_detach(struct uda1341 *uda); + --- linux-2.6.5/include/linux/i2c-id.h~heh 2004-04-03 22:36:16.000000000 -0500 +++ linux-2.6.5/include/linux/i2c-id.h 2004-04-30 20:57:36.000000000 -0400 @@ -187,6 +187,7 @@ #define I2C_ALGO_BITHS 0x130000 /* enhanced bit style adapters */ #define I2C_ALGO_OCP_IOP3XX 0x140000 /* XSCALE IOP3XX On-chip I2C alg */ +#define I2C_ALGO_PXA 0x200000 /* Intel PXA I2C algorithm */ #define I2C_ALGO_EXP 0x800000 /* experimental */ #define I2C_ALGO_MASK 0xff0000 /* Mask for algorithms */ --- linux-2.6.5/include/linux/serial_core.h~heh 2004-04-03 22:36:18.000000000 -0500 +++ linux-2.6.5/include/linux/serial_core.h 2004-04-30 20:57:36.000000000 -0400 @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: serial_core.h,v 1.49 2002/07/20 18:06:32 rmk Exp $ + * $Id: serial_core.h,v 1.53 2002/08/02 12:55:08 rmk Exp $ */ /* @@ -159,8 +159,6 @@ spinlock_t lock; /* port lock */ unsigned int iobase; /* in/out[bwl] */ char *membase; /* read/write[bwl] */ - unsigned int irq; /* irq number */ - unsigned int uartclk; /* base uart clock */ unsigned char fifosize; /* tx fifo size */ unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ @@ -172,6 +170,7 @@ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ + struct uart_info *info; /* pointer to parent info */ struct uart_icount icount; /* statistics */ @@ -182,7 +181,6 @@ unsigned int flags; -#define UPF_HUP_NOTIFY (1 << 0) #define UPF_FOURPORT (1 << 1) #define UPF_SAK (1 << 2) #define UPF_SPD_MASK (0x1030) @@ -212,9 +210,12 @@ unsigned int timeout; /* character-based timeout */ unsigned int type; /* port type */ struct uart_ops *ops; + unsigned int uartclk; /* base uart clock */ unsigned int custom_divisor; + unsigned int irq; /* irq number */ unsigned int line; /* port index */ unsigned long mapbase; /* for ioremap */ + struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char unused[3]; }; --- linux-2.6.5/include/linux/vmalloc.h~heh 2004-04-03 22:38:23.000000000 -0500 +++ linux-2.6.5/include/linux/vmalloc.h 2004-04-30 20:57:36.000000000 -0400 @@ -35,6 +35,8 @@ * Lowlevel-APIs (not for driver use!) */ extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags); +extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, + unsigned long start, unsigned long end); extern struct vm_struct *remove_vm_area(void *addr); extern int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages); --- linux-2.6.5/include/linux/mm.h~heh 2004-04-03 22:36:15.000000000 -0500 +++ linux-2.6.5/include/linux/mm.h 2004-04-30 20:57:36.000000000 -0400 @@ -655,5 +655,12 @@ int in_gate_area(struct task_struct *task, unsigned long addr); #endif +#ifndef __arm__ +#define memc_update_addr(x,y,z) +#define memc_update_mm(x) +#define memc_clear(x,y) +#endif + #endif /* __KERNEL__ */ #endif /* _LINUX_MM_H */ + --- linux-2.6.5/init/do_mounts.c~heh 2004-04-03 22:36:56.000000000 -0500 +++ linux-2.6.5/init/do_mounts.c 2004-04-30 20:57:36.000000000 -0400 @@ -391,7 +391,7 @@ root_device_name += 5; } - is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; + is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR || MAJOR(ROOT_DEV) == 31; if (initrd_load()) goto out; --- linux-2.6.5/fs/binfmt_aout.c~heh 2004-04-03 22:36:26.000000000 -0500 +++ linux-2.6.5/fs/binfmt_aout.c 2004-04-30 20:57:36.000000000 -0400 @@ -432,7 +432,11 @@ else send_sig(SIGTRAP, current, 0); } +#ifndef __arm__ return 0; +#else + return regs->ARM_r0; +#endif } static int load_aout_library(struct file *file) @@ -462,8 +466,11 @@ /* For QMAGIC, the starting address is 0x20 into the page. We mask this off to get the starting address for the page */ - - start_addr = ex.a_entry & 0xfffff000; +#ifndef __arm__ + start_addr = ex.a_entry & 0xfffff000; +#else + start_addr = ex.a_entry & 0xffff8000; +#endif if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) { static unsigned long error_time; --- linux-2.6.5/mm/slab.c~heh 2004-04-03 22:37:41.000000000 -0500 +++ linux-2.6.5/mm/slab.c 2004-04-30 20:57:36.000000000 -0400 @@ -2115,12 +2115,12 @@ * * Called with disabled ints. */ -static inline void __cache_free (kmem_cache_t *cachep, void* objp) +static inline void __cache_free (kmem_cache_t *cachep, void* objp, void *caller) { struct array_cache *ac = ac_data(cachep); check_irq_off(); - objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0)); + objp = cache_free_debugcheck(cachep, objp, caller /*__builtin_return_address(0)*/); if (likely(ac->avail < ac->limit)) { STATS_INC_FREEHIT(cachep); @@ -2289,7 +2289,7 @@ unsigned long flags; local_irq_save(flags); - __cache_free(cachep, objp); + __cache_free(cachep, objp, __builtin_return_address(0)); local_irq_restore(flags); } @@ -2312,7 +2312,7 @@ local_irq_save(flags); kfree_debugcheck(objp); c = GET_PAGE_CACHE(virt_to_page(objp)); - __cache_free(c, (void*)objp); + __cache_free(c, (void*)objp, __builtin_return_address(0)); local_irq_restore(flags); } --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/Documentation/l3/structure 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,36 @@ +L3 Bus Driver +------------- + +The structure of the driver is as follows: + + +----------+ +----------+ +----------+ + | client 1 | | client 2 | | client 3 | + +-----^----+ +----^-----+ +----^-----+ + | | | + +-----v--------------v---------------v-----+ + | | + +-----^-------+ +-------^-----+ + | | core | | + +-----v----+ | | +----v-----+ + | device | | | | device | + | driver 1 | | | | driver 2 | + +-----^----+ | | +----^-----+ + | | services | | + +-----v-------+ +-------v-----+ + | | + +-----------------^----^-------------------+ + | | + | +-v---------+ + | | algorithm | + | | driver | + | +-v---------+ + | | + +-v----v-+ + | bus | + | driver | + +--------+ + +Clients talk to the core to attach device drivers and bus adapters, and +to instruct device drivers to perform actions. Device drivers then talk +to the core to perform L3 bus transactions via the algorithm driver and +ultimately bus driver. --- linux-2.6.5/arch/arm/kernel/debug.S~heh 2004-04-03 22:36:55.000000000 -0500 +++ linux-2.6.5/arch/arm/kernel/debug.S 2004-04-30 20:57:36.000000000 -0400 @@ -192,6 +192,20 @@ @ if all ports are inactive, then there is nothing we can do moveq pc, lr + ldr r1, [\rx, #UTCR2] + teq r1, #5 + movne r1, #0 + strne r1, [\rx, #UTCR3] + movne r1, #8 + strne r1, [\rx, #UTCR0] + movne r1, #5 + strne r1, [\rx, #UTCR2] + movne r1, #0 + strne r1, [\rx, #UTCR1] + movne r1, #3 + strne r1, [\rx, #UTCR3] + movne r1, #255 + strne r1, [\rx, #UTSR0] .endm .macro senduart,rd,rx @@ -287,7 +301,7 @@ #elif defined(CONFIG_ARCH_INTEGRATOR) -#include +#include .macro addruart,rx mrc p15, 0, \rx, c1, c0 @@ -298,7 +312,7 @@ .endm .macro senduart,rd,rx - strb \rd, [\rx, #AMBA_UARTDR] + strb \rd, [\rx, #UART01x_DR] .endm .macro waituart,rd,rx --- linux-2.6.5/arch/arm/kernel/entry-armv.S~heh 2004-04-03 22:36:54.000000000 -0500 +++ linux-2.6.5/arch/arm/kernel/entry-armv.S 2004-04-30 20:57:36.000000000 -0400 @@ -1181,6 +1181,9 @@ * get out of that mode without clobbering one register. */ vector_FIQ: disable_fiq + mrs r13, spsr + orr r13, r13, #PSR_F_BIT + msr spsr, r13 subs pc, lr, #4 /*============================================================================= --- linux-2.6.5/arch/arm/kernel/irq.c~heh 2004-04-03 22:36:12.000000000 -0500 +++ linux-2.6.5/arch/arm/kernel/irq.c 2004-04-30 20:57:36.000000000 -0400 @@ -47,12 +47,36 @@ #define MAX_IRQ_CNT 100000 static volatile unsigned long irq_err_count; -static spinlock_t irq_controller_lock; static LIST_HEAD(irq_pending); struct irqdesc irq_desc[NR_IRQS]; void (*init_arch_irq)(void) __initdata = NULL; +#if NR_IRQ_DEVICES > 1 +struct irq_device { + spinlock_t lock; + int nr_irqs; + struct irq_desc *irqs; +}; + +static struct irq_device irq_devices[NR_IRQ_DEVICES] = { + [0] = { + .lock = SPIN_LOCK_UNLOCKED, + .nr_irqs = NR_IRQS, + .irqs = irq_desc, + }, +}; +#define IRQ_LOCK(irq) (&irq_devices[IRQ_DEVICE(irq)].lock) +#define IRQ_DESC(irq) (&irq_devices[IRQ_DEVICE(irq)].irqs[IRQ_INDEX(irq)]) +#define IRQ_VALID(irq) (IRQ_DEVICE(irq) < NR_IRQ_DEVICES && \ + IRQ_INDEX(irq) < irq_devices[IRQ_DEVICE(irq)].nr_irqs) +#else +static spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED; +#define IRQ_LOCK(irq) (&irq_controller_lock) +#define IRQ_DESC(irq) (&irq_desc[irq]) +#define IRQ_VALID(irq) ((irq) < NR_IRQS) +#endif + /* * Dummy mask/unmask handler */ @@ -95,13 +119,13 @@ */ void disable_irq(unsigned int irq) { - struct irqdesc *desc = irq_desc + irq; + struct irqdesc *desc = IRQ_DESC(irq); unsigned long flags; - spin_lock_irqsave(&irq_controller_lock, flags); + spin_lock_irqsave(IRQ_LOCK(irq), flags); desc->disable_depth++; list_del_init(&desc->pend); - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } /** @@ -116,10 +140,10 @@ */ void enable_irq(unsigned int irq) { - struct irqdesc *desc = irq_desc + irq; + struct irqdesc *desc = IRQ_DESC(irq); unsigned long flags; - spin_lock_irqsave(&irq_controller_lock, flags); + spin_lock_irqsave(IRQ_LOCK(irq), flags); if (unlikely(!desc->disable_depth)) { printk("enable_irq(%u) unbalanced from %p\n", irq, __builtin_return_address(0)); @@ -140,7 +164,7 @@ list_add(&desc->pend, &irq_pending); } } - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } /* @@ -148,24 +172,24 @@ */ void enable_irq_wake(unsigned int irq) { - struct irqdesc *desc = irq_desc + irq; + struct irqdesc *desc = IRQ_DESC(irq); unsigned long flags; - spin_lock_irqsave(&irq_controller_lock, flags); + spin_lock_irqsave(IRQ_LOCK(irq), flags); if (desc->chip->wake) desc->chip->wake(irq, 1); - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } void disable_irq_wake(unsigned int irq) { - struct irqdesc *desc = irq_desc + irq; + struct irqdesc *desc = IRQ_DESC(irq); unsigned long flags; - spin_lock_irqsave(&irq_controller_lock, flags); + spin_lock_irqsave(IRQ_LOCK(irq), flags); if (desc->chip->wake) desc->chip->wake(irq, 0); - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } int show_interrupts(struct seq_file *p, void *v) @@ -175,8 +199,8 @@ unsigned long flags; if (i < NR_IRQS) { - spin_lock_irqsave(&irq_controller_lock, flags); - action = irq_desc[i].action; + spin_lock_irqsave(IRQ_LOCK(irq), flags); + action = IRQ_DESC(i)->action; if (!action) goto unlock; @@ -187,7 +211,7 @@ seq_putc(p, '\n'); unlock: - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } else if (i == NR_IRQS) { #ifdef CONFIG_ARCH_ACORN show_fiq_list(p, v); @@ -259,7 +283,7 @@ unsigned int status; int retval = 0; - spin_unlock(&irq_controller_lock); + spin_unlock(IRQ_LOCK(irq)); if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); @@ -274,7 +298,7 @@ if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); - spin_lock_irq(&irq_controller_lock); + spin_lock_irq(IRQ_LOCK(irq)); return retval; } @@ -455,7 +479,7 @@ desc = &bad_irq_desc; irq_enter(); - spin_lock(&irq_controller_lock); + spin_lock(IRQ_LOCK(irq)); desc->handle(irq, desc, regs); /* @@ -464,7 +488,7 @@ if (!list_empty(&irq_pending)) do_pending_irqs(regs); - spin_unlock(&irq_controller_lock); + spin_unlock(IRQ_LOCK(irq)); irq_exit(); } @@ -473,7 +497,7 @@ struct irqdesc *desc; unsigned long flags; - if (irq >= NR_IRQS) { + if (!IRQ_VALID(irq)) { printk(KERN_ERR "Trying to install handler for IRQ%d\n", irq); return; } @@ -481,12 +505,12 @@ if (handle == NULL) handle = do_bad_IRQ; - desc = irq_desc + irq; + desc = IRQ_DESC(irq); if (is_chained && desc->chip == &bad_chip) printk(KERN_WARNING "Trying to install chained handler for IRQ%d\n", irq); - spin_lock_irqsave(&irq_controller_lock, flags); + spin_lock_irqsave(IRQ_LOCK(irq), flags); if (handle == do_bad_IRQ) { desc->chip->mask(irq); desc->chip->ack(irq); @@ -499,7 +523,7 @@ desc->disable_depth = 0; desc->chip->unmask(irq); } - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } void set_irq_chip(unsigned int irq, struct irqchip *chip) @@ -507,7 +531,7 @@ struct irqdesc *desc; unsigned long flags; - if (irq >= NR_IRQS) { + if (!IRQ_VALID(irq)) { printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq); return; } @@ -515,10 +539,10 @@ if (chip == NULL) chip = &bad_chip; - desc = irq_desc + irq; - spin_lock_irqsave(&irq_controller_lock, flags); + desc = IRQ_DESC(irq); + spin_lock_irqsave(IRQ_LOCK(irq), flags); desc->chip = chip; - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } int set_irq_type(unsigned int irq, unsigned int type) @@ -527,16 +551,21 @@ unsigned long flags; int ret = -ENXIO; - if (irq >= NR_IRQS) { + if (!IRQ_VALID(irq)) { printk(KERN_ERR "Trying to set irq type for IRQ%d\n", irq); return -ENODEV; } - desc = irq_desc + irq; + desc = IRQ_DESC(irq); + if (!desc->action && desc->handle != do_bad_IRQ) { + printk(KERN_ERR "Setting type of unclaimed IRQ%d from ", irq); + print_symbol("%s\n", (unsigned long)__builtin_return_address(0)); + } + if (desc->chip->type) { - spin_lock_irqsave(&irq_controller_lock, flags); + spin_lock_irqsave(IRQ_LOCK(irq), flags); ret = desc->chip->type(irq, type); - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } return ret; @@ -547,17 +576,17 @@ struct irqdesc *desc; unsigned long flags; - if (irq >= NR_IRQS) { + if (!IRQ_VALID(irq)) { printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq); return; } - desc = irq_desc + irq; - spin_lock_irqsave(&irq_controller_lock, flags); + desc = IRQ_DESC(irq); + spin_lock_irqsave(IRQ_LOCK(irq), flags); desc->valid = (iflags & IRQF_VALID) != 0; desc->probe_ok = (iflags & IRQF_PROBE) != 0; desc->noautoenable = (iflags & IRQF_NOAUTOEN) != 0; - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); } int setup_irq(unsigned int irq, struct irqaction *new) @@ -587,13 +616,13 @@ /* * The following block of code has to be executed atomically */ - desc = irq_desc + irq; - spin_lock_irqsave(&irq_controller_lock, flags); + desc = IRQ_DESC(irq); + spin_lock_irqsave(IRQ_LOCK(irq), flags); p = &desc->action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) { - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); return -EBUSY; } @@ -618,7 +647,7 @@ } } - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); return 0; } @@ -659,7 +688,7 @@ unsigned long retval; struct irqaction *action; - if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler || + if (!IRQ_VALID(irq) || !IRQ_DESC(irq)->valid || !handler || (irq_flags & SA_SHIRQ && !dev_id)) return -EINVAL; @@ -700,14 +729,14 @@ struct irqaction * action, **p; unsigned long flags; - if (irq >= NR_IRQS || !irq_desc[irq].valid) { + if (!IRQ_VALID(irq) || !IRQ_DESC(irq)->valid) { printk(KERN_ERR "Trying to free IRQ%d\n",irq); dump_stack(); return; } - spin_lock_irqsave(&irq_controller_lock, flags); - for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) { + spin_lock_irqsave(IRQ_LOCK(irq), flags); + for (p = &IRQ_DESC(irq)->action; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; @@ -715,7 +744,7 @@ *p = action->next; break; } - spin_unlock_irqrestore(&irq_controller_lock, flags); + spin_unlock_irqrestore(IRQ_LOCK(irq), flags); if (!action) { printk(KERN_ERR "Trying to free free IRQ%d\n",irq); @@ -747,19 +776,18 @@ * first snaffle up any unassigned but * probe-able interrupts */ - spin_lock_irq(&irq_controller_lock); for (i = 0; i < NR_IRQS; i++) { - if (!irq_desc[i].probe_ok || irq_desc[i].action) - continue; - - irq_desc[i].probing = 1; - irq_desc[i].triggered = 0; - if (irq_desc[i].chip->type) - irq_desc[i].chip->type(i, IRQT_PROBE); - irq_desc[i].chip->unmask(i); - irqs += 1; + spin_lock_irq(IRQ_LOCK(i)); + if (irq_desc[i].probe_ok && !irq_desc[i].action) { + irq_desc[i].probing = 1; + irq_desc[i].triggered = 0; + if (irq_desc[i].chip->type) + irq_desc[i].chip->type(i, IRQT_PROBE); + irq_desc[i].chip->unmask(i); + irqs += 1; + } + spin_unlock_irq(IRQ_LOCK(i)); } - spin_unlock_irq(&irq_controller_lock); /* * wait for spurious interrupts to mask themselves out again @@ -770,14 +798,14 @@ /* * now filter out any obviously spurious interrupts */ - spin_lock_irq(&irq_controller_lock); for (i = 0; i < NR_IRQS; i++) { + spin_lock_irq(IRQ_LOCK(i)); if (irq_desc[i].probing && irq_desc[i].triggered) { irq_desc[i].probing = 0; irqs -= 1; } + spin_unlock_irq(IRQ_LOCK(i)); } - spin_unlock_irq(&irq_controller_lock); return irqs; } @@ -788,11 +816,13 @@ { unsigned int mask = 0, i; - spin_lock_irq(&irq_controller_lock); - for (i = 0; i < 16 && i < NR_IRQS; i++) - if (irq_desc[i].probing && irq_desc[i].triggered) + for (i = 0; i < 16 && i < NR_IRQS; i++) { + struct irqdesc *desc = IRQ_DESC(i); + spin_lock_irq(IRQ_LOCK(i)); + if (desc->probing && desc->triggered) mask |= 1 << i; - spin_unlock_irq(&irq_controller_lock); + spin_unlock_irq(IRQ_LOCK(i)); + } up(&probe_sem); @@ -813,23 +843,21 @@ * look at the interrupts, and find exactly one * that we were probing has been triggered */ - spin_lock_irq(&irq_controller_lock); for (i = 0; i < NR_IRQS; i++) { - if (irq_desc[i].probing && - irq_desc[i].triggered) { + struct irqdesc *desc = IRQ_DESC(i); + + spin_lock_irq(IRQ_LOCK(i)); + if (desc->probing && desc->triggered) { if (irq_found != NO_IRQ) { + spin_unlock_irq(IRQ_LOCK(i)); irq_found = NO_IRQ; - goto out; + break; } irq_found = i; } + spin_unlock_irq(IRQ_LOCK(i)); } - if (irq_found == -1) - irq_found = NO_IRQ; -out: - spin_unlock_irq(&irq_controller_lock); - up(&probe_sem); return irq_found; --- linux-2.6.5/arch/arm/kernel/bios32.c~heh 2004-04-03 22:36:56.000000000 -0500 +++ linux-2.6.5/arch/arm/kernel/bios32.c 2004-04-30 20:57:36.000000000 -0400 @@ -565,8 +565,6 @@ if (hw->postinit) hw->postinit(); - pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq); - list_for_each_entry(sys, &hw->buses, node) { struct pci_bus *bus = sys->bus; @@ -581,6 +579,11 @@ pci_bus_assign_resources(bus); /* + * Fixup IRQs. + */ + pci_bus_fixup_irqs(bus, pcibios_swizzle, pcibios_map_irq); + + /* * Tell drivers about devices found. */ pci_bus_add_devices(bus); --- linux-2.6.5/arch/arm/kernel/traps.c~heh 2004-04-03 22:36:57.000000000 -0500 +++ linux-2.6.5/arch/arm/kernel/traps.c 2004-04-30 20:57:36.000000000 -0400 @@ -206,33 +206,43 @@ c_backtrace(fp, 0x10); } -spinlock_t die_lock = SPIN_LOCK_UNLOCKED; - -/* - * This function is protected against re-entrancy. - */ -NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) +static void __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs) { - struct task_struct *tsk = current; + struct task_struct *tsk = thread->task; static int die_counter; - console_verbose(); - spin_lock_irq(&die_lock); - bust_spinlocks(1); - printk("Internal error: %s: %x [#%d]\n", str, err, ++die_counter); print_modules(); printk("CPU: %d\n", smp_processor_id()); show_regs(regs); printk("Process %s (pid: %d, stack limit = 0x%p)\n", - tsk->comm, tsk->pid, tsk->thread_info + 1); + tsk->comm, tsk->pid, thread + 1); if (!user_mode(regs) || in_interrupt()) { dump_mem("Stack: ", regs->ARM_sp, 8192+(unsigned long)tsk->thread_info); dump_backtrace(regs, tsk); dump_instr(regs); } +} + +void nmi_watchdog(struct thread_info *thread, struct pt_regs *regs) +{ + __die("NMI watchdog", 0, thread, regs); +} +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; + +/* + * This function is protected against re-entrancy. + */ +NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) +{ + struct thread_info *thread = current_thread_info(); + + console_verbose(); + spin_lock_irq(&die_lock); + bust_spinlocks(1); + __die(str, err, thread, regs); bust_spinlocks(0); spin_unlock_irq(&die_lock); do_exit(SIGSEGV); --- linux-2.6.5/arch/arm/Kconfig~heh 2004-04-03 22:37:07.000000000 -0500 +++ linux-2.6.5/arch/arm/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -297,7 +297,7 @@ config CPU_FREQ bool "Support CPU clock change (EXPERIMENTAL)" - depends on (ARCH_SA1100 || ARCH_INTEGRATOR) && EXPERIMENTAL + depends on (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_PXA) && EXPERIMENTAL help CPU clock scaling allows you to change the clock speed of the running CPU on the fly. This is a nice method to save battery power, --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/fastfpe/entry.S 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,294 @@ +/* +At entry the registers contain the following information: + +r14 return address for undefined exception return +r9 return address for return from exception +r13 user registers on stack, offset 0 up to offset 4*15 contains + registers r0..15, then the psr +r10 FP workspace 35 words (init, reg[8][4], fpsr, fpcr) + +*/ + +/*---------------------------------------------------------------------------*/ + + .data +fp_const: + .word 0, 0x00000000, 0, 0x80000000 @ 0 + .word 0, 0x80000000, 0, 0 @ 1 + .word 0, 0x80000000, 0, 1 @ 2 + .word 0, 0xc0000000, 0, 1 @ 3 + .word 0, 0x80000000, 0, 2 @ 4 + .word 0, 0xa0000000, 0, 2 @ 5 + .word 0, 0x80000000, 0, -1 @ 0.5 + .word 0, 0xa0000000, 0, 3 @ 10 +fp_undef: + .word 0 +fp_cond: + .word 0xf0f0 @ eq + .word 0x0f0f @ ne + .word 0xcccc @ cs + .word 0x3333 @ cc + .word 0xff00 @ mi + .word 0x00ff @ pl + .word 0xaaaa @ vs + .word 0x5555 @ vc + .word 0x0c0c @ hi + .word 0xf3f3 @ ls + .word 0xaa55 @ ge + .word 0x55aa @ lt + .word 0x0a05 @ gt + .word 0xf5fa @ le + .word 0xffff @ al + .word 0x0000 @ nv + +/*---------------------------------------------------------------------------*/ + + .text + .globl fastfpe_enter +fastfpe_enter: + ldr r4,=fp_undef + str r14,[r4] @ to free one register + add r10,r10,#4 @ to make the code simpler + mov r4, r0 @ r4=trapped instruction + and r1,r4,#0x00000f00 @ r1=coprocessor << 8 +next_enter: + cmp r1,#1<<8 @ copro 1 ? + beq copro_1 + cmp r1,#2<<8 + movne pc,r14 + +copro_2: + and r1,r4,#0x0f000000 + cmp r1,#0x0c000000 @ CPDT with post indexing + cmpne r1,#0x0d000000 @ CPDT with pre indexing + beq CPDT_M_enter + mov pc,r14 + +copro_1: + and r1,r4,#0x0f000000 + cmp r1,#0x0e000000 @ CPDO + beq CPDO_CPRT_enter + cmp r1,#0x0c000000 @ CPDT with post indexing + cmpne r1,#0x0d000000 @ CPDT with pre indexing + beq CPDT_1_enter + mov pc,r14 + +/*---------------------------------------------------------------------------*/ + + .globl fastfpe_next +fastfpe_next: + ldr r5,[r13,#60] +next_after_cond: +__x1: + ldrt r4,[r5],#4 + + ldr r0,=fp_cond @ check condition of next instruction + ldr r1,[r13,#64] @ psr containing flags + mov r2,r4,lsr#28 + mov r1,r1,lsr#28 + ldr r0,[r0,r2,lsl#2] + mov r0,r0,lsr r1 + tst r0,#1 + beq next_after_cond @ must not necessarily have been an + @ FP instruction ! + and r1,r4,#0x0f000000 @ Test for copro instruction + cmp r1,#0x0c000000 + rsbgts r0,r1,#0x0e000000 @ cmpgt #0x0e000000,r1 + movlt pc,r9 @ next is no copro instruction, return + + ands r1,r4,#0x00000f00 @ r1 = coprocessor << 8 + cmpne r1,#3<<8 + movge pc,r9 @ copro = 0 or >=3, return + + str r5,[r13,#60] @ save updated pc + b next_enter + +/*---------------------------------------------------------------------------*/ + +undefined: + ldr r4,=fp_undef + ldr pc,[r4] + +/*---------------------------------------------------------------------------*/ + +CPDT_1_enter: + and r5,r4,#0x000f0000 @ r5=base register number << 16 + ldr r6,[r13,r5,lsr#14] @ r6=base address + cmp r5,#0x000f0000 @ base register = pc ? + addeq r6,r6,#4 + and r7,r4,#0x000000ff @ r7=offset value + + tst r4,#0x00800000 @ up or down? + addne r7,r6,r7,lsl#2 + subeq r7,r6,r7,lsl#2 @ r6=base address +/- offset + tst r4,#0x01000000 @ preindexing ? + movne r6,r7 + tst r4,#0x00200000 @ write back ? + cmpne r5,#0x000f0000 @ base register = pc ? + strne r7,[r13,r5,lsr#14] + + and r0,r4,#0x00007000 @ r0=fp register number << 12 + add r0,r10,r0,lsr#8 @ r0=address of fp register + mov r1,#0 + tst r4,#0x00008000 + orrne r1,r1,#1 @ T0 + tst r4,#0x00400000 + orrne r1,r1,#2 @ T1 + tst r4,#0x00100000 + orrne r1,r1,#4 @ L/S + + add pc,pc,r1,lsl#2 + mov r0,r0 + b CPDT_store_single @ these functions get + b CPDT_store_double @ r0=address of fp register + b CPDT_store_extended @ r6=address of data + b undefined @ CPDT_store_decimal + b CPDT_load_single + b CPDT_load_double + b CPDT_load_extended + b undefined @ CPDT_load_decimal + +/*---------------------------------------------------------------------------*/ + +CPDT_M_enter: + and r5,r4,#0x000f0000 @ r5=base register number << 16 + ldr r6,[r13,r5,lsr#14] @ r6=base address + cmp r5,#0x000f0000 @ base register = pc ? + addeq r6,r6,#4 + and r7,r4,#0x000000ff @ r7=offset value + + tst r4,#0x00800000 @ up or down? + addne r7,r6,r7,lsl#2 + subeq r7,r6,r7,lsl#2 @ r7=base address +/- offset + tst r4,#0x01000000 @ preindexing ? + movne r6,r7 + tst r4,#0x00200000 @ write back ? + cmpne r5,#0x000f0000 @ base register = pc ? + strne r7,[r13,r5,lsr#14] + + and r0,r4,#0x00007000 @ r0=fp register number << 12 + and r1,r4,#0x00008000 + mov r1,r1,lsr#15 @ N0 + and r2,r4,#0x00400000 + orrs r1,r1,r2,lsr#21 @ N1 + addeq r1,r1,#4 @ r1=register count + + tst r4,#0x00100000 @ load/store + beq CPDT_sfm + b CPDT_lfm + +/*---------------------------------------------------------------------------*/ + +CPDO_CPRT_enter: + tst r4,#0x00000010 + bne CPRT_enter + + and r0,r4,#0x00007000 + add r0,r10,r0,lsr#8 @ r0=address of Fd + and r1,r4,#0x00070000 + add r1,r10,r1,lsr#12 @ r1=address of Fn + tst r4,#0x00000008 + bne CPDO_const + and r2,r4,#0x00000007 + add r2,r10,r2,lsl#4 @ r2=address of Fm + +CPDO_constback: + and r3,r4,#0x00f00000 + tst r4,#0x00008000 + orrne r3,r3,#0x01000000 + + add pc,pc,r3,lsr#18 + mov r0,r0 + b CPDO_adf + b CPDO_muf + b CPDO_suf + b CPDO_rsf + b CPDO_dvf + b CPDO_rdf + b undefined + b undefined + b undefined @ CPDO_rmf + b CPDO_muf + b CPDO_dvf + b CPDO_rdf + b undefined + b undefined + b undefined + b undefined + b CPDO_mvf + b CPDO_mnf + b CPDO_abs + b CPDO_rnd + b CPDO_sqt + b undefined + b undefined + b undefined + b undefined + b undefined + b undefined + b undefined + b undefined + b undefined + b CPDO_rnd + b fastfpe_next + +CPDO_const: + ldr r2,=fp_const + and r3,r4,#0x00000007 + add r2,r2,r3,lsl#4 + b CPDO_constback + +/*---------------------------------------------------------------------------*/ + +CPRT_enter: + and r0,r4,#0x0000f000 @ r0=Rd<<12 + and r1,r4,#0x00070000 + add r1,r10,r1,lsr#12 @ r1=address of Fn + tst r4,#0x00000008 + bne CPRT_const + and r2,r4,#0x00000007 + add r2,r10,r2,lsl#4 @ r2=address of Fm + +CPRT_constback: + and r3,r4,#0x00f00000 + + add pc,pc,r3,lsr#18 + mov r0,r0 + b CPRT_flt + b CPRT_fix + b CPRT_wfs + b CPRT_rfs + b undefined + b undefined + b undefined + b undefined + b undefined + b CPRT_cmf + b undefined + b CPRT_cnf + b undefined + b CPRT_cmf + b undefined + b CPRT_cnf + +CPRT_const: + ldr r2,=fp_const + and r3,r4,#0x00000007 + add r2,r2,r3,lsl#4 + b CPRT_constback + +/*---------------------------------------------------------------------------*/ + + @ The fetch of the next instruction to emulate could fault + + .section .fixup,"ax" + .align +__f1: + mov pc,r9 + .previous + .section __ex_table,"a" + .align 3 + .long __x1,__f1 + .previous + +/*---------------------------------------------------------------------------*/ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/fastfpe/module.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,62 @@ +/* + Fast Floating Point Emulator + (c) Peter Teichmann + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef MODULE +#define kern_fp_enter fp_enter + +extern char fpe_type[]; +#endif + +static void (*orig_fp_enter)(void); /* old kern_fp_enter value */ +extern void (*kern_fp_enter)(void); /* current FP handler */ +extern void fastfpe_enter(void); /* forward declarations */ + +static int __init fpe_init(void) +{ + if (fpe_type[0] && strcmp(fpe_type, "fastfpe")) + return 0; + + printk("Fast Floating Point Emulator V0.9 (c) Peter Teichmann.\n"); + + /* Save pointer to the old FP handler and then patch ourselves in */ + orig_fp_enter = kern_fp_enter; + kern_fp_enter = fastfpe_enter; + + return 0; +} + +static void __exit fpe_exit(void) +{ + /* Restore the values we saved earlier. */ + kern_fp_enter = orig_fp_enter; +} + +module_init(fpe_init); +module_exit(fpe_exit); + +MODULE_AUTHOR("Peter Teichmann "); +MODULE_DESCRIPTION("Fast floating point emulator with full precision"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/fastfpe/CPDO.S 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,682 @@ +/* +The FP structure has 4 words reserved for each register, the first is used just +for the sign in bit 31, the second and third are for the mantissa (unsigned +integer, high 32 bit first) and the fourth is the exponent (signed integer). +The mantissa is always normalized. + +If the exponent is 0x80000000, that is the most negative value, the number +represented is 0 and both mantissa words are also 0. + +If the exponent is 0x7fffffff, that is the biggest positive value, the number +represented is infinity if the high 32 mantissa bit are also 0, otherwise it is +a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. + +Decimal and packed decimal numbers are not supported yet. + +The parameters to these functions are r0=destination pointer, r1 and r2 +source pointers. r4 is the instruction. They may use r0-r8 and r14. They return +to fastfpe_next, except CPDO_rnf_core which expects the return address in r14. +*/ + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_adf +CPDO_adf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + + cmp r7,#0x7fffffff + cmpne r8,#0x7fffffff + beq CPDO_adf_extra + + cmp r1,r2 + bne CPDO_suf_s + +CPDO_adf_s: + subs r2,r7,r8 + bge CPDO_adf_2nd + + mov r7,r8 + rsb r2,r2,#0 + cmp r2,#32 + ble CPDO_adf_1st2 + + sub r2,r2,#32 + cmp r2,#32 + movgt r2,#32 + mov r5,r3,lsr r2 + mov r3,#0 + b CPDO_adf_add + +CPDO_adf_1st2: + rsb r8,r2,#32 + mov r5,r5,lsr r2 + orr r5,r5,r3,lsl r8 + mov r3,r3,lsr r2 @ 1. op normalized + b CPDO_adf_add + +CPDO_adf_2nd: + cmp r2,#32 + ble CPDO_adf_2nd2 + + sub r2,r2,#32 + cmp r2,#32 + movgt r2,#32 + mov r6,r4,lsr r2 + mov r4,#0 + b CPDO_adf_add + +CPDO_adf_2nd2: + rsb r8,r2,#32 + mov r6,r6,lsr r2 + orr r6,r6,r4,lsl r8 + mov r4,r4,lsr r2 @ 2. op normalized + +CPDO_adf_add: + adds r5,r5,r6 + adcs r3,r3,r4 @ do addition + bcc CPDO_adf_end + + add r7,r7,#1 + movs r3,r3,rrx + mov r5,r5,rrx @ correct for overflow + +CPDO_adf_end: + cmp r7,#0x20000000 + bge CPDO_inf + + stmia r0,{r1,r3,r5,r7} + b fastfpe_next + +CPDO_adf_extra: + cmp r7,#0x7fffffff @ was it the 1st ? + bne CPDO_infnan_2 @ no it was the 2nd + cmp r8,#0x7fffffff @ if 1st, 2nd too ? + bne CPDO_infnan_1 @ no only 1st + cmp r3,#0 + cmpeq r4,#0 + bne CPDO_nan_12 + b CPDO_inf + +/*---------------------------------------------------------------------------*/ + +CPDO_infnan_1: + stmia r0,{r1,r3,r5,r7} + b fastfpe_next + +CPDO_infnan_2: + stmia r0,{r2,r4,r6,r8} + b fastfpe_next + +CPDO_nan_12: + orr r2,r3,r4 + b CPDO_inf_1 + +CPDO_nan: + mov r2,#0x40000000 @ create non signalling NaN + b CPDO_inf_1 + +CPDO_inf: + mov r2,#0 +CPDO_inf_1: + mov r3,#0 + mov r4,#0x7fffffff +CPDO_store_1234: + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +CPDO_zero: + mov r1,#0 +CPDO_zero_1: + mov r2,#0 + mov r3,#0 + mov r4,#0x80000000 + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_suf +CPDO_suf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + +CPDO_suf_l: + cmp r7,#0x7fffffff + cmpne r8,#0x7fffffff + beq CPDO_suf_extra + + cmp r1,r2 + bne CPDO_adf_s + +CPDO_suf_s: + subs r2,r7,r8 @ determine greater number + bgt CPDO_suf_2nd @ first number is greater + blt CPDO_suf_1st @ second number is greater + cmp r3,r4 @ also mantissa is important + cmpeq r5,r6 + bhi CPDO_suf_2nd @ first number is greater + beq CPDO_zero + +CPDO_suf_1st: + eor r1,r1,#0x80000000 @ second number is greater, invert sign + mov r7,r8 + rsb r2,r2,#0 + cmp r2,#32 + ble CPDO_suf_1st2 + + sub r2,r2,#32 + cmp r2,#32 + movgt r2,#32 + mov r5,r3,lsr r2 + mov r3,#0 + b CPDO_suf_1st_sub + +CPDO_suf_1st2: + rsb r8,r2,#32 + mov r5,r5,lsr r2 + orr r5,r5,r3,lsl r8 + mov r3,r3,lsr r2 @ 1. op normalized + +CPDO_suf_1st_sub: + subs r5,r6,r5 @ do subtraction + sbc r3,r4,r3 + b CPDO_suf_norm + +CPDO_suf_2nd: + cmp r2,#32 + ble CPDO_suf_2nd2 + + sub r2,r2,#32 + cmp r2,#32 + movgt r2,#32 + mov r6,r4,lsr r2 + mov r4,#0 + b CPDO_suf_2nd_sub + +CPDO_suf_2nd2: + rsb r8,r2,#32 + mov r6,r6,lsr r2 + orr r6,r6,r4,lsl r8 + mov r4,r4,lsr r2 @ 2. op normalized + +CPDO_suf_2nd_sub: + subs r5,r5,r6 + sbc r3,r3,r4 @ do subtraction + +CPDO_suf_norm: + teq r3,#0 @ normalize 32bit + moveq r3,r5 + moveq r5,#0 + subeq r7,r7,#32 + + cmp r3,#0x00010000 @ 16bit + movcc r3,r3,lsl#16 + orrcc r3,r3,r5,lsr#16 + movcc r5,r5,lsl#16 + subcc r7,r7,#16 + + cmp r3,#0x01000000 @ 8bit + movcc r3,r3,lsl#8 + orrcc r3,r3,r5,lsr#24 + movcc r5,r5,lsl#8 + subcc r7,r7,#8 + + cmp r3,#0x10000000 @ 4bit + movcc r3,r3,lsl#4 + orrcc r3,r3,r5,lsr#28 + movcc r5,r5,lsl#4 + subcc r7,r7,#4 + + cmp r3,#0x40000000 @ 2bit + movcc r3,r3,lsl#2 + orrcc r3,r3,r5,lsr#30 + movcc r5,r5,lsl#2 + subcc r7,r7,#2 + + cmp r3,#0x80000000 @ 1bit + movcc r3,r3,lsl#1 + orrcc r3,r3,r5,lsr#31 + movcc r5,r5,lsl#1 + subcc r7,r7,#1 + + cmp r7,#0xe0000000 + ble CPDO_zero_1 + + stmia r0,{r1,r3,r5,r7} + b fastfpe_next + +CPDO_suf_extra: + cmp r7,#0x7fffffff @ was it the 1st ? + eorne r2,r2,#0x80000000 @ change sign, might have been INF + bne CPDO_infnan_2 @ no it was the 2nd + cmp r8,#0x7fffffff @ if 1st, 2nd too ? + bne CPDO_infnan_1 @ no only 1st + cmp r3,#0 + cmpeq r4,#0 + bne CPDO_nan_12 + b CPDO_nan @ here is difference with adf ! + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rsf +CPDO_rsf: + mov r3,r2 + ldmia r1,{r2,r4,r6,r8} + ldmia r3,{r1,r3,r5,r7} + b CPDO_suf_l + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_muf +CPDO_muf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + + cmp r7,#0x7fffffff + cmpne r8,#0x7fffffff + beq CPDO_muf_extra + + eor r1,r1,r2 + adds r8,r7,r8 + bvs CPDO_zero_1 + + umull r7,r2,r3,r4 + umull r14,r3,r6,r3 + adds r7,r7,r3 @ r2|r7|r14 = r2|r7|#0 + #0|r3|r14 + adc r2,r2,#0 + umull r4,r3,r5,r4 + adds r14,r14,r4 @ r2|r7|r14 += #0|r3|r4 + adcs r7,r7,r3 + adc r2,r2,#0 + umull r4,r3,r5,r6 + adds r14,r14,r3 @ r2|r7|r14 += #0|#0|r3 + adcs r7,r7,#0 + adcs r2,r2,#0 + + bpl CPDO_muf_norm + + add r8,r8,#1 + b CPDO_muf_end + +CPDO_muf_norm: + adds r14,r14,r14 + adcs r7,r7,r7 + adcs r2,r2,r2 + +CPDO_muf_end: + cmp r8,#0x20000000 + bge CPDO_inf + cmp r8,#0xe0000000 + ble CPDO_zero_1 + stmia r0,{r1,r2,r7,r8} + b fastfpe_next + +CPDO_muf_extra: + cmp r7,#0x7fffffff @ was it the first? + bne CPDO_muf_extra_2nd @ no, so it was the second + cmp r8,#0x7fffffff @ yes, second too? + bne CPDO_muf_extra_1st @ no, only first + orr r3,r3,r4 @ if both inf -> inf, otherwise nan + eor r1,r1,r2 @ sign for the inf case + b CPDO_infnan_1 + +CPDO_muf_extra_1st: + cmp r3,#0 @ is it a nan? + bne CPDO_infnan_1 + cmp r8,#0x80000000 @ is the second 0? + beq CPDO_nan + eor r1,r1,r2 @ correct sign for inf + b CPDO_inf + +CPDO_muf_extra_2nd: + cmp r4,#0 @ is it a nan? + bne CPDO_infnan_2 + cmp r7,#0x80000000 @ is the first 0? + beq CPDO_nan + eor r1,r1,r2 @ correct sign for inf + b CPDO_inf + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_dvf +CPDO_dvf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + +CPDO_dvf_l: + cmp r7,#0x7fffffff + cmpne r8,#0x7fffffff + beq CPDO_dvf_extra + cmp r8,#0x80000000 + beq CPDO_dvf_by0 + + eor r1,r1,r2 + cmp r7,#0x80000000 + beq CPDO_zero_1 + + sub r8,r7,r8 + + mov r2,#0 + mov r7,#1 + + cmp r3,r4 + cmpeq r5,r6 + bcs CPDO_dvf_loop_ + + sub r8,r8,#1 + +CPDO_dvf_loop: + adds r5,r5,r5 + adcs r3,r3,r3 + bcs CPDO_dvf_anyway +CPDO_dvf_loop_: + subs r5,r5,r6 + sbcs r3,r3,r4 + bcs CPDO_dvf_okay + + adds r5,r5,r6 + adc r3,r3,r4 + adds r7,r7,r7 + adcs r2,r2,r2 + bcc CPDO_dvf_loop + b CPDO_dvf_end + +CPDO_dvf_anyway: + adcs r7,r7,r7 + adcs r2,r2,r2 + bcs CPDO_dvf_end + subs r5,r5,r6 + sbc r3,r3,r4 + b CPDO_dvf_loop + +CPDO_dvf_okay: + adcs r7,r7,r7 + adcs r2,r2,r2 + bcc CPDO_dvf_loop + +CPDO_dvf_end: + b CPDO_muf_end + +CPDO_dvf_by0: + cmp R7,#0x80000000 + beq CPDO_nan @ first also 0 -> nan + eor r1,r1,r2 @ otherwise calculatesign for inf + b CPDO_inf + +CPDO_dvf_extra: + cmp r7,#0x7fffffff @ was it the first? + bne CPDO_dvf_extra_2nd @ no, so it was the second + cmp r8,#0x7fffffff @ yes, second too? + bne CPDO_dvf_extra_1st @ no, only first + orrs r3,r3,r4 + beq CPDO_nan @ if both inf -> create nan + b CPDO_nan_12 @ otherwise keep nan + +CPDO_dvf_extra_1st: + eor r1,r1,r2 @ correct sign for inf + b CPDO_infnan_1 + +CPDO_dvf_extra_2nd: + cmp r4,#0 @ is it a nan? + bne CPDO_infnan_2 + eor r1,r1,r2 @ correct sign for zero + b CPDO_zero_1 + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rdf +CPDO_rdf: + mov r3,r2 + ldmia r1,{r2,r4,r6,r8} + ldmia r3,{r1,r3,r5,r7} + b CPDO_dvf_l + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rmf +CPDO_rmf: + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_mvf +CPDO_mvf: + ldmia r2,{r1,r2,r3,r4} + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_mnf +CPDO_mnf: + ldmia r2,{r1,r2,r3,r4} + eor r1,r1,#0x80000000 + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_abs +CPDO_abs: + ldmia r2,{r1,r2,r3,r4} + bic r1,r1,#0x80000000 + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_sqt +CPDO_sqt: + ldmia r2,{r1,r2,r3,r4} + cmp r1,#0 + bne CPDO_nan + cmp r4,#0x7fffffff + beq CPDO_store_1234 + + tst r4,r4,lsr#1 @carry=exponent bit 0 + bcc CPDO_sqt_exponenteven + adds r3,r3,r3 + adcs r2,r2,r2 @carry is needed in loop! +CPDO_sqt_exponenteven: + mov r4,r4,asr #1 + str r4,[r0,#12] + + mov r4,#0x80000000 + mov r5,#0 + sub r2,r2,#0x80000000 + + mov r8,#0x40000000 + mov r14,#0x80000000 + + mov r1,#1 + b CPDO_sqt_loop1_first +CPDO_sqt_loop1: + adds r3,r3,r3 + adcs r2,r2,r2 +CPDO_sqt_loop1_first: + add r6,r4,r8,lsr r1 @r7 const = r5 + bcs CPDO_sqt_loop1_1 + cmp r2,r6 + cmpeq r3,r5 @r5 for r7 + bcc CPDO_sqt_loop1_0 +CPDO_sqt_loop1_1: + orr r4,r4,r14,lsr r1 + subs r3,r3,r5 @r5 for r7 + sbc r2,r2,r6 +CPDO_sqt_loop1_0: + add r1,r1,#1 + cmp r1,#30 + ble CPDO_sqt_loop1 + + adds r3,r3,r3 + adcs r2,r2,r2 + bcs CPDO_sqt_between_1 + adds r7,r5,#0x80000000 + adc r6,r4,#0 + cmp r2,r6 + cmpeq r3,r7 + bcc CPDO_sqt_between_0 +CPDO_sqt_between_1: + orr r4,r4,#0x00000001 + subs r3,r3,r5 + sbc r2,r2,r4 + subs r3,r3,#0x80000000 + sbc r2,r2,#0 +CPDO_sqt_between_0: + mov r1,#0 + +CPDO_sqt_loop2: + adds r3,r3,r3 + adcs r2,r2,r2 + bcs CPDO_sqt_loop2_1 + adds r7,r5,r8,lsr r1 + adc r6,r4,#0 + cmp r2,r6 + cmpeq r3,r7 + bcc CPDO_sqt_loop2_0 +CPDO_sqt_loop2_1: + orr r5,r5,r14,lsr r1 + subs r3,r3,r5 + sbc r2,r2,r4 + subs r3,r3,r8,lsr r1 + sbc r2,r2,#0 +CPDO_sqt_loop2_0: + add r1,r1,#1 + cmp r1,#30 + ble CPDO_sqt_loop2 + + adds r3,r3,r3 + adcs r2,r2,r2 + bcs CPDO_sqt_after_1 + cmp r2,r6 + cmpeq r3,r7 + bcc CPDO_sqt_after_0 +CPDO_sqt_after_1: + orr r5,r5,#0x00000001 +CPDO_sqt_after_0: + + mov r1,#0 + stmia r0,{r1,r4,r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rnd +CPDO_rnd: + ldmia r2,{r1,r2,r3,r5} + bl CPDO_rnd_core + +CPDO_rnd_store: + stmia r0,{r1,r2,r3,r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rnd_core +CPDO_rnd_core: + and r4,r4,#0x00000060 + add pc,pc,r4,lsr#3 + mov r0,r0 + b CPDO_rnd_N + b CPDO_rnd_P + b CPDO_rnd_M + b CPDO_rnd_Z + +CPDO_rnd_N: + cmp r5,#-1 + blt CPDO_rnd_zero + cmp r5,#63 + movge pc,r14 + mov r4,#0x40000000 + cmp r5,#31 + bge CPDO_rnd_N_2 + + adds r2,r2,r4,lsr r5 + bcc CPDO_rnd_end + b CPDO_rnd_end_norm + +CPDO_rnd_N_2: +CPDO_rnd_P_2: + sub r6,r5,#32 + adds r3,r3,r4,ror r6 @ror ist needed to handle a -1 correctly + adcs r2,r2,#0 + bcc CPDO_rnd_end + b CPDO_rnd_end_norm + +CPDO_rnd_P: + tst r1,#0x80000000 + bne CPDO_rnd_M_entry +CPDO_rnd_P_entry: + cmp r5,#0 + blt CPDO_rnd_P_small + cmp r5,#63 + movge pc,r14 + mov r4,#0x7fffffff + cmp r5,#32 + bge CPDO_rnd_P_2 + + adds r3,r3,#0xffffffff + adcs r2,r2,r4,lsr r5 + bcc CPDO_rnd_end + b CPDO_rnd_end_norm + +CPDO_rnd_P_small: + cmp r5,#0x80000000 + moveq pc,r14 + b CPDO_rnd_one + +CPDO_rnd_M: + tst r1,#0x80000000 + bne CPDO_rnd_P_entry +CPDO_rnd_M_entry: + cmp r5,#0 + blt CPDO_rnd_zero + cmp r5,#63 + movge pc,r14 + + b CPDO_rnd_end + +CPDO_rnd_Z: + cmp r5,#0 + blt CPDO_rnd_zero + cmp r5,#63 + movge pc,r14 + b CPDO_rnd_end + +CPDO_rnd_end_norm: + add r5,r5,#1 + movs r2,r2,rrx + mov r3,r3,rrx +CPDO_rnd_end: + rsbs r4,r5,#31 + bmi CPDO_rnd_end_2 + mov r3,#0 + mov r2,r2,lsr r4 + mov r2,r2,lsl r4 + mov pc,r14 + +CPDO_rnd_end_2: + rsb r4,r5,#63 + mov r3,r3,lsr r4 + mov r3,r3,lsl r4 + mov pc,r14 + +CPDO_rnd_one: + mov r2,#0x80000000 + mov r3,#0 + mov r5,#0 + mov pc,r14 + +CPDO_rnd_zero: + mov r1,#0 + mov r2,#0 + mov r3,#0 + mov r5,#0x80000000 + mov pc,r14 + +/*---------------------------------------------------------------------------*/ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/fastfpe/CPRT.S 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,185 @@ +/* +The FP structure has 4 words reserved for each register, the first is used +just +for the sign in bit 31, the second and third are for the mantissa (unsigned +integer, high 32 bit first) and the fourth is the exponent (signed integer). +The mantissa is always normalized. + +If the exponent is 0x80000000, that is the most negative value, the number +represented is 0 and both mantissa words are also 0. + +If the exponent is 0x7fffffff, that is the biggest positive value, the +number +represented is infinity if the high 32 mantissa bit are also 0, otherwise it +is +a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. + +Decimal and packed decimal numbers are not supported yet. +*/ + +/*---------------------------------------------------------------------------*/ + + .text + .globl CPRT_flt +CPRT_flt: + add r0,r13,r0,lsr#10 + ldr r2,[r0] + mov r3,#0 + cmp r2,#0 + beq CPRT_flt_zero + + ands r0,r2,#0x80000000 + rsbne r2,r2,#0 + mov r4,#31 + + cmp r2,#0x00010000 + movcc r2,r2,lsl#16 + subcc r4,r4,#16 + + cmp r2,#0x01000000 + movcc r2,r2,lsl#8 + subcc r4,r4,#8 + + cmp r2,#0x10000000 + movcc r2,r2,lsl#4 + subcc r4,r4,#4 + + cmp r2,#0x40000000 + movcc r2,r2,lsl#2 + subcc r4,r4,#2 + + cmp r2,#0x80000000 + movcc r2,r2,lsl#1 + subcc r4,r4,#1 + + stmia r1,{r0,r2,r3,r4} + b fastfpe_next + +CPRT_flt_zero: + mov r0,#0 + mov r4,#0x80000000 + stmia r1,{r0,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_fix +CPRT_fix: + ldmia r2,{r1,r2,r3,r5} + bl CPDO_rnd_core + +CPRT_back: + add r0,r13,r0,lsr#10 + cmp r5,#0 + blt CPRT_int_zero + cmp r5,#30 + bgt CPRT_overflow + + rsb r5,r5,#31 + mov r2,r2,lsr r5 + tst r1,#0x80000000 + rsbne r2,r2,#0 + + str r2,[r0] + b fastfpe_next + +CPRT_int_zero: + mov r2,#0 + str r2,[r0] + b fastfpe_next + +CPRT_overflow: + mov r2,#0x80000000 + tst r1,#0x80000000 + subeq r2,r2,#1 + str r2,[r0] + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_wfs +CPRT_wfs: + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_rfs +CPRT_rfs: + add r0,r13,r0,lsr#10 + mov r1,#0x02000000 @ Software Emulation, not Acorn FPE + str r1,[r0] + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_cmf +CPRT_cmf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + +CPRT_cmf_e: + ldr r0,[r13,#16*4] + + cmp r7,#0x7fffffff + bic r0,r0,#0xf0000000 + + cmpeq r3,#0xffffffff + beq CPRT_cmf_unordered + cmp r8,#0x7fffffff + cmpeq r4,#0xffffffff + beq CPRT_cmf_unordered + + cmp r1,r2 + beq CPRT_cmf_equalsign + b CPRT_cmf_sign + +CPRT_cmf_equalsign: + cmp r7,r8 + beq CPRT_cmf_equalexponent + bgt CPRT_cmf_sign + b CPRT_cmf_signb + +CPRT_cmf_equalexponent: + cmp r3,r4 + cmpeq r5,r6 + beq CPRT_cmf_equal + bhi CPRT_cmf_sign + b CPRT_cmf_signb + +CPRT_cmf_sign: + cmp r7,#0x80000000 @ (0.0 == -0.0)? + cmpeq r7,r8 + beq CPRT_cmf_equal + tst r1,#0x80000000 + orreq r0,r0,#0x20000000 + orrne r0,r0,#0x80000000 + str r0,[r13,#16*4] + b fastfpe_next + +CPRT_cmf_signb: + tst r1,#0x80000000 + orrne r0,r0,#0x20000000 + orreq r0,r0,#0x80000000 + str r0,[r13,#16*4] + b fastfpe_next + +CPRT_cmf_equal: + orr r0,r0,#0x60000000 + str r0,[r13,#16*4] + b fastfpe_next + +CPRT_cmf_unordered: + orr r0,r0,#0x10000000 + str r0,[r13,#16*4] + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_cnf +CPRT_cnf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + eor r2,r2,#0x80000000 + b CPRT_cmf_e + +/*---------------------------------------------------------------------------*/ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/fastfpe/CPDT.S 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,430 @@ +/* +The FP structure has 4 words reserved for each register, the first is used just +for the sign in bit 31, the second and third are for the mantissa (unsigned +integer, high 32 bit first) and the fourth is the exponent (signed integer). +The mantissa is always normalized. + +If the exponent is 0x80000000, that is the most negative value, the number +represented is 0 and both mantissa words are also 0. + +If the exponent is 0x7fffffff, that is the biggest positive value, the number +represented is infinity if the high 32 mantissa bit are also 0, otherwise it is +a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. + +Decimal and packed decimal numbers are not supported yet. +*/ + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_load_single +CPDT_load_single: + ldr r1,[r6] + + and r2,r1,#0x80000000 @ r2 = sign + + mov r5,r1,lsr#23 + bics r5,r5,#0x100 + beq CPDT_ls_e0 @ exponent = 0; zero/denormalized + teq r5,#255 + beq CPDT_ls_e255 @ exponent = 255; infinity/NaN + + sub r5,r5,#127 @ r5 = exponent, remove normalized bias + + mov r3,r1,lsl#8 + orr r3,r3,#0x80000000 + mov r4,#0 @ r3,r4 = mantissa + + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_ls_e0: + movs r3,r1,lsl#9 + beq CPDT_load_zero + + mov r5,#-127 + +CPDT_ls_e0_norm: + tst r3,#0x80000000 + subeq r5,r5,#1 + moveq r3,r3,lsl#1 + beq CPDT_ls_e0_norm + + mov r4,#0 + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_ls_e255: + mov r3,r1,lsl#9 + mov r4,#0 + mov r5,#0x7fffffff + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_load_zero: + mov r3,#0 + mov r4,#0 + mov r5,#0x80000000 + stmia r0,{r2-r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_load_double +CPDT_load_double: + ldr r1,[r6] + ldr r6,[r6,#4] + + and r2,r1,#0x80000000 @ r2 = sign + + mov r5,r1,lsr#20 + bics r5,r5,#0x800 + beq CPDT_ld_e0 @ exponent = 0; zero/denormalized + add r4,r5,#1 + teq r4,#2048 + beq CPDT_ld_e2047 @ exponent = 2047; infinity/NaN + + add r5,r5,#1 + sub r5,r5,#1024 @ r5 = exponent, remove normalized bias + + mov r3,r1,lsl#11 + orr r3,r3,#0x80000000 + orr r3,r3,r6,lsr #21 + mov r4,r6,lsl#11 @ r3,r4 = mantissa + + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_ld_e0: + mov r3,r1,lsl#12 + orr r3,r3,r6,lsr#20 + movs r4,r6,lsl#12 + teqeq r3,#0 + beq CPDT_load_zero + + mov r5,#1 + sub r5,r5,#1024 + +CPDT_ld_e0_norm: + tst r3,#0x80000000 + subeq r5,r5,#1 + moveqs r4,r4,lsl#1 + adceq r3,r3,r3 + beq CPDT_ld_e0_norm + + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_ld_e2047: + mov r3,r1,lsl#12 + orr r3,r3,r6,lsr#1 + bic r6,r6,#0x80000000 + orr r3,r3,r6 @ to get all fraction bits ! + mov r4,#0 + mov r5,#0x7fffffff + stmia r0,{r2-r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_load_extended +CPDT_load_extended: + ldr r1,[r6] + ldr r3,[r6,#4] + ldr r4,[r6,#8] + + and r2,r1,#0x80000000 + bics r5,r1,#0x80000000 + beq CPDT_le_e0 + add r1,r5,#1 + teq r4,#32768 + beq CPDT_le_e32767 + + add r5,r5,#1 + sub r5,r5,#16384 + + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_le_e0: + teq r3,#0 + teqeq r4,#0 + beq CPDT_load_zero + + mov r5,#2 + sub r5,r5,#16384 + b CPDT_ld_e0_norm + +CPDT_le_e32767: + mov r3,r3,lsl#1 + orr r3,r3,r4,lsr#1 + bic r4,r4,#0x80000000 + orr r3,r3,r4 + mov r5,#0x7fffffff + stmia r0,{r2-r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_load_decimal +CPDT_load_decimal: + + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_store_single +CPDT_store_single: + ldmia r0,{r1-r4} + + cmp r4,#-127 + ble CPDT_ss_e0 + cmp r4,#128 + bge CPDT_ss_e255 + + adds r2,r2,#1<<7 @ round to nearest + bcs CPDT_ss_rnd_ovfl @ very very seldom taken + +CPDT_ss_store: + add r4,r4,#127 + orr r1,r1,r4,lsl#23 + + bic r2,r2,#0x80000000 + orr r1,r1,r2,lsr#8 + + str r1,[r6] + b fastfpe_next + +CPDT_ss_rnd_ovfl: + add r4,r4,#1 + cmp r4,#128 + bge CPDT_ss_e255 + + mov r2,#0x80000000 + mov r3,#0 + b CPDT_ss_store + +CPDT_ss_e0: + cmp r4,#-150 + ble CPDT_ss_zero + + add r4,r4,#126 +CPDT_ss_unnormalize: + mov r2,r2,lsr#1 + adds r4,r4,#1 + bne CPDT_ss_unnormalize + + orr r1,r1,r2,lsr#8 + +CPDT_ss_zero: + str r1,[r6] + b fastfpe_next + +CPDT_ss_e255: + cmp r4,#0x7fffffff + bne CPDT_ss_inf + cmp r2,#0 + beq CPDT_ss_inf + + orr r1,r1,#0x00200000 @ for safety so that it is not INF + orr r1,r1,r2,lsr#9 @ get highest bit of mantissa + +CPDT_ss_inf: + orr r1,r1,#0x7f000000 + orr r1,r1,#0x00800000 + str r1,[r6] + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_store_double +CPDT_store_double: + ldmia r0,{r1-r4} + + cmp r4,#1024 @ this check has to be first, or + bge CPDT_sd_e2047 @ overflow can occur on second ! + add r0,r4,#3 + cmp r0,#-1023+3 @ cmp with -1023 + ble CPDT_sd_e0 + + adds r3,r3,#1<<10 @ round to nearest + adcs r2,r2,#0 + bcs CPDT_sd_rnd_ovfl @ very very seldom taken + +CPDT_sd_store: + sub r4,r4,#1 + add r4,r4,#1024 + orr r1,r1,r4,lsl#20 + + bic r2,r2,#0x80000000 + orr r1,r1,r2,lsr#11 + + mov r2,r2,lsl#21 + orr r2,r2,r3,lsr#11 + + stmia r6,{r1,r2} + b fastfpe_next + +CPDT_sd_rnd_ovfl: + add r4,r4,#1 + cmp r4,#1024 + bge CPDT_sd_e2047 + + mov r2,#0x80000000 + mov r3,#0 + b CPDT_sd_store + +CPDT_sd_e0: + add r0,r4,#1075-1024 + cmp r0,#-1024 + ble CPDT_sd_zero + + add r4,r4,#1024 + sub r4,r4,#2 +CPDT_sd_unnormalize: + movs r2,r2,lsr#1 + mov r3,r3,rrx + adds r4,r4,#1 + bne CPDT_sd_unnormalize + + orr r1,r1,r2,lsr#11 + mov r2,r2,lsl#21 + orr r2,r2,r3,lsr#11 + + stmia r6,{r1,r2} + b fastfpe_next + +CPDT_sd_zero: + mov r2,#0 + stmia r6,{r1,r2} + b fastfpe_next + +CPDT_sd_e2047: + cmp r4,#0x7fffffff + bne CPDT_sd_inf + cmp r2,#0 + beq CPDT_sd_inf + + orr r1,r1,#0x00040000 @ for safety so that it is not INF + orr r1,r1,r2,lsr#12 @ get highest bit of mantissa + +CPDT_sd_inf: + orr r1,r1,#0x7f000000 + orr r1,r1,#0x00f00000 + stmia r6,{r1,r2} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_store_extended +CPDT_store_extended: + ldmia r0,{r1-r4} + + cmp r4,#16384 @ this check has to be first, or + bge CPDT_se_e32767 @ overflow can occur with second ! + add r0,r4,#63 + cmp r0,#-16383+63 + ble CPDT_se_e0 + + sub r4,r4,#1 + add r4,r4,#16384 + orr r1,r1,r4 + + stmia r6,{r1-r3} + b fastfpe_next + +CPDT_se_e0: + add r0,r4,#16446-16384 + cmp r0,#-16384 + ble CPDT_se_zero + + add r4,r4,#16384 + sub r4,r4,#2 +CPDT_se_unnormalize: + movs r2,r2,lsr#1 + mov r3,r3,rrx + adds r4,r4,#1 + bne CPDT_se_unnormalize + + stmia r6,{r1-r3} + b fastfpe_next + +CPDT_se_zero: + mov r2,#0 + mov r3,#0 + stmia r6,{r1-r3} + b fastfpe_next + +CPDT_se_e32767: + cmp r4,#0x7fffffff + bne CPDT_se_inf + cmp r2,#0 + beq CPDT_se_inf + + mov r2,r2,lsl#1 + orr r2,r2,#0x20000000 + +CPDT_se_inf: + orr r1,r1,#0x00007f00 + orr r1,r1,#0x000000ff + stmia r6,{r1-r3} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_store_decimal +CPDT_store_decimal: + + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_sfm +CPDT_sfm: + add r2,r10,r0,lsr#8 + ldr r4,[r2,#0] + ldr r3,[r2,#4] + bic r3,r3,#0x80000000 + orr r3,r3,r4 + str r3,[r6],#4 + ldr r3,[r2,#8] + str r3,[r6],#4 + ldr r3,[r2,#12] + str r3,[r6],#4 + + add r0,r0,#1<<12 + and r0,r0,#7<<12 + subs r1,r1,#1 + bne CPDT_sfm + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_lfm +CPDT_lfm: + add r2,r10,r0,lsr#8 + ldr r4,[r6],#4 + and r3,r4,#0x80000000 + str r3,[r2,#0] + ldr r3,[r6],#4 + str r3,[r2,#8] + ldr r3,[r6],#4 + str r3,[r2,#12] + + cmp r3,#0x80000000 @ does the exp indicate zero? + biceq r4,r4,#0x80000000 @ if so, indicate 'denormalized' + beq CPDT_lfm_storer4 + cmp r3,#0x7fffffff @ does the exp indicate inf or NaN? + biceq r4,r4,#0x80000000 @ if so, indicate 'denormalized' + beq CPDT_lfm_storer4 + orrne r4,r4,#0x80000000 @ otherwise, set normalized bit + +CPDT_lfm_storer4: + str r4,[r2,#4] + + add r0,r0,#1<<12 + and r0,r0,#7<<12 + subs r1,r1,#1 + bne CPDT_lfm + b fastfpe_next + +/*---------------------------------------------------------------------------*/ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/fastfpe/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,14 @@ +# +# linux/arch/arm/fastfpe/Makefile +# +# Copyright (C) Peter Teichmann +# + +obj-y := +obj-m := +obj-n := +obj- := + +fastfpe-objs := module.o entry.o CPDO.o CPRT.o CPDT.o + +obj-$(CONFIG_FPE_FASTFPE) += fastfpe.o --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/common/rtctime.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,482 @@ +/* + * linux/arch/arm/common/rtctime.c + * + * Copyright (C) 2003 Deep Blue Solutions Ltd. + * Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre. + * Based on rtc.c by Paul Gortmaker + * + * 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 +#include +#include +#include +#include + +#include +#include + +static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); +static struct fasync_struct *rtc_async_queue; + +/* + * rtc_lock protects rtc_irq_data + */ +static spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; +static unsigned long rtc_irq_data; + +/* + * rtc_sem protects rtc_inuse and rtc_ops + */ +static DECLARE_MUTEX(rtc_sem); +static unsigned long rtc_inuse; +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; +} + +/* + * Convert Gregorian date to seconds since 01-01-1970 00:00:00. + */ +int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) +{ + unsigned int yrs = tm->tm_year + 1900; + + *time = 0; + + if (yrs < 1970 || + tm->tm_mon >= 12 || + tm->tm_mday < 1 || + tm->tm_mday > month_days(tm->tm_mon, yrs) || + tm->tm_hour >= 24 || + tm->tm_min >= 60 || + tm->tm_sec >= 60) + return -EINVAL; + + *time = mktime(yrs, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return 0; +} + +/* + * Calculate the next alarm time given the requested alarm time mask + * and the current time. + * + * FIXME: for now, we just copy the alarm time because we're lazy (and + * is therefore buggy - setting a 10am alarm at 8pm will not result in + * the alarm triggering.) + */ +void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm) +{ + next->tm_year = now->tm_year; + next->tm_mon = now->tm_mon; + next->tm_mday = now->tm_mday; + next->tm_hour = alrm->tm_hour; + next->tm_min = alrm->tm_min; + next->tm_sec = alrm->tm_sec; +} + +static inline void rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm) +{ + memset(tm, 0, sizeof(struct rtc_time)); + ops->read_time(tm); +} + +static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm) +{ + return ops->set_time(tm); +} + +static inline void rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) +{ + memset(alrm, 0, sizeof(struct rtc_wkalrm)); + ops->read_alarm(alrm); +} + +static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm) +{ + return ops->set_alarm(alrm); +} + +void rtc_update(unsigned long num, unsigned long events) +{ + spin_lock(&rtc_lock); + rtc_irq_data = (rtc_irq_data + (num << 8)) | events; + spin_unlock(&rtc_lock); + + wake_up_interruptible(&rtc_wait); + kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); +} + + +static ssize_t +rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t ret; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc_wait, &wait); + do { + __set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irq(&rtc_lock); + data = rtc_irq_data; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_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_wait, &wait); + + if (ret == 0) { + ret = put_user(data, (unsigned long *)buf); + if (ret == 0) + ret = sizeof(unsigned long); + } + return ret; +} + +static unsigned int rtc_poll(struct file *file, poll_table *wait) +{ + unsigned long data; + + poll_wait(file, &rtc_wait, wait); + + spin_lock_irq(&rtc_lock); + data = rtc_irq_data; + spin_unlock_irq(&rtc_lock); + + return data != 0 ? POLLIN | POLLRDNORM : 0; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_ops *ops = file->private_data; + struct rtc_time tm; + struct rtc_wkalrm alrm; + int ret; + + switch (cmd) { + case RTC_ALM_READ: + rtc_read_alarm(ops, &alrm); + ret = copy_to_user((void *)arg, &alrm.time, sizeof(tm)); + if (ret) + ret = -EFAULT; + break; + + case RTC_ALM_SET: + ret = copy_from_user(&alrm.time, (void *)arg, sizeof(tm)); + alrm.enabled = 0; + alrm.pending = 0; + alrm.time.tm_mday = -1; + alrm.time.tm_mon = -1; + alrm.time.tm_year = -1; + alrm.time.tm_wday = -1; + alrm.time.tm_yday = -1; + alrm.time.tm_isdst = -1; + if (ret == 0) + ret = rtc_set_alarm(ops, &alrm); + else + ret = -EFAULT; + break; + + case RTC_RD_TIME: + rtc_read_time(ops, &tm); + ret = copy_to_user((void *)arg, &tm, sizeof(tm)); + if (ret) + ret = -EFAULT; + break; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) { + ret = -EACCES; + break; + } + ret = copy_from_user(&tm, (void *)arg, sizeof(tm)); + if (ret == 0) + ret = rtc_set_time(ops, &tm); + else + ret = -EFAULT; + break; + +#ifndef rtc_epoch + case RTC_EPOCH_SET: + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) { + ret = -EINVAL; + break; + } + if (!capable(CAP_SYS_TIME)) { + ret = -EACCES; + break; + } + rtc_epoch = arg; + ret = 0; + break; +#endif + + case RTC_EPOCH_READ: + ret = put_user(rtc_epoch, (unsigned long *)arg); + break; + + case RTC_WKALM_SET: + ret = copy_from_user(&alrm, (void *)arg, sizeof(alrm)); + if (ret == 0) + ret = rtc_set_alarm(ops, &alrm); + else + ret = -EFAULT; + break; + + case RTC_WKALM_RD: + rtc_read_alarm(ops, &alrm); + ret = copy_to_user((void *)arg, &alrm, sizeof(alrm)); + if (ret) + ret = -EFAULT; + break; + + default: + ret = ops->ioctl(cmd, arg); + } + return ret; +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + int ret; + + down(&rtc_sem); + + if (rtc_inuse) { + ret = -EBUSY; + } else if (!rtc_ops || !try_module_get(rtc_ops->owner)) { + ret = -ENODEV; + } else { + file->private_data = rtc_ops; + + ret = rtc_ops->open ? rtc_ops->open() : 0; + if (ret == 0) { + spin_lock_irq(&rtc_lock); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + + rtc_inuse = 1; + } + } + up(&rtc_sem); + + return ret; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + struct rtc_ops *ops = file->private_data; + + if (ops->release) + ops->release(); + + spin_lock_irq(&rtc_lock); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + + module_put(rtc_ops->owner); + rtc_inuse = 0; + + return 0; +} + +static int rtc_fasync(int fd, struct file *file, int on) +{ + return fasync_helper(fd, file, on, &rtc_async_queue); +} + +static struct file_operations rtc_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = rtc_read, + .poll = rtc_poll, + .ioctl = rtc_ioctl, + .open = rtc_open, + .release = rtc_release, + .fasync = rtc_fasync, +}; + +static struct miscdevice rtc_miscdev = { + .minor = RTC_MINOR, + .name = "rtc", + .fops = &rtc_fops, +}; + + +static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct rtc_ops *ops = data; + struct rtc_wkalrm alrm; + struct rtc_time tm; + char *p = page; + int len; + + rtc_read_time(ops, &tm); + + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + rtc_epoch); + + rtc_read_alarm(ops, &alrm); + p += sprintf(p, "alrm_time\t: "); + if ((unsigned int)alrm.time.tm_hour <= 24) + p += sprintf(p, "%02d:", alrm.time.tm_hour); + else + p += sprintf(p, "**:"); + if ((unsigned int)alrm.time.tm_min <= 59) + p += sprintf(p, "%02d:", alrm.time.tm_min); + else + p += sprintf(p, "**:"); + if ((unsigned int)alrm.time.tm_sec <= 59) + p += sprintf(p, "%02d\n", alrm.time.tm_sec); + else + p += sprintf(p, "**\n"); + + p += sprintf(p, "alrm_date\t: "); + if ((unsigned int)alrm.time.tm_year <= 200) + p += sprintf(p, "%04d-", alrm.time.tm_year + 1900); + else + p += sprintf(p, "****-"); + if ((unsigned int)alrm.time.tm_mon <= 11) + p += sprintf(p, "%02d-", alrm.time.tm_mon + 1); + else + p += sprintf(p, "**-"); + if ((unsigned int)alrm.time.tm_mday <= 31) + p += sprintf(p, "%02d\n", alrm.time.tm_mday); + else + p += sprintf(p, "**\n"); + p += sprintf(p, "alrm_wakeup\t: %s\n", alrm.enabled ? "yes" : "no"); + p += sprintf(p, "alrm_pending\t: %s\n", alrm.pending ? "yes" : "no"); + + if (ops->proc) + p += ops->proc(p); + + len = (p - page) - off; + if (len < 0) + len = 0; + *eof = len <= count; + *start = page + off; + + return len; +} + +int register_rtc(struct rtc_ops *ops) +{ + int ret = -EBUSY; + + down(&rtc_sem); + if (rtc_ops == NULL) { + rtc_ops = ops; + + ret = misc_register(&rtc_miscdev); + if (ret == 0) + create_proc_read_entry("driver/rtc", 0, 0, + rtc_read_proc, ops); + } + up(&rtc_sem); + + return ret; +} + +void unregister_rtc(struct rtc_ops *rtc) +{ + down(&rtc_sem); + if (rtc == rtc_ops) { + remove_proc_entry("driver/rtc", NULL); + misc_deregister(&rtc_miscdev); + rtc_ops = NULL; + } + up(&rtc_sem); +} + +EXPORT_SYMBOL(rtc_time_to_tm); +EXPORT_SYMBOL(rtc_tm_to_time); +EXPORT_SYMBOL(rtc_update); +EXPORT_SYMBOL(register_rtc); +EXPORT_SYMBOL(unregister_rtc); --- linux-2.6.5/arch/arm/common/Makefile~heh 2004-04-03 22:36:57.000000000 -0500 +++ linux-2.6.5/arch/arm/common/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -2,7 +2,7 @@ # Makefile for the linux kernel. # -obj-y += platform.o +obj-y += platform.o rtctime.o obj-$(CONFIG_ARM_AMBA) += amba.o obj-$(CONFIG_ICST525) += icst525.o obj-$(CONFIG_SA1111) += sa1111.o sa1111-pcibuf.o --- linux-2.6.5/arch/arm/mach-pxa/generic.c~heh 2004-04-03 22:36:53.000000000 -0500 +++ linux-2.6.5/arch/arm/mach-pxa/generic.c 2004-04-30 20:57:36.000000000 -0400 @@ -132,7 +132,7 @@ /* virtual physical length type */ { 0xf6000000, 0x20000000, 0x01000000, MT_DEVICE }, /* PCMCIA0 IO */ { 0xf7000000, 0x30000000, 0x01000000, MT_DEVICE }, /* PCMCIA1 IO */ - { 0xf8000000, 0x40000000, 0x01400000, MT_DEVICE }, /* Devs */ + { 0xf8000000, 0x40000000, 0x01800000, MT_DEVICE }, /* Devs */ { 0xfa000000, 0x44000000, 0x00100000, MT_DEVICE }, /* LCD */ { 0xfc000000, 0x48000000, 0x00100000, MT_DEVICE }, /* Mem Ctl */ { 0xff000000, 0x00000000, 0x00100000, MT_DEVICE } /* UNCACHED_PHYS_0 */ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-pxa/cpu-pxa.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,322 @@ +/* + * linux/arch/arm/mach-pxa/cpu-pxa.c + * + * Copyright (C) 2002,2003 Intrinsyc Software + * + * 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 + * + * History: + * 31-Jul-2002 : Initial version [FB] + * 29-Jan-2003 : added PXA255 support [FB] + * 20-Apr-2003 : ported to v2.5 (Dustin McIntire, Sensoria Corp.) + * + * Note: + * This driver may change the memory bus clock rate, but will not do any + * platform specific access timing changes... for example if you have flash + * memory connected to CS0, you will need to register a platform specific + * notifier which will adjust the memory access strobes to maintain a + * minimum strobe width. + * + */ + +#include +#include +#include +#include +#include + +#include + +#define DEBUG 0 + +#ifdef DEBUG + static unsigned int freq_debug = DEBUG; + MODULE_PARM(freq_debug, "i"); + MODULE_PARM_DESC(freq_debug, "Set the debug messages to on=1/off=0"); +#else + #define freq_debug 0 +#endif + +typedef struct +{ + unsigned int khz; + unsigned int membus; + unsigned int cccr; + unsigned int div2; +} pxa_freqs_t; + +/* Define the refresh period in mSec for the SDRAM and the number of rows */ +#define SDRAM_TREF 64 /* standard 64ms SDRAM */ +#define SDRAM_ROWS 4096 /* 64MB=8192 32MB=4096 */ +#define MDREFR_DRI(x) ((x*SDRAM_TREF)/(SDRAM_ROWS*32)) + +#define CCLKCFG_TURBO 0x1 +#define CCLKCFG_FCS 0x2 +#define PXA25x_MIN_FREQ 99500 +#define PXA25x_MAX_FREQ 398100 +#define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2) +#define MDREFR_DRI_MASK 0xFFF + + +/* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */ +static pxa_freqs_t pxa255_run_freqs[] = +{ + /* CPU MEMBUS CCCR DIV2*/ + { 99500, 99500, 0x121, 1}, /* run= 99, turbo= 99, PXbus=50, SDRAM=50 */ + {132700, 132700, 0x123, 1}, /* run=133, turbo=133, PXbus=66, SDRAM=66 */ + {199100, 99500, 0x141, 0}, /* run=199, turbo=199, PXbus=99, SDRAM=99 */ + {265400, 132700, 0x143, 1}, /* run=265, turbo=265, PXbus=133, SDRAM=66 */ + {331800, 165900, 0x145, 1}, /* run=331, turbo=331, PXbus=166, SDRAM=83 */ + {398100, 99500, 0x161, 0}, /* run=398, turbo=398, PXbus=196, SDRAM=99 */ + {0,} +}; +#define NUM_RUN_FREQS (sizeof(pxa255_run_freqs)/sizeof(pxa_freqs_t)) + +static struct cpufreq_frequency_table pxa255_run_freq_table[NUM_RUN_FREQS+1]; + +/* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */ +static pxa_freqs_t pxa255_turbo_freqs[] = +{ + /* CPU MEMBUS CCCR DIV2*/ + { 99500, 99500, 0x121, 1}, /* run=99, turbo= 99, PXbus=50, SDRAM=50 */ + {199100, 99500, 0x221, 0}, /* run=99, turbo=199, PXbus=50, SDRAM=99 */ + {298500, 99500, 0x321, 0}, /* run=99, turbo=287, PXbus=50, SDRAM=99 */ + {298600, 99500, 0x1c1, 0}, /* run=199, turbo=287, PXbus=99, SDRAM=99 */ + {398100, 99500, 0x241, 0}, /* run=199, turbo=398, PXbus=99, SDRAM=99 */ + {0,} +}; +#define NUM_TURBO_FREQS (sizeof(pxa255_turbo_freqs)/sizeof(pxa_freqs_t)) + +static struct cpufreq_frequency_table pxa255_turbo_freq_table[NUM_TURBO_FREQS+1]; + +/* find a valid frequency point */ +static int pxa_verify_policy(struct cpufreq_policy *policy) +{ + int ret; + struct cpufreq_frequency_table *pxa_freqs_table; + + if(policy->policy == CPUFREQ_POLICY_PERFORMANCE) { + pxa_freqs_table = pxa255_run_freq_table; + } else if (policy->policy == CPUFREQ_POLICY_POWERSAVE) { + pxa_freqs_table = pxa255_turbo_freq_table; + } else if (policy->policy == CPUFREQ_POLICY_GOVERNOR) { + pxa_freqs_table = pxa255_run_freq_table; + } else { + printk("CPU PXA: Unknown policy found. " + "Using CPUFREQ_POLICY_PERFORMANCE\n"); + pxa_freqs_table = pxa255_run_freq_table; + } + ret=cpufreq_frequency_table_verify(policy, pxa_freqs_table); + + if(freq_debug) { + printk("Verified CPU policy: %dKhz min to %dKhz max\n", + policy->min, policy->max); + } + + return ret; +} + +static int pxa_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int idx; + unsigned long cpus_allowed; + int cpu = policy->cpu; + struct cpufreq_freqs freqs; + pxa_freqs_t *pxa_freq_settings; + struct cpufreq_frequency_table *pxa_freqs_table; + unsigned long flags; + unsigned int unused; + unsigned int preset_mdrefr, postset_mdrefr; + + /* + * Save this threads cpus_allowed mask. + */ + cpus_allowed = current->cpus_allowed; + + /* + * Bind to the specified CPU. When this call returns, + * we should be running on the right CPU. + */ + set_cpus_allowed(current, 1 << cpu); + BUG_ON(cpu != smp_processor_id()); + + /* Get the current policy */ + if(policy->policy == CPUFREQ_POLICY_PERFORMANCE) { + pxa_freq_settings = pxa255_run_freqs; + pxa_freqs_table = pxa255_run_freq_table; + }else if (policy->policy == CPUFREQ_POLICY_POWERSAVE) { + pxa_freq_settings = pxa255_turbo_freqs; + pxa_freqs_table = pxa255_turbo_freq_table; + }else if (policy->policy == CPUFREQ_POLICY_GOVERNOR) { + pxa_freq_settings = pxa255_run_freqs; + pxa_freqs_table = pxa255_run_freq_table; + }else { + printk("CPU PXA: Unknown policy found. " + "Using CPUFREQ_POLICY_PERFORMANCE\n"); + pxa_freq_settings = pxa255_run_freqs; + pxa_freqs_table = pxa255_run_freq_table; + } + + /* Lookup the next frequency */ + if (cpufreq_frequency_table_target(policy, pxa_freqs_table, + target_freq, relation, &idx)) { + return -EINVAL; + } + + freqs.old = policy->cur; + freqs.new = pxa_freq_settings[idx].khz; + freqs.cpu = policy->cpu; + if(freq_debug) { + printk(KERN_INFO "Changing CPU frequency to %d Mhz, (SDRAM %d Mhz)\n", + freqs.new/1000, (pxa_freq_settings[idx].div2) ? + (pxa_freq_settings[idx].membus/2000) : + (pxa_freq_settings[idx].membus/1000)); + } + + void *ramstart = phys_to_virt(0xa0000000); + + /* + * Tell everyone what we're about to do... + * you should add a notify client with any platform specific + * Vcc changing capability + */ + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + /* Calculate the next MDREFR. If we're slowing down the SDRAM clock + * we need to preset the smaller DRI before the change. If we're speeding + * up we need to set the larger DRI value after the change. + */ + preset_mdrefr = postset_mdrefr = MDREFR; + if((MDREFR & MDREFR_DRI_MASK) > MDREFR_DRI(pxa_freq_settings[idx].membus)) { + preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK) | + MDREFR_DRI(pxa_freq_settings[idx].membus); + } + postset_mdrefr = (postset_mdrefr & ~MDREFR_DRI_MASK) | + MDREFR_DRI(pxa_freq_settings[idx].membus); + + /* If we're dividing the memory clock by two for the SDRAM clock, this + * must be set prior to the change. Clearing the divide must be done + * after the change. + */ + if(pxa_freq_settings[idx].div2) { + preset_mdrefr |= MDREFR_DB2_MASK; + postset_mdrefr |= MDREFR_DB2_MASK; + } else { + postset_mdrefr &= ~MDREFR_DB2_MASK; + } + + local_irq_save(flags); + + /* Set new the CCCR */ + CCCR = pxa_freq_settings[idx].cccr; + + __asm__ __volatile__(" \ + ldr r4, [%1] ; /* load MDREFR */ \ + b 2f ; \ + .align 5 ; \ +1: \ + str %4, [%1] ; /* preset the MDREFR */ \ + mcr p14, 0, %2, c6, c0, 0 ; /* set CCLKCFG[FCS] */ \ + str %5, [%1] ; /* postset the MDREFR */ \ + \ + b 3f ; \ +2: b 1b ; \ +3: nop ; \ + " + : "=&r" (unused) + : "r" (&MDREFR), "r" (CCLKCFG_TURBO|CCLKCFG_FCS), "r" (ramstart), \ + "r" (preset_mdrefr), "r" (postset_mdrefr) + : "r4", "r5"); + local_irq_restore(flags); + + /* + * Restore the CPUs allowed mask. + */ + set_cpus_allowed(current, cpus_allowed); + + /* + * Tell everyone what we've just done... + * you should add a notify client with any platform specific + * SDRAM refresh timer adjustments + */ + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int pxa_cpufreq_init(struct cpufreq_policy *policy) +{ + unsigned long cpus_allowed; + unsigned int cpu = policy->cpu; + int i; + + cpus_allowed = current->cpus_allowed; + + set_cpus_allowed(current, 1 << cpu); + BUG_ON(cpu != smp_processor_id()); + + /* set default policy and cpuinfo */ + policy->policy = CPUFREQ_POLICY_PERFORMANCE; + policy->cpuinfo.max_freq = PXA25x_MAX_FREQ; + policy->cpuinfo.min_freq = PXA25x_MIN_FREQ; + policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */ + policy->cur = get_clk_frequency_khz(0); /* current freq */ + policy->min = policy->max = policy->cur; + + /* Generate the run cpufreq_frequency_table struct */ + for(i=0;i #include +#include #include #include @@ -29,31 +32,162 @@ #include #include -static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, - unsigned long phys_addr, pgprot_t pgprot) +extern rwlock_t vmlist_lock; +extern struct vm_struct *vmlist; + +static struct vm_struct * +get_io_vm_area(unsigned long size, unsigned long align, unsigned long flags) { + struct vm_struct **p, *tmp, *area; + unsigned long addr; + + area = (struct vm_struct *)kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return NULL; + + align -= 1; + + size += PAGE_SIZE; + addr = VMALLOC_START; + write_lock(&vmlist_lock); + for (p = &vmlist; (tmp = *p); p = &tmp->next) { + if ((unsigned long)tmp->addr < addr) + continue; + if ((size + addr) < addr) + goto out; + if (size + addr <= (unsigned long) tmp->addr) + break; + addr = tmp->size + (unsigned long) tmp->addr; + if ((addr + align) < addr) + goto out; + addr = (addr + align) & ~align; + if (addr > VMALLOC_END - size) + goto out; + } + area->flags = flags; + area->addr = (void *)addr; + area->size = size; + area->next = *p; + *p = area; + write_unlock(&vmlist_lock); + return area; + +out: + write_unlock(&vmlist_lock); + kfree(area); + return NULL; +} + +static inline void unmap_area_pte(pmd_t *pmd, unsigned long address, unsigned long size) +{ + pte_t *ptep; unsigned long end; + if (pmd_none(*pmd)) + return; + if (pmd_bad(*pmd)) { + pmd_ERROR(*pmd); + pmd_clear(pmd); + return; + } + ptep = pte_offset_kernel(pmd, address); address &= ~PMD_MASK; end = address + size; if (end > PMD_SIZE) end = PMD_SIZE; - if (address >= end) - BUG(); do { - if (!pte_none(*pte)) { - printk("remap_area_pte: page already exists\n"); - BUG(); + pte_t pte; + pte = ptep_get_and_clear(ptep); + address += PAGE_SIZE; + ptep++; + if (pte_none(pte)) + continue; + if (pte_present(pte)) { + unsigned long pfn = pte_pfn(pte); + struct page *page; + + if (!pfn_valid(pfn)) + continue; + page = pfn_to_page(pfn); + if (!PageReserved(page)) + __free_page(page); + continue; } - set_pte(pte, pfn_pte(phys_addr >> PAGE_SHIFT, pgprot)); + printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n"); + } while (address < end); +} + +static inline void unmap_area_pmd(pgd_t *dir, unsigned long address, unsigned long size) +{ + pmd_t *pmd; + unsigned long end; + + if (pgd_none(*dir)) + return; + if (pgd_bad(*dir)) { + pgd_ERROR(*dir); + pgd_clear(dir); + return; + } + pmd = pmd_offset(dir, address); + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + unmap_area_pte(pmd, address, end - address); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); +} + +static void +unmap_area_pages(unsigned long address, unsigned long size) +{ + unsigned long start = address; + unsigned long end = address + size; + pgd_t *dir; + + dir = pgd_offset_k(address); + flush_cache_vunmap(start, end); + do { + unmap_area_pmd(dir, address, end - address); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + flush_tlb_kernel_range(start, end); +} + +static inline void +remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long pfn, pgprot_t pgprot) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + BUG_ON(address >= end); + do { + if (!pte_none(*pte)) + goto bad; + + set_pte(pte, pfn_pte(pfn, pgprot)); address += PAGE_SIZE; - phys_addr += PAGE_SIZE; + pfn++; pte++; } while (address && (address < end)); + return; + + bad: + printk("remap_area_pte: page already exists\n"); + BUG(); } -static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, - unsigned long phys_addr, unsigned long flags) +static inline int +remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long pfn, unsigned long flags) { unsigned long end; pgprot_t pgprot; @@ -64,51 +198,53 @@ if (end > PGDIR_SIZE) end = PGDIR_SIZE; - phys_addr -= address; - if (address >= end) - BUG(); + pfn -= address >> PAGE_SHIFT; + BUG_ON(address >= end); pgprot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | flags); do { pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; - remap_area_pte(pte, address, end - address, address + phys_addr, pgprot); + remap_area_pte(pte, address, end - address, pfn + (address >> PAGE_SHIFT), pgprot); address = (address + PMD_SIZE) & PMD_MASK; pmd++; } while (address && (address < end)); return 0; } -static int remap_area_pages(unsigned long address, unsigned long phys_addr, - unsigned long size, unsigned long flags) +static int +remap_area_pages(unsigned long start, unsigned long pfn, + unsigned long size, unsigned long flags) { - int error; + unsigned long address = start; + unsigned long end = start + size; + int err = 0; pgd_t * dir; - unsigned long end = address + size; - phys_addr -= address; + pfn -= address >> PAGE_SHIFT; dir = pgd_offset(&init_mm, address); - flush_cache_all(); - if (address >= end) - BUG(); + BUG_ON(address >= end); spin_lock(&init_mm.page_table_lock); do { - pmd_t *pmd; - pmd = pmd_alloc(&init_mm, dir, address); - error = -ENOMEM; - if (!pmd) + pmd_t *pmd = pmd_alloc(&init_mm, dir, address); + if (!pmd) { + err = -ENOMEM; break; + } if (remap_area_pmd(pmd, address, end - address, - phys_addr + address, flags)) + pfn + (address >> PAGE_SHIFT), flags)) { + err = -ENOMEM; break; - error = 0; + } + address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } while (address && (address < end)); + spin_unlock(&init_mm.page_table_lock); - flush_tlb_all(); - return error; + flush_cache_vmap(start, end); + return err; } /* @@ -146,11 +282,11 @@ /* * Ok, go for it.. */ - area = get_vm_area(size, VM_IOREMAP); + area = get_io_vm_area(size, align, VM_IOREMAP); if (!area) return NULL; addr = area->addr; - if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { + if (remap_area_pages((unsigned long) addr, phys_addr >> PAGE_SHIFT, size, flags)) { vfree(addr); return NULL; } @@ -159,5 +295,26 @@ void __iounmap(void *addr) { - vfree((void *) (PAGE_MASK & (unsigned long) addr)); + struct vm_struct **p, *tmp; + + if (!addr) + return; + + if ((PAGE_SIZE - 1) & (unsigned long)addr) { + printk(KERN_ERR "Trying to iounmap() bad address (%p)\n", addr); + return; + } + + write_lock(&vmlist_lock); + for (p = &vmlist; (tmp = *p); p = &tmp->next) { + if (tmp->addr == addr) { + *p = tmp->next; + unmap_area_pages((unsigned long) tmp->addr, tmp->size); + write_unlock(&vmlist_lock); + kfree(tmp); + return; + } + } + write_unlock(&vmlist_lock); + printk(KERN_ERR "Trying to iounmap nonexistent area (%p)\n", addr); } --- linux-2.6.5/arch/arm/mm/proc-xscale.S~heh 2004-04-03 22:38:05.000000000 -0500 +++ linux-2.6.5/arch/arm/mm/proc-xscale.S 2004-04-30 20:57:36.000000000 -0400 @@ -563,11 +563,62 @@ movne r2, #0 @ no -> fault str r2, [r0] @ hardware version + + @ We try to map 64K page entries when possible. + @ We do that for kernel space only since the usage pattern from + @ the setting of VM area is quite simple. User space is not worth + @ the implied complexity because of ever randomly changing PTEs + @ (page aging, swapout, etc) requiring constant coherency checks. + @ Since PTEs are usually set in increasing order, we test the + @ possibility for a large page only when given the last PTE of a + @ 64K boundary. + tsteq r1, #L_PTE_USER + andeq r1, r0, #(15 << 2) + teqeq r1, #(15 << 2) + beq 1f + mov ip, #0 mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer mov pc, lr + @ See if we have 16 identical PTEs but with consecutive base addresses +1: bic r3, r2, #0x0000f000 + mov r1, #0x0000f000 +2: eor r2, r2, r3 + teq r2, r1 + bne 4f + subs r1, r1, #0x00001000 + ldr r2, [r0, #-4]! + bne 2b + eors r2, r2, r3 + bne 4f + + @ Now create our LARGE PTE from the current EXT one. + bic r3, r3, #PTE_TYPE_MASK + orr r3, r3, #PTE_TYPE_LARGE + and r2, r3, #0x30 @ EXT_AP --> LARGE_AP0 + orr r2, r2, r2, lsl #2 @ add LARGE_AP1 + orr r2, r2, r2, lsl #4 @ add LARGE_AP3 + LARGE_AP2 + and r1, r3, #0x3c0 @ EXT_TEX + bic r3, r3, #0x3c0 + orr r2, r2, r1, lsl #(12 - 6) @ --> LARGE_TEX + orr r2, r2, r3 @ add remaining bits + + @ then put it in the pagetable + mov r3, r2 +3: strd r2, [r0], #8 + tst r0, #(15 << 2) + bne 3b + + @ Then sync the 2 corresponding cache lines + sub r0, r0, #(16 << 2) + mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line +4: orr r0, r0, #(15 << 2) + mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line + mov ip, #0 + mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer + mov pc, lr .ltorg --- linux-2.6.5/arch/arm/mach-sa1100/Makefile~heh 2004-04-03 22:38:26.000000000 -0500 +++ linux-2.6.5/arch/arm/mach-sa1100/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -3,7 +3,7 @@ # # Common support -obj-y := generic.o irq.o dma.o +obj-y := generic.o irq.o dma.o nmi-oopser.o obj-m := obj-n := obj- := @@ -88,7 +88,7 @@ obj-$(CONFIG_LEDS) += $(led-y) # SA1110 USB client support -#obj-$(CONFIG_SA1100_USB) += usb/ +obj-$(CONFIG_SA1100_USB) += usb/ # Miscelaneous functions obj-$(CONFIG_PM) += pm.o sleep.o --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/strings.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,43 @@ +/* + * usb/strings.h + * + * Copyright (C) 2002 Russell King. + * + * USB device string handling, built upon usb buffers. + */ +#ifndef USBDEV_STRINGS_H +#define USBDEV_STRINGS_H + +#include + +struct usb_buf; + +#define NR_STRINGS 8 + +struct usb_string_descriptor; + +struct usbc_strs { + spinlock_t lock; + struct usb_buf *buf[NR_STRINGS]; +}; + +#define usbc_string_desc(buf) ((struct usb_string_descriptor *)(buf)->data) + +void usbc_string_from_cstr(struct usb_buf *buf, const char *str); +struct usb_buf *usbc_string_alloc(int len); +void usbc_string_free(struct usb_buf *buf); + +int usbc_string_add(struct usbc_strs *table, struct usb_buf *buf); +void usbc_string_del(struct usbc_strs *table, int nr); + +/* + * Note: usbc_string_find() increments the buffer use count. + * You must call usbb_put() after use. + */ +struct usb_buf * +usbc_string_find(struct usbc_strs *table, unsigned int lang, unsigned int idx); + +void usbc_string_free_all(struct usbc_strs *table); +void usbc_string_init(struct usbc_strs *table); + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb_ctl.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,114 @@ +/* + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation 2001 + * + * usb_ctl.h + * + * PRIVATE interface used to share info among components of the SA-1100 USB + * core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core + * should use sa1100_usb.h. + * + */ + +#ifndef _USB_CTL_H +#define _USB_CTL_H + +#include /* dmach_t */ + +struct usb_client; + +struct usb_stats_t { + unsigned long ep0_fifo_write_failures; + unsigned long ep0_bytes_written; + unsigned long ep0_fifo_read_failures; + unsigned long ep0_bytes_read; +}; + +struct usb_info_t +{ + struct usb_client *client; + dma_regs_t *dmach_tx, *dmach_rx; + int state; + unsigned char address; + struct usb_stats_t stats; +}; + +/* in usb_ctl.c */ +extern struct usb_info_t usbd_info; + +/* + * Function Prototypes + */ +enum { kError=-1, kEvSuspend=0, kEvReset=1, + kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 }; +int usbctl_next_state_on_event( int event ); + +/* endpoint zero */ +void ep0_reset(void); +void ep0_int_hndlr(void); + +/* receiver */ +int ep1_recv(void); +int ep1_init(dma_regs_t *dma); +void ep1_int_hndlr(int status); +void ep1_reset(void); +void ep1_stall(void); + +/* xmitter */ +void ep2_reset(void); +int ep2_init(dma_regs_t *dma); +void ep2_int_hndlr(int status); +void ep2_stall(void); + +#define UDC_write(reg, val) { \ + int i = 10000; \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: write %#x to %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) != (val)); \ +} + +#define UDC_set(reg, val) { \ + int i = 10000; \ + do { \ + (reg) |= (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: set %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(!((reg) & (val))); \ +} + +#define UDC_clear(reg, val) { \ + int i = 10000; \ + do { \ + (reg) &= ~(val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) & (val)); \ +} + +#define UDC_flip(reg, val) { \ + int i = 10000; \ + (reg) = (val); \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(((reg) & (val))); \ +} + + +#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}} +#endif /* _USB_CTL_H */ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb_send.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,302 @@ +/* + * Generic xmit layer for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original version which was + * Copyright (c) Compaq Computer Corporation, 1998-1999 + * + * 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 is still work in progress... + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * 15/03/2001 - ep2_start now sets UDCAR to overcome something that is hardware + * bug, I think. green@iXcelerator.com + */ + +#include +#include +#include +#include // for the massive_attack hack 28Feb01ww +#include + +#include + +#include "usbdev.h" +#include "sa1100_usb.h" +#include "sa1100usb.h" + +static unsigned int ep2_curdmalen; +static unsigned int ep2_remain; + +static struct sausb_dev *ep2_dev; + +static void udc_set_cs2(u32 val, u32 mask, u32 check) +{ + int i = 0; + + do { + Ser0UDCCS2 = val; + udelay(1); + if ((Ser0UDCCS2 & mask) == check) + return; + } while (i++ < 10000); + + printk("UDC: UDCCS2 write timed out: val=0x%08x\n", val); +} + +/* set feature stall executing, async */ +static void ep2_start(struct sausb_dev *usb) +{ + ep2_curdmalen = min(ep2_remain, usb->ep[1].maxpktsize); + if (ep2_curdmalen == 0) + return; + + /* + * must do this _before_ queue buffer.. + * stop NAKing IN tokens + */ + udc_set_cs2(usb->ep[1].udccs | UDCCS2_TPC, UDCCS2_TPC, 0); + + UDC_write(Ser0UDCIMP, ep2_curdmalen - 1); + + /* Remove if never seen...8Mar01ww */ + { + int massive_attack = 20; + while (Ser0UDCIMP != ep2_curdmalen - 1 && massive_attack--) { + printk("usbsnd: Oh no you don't! Let me spin..."); + udelay(500); + printk("and try again...\n"); + UDC_write(Ser0UDCIMP, ep2_curdmalen - 1); + } + if (massive_attack != 20) { + if (Ser0UDCIMP != ep2_curdmalen - 1) + printk("usbsnd: Massive attack FAILED. %d\n", + 20 - massive_attack); + else + printk("usbsnd: Massive attack WORKED. %d\n", + 20 - massive_attack); + } + } + /* End remove if never seen... 8Mar01ww */ + + /* + * fight stupid silicon bug + */ + Ser0UDCAR = usb->ctl->address; + + sa1100_start_dma(usb->ep[1].dmach, usb->ep[1].pktdma, ep2_curdmalen); +} + +static void udc_ep2_done(struct sausb_dev *usb, int flag) +{ + int size = usb->ep[1].buflen - ep2_remain; + + if (!usb->ep[1].buflen) + return; + + dma_unmap_single(usb->dev, usb->ep[1].bufdma, usb->ep[1].buflen, + DMA_TO_DEVICE); + + usb->ep[1].bufdma = 0; + usb->ep[1].buflen = 0; + usb->ep[1].pktdma = 0; + + if (usb->ep[1].cb_func) + usb->ep[1].cb_func(usb->ep[1].cb_data, flag, size); +} + +/* + * Initialisation. Clear out the status. + */ +void udc_ep2_init(struct sausb_dev *usb) +{ + ep2_dev = usb; + + usb->ep[1].udccs = UDCCS2_FST; + + BUG_ON(usb->ep[1].buflen); + BUG_ON(usb->ep[1].pktlen); + + sa1100_reset_dma(usb->ep[1].dmach); +} + +/* + * Note: rev A0-B2 chips don't like FST + */ +void udc_ep2_halt(struct sausb_dev *usb, int halt) +{ + usb->ep[1].host_halt = halt; + + if (halt) { + usb->ep[1].udccs |= UDCCS2_FST; + udc_set_cs2(UDCCS2_FST, UDCCS2_FST, UDCCS2_FST); + } else { + sa1100_clear_dma(usb->ep[1].dmach); + + udc_set_cs2(UDCCS2_FST, UDCCS2_FST, UDCCS2_FST); + udc_set_cs2(0, UDCCS2_FST, 0); + udc_set_cs2(UDCCS2_SST, UDCCS2_SST, 0); + + usb->ep[1].udccs &= ~UDCCS2_FST; + + udc_ep2_done(usb, -EINTR); + } +} + +/* + * This gets called when we receive a SET_CONFIGURATION packet to EP0. + * We were configured. We can now send packets to the host. + */ +void udc_ep2_config(struct sausb_dev *usb, unsigned int maxpktsize) +{ + /* + * We shouldn't be transmitting anything... + */ + BUG_ON(usb->ep[1].buflen); + BUG_ON(usb->ep[1].pktlen); + + /* + * Set our configuration. + */ + usb->ep[1].maxpktsize = maxpktsize; + usb->ep[1].configured = 1; + + /* + * Clear any pending TPC status. + */ + udc_set_cs2(UDCCS2_TPC, UDCCS2_TPC, 0); + + /* + * Enable EP2 interrupts. + */ + usb->udccr &= ~UDCCR_TIM; + UDC_write(Ser0UDCCR, usb->udccr); + + usb->ep[1].udccs = 0; +} + +/* + * We saw a reset from the attached hub, or were deconfigured. + * This means we are no longer configured. + */ +void udc_ep2_reset(struct sausb_dev *usb) +{ + /* + * Disable EP2 interrupts. + */ + usb->udccr |= UDCCR_TIM; + UDC_write(Ser0UDCCR, usb->udccr); + + usb->ep[1].configured = 0; + usb->ep[1].maxpktsize = 0; + + sa1100_reset_dma(usb->ep[1].dmach); + udc_ep2_done(usb, -EINTR); +} + +void udc_ep2_int_hndlr(struct sausb_dev *usb) +{ + u32 status = Ser0UDCCS2; + + // check for stupid silicon bug. + if (Ser0UDCAR != usb->ctl->address) + Ser0UDCAR = usb->ctl->address; + + udc_set_cs2(usb->ep[1].udccs | UDCCS2_SST, UDCCS2_SST, 0); + + if (!(status & UDCCS2_TPC)) { + printk("usb_send: Not TPC: UDCCS2 = %x\n", status); + return; + } + + sa1100_stop_dma(usb->ep[1].dmach); + + if (status & (UDCCS2_TPE | UDCCS2_TUR)) { + printk("usb_send: transmit error %x\n", status); + usb->ep[1].fifo_errs ++; + udc_ep2_done(usb, -EIO); + } else { + unsigned int imp; +#if 1 // 22Feb01ww/Oleg + imp = ep2_curdmalen; +#else + // this is workaround for case when setting + // of Ser0UDCIMP was failed + imp = Ser0UDCIMP + 1; +#endif + usb->ep[1].pktdma += imp; + ep2_remain -= imp; + + usb->ep[1].bytes += imp; + usb->ep[1].packets++; + + sa1100_clear_dma(usb->ep[1].dmach); + + if (ep2_remain != 0) { + ep2_start(usb); + } else { + udc_ep2_done(usb, 0); + } + } +} + +int udc_ep2_send(struct sausb_dev *usb, char *buf, int len) +{ + unsigned long flags; + dma_addr_t dma; + int ret; + + if (!buf || len == 0) + return -EINVAL; + + dma = dma_map_single(usb->dev, buf, len, DMA_TO_DEVICE); + + spin_lock_irqsave(&usb->lock, flags); + do { + if (!usb->ep[1].configured) { + ret = -ENODEV; + break; + } + + if (usb->ep[1].buflen) { + ret = -EBUSY; + break; + } + + usb->ep[1].bufdma = dma; + usb->ep[1].buflen = len; + usb->ep[1].pktdma = dma; + ep2_remain = len; + + sa1100_clear_dma(usb->ep[1].dmach); + + ep2_start(usb); + ret = 0; + } while (0); + spin_unlock_irqrestore(&usb->lock, flags); + + if (ret) + dma_unmap_single(usb->dev, dma, len, DMA_TO_DEVICE); + + return ret; +} + +void udc_ep2_send_reset(struct sausb_dev *usb) +{ + sa1100_reset_dma(usb->ep[1].dmach); + udc_ep2_done(usb, -EINTR); +} + +int udc_ep2_idle(struct sausb_dev *usb) +{ + if (!usb->ep[1].configured) + return -ENODEV; + + if (usb->ep[1].buflen) + return -EBUSY; + + return 0; +} --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb-char.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,709 @@ +/* + * (C) Copyright 2000-2001 Extenex Corporation + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * usb-char.c + * + * Miscellaneous character device interface for SA1100 USB function + * driver. + * + * Background: + * The SA1100 function driver ported from the Compaq Itsy project + * has an interface, usb-eth.c, to feed network packets over the + * usb wire and into the Linux TCP/IP stack. + * + * This file replaces that one with a simple character device + * interface that allows unstructured "byte pipe" style reads and + * writes over the USB bulk endpoints by userspace programs. + * + * A new define, CONFIG_SA1100_USB_NETLINK, has been created that, + * when set, (the default) causes the ethernet interface to be used. + * When not set, this more pedestrian character interface is linked + * in instead. + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * + * ward.willats@extenex.com + * + * To do: + * - Can't dma into ring buffer directly with dma_map/unmap usb_recv + * uses and get bytes out at the same time DMA is going on. Investigate: + * a) changing usb_recv to use alloc_consistent() at client request; or + * b) non-ring-buffer based data structures. In the meantime, I am using + * a bounce buffer. Simple, but wasteful. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "usb-char.h" +#include "client.h" + + + +////////////////////////////////////////////////////////////////////////////// +// Driver Options +////////////////////////////////////////////////////////////////////////////// + +#define VERSION "0.4" + + +#define VERBOSITY 1 + +#if VERBOSITY +# define PRINTK(x, a...) printk (x, ## a) +#else +# define PRINTK(x, a...) /**/ +#endif + +////////////////////////////////////////////////////////////////////////////// +// Globals - Macros - Enums - Structures +////////////////////////////////////////////////////////////////////////////// +#ifndef MIN +#define MIN( a, b ) ((a)<(b)?(a):(b)) +#endif + +typedef int bool; enum { false = 0, true = 1 }; + +static const char pszMe[] = "usbchr: "; + +static wait_queue_head_t wq_read; +static wait_queue_head_t wq_write; +static wait_queue_head_t wq_poll; + +/* Serialze multiple writers onto the transmit hardware +.. since we sleep the writer during transmit to stay in +.. sync. (Multiple writers don't make much sense, but..) */ +static DECLARE_MUTEX( xmit_sem ); + +// size of usb DATA0/1 packets. 64 is standard maximum +// for bulk transport, though most hosts seem to be able +// to handle larger. +#define TX_PACKET_SIZE 64 +#define RX_PACKET_SIZE 64 +#define RBUF_SIZE (4*PAGE_SIZE) + +static struct wcirc_buf { + char *buf; + int in; + int out; +} rx_ring = { NULL, 0, 0 }; + +static struct { + unsigned long cnt_rx_complete; + unsigned long cnt_rx_errors; + unsigned long bytes_rx; + unsigned long cnt_tx_timeouts; + unsigned long cnt_tx_errors; + unsigned long bytes_tx; +} charstats; + + +static char * tx_buf = NULL; +static char * packet_buffer = NULL; +static int sending = 0; +static int usb_ref_count = 0; +static int last_tx_result = 0; +static int last_rx_result = 0; +static int last_tx_size = 0; +static struct timer_list tx_timer; + +////////////////////////////////////////////////////////////////////////////// +// Prototypes +////////////////////////////////////////////////////////////////////////////// +static char * what_the_f( int e ); +static void free_txrx_buffers( void ); +static void twiddle_descriptors(struct usb_client *client); +static int usbc_open( struct inode *pInode, struct file *pFile ); +static void rx_done_callback_packet_buffer(void *data, int flag, int size ); + +static void tx_timeout( unsigned long ); +static void tx_done_callback(void *data, int flag, int size ); + +static ssize_t usbc_read( struct file *, char *, size_t, loff_t * ); +static ssize_t usbc_write( struct file *, const char *, size_t, loff_t * ); +static unsigned int usbc_poll( struct file *pFile, poll_table * pWait ); +static int usbc_ioctl( struct inode *pInode, struct file *pFile, + unsigned int nCmd, unsigned long argument ); +static int usbc_close( struct inode *pInode, struct file *pFile ); + +#ifdef CONFIG_SA1100_EXTENEX1 +static void extenex_configured_notify_proc( void ); +#endif +////////////////////////////////////////////////////////////////////////////// +// Private Helpers +////////////////////////////////////////////////////////////////////////////// + +static char * what_the_f( int e ) +{ + char * p; + switch( e ) { + case 0: + p = "noErr"; + break; + case -ENODEV: + p = "ENODEV - usb not in config state"; + break; + case -EBUSY: + p = "EBUSY - another request on the hardware"; + break; + case -EAGAIN: + p = "EAGAIN"; + break; + case -EINTR: + p = "EINTR - interrupted\n"; + break; + case -EPIPE: + p = "EPIPE - zero length xfer\n"; + break; + default: + p = "????"; + break; + } + return p; +} + +static void free_txrx_buffers( void ) +{ + if ( rx_ring.buf != NULL ) { + kfree( rx_ring.buf ); + rx_ring.buf = NULL; + } + if ( packet_buffer != NULL ) { + kfree( packet_buffer ); + packet_buffer = NULL; + } + if ( tx_buf != NULL ) { + kfree( tx_buf ); + tx_buf = NULL; + } +} + +/* twiddle_descriptors() + * It is between open() and start(). Setup descriptors. + */ +static void twiddle_descriptors(struct usb_client *client) +{ + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + cdb->ep1.wMaxPacketSize = cpu_to_le16(RX_PACKET_SIZE); + cdb->ep2.wMaxPacketSize = cpu_to_le16(TX_PACKET_SIZE); + +#ifdef CONFIG_SA1100_EXTENEX1 + if (machine_is_extenex1()) { + int nr; + + cdb->cfg.bmAttributes = USB_CONFIG_SELFPOWERED; + cdb->cfg.MaxPower = 0; + + nr = sa1100_usb_add_string(client->ctl, "HHT Bulk Transfer"); + + if (nr > 0) + cdb->intf.iInterface = nr; + } +#endif +} + +////////////////////////////////////////////////////////////////////////////// +// ASYNCHRONOUS +////////////////////////////////////////////////////////////////////////////// +static void kick_start_rx( void ) +{ + if ( usb_ref_count ) { + int total_space = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE ); + if ( total_space >= RX_PACKET_SIZE ) { + sa1100_usb_recv_set_callback(rx_done_callback_packet_buffer, NULL); + sa1100_usb_recv( packet_buffer, RX_PACKET_SIZE); + } + } +} +/* + * rx_done_callback_packet_buffer() + * We have completed a DMA xfer into the temp packet buffer. + * Move to ring. + * + * flag values: + * on init, -EAGAIN + * on reset, -EINTR + * on RPE, -EIO + * on short packet -EPIPE + */ +static void +rx_done_callback_packet_buffer(void *data, int flag, int size ) +{ + charstats.cnt_rx_complete++; + + if ( flag == 0 || flag == -EPIPE ) { + size_t n; + + charstats.bytes_rx += size; + + n = CIRC_SPACE_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE ); + n = MIN( n, size ); + size -= n; + + memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer, n ); + rx_ring.in = (rx_ring.in + n) & (RBUF_SIZE-1); + memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer + n, size ); + rx_ring.in = (rx_ring.in + size) & (RBUF_SIZE-1); + + wake_up_interruptible( &wq_read ); + wake_up_interruptible( &wq_poll ); + + last_rx_result = 0; + + kick_start_rx(); + + } else if ( flag != -EAGAIN ) { + charstats.cnt_rx_errors++; + last_rx_result = flag; + wake_up_interruptible( &wq_read ); + wake_up_interruptible( &wq_poll ); + } + else /* init, start a read */ + kick_start_rx(); +} + + +static void tx_timeout( unsigned long unused ) +{ + printk( "%stx timeout\n", pszMe ); + sa1100_usb_send_reset(); + charstats.cnt_tx_timeouts++; +} + + +// on init, -EAGAIN +// on reset, -EINTR +// on TPE, -EIO +static void tx_done_callback(void *data, int flags, int size ) +{ + if ( flags == 0 ) + charstats.bytes_tx += size; + else + charstats.cnt_tx_errors++; + last_tx_size = size; + last_tx_result = flags; + sending = 0; + wake_up_interruptible( &wq_write ); + wake_up_interruptible( &wq_poll ); +} + + +static struct usb_client usbc_client = { + .name = "usb-char", + + /* + * USB client identification for host use in CPU endian. + */ + .vendor = 0, + .product = 0, + .version = 0, + .class = 0xff, + .subclass = 0, + .protocol = 0, +}; + +#ifdef CONFIG_SA1100_EXTENEX1 +#include "../../../drivers/char/ex_gpio.h" +static void extenex_state_change(void *data, int state, int oldstate) +{ + if (exgpio_play_string( "440,1:698,1") == -EAGAIN) + printk( "%sWanted to BEEP but ex_gpio not open\n", pszMe ); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// Workers +////////////////////////////////////////////////////////////////////////////// + +static int usbc_open(struct inode *pInode, struct file *pFile) +{ + int retval = 0; + + PRINTK( KERN_DEBUG "%sopen()\n", pszMe ); + +#ifdef CONFIG_SA1100_EXTENEX1 + if (machine_is_extenex1()) { + usbc_client.vendor = 0x0c9f; + usbc_client.product = 0x0100; + usbc_client.version = 0x0001; + usbc_client.manufacturer_str = "Extenex"; + usbc_client.product_str = "Handheld Theater"; + usbc_client.serial_str = "00000000"; + usbc_client.state_change = extenex_state_change; + } +#endif + + /* start usb core */ + retval = usbctl_open(&usbc_client); + if (retval) + return retval; + + /* allocate memory */ + if ( usb_ref_count == 0 ) { + tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); + if ( tx_buf == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE TX BUFFER\n", pszMe ); + goto malloc_fail; + } + rx_ring.buf = + (char*) kmalloc( RBUF_SIZE, GFP_KERNEL ); + + if ( rx_ring.buf == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE RX BUFFER\n", pszMe ); + goto malloc_fail; + } + + packet_buffer = + (char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); + + if ( packet_buffer == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n", pszMe ); + goto malloc_fail; + } + rx_ring.in = rx_ring.out = 0; + memset( &charstats, 0, sizeof( charstats ) ); + sending = 0; + last_tx_result = 0; + last_tx_size = 0; + } + + /* modify default descriptors */ + twiddle_descriptors(&usbc_client); + + retval = usbctl_start(&usbc_client); + if ( retval ) { + printk( "%sAGHH! Could not USB core\n", pszMe ); + free_txrx_buffers(); + return retval; + } + usb_ref_count++; /* must do _before_ kick_start() */ + MOD_INC_USE_COUNT; + kick_start_rx(); + return 0; + + malloc_fail: + free_txrx_buffers(); + return -ENOMEM; +} + +/* + * Read endpoint. Note that you can issue a read to an + * unconfigured endpoint. Eventually, the host may come along + * and configure underneath this module and data will appear. + */ +static ssize_t usbc_read( struct file *pFile, char *pUserBuffer, + size_t stCount, loff_t *pPos ) +{ + ssize_t retval; + unsigned long flags; + DECLARE_WAITQUEUE( wait, current ); + + PRINTK( KERN_DEBUG "%sread()\n", pszMe ); + + local_irq_save(flags); + if ( last_rx_result == 0 ) { + local_irq_restore( flags ); + } else { /* an error happended and receiver is paused */ + local_irq_restore( flags ); + last_rx_result = 0; + kick_start_rx(); + } + + add_wait_queue( &wq_read, &wait ); + while( 1 ) { + ssize_t bytes_avail; + ssize_t bytes_to_end; + + set_current_state( TASK_INTERRUPTIBLE ); + + /* snap ring buf state */ + local_irq_save( flags ); + bytes_avail = CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ); + bytes_to_end = CIRC_CNT_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE ); + local_irq_restore( flags ); + + if ( bytes_avail != 0 ) { + ssize_t bytes_to_move = MIN( stCount, bytes_avail ); + retval = 0; // will be bytes transfered + if ( bytes_to_move != 0 ) { + size_t n = MIN( bytes_to_end, bytes_to_move ); + if ( copy_to_user( pUserBuffer, + &rx_ring.buf[ rx_ring.out ], + n ) ) { + retval = -EFAULT; + break; + } + bytes_to_move -= n; + retval += n; + // might go 1 char off end, so wrap + rx_ring.out = ( rx_ring.out + n ) & (RBUF_SIZE-1); + if ( copy_to_user( pUserBuffer + n, + &rx_ring.buf[ rx_ring.out ], + bytes_to_move ) + ) { + retval = -EFAULT; + break; + } + rx_ring.out += bytes_to_move; // cannot wrap + retval += bytes_to_move; + kick_start_rx(); + } + break; + } + else if ( last_rx_result ) { + retval = last_rx_result; + break; + } + else if ( pFile->f_flags & O_NONBLOCK ) { // no data, can't sleep + retval = -EAGAIN; + break; + } + else if ( signal_pending( current ) ) { // no data, can sleep, but signal + retval = -ERESTARTSYS; + break; + } + schedule(); // no data, can sleep + } + set_current_state( TASK_RUNNING ); + remove_wait_queue( &wq_read, &wait ); + + if ( retval < 0 ) + printk( "%sread error %d - %s\n", pszMe, retval, what_the_f( retval ) ); + return retval; +} + +/* + * Write endpoint. This routine attempts to break the passed in buffer + * into usb DATA0/1 packet size chunks and send them to the host. + * (The lower-level driver tries to do this too, but easier for us + * to manage things here.) + * + * We are at the mercy of the host here, in that it must send an IN + * token to us to pull this data back, so hopefully some higher level + * protocol is expecting traffic to flow in that direction so the host + * is actually polling us. To guard against hangs, a 5 second timeout + * is used. + * + * This routine takes some care to only report bytes sent that have + * actually made it across the wire. Thus we try to stay in lockstep + * with the completion routine and only have one packet on the xmit + * hardware at a time. Multiple simultaneous writers will get + * "undefined" results. + * + */ +static ssize_t usbc_write( struct file *pFile, const char * pUserBuffer, + size_t stCount, loff_t *pPos ) +{ + ssize_t retval = 0; + ssize_t stSent = 0; + + DECLARE_WAITQUEUE( wait, current ); + + PRINTK( KERN_DEBUG "%swrite() %d bytes\n", pszMe, stCount ); + + down( &xmit_sem ); // only one thread onto the hardware at a time + + while( stCount != 0 && retval == 0 ) { + int nThisTime = MIN( TX_PACKET_SIZE, stCount ); + copy_from_user( tx_buf, pUserBuffer, nThisTime ); + sending = nThisTime; + sa1100_usb_send_set_callback(tx_done_callback, NULL); + retval = sa1100_usb_send( tx_buf, nThisTime); + if ( retval < 0 ) { + char * p = what_the_f( retval ); + printk( "%sCould not queue xmission. rc=%d - %s\n", + pszMe, retval, p ); + sending = 0; + break; + } + /* now have something on the diving board */ + add_wait_queue( &wq_write, &wait ); + tx_timer.expires = jiffies + ( HZ * 5 ); + add_timer( &tx_timer ); + while( 1 ) { + set_current_state( TASK_INTERRUPTIBLE ); + if ( sending == 0 ) { /* it jumped into the pool */ + del_timer( &tx_timer ); + retval = last_tx_result; + if ( retval == 0 ) { + stSent += last_tx_size; + pUserBuffer += last_tx_size; + stCount -= last_tx_size; + } + else + printk( "%sxmission error rc=%d - %s\n", + pszMe, retval, what_the_f(retval) ); + break; + } + else if ( signal_pending( current ) ) { + del_timer( &tx_timer ); + printk( "%ssignal\n", pszMe ); + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state( TASK_RUNNING ); + remove_wait_queue( &wq_write, &wait ); + } + + up( &xmit_sem ); + + if ( 0 == retval ) + retval = stSent; + return retval; +} + +static unsigned int usbc_poll( struct file *pFile, poll_table * pWait ) +{ + unsigned int retval = 0; + + PRINTK( KERN_DEBUG "%poll()\n", pszMe ); + + poll_wait( pFile, &wq_poll, pWait ); + + if ( CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ) ) + retval |= POLLIN | POLLRDNORM; + if ( sa1100_usb_xmitter_avail() ) + retval |= POLLOUT | POLLWRNORM; + return retval; +} + +static int usbc_ioctl( struct inode *pInode, struct file *pFile, + unsigned int nCmd, unsigned long argument ) +{ + int retval = 0; + + switch( nCmd ) { + + case USBC_IOC_FLUSH_RECEIVER: + sa1100_usb_recv_reset(); + rx_ring.in = rx_ring.out = 0; + break; + + case USBC_IOC_FLUSH_TRANSMITTER: + sa1100_usb_send_reset(); + break; + + case USBC_IOC_FLUSH_ALL: + sa1100_usb_recv_reset(); + rx_ring.in = rx_ring.out = 0; + sa1100_usb_send_reset(); + break; + + default: + retval = -ENOIOCTLCMD; + break; + + } + return retval; +} + + +static int usbc_close( struct inode *pInode, struct file * pFile ) +{ + PRINTK( KERN_DEBUG "%sclose()\n", pszMe ); + if ( --usb_ref_count == 0 ) { + down( &xmit_sem ); + usbctl_stop(&usbc_client); + free_txrx_buffers(); + del_timer( &tx_timer ); + usbctl_close(&usbc_client); + up(&xmit_sem); + } + MOD_DEC_USE_COUNT; + return 0; +} + +////////////////////////////////////////////////////////////////////////////// +// Initialization +////////////////////////////////////////////////////////////////////////////// + +static struct file_operations usbc_fops = { + owner: THIS_MODULE, + open: usbc_open, + read: usbc_read, + write: usbc_write, + poll: usbc_poll, + ioctl: usbc_ioctl, + release: usbc_close, +}; + +static struct miscdevice usbc_misc_device = { + USBC_MINOR, + "usb_char", + &usbc_fops +}; + +/* + * usbc_init() + */ + +static int __init usbc_init( void ) +{ + int rc; + +#if !defined( CONFIG_ARCH_SA1100 ) + return -ENODEV; +#endif + + if ( (rc = misc_register( &usbc_misc_device )) != 0 ) { + printk( KERN_WARNING "%sCould not register device 10, " + "%d. (%d)\n", pszMe, USBC_MINOR, rc ); + return -EBUSY; + } + + // initialize wait queues + init_waitqueue_head( &wq_read ); + init_waitqueue_head( &wq_write ); + init_waitqueue_head( &wq_poll ); + + // initialize tx timeout timer + init_timer( &tx_timer ); + tx_timer.function = tx_timeout; + + printk( KERN_INFO "USB Function Character Driver Interface" + " - %s, (C) 2001, Extenex Corp.\n", VERSION + ); + + return rc; +} + +static void __exit usbc_exit( void ) +{ +} + +module_init(usbc_init); +module_exit(usbc_exit); + +// end: usb-char.c + +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb-eth.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,535 @@ +/* + * Network driver for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original initial ethernet test driver + * Copyright (c) Compaq Computer Corporation, 1999 + * + * 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. + * + * Issues: + * - DMA needs 8 byte aligned buffer, but causes inefficiencies + * in the IP code. + * - stall endpoint operations appeared to be very unstable. + */ + +/* + * Define RX_NO_COPY if you want data to arrive directly into the + * receive network buffers, instead of arriving into bounce buffer + * and then get copied to network buffer. + * + * Since the SA1100 DMA engine is unable to cope with unaligned + * buffer addresses, we need to use bounce buffers or suffer the + * alignment trap performance hit. + */ +#undef RX_NO_COPY + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" + + +#define ETHERNET_VENDOR_ID 0x049f +#define ETHERNET_PRODUCT_ID 0x505A +#define MAX_PACKET 32768 + +/* + * This is our usb "packet size", and must match the host "packet size". + */ +static int usb_rsize = 64; +static int usb_wsize = 64; + +struct usbe_info { + struct net_device dev; + struct usb_client client; + struct sk_buff *cur_tx_skb; + struct sk_buff *next_tx_skb; + struct sk_buff *cur_rx_skb; + struct sk_buff *next_rx_skb; +#ifndef RX_NO_COPY + char *dmabuf; // dma expects it's buffers to be aligned on 8 bytes boundary +#endif + struct net_device_stats stats; +}; + + +static int usbeth_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu <= sizeof(struct ethhdr) || new_mtu > MAX_PACKET) + return -EINVAL; + + // no second zero-length packet read wanted after mtu-sized packets + if (((new_mtu + sizeof(struct ethhdr)) % usb_rsize) == 0) + return -EDOM; + + dev->mtu = new_mtu; + return 0; +} + +static struct sk_buff *usb_new_recv_skb(struct usbe_info *usbe) +{ + struct sk_buff *skb; + + skb = alloc_skb(2 + sizeof(struct ethhdr) + usbe->dev.mtu, + GFP_ATOMIC); + + if (skb) + skb_reserve(skb, 2); + + return skb; +} + +static void usbeth_recv_callback(void *data, int flag, int len) +{ + struct usbe_info *usbe = data; + struct sk_buff *skb; + unsigned int size; + char *buf; + + skb = usbe->cur_rx_skb; + + /* flag validation */ + if (flag != 0) + goto error; + + /* + * Make sure we have enough room left in the buffer. + */ + if (len > skb_tailroom(skb)) { + usbe->stats.rx_over_errors++; + usbe->stats.rx_errors++; + goto oversize; + } + + /* + * If the packet is smaller than usb_rsize bytes, the packet + * is complete, and we need to use the next receive buffer. + */ + if (len != usb_rsize) + usbe->cur_rx_skb = usbe->next_rx_skb; + + /* + * Put the data onto the socket buffer and resume USB receive. + */ +#ifndef RX_NO_COPY + memcpy(skb_put(skb, len), usbe->dmabuf, len); + buf = usbe->dmabuf; + size = usb_rsize; +#else + skb_put(skb, len); + buf = usbe->cur_rx_skb->tail; + size = skb_tailroom(usbe->cur_rx_skb); +#endif + usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size); + + if (len == usb_rsize) + return; + + /* + * A frame must contain at least an ethernet header. + */ + if (skb->len < sizeof(struct ethhdr)) { + usbe->stats.rx_length_errors++; + usbe->stats.rx_errors++; + goto recycle; + } + + /* + * MAC must match our address or the broadcast address. + * Really, we should let any packet through, otherwise + * things that rely on multicast won't work. + */ + if (memcmp(skb->data, usbe->dev.dev_addr, ETH_ALEN) && + memcmp(skb->data, usbe->dev.broadcast, ETH_ALEN)) { + usbe->stats.rx_frame_errors++; + usbe->stats.rx_errors++; + goto recycle; + } + + /* + * We're going to consume this SKB. Get a new skb to + * replace it with. IF this fails, we'd better recycle + * the one we have. + */ + usbe->next_rx_skb = usb_new_recv_skb(usbe); + if (!usbe->next_rx_skb) { + if (net_ratelimit()) + printk(KERN_ERR "%s: can't allocate new rx skb\n", + usbe->dev.name); + usbe->stats.rx_dropped++; + goto recycle; + } + +// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ? + + usbe->stats.rx_packets++; + usbe->stats.rx_bytes += skb->len; + usbe->dev.last_rx = jiffies; + + skb->dev = &usbe->dev; + skb->protocol = eth_type_trans(skb, &usbe->dev); + skb->ip_summed = CHECKSUM_NONE; + + if (netif_rx(skb) == NET_RX_DROP) + usbe->stats.rx_dropped++; + return; + + error: + /* + * Oops, IO error, or stalled. + */ + switch (flag) { + case -EIO: /* aborted transfer */ + usbe->stats.rx_errors++; + break; + + case -EPIPE: /* fifo screwed/no data */ + usbe->stats.rx_fifo_errors++; + usbe->stats.rx_errors++; + break; + + case -EINTR: /* reset */ + break; + + case -EAGAIN: /* initialisation */ + break; + } + + oversize: + skb_trim(skb, 0); + +#ifndef RX_NO_COPY + buf = usbe->dmabuf; + size = usb_rsize; +#else + buf = skb->tail; + size = skb_tailroom(skb); +#endif + usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size); + return; + + recycle: + skb_trim(skb, 0); + usbe->next_rx_skb = skb; + return; +} + +/* + * Send a skb. + * + * Note that the receiver expects the last packet to be a non-multiple + * of its rsize. If the packet length is a muliple of wsize (and + * therefore the remote rsize) tweak the length. + */ +static void usbeth_send(struct sk_buff *skb, struct usbe_info *usbe) +{ + unsigned int len = skb->len; + int ret; + + if ((len % usb_wsize) == 0) + len++; + + ret = usbctl_ep_queue_buffer(usbe->client.ctl, 2, skb->data, len); + if (ret) { + printk(KERN_ERR "%s: tx dropping packet: %d\n", + usbe->dev.name, ret); + + /* + * If the USB core can't accept the packet, we drop it. + */ + dev_kfree_skb_irq(skb); + + usbe->cur_tx_skb = NULL; + usbe->stats.tx_carrier_errors++; + } else { + usbe->dev.trans_start = jiffies; + } +} + +static void usbeth_send_callback(void *data, int flag, int size) +{ + struct usbe_info *usbe = data; + struct sk_buff *skb = usbe->cur_tx_skb; + + switch (flag) { + case 0: + usbe->stats.tx_packets++; + usbe->stats.tx_bytes += skb->len; + break; + case -EIO: + usbe->stats.tx_errors++; + break; + default: + usbe->stats.tx_dropped++; + break; + } + + dev_kfree_skb_irq(skb); + + skb = usbe->cur_tx_skb = usbe->next_tx_skb; + usbe->next_tx_skb = NULL; + + if (skb) + usbeth_send(skb, usbe); + + netif_wake_queue(&usbe->dev); +} + +static int usbeth_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + unsigned long flags; + + if (usbe->next_tx_skb) { + printk(KERN_ERR "%s: called with next_tx_skb != NULL\n", + usbe->dev.name); + return 1; + } + + local_irq_save(flags); + if (usbe->cur_tx_skb) { + usbe->next_tx_skb = skb; + netif_stop_queue(dev); + } else { + usbe->cur_tx_skb = skb; + + usbeth_send(skb, usbe); + } + local_irq_restore(flags); + return 0; +} + +/* + * Transmit timed out. Reset the endpoint, and re-queue the pending + * packet. If we have a free transmit slot, wake the transmit queue. + */ +static void usbeth_xmit_timeout(struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + unsigned long flags; + + usbctl_ep_reset(usbe->client.ctl, 2); + + local_irq_save(flags); + if (usbe->cur_tx_skb) + usbeth_send(usbe->cur_tx_skb, usbe); + + if (usbe->next_tx_skb == NULL) + netif_wake_queue(dev); + + usbe->stats.tx_errors++; + local_irq_restore(flags); +} + +static int usbeth_open(struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + unsigned char *buf; + unsigned int size; + + usbctl_ep_set_callback(usbe->client.ctl, 2, usbeth_send_callback, usbe); + usbctl_ep_set_callback(usbe->client.ctl, 1, usbeth_recv_callback, usbe); + + usbe->cur_tx_skb = usbe->next_tx_skb = NULL; + usbe->cur_rx_skb = usb_new_recv_skb(usbe); + usbe->next_rx_skb = usb_new_recv_skb(usbe); + if (!usbe->cur_rx_skb || !usbe->next_rx_skb) { + printk(KERN_ERR "%s: can't allocate new skb\n", + usbe->dev.name); + if (usbe->cur_rx_skb) + kfree_skb(usbe->cur_rx_skb); + if (usbe->next_rx_skb) + kfree_skb(usbe->next_rx_skb); + return -ENOMEM;; + } +#ifndef RX_NO_COPY + buf = usbe->dmabuf; + size = usb_rsize; +#else + buf = usbe->cur_rx_skb->tail; + size = skb_tailroom(usbe->cur_rx_skb); +#endif + usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size); + + if (netif_carrier_ok(dev)) + netif_start_queue(dev); + + return 0; +} + +static int usbeth_close(struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + + netif_stop_queue(dev); + + usbctl_ep_set_callback(usbe->client.ctl, 2, NULL, NULL); + usbctl_ep_set_callback(usbe->client.ctl, 1, NULL, NULL); + usbctl_ep_reset(usbe->client.ctl, 2); + usbctl_ep_reset(usbe->client.ctl, 1); + + if (usbe->cur_tx_skb) + kfree_skb(usbe->cur_tx_skb); + if (usbe->next_tx_skb) + kfree_skb(usbe->next_tx_skb); + if (usbe->cur_rx_skb) + kfree_skb(usbe->cur_rx_skb); + if (usbe->next_rx_skb) + kfree_skb(usbe->next_rx_skb); + + return 0; +} + +static struct net_device_stats *usbeth_stats(struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + + return &usbe->stats; +} + +static int __init usbeth_probe(struct net_device *dev) +{ + u8 node_id[ETH_ALEN]; + + SET_MODULE_OWNER(dev); + + /* + * Assign the hardware address of the board: + * generate it randomly, as there can be many such + * devices on the bus. + */ + get_random_bytes(node_id, sizeof node_id); + node_id[0] &= 0xfe; // clear multicast bit + memcpy(dev->dev_addr, node_id, sizeof node_id); + + ether_setup(dev); + dev->flags &= ~IFF_MULTICAST; + dev->flags &= ~IFF_BROADCAST; + //dev->flags |= IFF_NOARP; + + return 0; +} + +/* + * This is called when something in the upper usb client layers + * changes that affects the endpoint connectivity state (eg, + * connection or disconnection from the host.) We probably want + * to do some more handling here, like kicking off a pending + * transmission if we're running? + */ +static void usbeth_state_change(void *data, int state, int oldstate) +{ + struct usbe_info *usbe = data; + + if (state == USB_STATE_CONFIGURED) { + netif_carrier_on(&usbe->dev); + if (netif_running(&usbe->dev)) + netif_wake_queue(&usbe->dev); + } else { + if (netif_running(&usbe->dev)) + netif_stop_queue(&usbe->dev); + netif_carrier_off(&usbe->dev); + } +} + +static struct usbe_info usbe_info = { + .dev = { + .name = "usbf", + .init = usbeth_probe, + .get_stats = usbeth_stats, + .watchdog_timeo = 1 * HZ, + .open = usbeth_open, + .stop = usbeth_close, + .hard_start_xmit = usbeth_xmit, + .change_mtu = usbeth_change_mtu, + .tx_timeout = usbeth_xmit_timeout, + .priv = &usbe_info, + }, + .client = { + .name = "usbeth", + .priv = &usbe_info, + .state_change = usbeth_state_change, + + /* + * USB client identification for host use in CPU endian. + */ + .vendor = ETHERNET_VENDOR_ID, + .product = ETHERNET_PRODUCT_ID, + .version = 0, + .class = 0xff, /* vendor specific */ + .subclass = 0, + .protocol = 0, + + .product_str = "SA1100 USB NIC", + }, +}; + +static int __init usbeth_init(void) +{ + int rc; + +#ifndef RX_NO_COPY + usbe_info.dmabuf = kmalloc(usb_rsize, GFP_KERNEL | GFP_DMA); + if (!usbe_info.dmabuf) + return -ENOMEM; +#endif + + if (register_netdev(&usbe_info.dev) != 0) { +#ifndef RX_NO_COPY + kfree(usbe_info.dmabuf); +#endif + return -EIO; + } + + rc = usbctl_open(&usbe_info.client); + if (rc == 0) { + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + cdb->ep1.wMaxPacketSize = cpu_to_le16(usb_rsize); + cdb->ep2.wMaxPacketSize = cpu_to_le16(usb_wsize); + + rc = usbctl_start(&usbe_info.client); + if (rc) + usbctl_close(&usbe_info.client); + } + + if (rc) { + unregister_netdev(&usbe_info.dev); +#ifndef RX_NO_COPY + kfree(usbe_info.dmabuf); +#endif + } + + return rc; +} + +static void __exit usbeth_cleanup(void) +{ + usbctl_stop(&usbe_info.client); + usbctl_close(&usbe_info.client); + + unregister_netdev(&usbe_info.dev); +#ifndef RX_NO_COPY + kfree(usbe_info.dmabuf); +#endif +} + +module_init(usbeth_init); +module_exit(usbeth_cleanup); + +MODULE_DESCRIPTION("USB client ethernet driver"); +MODULE_PARM(usb_rsize, "1i"); +MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to sa11x0"); +MODULE_PARM(usb_wsize, "1i"); +MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from sa11x0 to host"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb_recv.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,318 @@ +/* + * Generic receive layer for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original version which was + * Copyright (c) Compaq Computer Corporation, 1998-1999 + * + * 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 is still work in progress... + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + */ + +#include +#include +#include +#include + +#include +#include + +#include "sa1100_usb.h" +#include "sa1100usb.h" + +static int naking; + +#if 1 +static void dump_buf(struct sausb_dev *usb, const char *prefix) +{ + printk("%s: buf [dma=%08x len=%3d] pkt [cpu=%08x dma=%08x len=%3d rem=%3d]\n", + prefix, + usb->ep[0].bufdma, + usb->ep[0].buflen, + (unsigned int)usb->ep[0].pktcpu, + usb->ep[0].pktdma, + usb->ep[0].pktlen, + usb->ep[0].pktrem); +} +#endif + +static void udc_ep1_done(struct sausb_dev *usb, int flag, int size) +{ +// printk("UDC: rxd: %3d %3d\n", flag, size); + dump_buf(usb, "UDC: rxd"); + + if (!usb->ep[0].buflen) + return; + + dma_unmap_single(usb->dev, usb->ep[0].bufdma, usb->ep[0].buflen, + DMA_FROM_DEVICE); + + usb->ep[0].bufdma = 0; + usb->ep[0].buflen = 0; + usb->ep[0].pktcpu = NULL; + usb->ep[0].pktdma = 0; + usb->ep[0].pktlen = 0; + usb->ep[0].pktrem = 0; + + if (usb->ep[0].cb_func) + usb->ep[0].cb_func(usb->ep[0].cb_data, flag, size); +} + +/* + * Initialisation. Clear out the status, and set FST. + */ +void udc_ep1_init(struct sausb_dev *usb) +{ + sa1100_reset_dma(usb->ep[0].dmach); + + UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC); + + BUG_ON(usb->ep[0].buflen); + BUG_ON(usb->ep[0].pktlen); +} + +void udc_ep1_halt(struct sausb_dev *usb, int halt) +{ + if (halt) { + /* force stall at UDC */ + UDC_set(Ser0UDCCS1, UDCCS1_FST); + } else { + sa1100_reset_dma(usb->ep[0].dmach); + + UDC_clear(Ser0UDCCS1, UDCCS1_FST); + + udc_ep1_done(usb, -EINTR, 0); + } +} + +/* + * This gets called when we receive a SET_CONFIGURATION packet to EP0. + * We were configured. We can now accept packets from the host. + */ +void udc_ep1_config(struct sausb_dev *usb, unsigned int maxpktsize) +{ + usb->ep[0].maxpktsize = maxpktsize; + usb->ep[0].configured = 1; + + Ser0UDCOMP = maxpktsize - 1; + + sa1100_reset_dma(usb->ep[0].dmach); + udc_ep1_done(usb, -EINTR, 0); + + /* + * Enable EP1 interrupts. + */ + usb->udccr &= ~UDCCR_RIM; + UDC_write(Ser0UDCCR, usb->udccr); +} + +/* + * We saw a reset from the attached hub. This means we are no + * longer configured, and as far as the rest of the world is + * concerned, we don't exist. + */ +void udc_ep1_reset(struct sausb_dev *usb) +{ + /* + * Disable EP1 interrupts. + */ + usb->udccr |= UDCCR_RIM; + UDC_write(Ser0UDCCR, usb->udccr); + + usb->ep[0].configured = 0; + usb->ep[0].maxpktsize = 0; + + sa1100_reset_dma(usb->ep[0].dmach); + udc_ep1_done(usb, -EINTR, 0); +} + +void udc_ep1_int_hndlr(struct sausb_dev *usb) +{ + dma_addr_t dma_addr; + unsigned int len; + u32 status = Ser0UDCCS1; + + dump_buf(usb, "UDC: int"); + + if (naking) { + printk("UDC: usbrx: in ISR but naking [0x%02x]\n", status); + return; + } + + if (!(status & UDCCS1_RPC)) + /* you can get here if we are holding NAK */ + return; + + if (!usb->ep[0].buflen) { + printk("UDC: usb_recv: RPC for non-existent buffer [0x%02x]\n", status); + naking = 1; + return; + } + + sa1100_stop_dma(usb->ep[0].dmach); + + dma_addr = sa1100_get_dma_pos(usb->ep[0].dmach); + + /* + * We've finished with the DMA for this packet. + */ + sa1100_clear_dma(usb->ep[0].dmach); + + if (status & UDCCS1_SST) { + printk("UDC: usb_recv: stall sent\n"); + UDC_flip(Ser0UDCCS1, UDCCS1_SST); + + /* + * UDC aborted current transfer, so we do. + * + * It would be better to re-queue this buffer IMHO. It + * hasn't gone anywhere yet. --rmk + */ + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + udc_ep1_done(usb, -EIO, 0); + return; + } + + if (status & UDCCS1_RPE) { + printk("UDC: usb_recv: RPError %x\n", status); + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + udc_ep1_done(usb, -EIO, 0); + return; + } + + len = dma_addr - usb->ep[0].pktdma; + if (len < 0) { + printk("UDC: usb_recv: dma_addr (%x) < pktdma (%x)\n", + dma_addr, usb->ep[0].pktdma); + len = 0; + } + + if (len > usb->ep[0].pktlen) + len = usb->ep[0].pktlen; + + /* + * If our transfer was smaller, and we have bytes left in + * the FIFO, we need to read them out manually. + */ + if (len < usb->ep[0].pktlen && (Ser0UDCCS1 & UDCCS1_RNE)) { + char *buf; + + dma_sync_single(usb->dev, usb->ep[0].pktdma + len, + usb->ep[0].pktlen - len, DMA_FROM_DEVICE); + + buf = (char *)usb->ep[0].pktcpu + len; + + do { + *buf++ = Ser0UDCDR; + len++; + } while (len < usb->ep[0].pktlen && (Ser0UDCCS1 & UDCCS1_RNE)); + + /* + * Note: knowing the internals of this macro is BAD, but we + * need this to cause the data to be written back to memory. + */ + dma_sync_single(usb->dev, usb->ep[0].pktdma + len, + usb->ep[0].pktlen - len, DMA_TO_DEVICE); + } + + /* + * If the FIFO still contains data, something's definitely wrong. + */ + if (Ser0UDCCS1 & UDCCS1_RNE) { + printk("UDC: usb_recv: fifo screwed, shouldn't contain data\n"); + usb->ep[0].fifo_errs++; + naking = 1; + udc_ep1_done(usb, -EPIPE, 0); + return; + } + + /* + * Do statistics. + */ + if (len) { + usb->ep[0].bytes += len; + usb->ep[0].packets ++; + } + + /* + * Update remaining byte count for this buffer. + */ + usb->ep[0].pktrem -= len; + + /* + * If we received a full-sized packet, and there's more + * data remaining, th, queue up another receive. + */ + if (len == usb->ep[0].pktlen && usb->ep[0].pktrem != 0) { + usb->ep[0].pktcpu += len; + usb->ep[0].pktdma += len; + usb->ep[0].pktlen = min(usb->ep[0].pktrem, usb->ep[0].maxpktsize); + sa1100_start_dma(usb->ep[0].dmach, usb->ep[0].pktdma, usb->ep[0].pktlen); + /* + * Clear RPC to receive next packet. + */ + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + dump_buf(usb, "UDC: req"); + return; + } + + naking = 1; + udc_ep1_done(usb, 0, usb->ep[0].buflen - usb->ep[0].pktrem); +} + +int udc_ep1_queue_buffer(struct sausb_dev *usb, char *buf, unsigned int len) +{ + unsigned long flags; + dma_addr_t dma; + int ret; + + if (!buf || len == 0) + return -EINVAL; + + dma = dma_map_single(usb->dev, buf, len, DMA_FROM_DEVICE); + + spin_lock_irqsave(&usb->lock, flags); + do { + if (usb->ep[0].buflen) { + ret = -EBUSY; + break; + } + + sa1100_clear_dma(usb->ep[0].dmach); + + usb->ep[0].bufdma = dma; + usb->ep[0].buflen = len; + usb->ep[0].pktcpu = buf; + usb->ep[0].pktdma = dma; + usb->ep[0].pktlen = min(len, usb->ep[0].maxpktsize); + usb->ep[0].pktrem = len; + + sa1100_start_dma(usb->ep[0].dmach, usb->ep[0].bufdma, usb->ep[0].buflen); + dump_buf(usb, "UDC: que"); + + if (naking) { + /* turn off NAK of OUT packets, if set */ + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + naking = 0; + } + + ret = 0; + } while (0); + spin_unlock_irqrestore(&usb->lock, flags); + + if (ret) + dma_unmap_single(usb->dev, dma, len, DMA_FROM_DEVICE); + + return 0; +} + +void udc_ep1_recv_reset(struct sausb_dev *usb) +{ + sa1100_reset_dma(usb->ep[0].dmach); + udc_ep1_done(usb, -EINTR, 0); +} --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/buffer.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,63 @@ +/* + * usb/buffer.c + * + * Copyright (C) 2002 Russell King. + */ +#include +#include +#include + +#include "buffer.h" + +static LIST_HEAD(buffers); + +struct usb_buf *usbb_alloc(int size, int gfp) +{ + unsigned long flags; + struct usb_buf *buf; + + buf = kmalloc(sizeof(struct usb_buf) + size, gfp); + if (buf) { + atomic_set(&buf->users, 1); + local_irq_save(flags); + list_add(&buf->list, &buffers); + local_irq_restore(flags); + buf->len = 0; + buf->data = (unsigned char *) (buf + 1); + buf->head = (unsigned char *) (buf + 1); + } + + return buf; +} + +void __usbb_free(struct usb_buf *buf) +{ + unsigned long flags; + local_irq_save(flags); + list_del(&buf->list); + local_irq_restore(flags); + kfree(buf); +} + +EXPORT_SYMBOL(usbb_alloc); +EXPORT_SYMBOL(__usbb_free); + +static void __exit usbb_exit(void) +{ + if (!list_empty(&buffers)) { + struct list_head *l, *n; + printk("usbb: buffers not freed:\n"); + + list_for_each_safe(l, n, &buffers) { + struct usb_buf *b = list_entry(l, struct usb_buf, list); + + printk(" %p: alloced from %p count %d\n", + b, b->alloced_by, atomic_read(&b->users)); + + __usbb_free(b); + } + } +} + +module_exit(usbb_exit); + --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb-char.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2001 Extenex Corporation + * + * usb-char.h + * + * Character device emulation client for SA-1100 client usb core. + * + * + * + */ +#ifndef _USB_CHAR_H +#define _USB_CHAR_H + +#define USBC_MAJOR 10 /* miscellaneous character device */ +#define USBC_MINOR 240 /* in the "reserved for local use" range */ + +#define USBC_MAGIC 0x8E + +/* zap everything in receive ring buffer */ +#define USBC_IOC_FLUSH_RECEIVER _IO( USBC_MAGIC, 0x01 ) + +/* reset transmitter */ +#define USBC_IOC_FLUSH_TRANSMITTER _IO( USBC_MAGIC, 0x02 ) + +/* do both of above */ +#define USBC_IOC_FLUSH_ALL _IO( USBC_MAGIC, 0x03 ) + + + + + + +#endif /* _USB_CHAR_H */ + --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/buffer.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,45 @@ +/* + * usb/buffer.h: USB client buffers + * + * Copyright (C) 2002 Russell King. + * + * Loosely based on linux/skbuff.h + */ +#ifndef USBDEV_BUFFER_H +#define USBDEV_BUFFER_H + +#include + +struct usb_buf { + atomic_t users; + struct list_head list; + void *alloced_by; + unsigned char *data; + unsigned char *head; + unsigned int len; +}; + +extern struct usb_buf *usbb_alloc(int size, int gfp); +extern void __usbb_free(struct usb_buf *); + +static inline struct usb_buf *usbb_get(struct usb_buf *buf) +{ + atomic_inc(&buf->users); + return buf; +} + +static inline void usbb_put(struct usb_buf *buf) +{ + if (atomic_dec_and_test(&buf->users)) + __usbb_free(buf); +} + +static inline void *usbb_push(struct usb_buf *buf, int len) +{ + unsigned char *b = buf->head; + buf->head += len; + buf->len += len; + return b; +} + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/sa1100usb.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,1160 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "buffer.h" +#include "usbdev.h" +#include "sa1100_usb.h" +#include "sa1100usb.h" + +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk( fmt , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +static inline void pcs(const char *prefix) +{ +#ifdef DEBUG + __u32 foo = Ser0UDCCS0; + + DPRINTK("%s UDCAR: %d\n", prefix, Ser0UDCAR); + + printk("UDC: %s: %08x [ %s%s%s%s%s%s]\n", prefix, + foo, + foo & UDCCS0_SE ? "SE " : "", + foo & UDCCS0_DE ? "DE " : "", + foo & UDCCS0_FST ? "FST " : "", + foo & UDCCS0_SST ? "SST " : "", + foo & UDCCS0_IPR ? "IPR " : "", + foo & UDCCS0_OPR ? "OPR " : ""); +#endif +} + +/* + * soft_connect_hook() + * + * Some devices have platform-specific circuitry to make USB + * not seem to be plugged in, even when it is. This allows + * software to control when a device 'appears' on the USB bus + * (after Linux has booted and this driver has loaded, for + * example). If you have such a circuit, control it here. + */ +static inline void soft_connect_hook(int enable) +{ +#ifdef CONFIG_SA1100_EXTENEX1 + if (machine_is_extenex1()) { + if (enable) { + PPDR |= PPC_USB_SOFT_CON; + PPSR |= PPC_USB_SOFT_CON; + } else { + PPSR &= ~PPC_USB_SOFT_CON; + PPDR &= ~PPC_USB_SOFT_CON; + } + } +#endif +} + +/* + * disable the UDC at the source + */ +static inline void udc_disable(struct sausb_dev *usb) +{ + soft_connect_hook(0); + + usb->udccr = UDCCR_UDD | UDCCR_SUSIM; + + UDC_write(Ser0UDCCR, usb->udccr); +} + +/* + * Clear any pending write from the EP0 write buffer. + */ +static void ep0_clear_write(struct sausb_dev *usb) +{ + struct usb_buf *buf; + + buf = usb->wrbuf; + usb->wrint = NULL; + usb->wrbuf = NULL; + usb->wrptr = NULL; + usb->wrlen = 0; + + if (buf) + usbb_put(buf); +} + +static int udc_start(void *priv) +{ + struct sausb_dev *usb = priv; + + usb->ep[0].maxpktsize = 0; + usb->ep[1].maxpktsize = 0; + + /* + * start UDC internal machinery running, but mask interrupts. + */ + usb->udccr = UDCCR_SUSIM | UDCCR_TIM | UDCCR_RIM | UDCCR_EIM | + UDCCR_RESIM; + UDC_write(Ser0UDCCR, usb->udccr); + + udelay(100); + + /* + * clear all interrupt sources + */ + Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR | + UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR; + + /* + * flush DMA and fire through some -EAGAINs + */ + udc_ep1_init(usb); + udc_ep2_init(usb); + + /* + * enable any platform specific hardware + */ + soft_connect_hook(1); + + /* + * Enable resume, suspend and endpoint 0 interrupts. Leave + * endpoint 1 and 2 interrupts masked. + * + * If you are unplugged you will immediately get a suspend + * interrupt. If you are plugged and have a soft connect-circuit, + * you will get a reset. If you are plugged without a soft-connect, + * I think you also get suspend. + */ + usb->udccr &= ~(UDCCR_SUSIM | UDCCR_EIM | UDCCR_RESIM); + UDC_write(Ser0UDCCR, usb->udccr); + + return 0; +} + +static int udc_stop(void *priv) +{ + struct sausb_dev *usb = priv; + + ep0_clear_write(usb); + + /* mask everything */ + Ser0UDCCR = 0xFC; + + udc_ep1_reset(usb); + udc_ep2_reset(usb); + + udc_disable(usb); + + return 0; +} + + + + + +/* + * some voodo I am adding, since the vanilla macros just aren't doing it + * 1Mar01ww + */ + +#define ABORT_BITS (UDCCS0_SST | UDCCS0_SE) +#define OK_TO_WRITE (!(Ser0UDCCS0 & ABORT_BITS)) +#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE) + +static void set_de(void) +{ + int i = 1; + + while (1) { + if (OK_TO_WRITE) { + Ser0UDCCS0 |= UDCCS0_DE; + } else { + DPRINTK("UDC: quitting set DE because SST or SE set\n"); + break; + } + if (Ser0UDCCS0 & UDCCS0_DE) + break; + udelay(i); + if (++i == 50) { + printk("UDC: Dangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n", + UDCCS0_DE, Ser0UDCCS0); + break; + } + } +} + +static void set_ipr(void) +{ + int i = 1; + + while (1) { + if (OK_TO_WRITE) { + Ser0UDCCS0 |= UDCCS0_IPR; + } else { + DPRINTK("UDC: Quitting set IPR because SST or SE set\n"); + break; + } + if (Ser0UDCCS0 & UDCCS0_IPR) + break; + udelay(i); + if (++i == 50) { + printk("UDC: Dangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n", + UDCCS0_IPR, Ser0UDCCS0); + break; + } + } +} + +static void set_ipr_and_de(void) +{ + int i = 1; + + while (1) { + if (OK_TO_WRITE) { + Ser0UDCCS0 |= BOTH_BITS; + } else { + DPRINTK("UDC: Quitting set IPR/DE because SST or SE set\n"); + break; + } + if ((Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS) + break; + udelay(i); + if (++i == 50) { + printk("UDC: Dangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n", + UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0); + break; + } + } +} + +static inline void set_cs_bits(__u32 bits) +{ + if (bits & (UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST | UDCCS0_SST)) + Ser0UDCCS0 = bits; + else if ((bits & BOTH_BITS) == BOTH_BITS) + set_ipr_and_de(); + else if (bits & UDCCS0_IPR) + set_ipr(); + else if (bits & UDCCS0_DE) + set_de(); +} + +/* + * udc_ep0_write_fifo() + * + * Stick bytes in the 8 bytes endpoint zero FIFO. This version uses a + * variety of tricks to make sure the bytes are written correctly: + * 1. The count register is checked to see if the byte went in, + * and the write is attempted again if not. + * 2. An overall counter is used to break out so we don't hang in + * those (rare) cases where the UDC reverses direction of the + * FIFO underneath us without notification (in response to host + * aborting a setup transaction early). + */ +static void udc_ep0_write_fifo(struct sausb_dev *usb) +{ + unsigned int bytes_this_time = min(usb->wrlen, 8U); + int bytes_written = 0; + + DPRINTK("WF=%d: ", bytes_this_time); + + while (bytes_this_time--) { + unsigned int cwc; + int i; + + DPRINTK("%2.2X ", *usb->wrptr); + + cwc = Ser0UDCWC & 15; + + i = 10; + do { + Ser0UDCD0 = *usb->wrptr; + udelay(20); /* voodo 28Feb01ww */ + } while ((Ser0UDCWC & 15) == cwc && --i); + + if (i == 0) { + printk("UDC: udc_ep0_write_fifo: write failure\n"); + usb->ep0_wr_fifo_errs++; + } + + usb->wrptr++; + bytes_written++; + } + usb->wrlen -= bytes_written; + + /* following propagation voodo so maybe caller writing IPR in + ..a moment might actually get it to stick 28Feb01ww */ + udelay(300); + + usb->ep0_wr_bytes += bytes_written; + DPRINTK("L=%d WCR=%8.8X\n", usb->wrlen, Ser0UDCWC); +} + +/* + * read_fifo() + * + * Read 1-8 bytes out of FIFO and put in request. Called to do the + * initial read of setup requests from the host. Return number of + * bytes read. + * + * Like write fifo above, this driver uses multiple reads checked + * against the count register with an overall timeout. + */ +static int +udc_ep0_read_fifo(struct sausb_dev *usb, struct usb_ctrlrequest *request, int sz) +{ + unsigned char *pOut = (unsigned char *) request; + unsigned int fifo_count, bytes_read = 0; + + fifo_count = Ser0UDCWC & 15; + + DPRINTK("RF=%d ", fifo_count); + BUG_ON(fifo_count > sz); + + while (fifo_count--) { + unsigned int cwc; + int i; + + cwc = Ser0UDCWC & 15; + + i = 10; + do { + *pOut = (unsigned char) Ser0UDCD0; + udelay(20); + } while ((Ser0UDCWC & 15) == cwc && --i); + + if (i == 0) { + printk(KERN_ERR "UDC: udc_ep0_read_fifo: read failure\n"); + usb->ep0_rd_fifo_errs++; + break; + } + pOut++; + bytes_read++; + } + + DPRINTK("fc=%d\n", bytes_read); + usb->ep0_rd_bytes += bytes_read; + usb->ep0_rd_packets ++; + return bytes_read; +} + +static void ep0_sh_write_data(struct sausb_dev *usb) +{ + /* + * If bytes left is zero, we are coming in on the + * interrupt after the last packet went out. And + * we know we don't have to empty packet this + * transfer so just set DE and we are done + */ + set_cs_bits(UDCCS0_DE); +} + +static void ep0_sh_write_with_empty_packet(struct sausb_dev *usb) +{ + /* + * If bytes left is zero, we are coming in on the + * interrupt after the last packet went out. + * We must do short packet suff, so set DE and IPR + */ + set_cs_bits(UDCCS0_IPR | UDCCS0_DE); + DPRINTK("UDC: sh_write_empty: Sent empty packet\n"); +} + +static int udc_clear_opr(void) +{ + int i = 10000; + int is_clear; + + /*FIXME*/ + do { + Ser0UDCCS0 = UDCCS0_SO; + is_clear = !(Ser0UDCCS0 & UDCCS0_OPR); + if (i-- <= 0) + break; + } while (!is_clear); + + return is_clear; +} + +static int udc_ep0_queue(void *priv, struct usb_buf *buf, + unsigned int req_len) +{ + struct sausb_dev *usb = priv; + __u32 cs_reg_bits = UDCCS0_IPR; + + DPRINTK("a=%d r=%d\n", buf->len, req_len); + + /* + * thou shalt not enter data phase until + * Out Packet Ready is clear + */ + if (!udc_clear_opr()) { + printk("UDC: SO did not clear OPR\n"); + set_cs_bits(UDCCS0_DE | UDCCS0_SO); + usbb_put(buf); + return 1; + } + + usb->ep0_wr_packets++; + + usb->wrbuf = buf; + usb->wrptr = buf->data; + usb->wrlen = min(buf->len, req_len); + + udc_ep0_write_fifo(usb); + + if (usb->wrlen == 0) { + /* + * out in one, so data end + */ + cs_reg_bits |= UDCCS0_DE; + ep0_clear_write(usb); + } else if (buf->len < req_len) { + /* + * we are going to short-change host + * so need nul to not stall + */ + usb->wrint = ep0_sh_write_with_empty_packet; + } else { + /* + * we have as much or more than requested + */ + usb->wrint = ep0_sh_write_data; + } + + /* + * note: IPR was set uncondtionally at start of routine + */ + set_cs_bits(cs_reg_bits); + return 0; +} + +/* + * When SO and DE sent, UDC will enter status phase and ack, propagating + * new address to udc core. Next control transfer will be on the new + * address. + * + * You can't see the change in a read back of CAR until then (about 250us + * later, on my box). The original Intel driver sets S0 and DE and code + * to check that address has propagated here. I tried this, but it would + * only sometimes work! The rest of the time it would never propagate and + * we'd spin forever. So now I just set it and pray... + */ +static void udc_set_address(void *priv, unsigned int addr) +{ + Ser0UDCAR = addr; +} + +static void udc_set_config(void *priv, struct cdb *cdb) +{ + struct sausb_dev *usb = priv; + + if (cdb) { + udc_ep1_config(usb, le16_to_cpu(cdb->ep1.wMaxPacketSize)); + udc_ep2_config(usb, le16_to_cpu(cdb->ep2.wMaxPacketSize)); + } else { + udc_ep1_reset(usb); + udc_ep2_reset(usb); + } +} + +static unsigned int udc_ep_get_status(void *priv, unsigned int ep) +{ + unsigned int status; + + switch (ep) { + case 0: + status = (Ser0UDCCS0 & UDCCS0_FST) ? 1 : 0; + break; + + case 1: + status = (Ser0UDCCS1 & UDCCS1_FST) ? 1 : 0; + break; + + case 2: + status = (Ser0UDCCS2 & UDCCS2_FST) ? 1 : 0; + break; + + default: + printk(KERN_ERR "UDC: get_status: bad end point %d\n", ep); + status = 0; + break; + } + + return status; +} + +static void udc_ep_halt(void *priv, unsigned int ep, int halt) +{ + struct sausb_dev *usb = priv; + + printk("UDC: ep%d %s halt\n", ep, halt ? "set" : "clear"); + + switch (ep) { + case 1: + udc_ep1_halt(usb, halt); + break; + + case 2: + udc_ep2_halt(usb, halt); + break; + } +} + +static int udc_ep_queue(void *priv, unsigned int ep, char *buf, unsigned int len) +{ + struct sausb_dev *usb = priv; + int ret = -EINVAL; + + switch (ep) { + case 1: + ret = udc_ep1_queue_buffer(usb, buf, len); + break; + case 2: + ret = udc_ep2_send(usb, buf, len); + break; + } + + return ret; +} + +static void udc_ep_reset(void *priv, unsigned int ep) +{ + struct sausb_dev *usb = priv; + + switch (ep) { + case 1: + udc_ep1_recv_reset(usb); + break; + case 2: + udc_ep2_send_reset(usb); + break; + } +} + +static void udc_ep_callback(void *priv, unsigned int ep, usb_callback_t cb, void *data) +{ + struct sausb_dev *usb = priv; + unsigned long flags; + + if (ep == 1 || ep == 2) { + ep -= 1; + + spin_lock_irqsave(&usb->lock, flags); + usb->ep[ep].cb_func = cb; + usb->ep[ep].cb_data = data; + spin_unlock_irqrestore(&usb->lock, flags); + } +} + +static int udc_ep_idle(void *priv, unsigned int ep) +{ + struct sausb_dev *usb = priv; + int ret = -EINVAL; + + switch (ep) { + case 1: + break; + case 2: + ret = udc_ep2_idle(usb); + break; + } + + return ret; +} + +static struct usbc_driver usb_sa1100_drv = { + .owner = THIS_MODULE, + .name = "SA1100", + .start = udc_start, + .stop = udc_stop, + .ep0_queue = udc_ep0_queue, + .set_address = udc_set_address, + .set_config = udc_set_config, + .ep_get_status = udc_ep_get_status, + .ep_halt = udc_ep_halt, + .ep_queue = udc_ep_queue, + .ep_reset = udc_ep_reset, + .ep_callback = udc_ep_callback, + .ep_idle = udc_ep_idle, +}; + + +/* + * udc_ep0_read_packet() + * + * This setup handler is the "idle" state of endpoint zero. It looks for + * OPR (OUT packet ready) to see if a setup request has been been received + * from the host. Requests without a return data phase are immediately + * handled. Otherwise, the handler may be set to one of the sh_write_xxxx + * data pumpers if more than 8 bytes need to get back to the host. + */ +static void udc_ep0_read_packet(struct sausb_dev *usb, u32 cs_reg_in) +{ + struct usb_ctrlrequest req; + int n, ret = RET_NOACTION; + + /* + * A control request has been received by EP0. + * Read the request. + */ + n = udc_ep0_read_fifo(usb, &req, sizeof(req)); + + if (n == sizeof(req)) { + ret = usbctl_parse_request(usb->ctl, &req); + } else { + /* + * The request wasn't fully received. Force a + * stall. + */ + set_cs_bits(UDCCS0_FST | UDCCS0_SO); + printk("UDC: fifo read error: wanted %d bytes got %d\n", + sizeof(req), n); + } + + switch (ret) { + case RET_ERROR: + case RET_NOACTION: + break; + + case RET_ACK: + set_cs_bits(UDCCS0_DE | UDCCS0_SO); + break; + + case RET_REQERROR: + /* + * Send stall PID to host. + */ + set_cs_bits(UDCCS0_DE | UDCCS0_SO | UDCCS0_FST); + break; + } +} + +/* + * HACK DEBUG 3Mar01ww + * Well, maybe not, it really seems to help! 08Mar01ww + */ +static void core_kicker(struct sausb_dev *usb) +{ + __u32 car = Ser0UDCAR; + __u32 imp = Ser0UDCIMP; + __u32 omp = Ser0UDCOMP; + + UDC_set(Ser0UDCCR, UDCCR_UDD); + udelay(300); + UDC_clear(Ser0UDCCR, UDCCR_UDD); + + Ser0UDCAR = car; + Ser0UDCIMP = imp; + Ser0UDCOMP = omp; +} + +static void enable_resume_mask_suspend(struct sausb_dev *usb) +{ + int i; + + usb->udccr |= UDCCR_SUSIM; + + i = 1; + do { + Ser0UDCCR = usb->udccr; + udelay(i); + if (Ser0UDCCR == usb->udccr) + break; + if (Ser0UDCSR & UDCSR_RSTIR) + break; + } while (i++ < 50); + + if (i == 50) + printk("UDC: enable_resume: could not set SUSIM 0x%08x\n", + Ser0UDCCR); + + usb->udccr &= ~UDCCR_RESIM; + + i = 1; + do { + Ser0UDCCR = usb->udccr; + udelay(i); + if (Ser0UDCCR == usb->udccr) + break; + if (Ser0UDCSR & UDCSR_RSTIR) + break; + } while (i++ < 50); + + if (i == 50) + printk("UDC: enable_resume: could not clear RESIM 0x%08x\n", + Ser0UDCCR); +} + +static void enable_suspend_mask_resume(struct sausb_dev *usb) +{ + int i; + + usb->udccr |= UDCCR_RESIM; + + i = 1; + do { + Ser0UDCCR = usb->udccr; + udelay(i); + if (Ser0UDCCR == usb->udccr) + break; + if (Ser0UDCSR & UDCSR_RSTIR) + break; + } while (i++ < 50); + + if (i == 50) + printk("UDC: enable_resume: could not set RESIM 0x%08x\n", + Ser0UDCCR); + + usb->udccr &= ~UDCCR_SUSIM; + + i = 1; + do { + Ser0UDCCR = usb->udccr; + udelay(i); + if (Ser0UDCCR == usb->udccr) + break; + if (Ser0UDCSR & UDCSR_RSTIR) + break; + } while (i++ < 50); + + if (i == 50) + printk("UDC: enable_resume: could not clear SUSIM 0x%08x\n", + Ser0UDCCR); +} + +/* + * Reset received from HUB (or controller just went nuts and reset by + * itself!) so UDC core has been reset, track this state here + */ +static void udc_reset(struct sausb_dev *usb) +{ + if (usbctl_reset(usb->ctl)) { + ep0_clear_write(usb); + + /* + * Clean up endpoints. + */ + udc_ep1_reset(usb); + udc_ep2_reset(usb); + } + + /* + * mask reset ints, they flood during sequence, enable + * suspend and resume + */ + usb->udccr = (usb->udccr & ~(UDCCR_SUSIM | UDCCR_RESIM)) | UDCCR_REM; + Ser0UDCCR = usb->udccr; +} + +/* + * handle interrupt for endpoint zero + */ +static void udc_ep0_int_hndlr(struct sausb_dev *usb) +{ + u32 cs_reg_in; + + pcs("-->"); + + cs_reg_in = Ser0UDCCS0; + + /* + * If "setup end" has been set, the usb controller has terminated + * a setup transaction before we set DE. This happens during + * enumeration with some hosts. For example, the host will ask for + * our device descriptor and specify a return of 64 bytes. When we + * hand back the first 8, the host will know our max packet size + * and turn around and issue a new setup immediately. This causes + * the UDC to auto-ack the new setup and set SE. We must then + * "unload" (process) the new setup, which is what will happen + * after this preamble is finished executing. + */ + if (cs_reg_in & UDCCS0_SE) { + DPRINTK("UDC: early termination of setup\n"); + + /* + * Clear setup end + */ + set_cs_bits(UDCCS0_SSE); + + /* + * Clear any pending write. + */ + ep0_clear_write(usb); + } + + /* + * UDC sent a stall due to a protocol violation. + */ + if (cs_reg_in & UDCCS0_SST) { + usb->ep0_stall_sent++; + + DPRINTK("UDC: write_preamble: UDC sent stall\n"); + + /* + * Clear sent stall + */ + set_cs_bits(UDCCS0_SST); + + /* + * Clear any pending write. + */ + ep0_clear_write(usb); + } + + switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) { + case UDCCS0_OPR | UDCCS0_IPR: + DPRINTK("UDC: write_preamble: see OPR. Stopping write to " + "handle new SETUP\n"); + + /* + * very rarely, you can get OPR and + * leftover IPR. Try to clear + */ + UDC_clear(Ser0UDCCS0, UDCCS0_IPR); + + /* + * Clear any pending write. + */ + ep0_clear_write(usb); + + /*FALLTHROUGH*/ + case UDCCS0_OPR: + /* + * A new setup request is pending. Handle + * it. Note that we don't try to read a + * packet if SE was set and OPR is clear. + */ + udc_ep0_read_packet(usb, cs_reg_in); + break; + + case 0: + if (usb->wrint) { + if (usb->wrlen != 0) { + /* + * More data to go + */ + udc_ep0_write_fifo(usb); + set_ipr(); + } + + if (usb->wrlen == 0) { + /* + * All data sent. + */ + usb->wrint(usb); + + ep0_clear_write(usb); + } + } + break; + + case UDCCS0_IPR: + DPRINTK("UDC: IPR set, not writing\n"); + usb->ep0_early_irqs++; + break; + } + + pcs("<--"); +} + +static irqreturn_t udc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sausb_dev *usb = dev_id; + u32 status = Ser0UDCSR; + + /* + * ReSeT Interrupt Request - UDC has been reset + */ + if (status & UDCSR_RSTIR) { + udc_reset(usb); + + /* + * clear all pending sources + */ + UDC_flip(Ser0UDCSR, status); + return IRQ_HANDLED; + } + + /* + * else we have done something other than reset, + * so be sure reset enabled + */ + usb->udccr &= ~UDCCR_REM; + UDC_write(Ser0UDCCR, usb->udccr); + + /* + * RESume Interrupt Request + */ + if (status & UDCSR_RESIR) { + usbctl_resume(usb->ctl); + core_kicker(usb); + enable_suspend_mask_resume(usb); + } + + /* + * SUSpend Interrupt Request + */ + if (status & UDCSR_SUSIR) { + usbctl_suspend(usb->ctl); + enable_resume_mask_suspend(usb); + } + + /* + * clear all pending sources + */ + UDC_flip(Ser0UDCSR, status); + + if (status & UDCSR_EIR) + udc_ep0_int_hndlr(usb); + + if (status & UDCSR_RIR) + udc_ep1_int_hndlr(usb); + + if (status & UDCSR_TIR) + udc_ep2_int_hndlr(usb); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PROC_FS + +#define SAY( fmt, args... ) p += sprintf(p, fmt, ## args ) +#define SAYV( num ) p += sprintf(p, num_fmt, "Value", num ) +#define SAYC( label, yn ) p += sprintf(p, yn_fmt, label, yn ) +#define SAYS( label, v ) p += sprintf(p, cnt_fmt, label, v ) + +static int +udc_read_proc(char *page, char **start, off_t off, int cnt, int *eof, + void *data) +{ + struct sausb_dev *usb = data; + char *p = page; + u32 v; + int len, i; + + p += usbctl_proc_info(usb->ctl, p); + p += sprintf(p, "\nUDC:\n"); + v = Ser0UDCAR; + p += sprintf(p, "Address\t: %d (0x%02x)\n", v, v); + v = Ser0UDCIMP; + p += sprintf(p, "IN max\t: %d (0x%02x)\n", v + 1, v); + v = Ser0UDCOMP; + p += sprintf(p, "OUT max\t: %d (0x%02x)\n", v + 1, v); + v = Ser0UDCCR; + p += sprintf(p, "UDCCR\t: 0x%02x " + "[ %cSUSIM %cTIM %cRIM %cEIM %cRESIM %cUDA %cUDD ] " + "(0x%02x)\n", + v, + v & UDCCR_SUSIM ? '+' : '-', v & UDCCR_TIM ? '+' : '-', + v & UDCCR_RIM ? '+' : '-', v & UDCCR_EIM ? '+' : '-', + v & UDCCR_RESIM ? '+' : '-', v & UDCCR_UDA ? '+' : '-', + v & UDCCR_UDD ? '+' : '-', usb->udccr); + v = Ser0UDCCS0; + p += sprintf(p, "UDCCS0\t: 0x%02x " + "[ %cSO %cSE %cDE %cFST %cSST %cIPR %cOPR ]\n", + v, + v & UDCCS0_SO ? '+' : '-', v & UDCCS0_SE ? '+' : '-', + v & UDCCS0_DE ? '+' : '-', v & UDCCS0_FST ? '+' : '-', + v & UDCCS0_SST ? '+' : '-', v & UDCCS0_IPR ? '+' : '-', + v & UDCCS0_OPR ? '+' : '-'); + v = Ser0UDCCS1; + p += sprintf(p, "UDCCS1\t: 0x%02x " + "[ %cRNE %cFST %cSST %cRPE %cRPC %cRFS ]\n", + v, + v & UDCCS1_RNE ? '+' : '-', v & UDCCS1_FST ? '+' : '-', + v & UDCCS1_SST ? '+' : '-', v & UDCCS1_RPE ? '+' : '-', + v & UDCCS1_RPC ? '+' : '-', v & UDCCS1_RFS ? '+' : '-'); + v = Ser0UDCCS2; + p += sprintf(p, "UDCCS2\t: 0x%02x " + "[ %cFST %cSST %cTUR %cTPE %cTPC %cTFS ]\n", + v, + v & UDCCS2_FST ? '+' : '-', v & UDCCS2_SST ? '+' : '-', + v & UDCCS2_TUR ? '+' : '-', v & UDCCS2_TPE ? '+' : '-', + v & UDCCS2_TPC ? '+' : '-', v & UDCCS2_TFS ? '+' : '-'); + + p += sprintf(p, "\n"); + p += sprintf(p, " Bytes Packets FIFO errs Max Sz\n"); + p += sprintf(p, "EP0 Rd: %10ld %10ld %10ld -\n", + usb->ep0_rd_bytes, + usb->ep0_rd_packets, + usb->ep0_rd_fifo_errs); + p += sprintf(p, "EP0 Wr: %10ld %10ld %10ld -\n", + usb->ep0_wr_bytes, + usb->ep0_wr_packets, + usb->ep0_wr_fifo_errs); + + for (i = 0; i < 2; i++) + p += sprintf(p, "EP%d : %10ld %10ld %10ld %6d\n", + i + 1, + usb->ep[i].bytes, + usb->ep[i].packets, + usb->ep[i].fifo_errs, + usb->ep[i].maxpktsize); + + p += sprintf(p, "Stalls sent\t: %ld\n", usb->ep0_stall_sent); + p += sprintf(p, "Early ints\t: %ld\n", usb->ep0_early_irqs); + +#if 0 + v = Ser0UDCSR; + SAY("\nUDC Interrupt Request Register\n"); + SAYV(v); + SAYC("Reset pending", (v & UDCSR_RSTIR) ? yes : no); + SAYC("Suspend pending", (v & UDCSR_SUSIR) ? yes : no); + SAYC("Resume pending", (v & UDCSR_RESIR) ? yes : no); + SAYC("ep0 pending", (v & UDCSR_EIR) ? yes : no); + SAYC("receiver pending", (v & UDCSR_RIR) ? yes : no); + SAYC("tramsitter pending", (v & UDCSR_TIR) ? yes : no); + +#ifdef CONFIG_SA1100_EXTENEX1 + SAYC("\nSoft connect", + (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden"); +#endif +#endif + + len = (p - page) - off; + if (len < 0) + len = 0; + *eof = (len <= cnt) ? 1 : 0; + *start = page + off; + + return len; +} + +#endif + +extern struct usbctl usbctl; + +static int __devinit udc_probe(struct device *dev) +{ + struct sausb_dev *usb; + int retval; + + if (!request_mem_region(0x80000000, 0x10000, "sa11x0-udc")) + return -EBUSY; + + usb = kmalloc(sizeof(struct sausb_dev), GFP_KERNEL); + if (!usb) + return -ENOMEM; + + memset(usb, 0, sizeof(struct sausb_dev)); + dev_set_drvdata(dev, usb); + + usb_sa1100_drv.priv = usb; + + usb->dev = dev; + usb->ctl = &usbctl; + + spin_lock_init(&usb->lock); + + udc_disable(usb); + + usbctl_init(usb->ctl, &usb_sa1100_drv); + +#ifdef CONFIG_PROC_FS + create_proc_read_entry("sausb", 0, NULL, udc_read_proc, usb); +#endif + + /* setup rx dma */ + retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive", + NULL, NULL, &usb->ep[0].dmach); + if (retval) { + printk("UDC: unable to register for rx dma rc=%d\n", + retval); + goto err; + } + + /* setup tx dma */ + retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit", + NULL, NULL, &usb->ep[1].dmach); + if (retval) { + printk("UDC: unable to register for tx dma rc=%d\n", + retval); + goto err; + } + + /* now allocate the IRQ. */ + retval = request_irq(IRQ_Ser0UDC, udc_interrupt, SA_INTERRUPT, + "SA USB core", usb); + if (retval) { + printk("UDC: couldn't request USB irq rc=%d\n", retval); + goto err; + } + + return retval; + + err: + if (usb->ep[2].dmach) { + sa1100_free_dma(usb->ep[2].dmach); + usb->ep[2].dmach = NULL; + } + if (usb->ep[1].dmach) { + sa1100_free_dma(usb->ep[1].dmach); + usb->ep[1].dmach = NULL; + } +#ifdef CONFIG_PROC_FS + remove_proc_entry("sausb", NULL); +#endif + release_mem_region(0x80000000, 0x10000); + return retval; +} + +/* + * Release DMA and interrupt resources + */ +static int __devexit udc_remove(struct device *dev) +{ + struct sausb_dev *usb = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("sausb", NULL); +#endif + + udc_disable(usb); + + free_irq(IRQ_Ser0UDC, usb); + sa1100_free_dma(usb->ep[1].dmach); + sa1100_free_dma(usb->ep[0].dmach); + + usbctl_exit(usb->ctl); + + release_mem_region(0x80000000, 0x10000); + + return 0; +} + +static struct device_driver sa11x0usb_driver = { + .name = "sa11x0-udc", + .bus = &platform_bus_type, + .probe = udc_probe, + .remove = __devexit_p(udc_remove), +}; + +static int __init udc_init(void) +{ + return driver_register(&sa11x0usb_driver); +} + +static void __exit udc_exit(void) +{ + driver_unregister(&sa11x0usb_driver); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SA1100 USB Gadget driver"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/control.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,933 @@ +/* + * usb/control.c + * + * This parses and handles all the control messages to/from endpoint 0. + */ +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "client.h" +#include "usbdev.h" + +#include "sa1100_usb.h" + +#define USB_ENDPOINT_HALT 0 +#define USB_DEVICE_REMOTE_WAKEUP 1 + +#undef DEBUG + +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG fmt , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +/* + * print string descriptor + */ +static char * __attribute__((unused)) +psdesc(char *str, int len, struct usb_string_descriptor *desc) +{ + char *start = str; + int nchars = (desc->bLength - 2) / sizeof(__u16) + 2; + int i; + + if (nchars >= len) + nchars = len - 1; + + nchars -= 2; + + *str++ = '"'; + for(i = 0; i < nchars; i++) + *str++ = le16_to_cpu(desc->wData[i]); + *str++ = '"'; + *str = '\0'; + + return start; +} + +enum { + kError = -1, + kEvSuspend = 0, + kEvReset = 1, + kEvResume = 2, + kEvAddress = 3, + kEvConfig = 4, + kEvDeConfig = 5 +}; + +enum { + kStateZombie = 0, + kStateZombieSuspend = 1, + kStateDefault = 2, + kStateDefaultSuspend = 3, + kStateAddr = 4, + kStateAddrSuspend = 5, + kStateConfig = 6, + kStateConfigSuspend = 7 +}; + +#define kE kError +#define kSZ kStateZombie +#define kSZS kStateZombieSuspend +#define kSD kStateDefault +#define kSDS kStateDefaultSuspend +#define kSA kStateAddr +#define kSAS kStateAddrSuspend +#define kSC kStateConfig +#define kSCS kStateConfigSuspend + +/* + * Fig 9-1 P192 + * Zombie == Attached | Powered + */ +static int device_state_machine[8][6] = { +// suspend reset resume addr config deconfig +{ kSZS, kSD, kE, kE, kE, kE }, /* zombie */ +{ kE, kSD, kSZ, kE, kE, kE }, /* zom sus */ +{ kSDS, kError, kSD, kSA, kE, kE }, /* default */ +{ kE, kSD, kSD, kE, kE, kE }, /* def sus */ +{ kSAS, kSD, kE, kE, kSC, kE }, /* addr */ +{ kE, kSD, kSA, kE, kE, kE }, /* addr sus */ +{ kSCS, kSD, kE, kE, kE, kSA }, /* config */ +{ kE, kSD, kSC, kE, kE, kE } /* cfg sus */ +}; + +/* + * "device state" is the usb device framework state, as opposed to the + * "state machine state" which is whatever the driver needs and is much + * more fine grained + */ +static int sm_state_to_device_state[8] = { + USB_STATE_POWERED, /* zombie */ + USB_STATE_SUSPENDED, /* zombie suspended */ + USB_STATE_DEFAULT, /* default */ + USB_STATE_SUSPENDED, /* default suspended */ + USB_STATE_ADDRESS, /* address */ + USB_STATE_SUSPENDED, /* address suspended */ + USB_STATE_CONFIGURED, /* config */ + USB_STATE_SUSPENDED /* config suspended */ +}; + +static char * state_names[8] = { + "zombie", + "zombie suspended", + "default", + "default suspended", + "address", + "address suspended", + "configured", + "config suspended" +}; + +static char * event_names[6] = { + "suspend", + "reset", + "resume", + "address assigned", + "configure", + "de-configure" +}; + +static char * device_state_names[] = { + "not attached", + "attached", + "powered", + "default", + "address", + "configured", + "suspended" +}; + +static void usbctl_callbacks(struct usbctl *ctl, int state, int oldstate) +{ + struct usb_client *clnt = ctl->clnt; + + /* + * Inform any clients currently attached + * that the connectivity state changed. + */ + if (clnt && clnt->state_change) + clnt->state_change(clnt->priv, state, oldstate); +} + +/* + * called by the interrupt handler here and the two endpoint + * files when interesting .."events" happen + */ +static int usbctl_next_state_on_event(struct usbctl *ctl, int event) +{ + int next_state, next_dev_state, old_dev_state; + + printk(KERN_DEBUG "usbctl: %s --[%s]--> ", state_names[ctl->sm_state], + event_names[event]); + + next_state = device_state_machine[ctl->sm_state][event]; + if (next_state != kError) { + next_dev_state = sm_state_to_device_state[next_state]; + + printk("%s. Device in %s state.\n", + state_names[next_state], + device_state_names[next_dev_state]); + + old_dev_state = ctl->state; + ctl->sm_state = next_state; + ctl->state = next_dev_state; + + if (old_dev_state != next_dev_state) + usbctl_callbacks(ctl, next_dev_state, old_dev_state); + } else + printk("(error)\n"); + + return next_state; +} + +/* + * Driver detected USB HUB reset. + */ +int usbctl_reset(struct usbctl *ctl) +{ + int ret; + + ret = usbctl_next_state_on_event(ctl, kEvReset) == kError; + + if (!ret) { + ctl->address = 0; + } + return ret; +} + +EXPORT_SYMBOL(usbctl_reset); + +void usbctl_suspend(struct usbctl *ctl) +{ + usbctl_next_state_on_event(ctl, kEvSuspend); +} + +EXPORT_SYMBOL(usbctl_suspend); + +void usbctl_resume(struct usbctl *ctl) +{ + usbctl_next_state_on_event(ctl, kEvResume); +} + +EXPORT_SYMBOL(usbctl_resume); + +static struct usb_interface_descriptor * +usbctl_get_interface_descriptor(struct usbctl *ctl, unsigned int interface) +{ + /*FIXME*/ + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + return (struct usb_interface_descriptor *)&cdb->intf; +} + +static inline int +__usbctl_queue(struct usbctl *ctl, struct usb_ctrlrequest *req, + struct usb_buf *buf) +{ + unsigned int reqlen = le16_to_cpu(req->wLength); + + return ctl->driver->ep0_queue(ctl->driver->priv, buf, reqlen) ? + RET_ERROR : RET_QUEUED; +} + +static int +usbctl_queue(struct usbctl *ctl, struct usb_ctrlrequest *req, + void *data, unsigned int len) +{ + struct usb_buf *buf; + + buf = usbb_alloc(len, GFP_ATOMIC); + if (!buf) { + printk(KERN_ERR "usb: out of memory\n"); + return RET_ERROR; + } + + if (data) + memcpy(usbb_push(buf, len), data, len); + + return __usbctl_queue(ctl, req, buf); +} + +/* + * 9.4.5: Get Status (device) + */ +static int +usbctl_parse_dev_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + u16 status; + + status = /* self_powered_hook() ? 1 : 0 */1; + + status = cpu_to_le16(status); + + return usbctl_queue(ctl, req, &status, 2); +} + +/* + * Send USB device description to the host. + */ +static int +usbctl_desc_device(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + return __usbctl_queue(ctl, req, usbb_get(ctl->dev_desc_buf)); +} + +/* + * Send USB configuration information to the host. + */ +static int +usbctl_desc_config(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + /*FIXME*/ + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + return usbctl_queue(ctl, req, cdb, sizeof(struct cdb)); +} + +/* + * Send a string to the host from the string table. + */ +static int +usbctl_desc_string(struct usbctl *ctl, struct usb_ctrlrequest *req, + unsigned int idx) +{ + struct usb_buf *buf; + unsigned int lang = le16_to_cpu(req->wIndex); + char string[32] __attribute__((unused)); + int ret; + + DPRINTK("usbctl: desc_string (index %u, lang 0x%04x): ", idx, lang); + + buf = usbc_string_find(&ctl->strings, lang, idx); + if (buf) { + DPRINTK("%s\n", idx == 0 ? "language" : + psdesc(string, sizeof(string), usbc_string_desc(buf))); + + ret = __usbctl_queue(ctl, req, buf); + } else { + DPRINTK("not found -> stall\n"); + ret = RET_REQERROR; + } + return ret; +} + +/* + * Send an interface description (and endpoints) to the host. + */ +static int +usbctl_desc_interface(struct usbctl *ctl, struct usb_ctrlrequest *req, + unsigned int idx) +{ + struct usb_interface_descriptor *desc; + int ret; + + DPRINTK("usbctl: desc_interface (index %d)\n", idx); + + desc = usbctl_get_interface_descriptor(ctl, idx); + + if (desc) { + ret = usbctl_queue(ctl, req, desc, desc->bLength); + } else { + printk("usbctl: unknown interface %d\n", idx); + ret = RET_REQERROR; + } + + return ret; +} + +/* + * Send an endpoint (1 .. n) to the host. + */ +static int +usbctl_desc_endpoint(struct usbctl *ctl, struct usb_ctrlrequest *req, + unsigned int idx) +{ + int ret; + + DPRINTK("usbctl: desc_endpoint (index %d)\n", idx); + + if (idx >= 1 && idx <= ctl->nr_ep) { + struct usb_endpoint_descriptor *ep = ctl->ep_desc[idx - 1]; + + ret = usbctl_queue(ctl, req, ep, ep->bLength); + } else { + printk("usbctl: unknown endpoint %d\n", idx); + ret = RET_REQERROR; + } + + return ret; +} + +/* + * 9.4.3: Parse a request for a descriptor. + * Unspecified conditions: + * None + * Valid states: default, address, configured. + */ +static int +usbctl_parse_dev_descriptor(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int idx = le16_to_cpu(req->wValue) & 255; + unsigned int type = le16_to_cpu(req->wValue) >> 8; + int ret; + + switch (type) { + case USB_DT_DEVICE: /* check if idx matters */ + ret = usbctl_desc_device(ctl, req); + break; + + case USB_DT_CONFIG: /* check if idx matters */ + ret = usbctl_desc_config(ctl, req); + break; + + case USB_DT_STRING: + ret = usbctl_desc_string(ctl, req, idx); + break; + + case USB_DT_INTERFACE: + ret = usbctl_desc_interface(ctl, req, idx); + break; + + case USB_DT_ENDPOINT: + ret = usbctl_desc_endpoint(ctl, req, idx); + break; + + case USB_DT_DEVICE_QUALIFIER: + case USB_DT_OTHER_SPEED_CONFIG: + case USB_DT_INTERFACE_POWER: + default: + printk(KERN_ERR "usbctl: unknown descriptor: " + "wValue = 0x%04x wIndex = 0x%04x\n", + le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex)); + ret = RET_REQERROR; + break; + } + + return ret; +} + +/* + * 9.4.6: Set Address + * The USB1.1 spec says the response to SetAddress() with value 0 + * is undefined. It then goes on to define the response. We + * acknowledge addresses of zero, but take no further action. + */ +static int +usbctl_parse_dev_set_address(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int address = le16_to_cpu(req->wValue) & 0x7f; + + if (ctl->state == USB_STATE_CONFIGURED) + return RET_REQERROR; + + if (address != 0) { + ctl->address = address; + + usbctl_next_state_on_event(ctl, kEvAddress); + + ctl->driver->set_address(ctl->driver->priv, address); + } + + return RET_ACK; +} + +/* + * 9.4.2: Get Configuration. + * Unspecified conditions: + * - non-zero wIndex, wValue or wLength (ignored) + * - default state (request error) + * Valid states: address, configured. + */ +static int +usbctl_parse_dev_get_config(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + u8 status = 0; + + if (ctl->state == USB_STATE_CONFIGURED) + status = 1; + + return usbctl_queue(ctl, req, &status, 1); +} + +/* + * 9.4.7: Set Configuration. + * Unspecified conditions: + * - default state (request error) + */ +static int +usbctl_parse_dev_set_config(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int cfg = le16_to_cpu(req->wValue); + int ret = RET_REQERROR; + + if (ctl->state == USB_STATE_DEFAULT) + return ret; + + if (cfg == 0) { + /* enter address state, or remain in address state */ + usbctl_next_state_on_event(ctl, kEvDeConfig); + + ctl->driver->set_config(ctl->driver->priv, NULL); + + ret = RET_ACK; + } else if (cfg == 1) { + /* enter configured state, and set configuration */ + /*FIXME*/ + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + usbctl_next_state_on_event(ctl, kEvConfig); + + ctl->driver->set_config(ctl->driver->priv, cdb); + ret = RET_ACK; + } + + return ret; +} + +/* + * Interface handling + */ + +/* + * 9.4.5: Get Status (interface) + */ +static int +usbctl_parse_int_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int interface = le16_to_cpu(req->wIndex) & 255; + u16 status; + + switch (ctl->state) { + case USB_STATE_DEFAULT: + return RET_REQERROR; + + case USB_STATE_ADDRESS: + if (interface != 0) + return RET_REQERROR; + break; + + case USB_STATE_CONFIGURED: + if (interface != 1) + return RET_REQERROR; + break; + } + + status = cpu_to_le16(0); + + return usbctl_queue(ctl, req, &status, 2); +} + +/* + * 9.4.4: Get Interface + * Unspecified conditions: + * - + * States: Default (unspecified), Address (Request Error), Configured (ok) + */ +static int +usbctl_parse_int_get_interface(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int interface = le16_to_cpu(req->wIndex) & 255; + u8 null = 0; + + if (ctl->state != USB_STATE_CONFIGURED) + return RET_REQERROR; + + /* + * If the interface doesn't exist, respond with request error + */ + if (interface != 1) + return RET_REQERROR; + + printk("usbctl: get interface %d not supported\n", interface); + + return usbctl_queue(ctl, req, &null, 1); +} + +static int +usbctl_parse_int_set_interface(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int interface = le16_to_cpu(req->wIndex) & 255; + + if (interface != 0) + printk("usbctl: set interface %d not supported (ignored)\n", + interface); + + return RET_ACK; +} + +/* + * Endpoint handling + */ + +/* + * 9.4.5: Get Status (endpoint) + */ +static int +usbctl_parse_ep_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int ep = le16_to_cpu(req->wIndex) & 15; + u16 status; + + if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) || + ep <= ctl->nr_ep) + return RET_REQERROR; + + status = ctl->driver->ep_get_status(ctl->driver->priv, ep); + status = cpu_to_le16(status); + + return usbctl_queue(ctl, req, &status, 2); +} + +/* + * 9.4.1: Clear an endpoint feature. We only support ENDPOINT_HALT. + * Unspecified conditions: + * - non-zero wLength is not specified (ignored) + * Valid states: Address, Configured. + */ +static int +usbctl_parse_ep_clear_feature(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int feature = le16_to_cpu(req->wValue); + unsigned int ep = le16_to_cpu(req->wIndex) & 15; + int ret; + + if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) || + ep <= ctl->nr_ep) + return RET_REQERROR; + + if (feature == USB_ENDPOINT_HALT) { + ctl->driver->ep_halt(ctl->driver->priv, ep, 0); + ret = RET_ACK; + } else { + printk(KERN_ERR "usbctl: unsupported clear feature: " + "wValue = 0x%04x wIndex = 0x%04x\n", + feature, ep); + + ret = RET_REQERROR; + } + return ret; +} + +/* + * 9.4.9: Set Feature (endpoint) + */ +static int +usbctl_parse_ep_set_feature(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int feature = le16_to_cpu(req->wValue); + unsigned int ep = le16_to_cpu(req->wIndex) & 15; + int ret; + + if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) || + ep <= ctl->nr_ep) + return RET_REQERROR; + + if (feature == USB_ENDPOINT_HALT) { + ctl->driver->ep_halt(ctl->driver->priv, ep, 1); + ret = RET_ACK; + } else { + printk(KERN_ERR "usbctl: unsupported set feature " + "wValue = 0x%04x wIndex = 0x%04x\n", + feature, ep); + + ret = RET_REQERROR; + } + return ret; +} + +/* + * This reflects Table 9.3 (p186) in the USB1.1 spec. + * + * Some notes: + * - USB1.1 specifies remote wakeup feature, so we don't implement + * USB_RECIP_DEVICE USB_REQ_{SET,CLEAR}_FEATURE + * - USB1.1 doesn't actually specify any interface features, so we + * don't implement USB_RECIP_INTERFACE USB_REQ_{SET,CLEAR}_FEATURE + */ +static int (*request_fns[4][16])(struct usbctl *, struct usb_ctrlrequest *) = { + [USB_RECIP_DEVICE] = { + [USB_REQ_GET_STATUS] = usbctl_parse_dev_get_status, + [USB_REQ_CLEAR_FEATURE] = NULL, + [USB_REQ_SET_FEATURE] = NULL, + [USB_REQ_SET_ADDRESS] = usbctl_parse_dev_set_address, + [USB_REQ_GET_DESCRIPTOR] = usbctl_parse_dev_descriptor, + [USB_REQ_SET_DESCRIPTOR] = NULL, + [USB_REQ_GET_CONFIGURATION] = usbctl_parse_dev_get_config, + [USB_REQ_SET_CONFIGURATION] = usbctl_parse_dev_set_config, + }, + + [USB_RECIP_INTERFACE] = { + [USB_REQ_GET_STATUS] = usbctl_parse_int_get_status, + [USB_REQ_CLEAR_FEATURE] = NULL, + [USB_REQ_SET_FEATURE] = NULL, + [USB_REQ_GET_INTERFACE] = usbctl_parse_int_get_interface, + [USB_REQ_SET_INTERFACE] = usbctl_parse_int_set_interface, + }, + + [USB_RECIP_ENDPOINT] = { + [USB_REQ_GET_STATUS] = usbctl_parse_ep_get_status, + [USB_REQ_CLEAR_FEATURE] = usbctl_parse_ep_clear_feature, + [USB_REQ_SET_FEATURE] = usbctl_parse_ep_set_feature, + }, +}; + +static void __attribute__((unused)) +usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) +{ + printk("%sbRequestType=0x%02x bRequest=0x%02x " + "wValue=0x%04x wIndex=0x%04x wLength=0x%04x\n", + prefix, req->bRequestType, req->bRequest, + le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex), + le16_to_cpu(req->wLength)); +} + +int usbctl_parse_request(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int type; + int (*fn)(struct usbctl *, struct usb_ctrlrequest *) = NULL; + int ret = RET_REQERROR; + + //usbctl_dump_request("usbctl: ", req); + + type = req->bRequestType & USB_TYPE_MASK; + if (type == USB_TYPE_STANDARD) { + unsigned int recip; + + recip = req->bRequestType & USB_RECIP_MASK; + if (recip < ARRAY_SIZE(request_fns) && + req->bRequest < ARRAY_SIZE(request_fns[0])) + fn = request_fns[recip][req->bRequest]; + } + + if (fn) + ret = fn(ctl, req); + else + usbctl_dump_request(KERN_ERR "usbctl: unknown request: ", + req); + + /* + * Make sure we're doing the right thing. + */ + if (req->bRequestType & USB_DIR_IN) { + if (ret != RET_QUEUED && ret != RET_REQERROR) + printk("Error: device to host transfer expected\n"); + } else { + if (ret == RET_QUEUED) + printk("Error: no device to host transfer expected\n"); + } + + return ret; +} + +EXPORT_SYMBOL(usbctl_parse_request); + +/* Start running. Must have called usb_open (above) first */ +int usbctl_start(struct usb_client *client) +{ + struct usbctl *ctl = client->ctl; + + if (ctl == NULL || ctl->clnt != client) { + printk("usbctl: start: no client registered\n"); + return -EPERM; + } + + ctl->sm_state = kStateZombie; + ctl->state = USB_STATE_POWERED; + + /* + * Notify the client as to our state. + */ + usbctl_callbacks(ctl, USB_STATE_POWERED, USB_STATE_SUSPENDED); + + return ctl->driver->start(ctl->driver->priv); +} + +EXPORT_SYMBOL(usbctl_start); + +/* + * Stop USB core from running + */ +void usbctl_stop(struct usb_client *client) +{ + struct usbctl *ctl = client->ctl; + + if (ctl == NULL || ctl->clnt != client) { + printk("USBDEV: stop: no client/driver registered\n"); + return; + } + + ctl->driver->stop(ctl->driver->priv); +} + +EXPORT_SYMBOL(usbctl_stop); + +struct usbctl usbctl; + +EXPORT_SYMBOL(usbctl); + +/* Open SA usb core on behalf of a client, but don't start running */ + +int usbctl_open(struct usb_client *client) +{ + struct usbctl *ctl = &usbctl; + int ret; +printk("usbctl_open: ctl %p driver %p\n", ctl, ctl->driver); + if (!ctl->driver || !try_module_get(ctl->driver->owner)) + return -ENODEV; + + if (ctl->clnt != NULL) { + ret = -EBUSY; + goto err; + } + + ctl->clnt = client; + ctl->state = USB_STATE_SUSPENDED; + ctl->nr_ep = 2; + /* start in zombie suspended state */ + ctl->sm_state = kStateZombieSuspend; + ctl->state = USB_STATE_SUSPENDED; + client->ctl = ctl; + + ctl->dev_desc_buf = usbb_alloc(sizeof(struct usb_device_descriptor), + GFP_KERNEL); + if (!ctl->dev_desc_buf) { + ret = -ENOMEM; + goto err; + } + + ctl->dev_desc = usbb_push(ctl->dev_desc_buf, + sizeof(struct usb_device_descriptor)); + + /* create descriptors for enumeration */ + initialize_descriptors(ctl); + + return 0; + + err: + module_put(ctl->driver->owner); + return ret; +} + +EXPORT_SYMBOL(usbctl_open); + +/* Tell SA core client is through using it */ +void usbctl_close(struct usb_client *client) +{ + struct usbctl *ctl = client->ctl; + + if (ctl == NULL || ctl->clnt != client) { + printk("usbctl: close: no client registered\n"); + return; + } + + usbb_put(ctl->dev_desc_buf); + + client->ctl = NULL; + ctl->clnt = NULL; + ctl->dev_desc = NULL; + ctl->dev_desc_buf = NULL; + /* reset to zombie suspended state */ + ctl->sm_state = kStateZombieSuspend; + ctl->state = USB_STATE_SUSPENDED; + + usbc_string_free_all(&ctl->strings); + + if (ctl->driver->owner) + module_put(ctl->driver->owner); +} + +EXPORT_SYMBOL(usbctl_close); + +int usbctl_proc_info(struct usbctl *ctl, char *buf) +{ + char *p = buf; + + p += sprintf(p, "USB Gadget Core:\n"); + p += sprintf(p, "Driver\t: %s\n", + ctl->driver ? ctl->driver->name : "none"); + p += sprintf(p, "Client\t: %s\n", + ctl->clnt ? ctl->clnt->name : "none"); + p += sprintf(p, "State\t: %s (%s) %d\n", + device_state_names[sm_state_to_device_state[ctl->sm_state]], + state_names[ctl->sm_state], + ctl->sm_state); + p += sprintf(p, "Address\t: %d\n", ctl->address); + + return p - buf; +} + +EXPORT_SYMBOL(usbctl_proc_info); + +int +usbctl_ep_queue_buffer(struct usbctl *ctl, unsigned int ep, + char *buf, unsigned int len) +{ + return ctl->driver->ep_queue(ctl->driver->priv, ep, buf, len); +} + +EXPORT_SYMBOL(usbctl_ep_queue_buffer); + +void usbctl_ep_reset(struct usbctl *ctl, unsigned int ep) +{ + return ctl->driver->ep_reset(ctl->driver->priv, ep); +} + +EXPORT_SYMBOL(usbctl_ep_reset); + +void +usbctl_ep_set_callback(struct usbctl *ctl, unsigned int ep, + usb_callback_t callback, void *data) +{ + ctl->driver->ep_callback(ctl->driver->priv, ep, callback, data); +} + +EXPORT_SYMBOL(usbctl_ep_set_callback); + +int usbctl_ep_idle(struct usbctl *ctl, unsigned int ep) +{ + return ctl->driver->ep_idle(ctl->driver->priv, ep); +} + +EXPORT_SYMBOL(usbctl_ep_idle); + +/* + * usbctl_init() + * Module load time. Allocate dma and interrupt resources. Setup /proc fs + * entry. Leave UDC disabled. + */ +int usbctl_init(struct usbctl *ctl, struct usbc_driver *drv) +{ + usbc_string_init(&ctl->strings); +printk("usbctl_init: %p %p\n", ctl, drv); + /* + * start in zombie suspended state + */ + ctl->sm_state = kStateZombieSuspend; + ctl->state = USB_STATE_SUSPENDED; + ctl->driver = drv; + + return 0; +} + +/* + * usbctl_exit() + */ +void usbctl_exit(struct usbctl *ctl) +{ + usbc_string_free_all(&ctl->strings); + + ctl->driver = NULL; +} + +EXPORT_SYMBOL(usbctl_init); +EXPORT_SYMBOL(usbctl_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB gadget core"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/client.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,40 @@ +#ifndef USBDEV_CLIENT_H +#define USBDEV_CLIENT_H + +#include "sa1100_usb.h" /* grr */ + +struct usbctl; + +struct usb_client { + struct usbctl *ctl; + const char *name; /* Client name */ + void *priv; /* Client-private data */ + void (*state_change)(void *priv, int state, int oldstate); + __u16 vendor; /* USB vendor ID */ + __u16 product; /* USB product ID */ + __u16 version; /* USB version ID */ + __u8 class; /* USB class */ + __u8 subclass; /* USB subclass */ + __u8 protocol; /* USB protocol */ + __u8 unused1; + __u16 unused2; + const char *manufacturer_str; + const char *product_str; + const char *serial_str; +}; + +int usbctl_start(struct usb_client *client); +void usbctl_stop(struct usb_client *client); +int usbctl_open(struct usb_client *client); +void usbctl_close(struct usb_client *client); + +int +usbctl_ep_queue_buffer(struct usbctl *ctl, unsigned int ep, + char *buf, unsigned int len); +void usbctl_ep_reset(struct usbctl *ctl, unsigned int ep); +void +usbctl_ep_set_callback(struct usbctl *ctl, unsigned int ep, + usb_callback_t callback, void *data); +int usbctl_ep_idle(struct usbctl *ctl, unsigned int ep); + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/sa1100_usb.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,50 @@ +/* + * sa1100_usb.h + * + * Public interface to the sa1100 USB core. For use by client modules + * like usb-eth and usb-char. + * + */ +#ifndef _SA1100_USB_H +#define _SA1100_USB_H + +typedef void (*usb_callback_t)(void *data, int flag, int size); + +/* in usb_send.c */ +int sa1100_usb_xmitter_avail( void ); +int sa1100_usb_send(char *buf, int len); +void sa1100_usb_send_set_callback(usb_callback_t callback, void *data); +void sa1100_usb_send_reset(void); + +/* in usb_recev.c */ +int sa1100_usb_recv(char *buf, int len); +void sa1100_usb_recv_set_callback(usb_callback_t callback, void *data); +void sa1100_usb_recv_reset(void); + +////////////////////////////////////////////////////////////////////////////// +// Descriptor Management +////////////////////////////////////////////////////////////////////////////// + +// MaxPower: +#define USB_POWER(x) ((x)>>1) /* convert mA to descriptor units of A for MaxPower */ + +/* "config descriptor buffer" - that is, one config, + ..one interface and 2 endpoints */ +struct cdb { + struct usb_config_descriptor cfg; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor ep1, ep2; +} __attribute__ ((packed)); + + +/*======================================================= + * Descriptor API + */ + +/* Get the address of the statically allocated desc_t structure + in the usb core driver. Clients can modify this between + the time they call sa1100_usb_open() and sa1100_usb_start() +*/ +struct cdb *sa1100_usb_get_descriptor_ptr(void); + +#endif /* _SA1100_USB_H */ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/sa1100usb.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,136 @@ +/* + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation 2001 + * + * usb_ctl.h + * + * PRIVATE interface used to share info among components of the SA-1100 USB + * core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core + * should use sa1100_usb.h. + * + */ +#ifndef SA1100USB_H +#define SA1100USB_H + +struct usbctl; + +struct sausb_dev { + struct device *dev; + struct usbctl *ctl; + spinlock_t lock; + + u32 udccr; + + /* + * EP0 write thread. + */ + void (*wrint)(struct sausb_dev *); + struct usb_buf *wrbuf; + unsigned char *wrptr; + unsigned int wrlen; + + /* + * EP0 statistics. + */ + unsigned long ep0_wr_fifo_errs; + unsigned long ep0_wr_bytes; + unsigned long ep0_wr_packets; + unsigned long ep0_rd_fifo_errs; + unsigned long ep0_rd_bytes; + unsigned long ep0_rd_packets; + unsigned long ep0_stall_sent; + unsigned long ep0_early_irqs; + + /* + * EP1 .. n + */ + struct { + dma_regs_t *dmach; + + dma_addr_t bufdma; + unsigned int buflen; + void *pktcpu; + dma_addr_t pktdma; + unsigned int pktlen; + unsigned int pktrem; + + void *cb_data; + void (*cb_func)(void *data, int flag, int size); + + u32 udccs; + unsigned int maxpktsize; + unsigned int configured; + unsigned int host_halt; + unsigned long fifo_errs; + unsigned long bytes; + unsigned long packets; + } ep[2]; +}; + +/* receiver */ +int ep1_recv(void); +void udc_ep1_init(struct sausb_dev *); +void udc_ep1_halt(struct sausb_dev *, int); +void udc_ep1_reset(struct sausb_dev *); +void udc_ep1_config(struct sausb_dev *, unsigned int); +void udc_ep1_int_hndlr(struct sausb_dev *); + +/* xmitter */ +void udc_ep2_init(struct sausb_dev *); +void udc_ep2_halt(struct sausb_dev *, int); +void udc_ep2_reset(struct sausb_dev *); +void udc_ep2_config(struct sausb_dev *, unsigned int); +void udc_ep2_int_hndlr(struct sausb_dev *); + +#define UDC_write(reg, val) do { \ + int i = 10000; \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: write %#x to %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) != (val)); \ +} while (0) + +#define UDC_set(reg, val) do { \ + int i = 10000; \ + do { \ + (reg) |= (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: set %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(!((reg) & (val))); \ +} while (0) + +#define UDC_clear(reg, val) do { \ + int i = 10000; \ + do { \ + (reg) &= ~(val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) & (val)); \ +} while (0) + +#define UDC_flip(reg, val) do { \ + int i = 10000; \ + (reg) = (val); \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(((reg) & (val))); \ +} while (0) + +#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}} + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/strings.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,117 @@ +/* + * usb/strings.c + * + * Copyright (C) 2002 Russell King. + */ +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "strings.h" + +struct usb_buf *usbc_string_alloc(int len) +{ + struct usb_buf *buf; + int tot_len; + + tot_len = sizeof(struct usb_descriptor_header) + sizeof(u16) * len; + + buf = usbb_alloc(tot_len, GFP_KERNEL); + + if (buf) { + struct usb_string_descriptor *desc = usbb_push(buf, tot_len); + + desc->bLength = tot_len; + desc->bDescriptorType = USB_DT_STRING; + } + return buf; +} + +void usbc_string_free(struct usb_buf *buf) +{ + if (buf) + usbb_put(buf); +} + +void usbc_string_from_cstr(struct usb_buf *buf, const char *str) +{ + struct usb_string_descriptor *desc = usbc_string_desc(buf); + int i, len; + + len = strlen(str); + BUG_ON((sizeof(__u16) * len) > desc->bLength - sizeof(struct usb_descriptor_header)); + + for (i = 0; i < len; i++) + desc->wData[i] = cpu_to_le16(str[i]); +} + +int usbc_string_add(struct usbc_strs *table, struct usb_buf *buf) +{ + int nr, i; + + nr = -ENOSPC; + spin_lock_irq(&table->lock); + for (i = 0; i < NR_STRINGS; i++) + if (table->buf[i] == NULL) { + table->buf[i] = buf; + nr = i; + break; + } + spin_unlock_irq(&table->lock); + + return nr; +} + +void usbc_string_del(struct usbc_strs *table, int nr) +{ + if (nr < NR_STRINGS) { + spin_lock_irq(&table->lock); + table->buf[nr] = NULL; + spin_unlock_irq(&table->lock); + } +} + +struct usb_buf * +usbc_string_find(struct usbc_strs *table, unsigned int lang, unsigned int idx) +{ + struct usb_buf *buf = NULL; + + if (idx < NR_STRINGS) { + spin_lock_irq(&table->lock); + buf = usbb_get(table->buf[idx]); + spin_unlock_irq(&table->lock); + } + + return buf; +} + +void usbc_string_free_all(struct usbc_strs *table) +{ + int i; + + spin_lock_irq(&table->lock); + for (i = 0; i < NR_STRINGS; i++) { + usbc_string_free(table->buf[i]); + table->buf[i] = NULL; + } + spin_unlock_irq(&table->lock); +} + +void usbc_string_init(struct usbc_strs *table) +{ + memset(table, 0, sizeof(struct usbc_strs)); + spin_lock_init(&table->lock); +} + +EXPORT_SYMBOL(usbc_string_from_cstr); +EXPORT_SYMBOL(usbc_string_alloc); +EXPORT_SYMBOL(usbc_string_free); +EXPORT_SYMBOL(usbc_string_add); +EXPORT_SYMBOL(usbc_string_del); +EXPORT_SYMBOL(usbc_string_find); +EXPORT_SYMBOL(usbc_string_free_all); +EXPORT_SYMBOL(usbc_string_init); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/usb_ctl.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,171 @@ + /* + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation, 2001 + * + * usb_ctl.c + * + * SA1100 USB controller core driver. + * + * This file provides interrupt routing and overall coordination + * of the three endpoints in usb_ep0, usb_receive (1), and usb_send (2). + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * + */ +#include +#include +#include + +#include "buffer.h" +#include "client.h" +#include "usbdev.h" +#include "sa1100_usb.h" + +////////////////////////////////////////////////////////////////////////////// +// Globals +////////////////////////////////////////////////////////////////////////////// + +/* device descriptors */ +static struct cdb cdb; + +////////////////////////////////////////////////////////////////////////////// +// Private Helpers +////////////////////////////////////////////////////////////////////////////// + +int sa1100_usb_add_string(struct usbctl *ctl, const char *str) +{ + int nr = 0; + + if (str) { + struct usb_buf *buf; + int len; + + len = strlen(str); + + nr = -ENOMEM; + buf = usbc_string_alloc(len); + if (buf) { + usbc_string_from_cstr(buf, str); + nr = usbc_string_add(&ctl->strings, buf); + + if (nr < 0) + usbc_string_free(buf); + } + } + + return nr; +} + +EXPORT_SYMBOL(sa1100_usb_add_string); + +static int sa1100_usb_add_language(struct usbctl *ctl, unsigned int lang) +{ + struct usb_buf *buf; + int nr = -ENOMEM; + + buf = usbc_string_alloc(1); + if (buf) { + usbc_string_desc(buf)->wData[0] = cpu_to_le16(lang); /* American English */ + nr = usbc_string_add(&ctl->strings, buf); + + if (nr < 0) + usbc_string_free(buf); + } + + return nr; +} + +/* setup default descriptors */ + +void initialize_descriptors(struct usbctl *ctl) +{ + struct usb_client *clnt = ctl->clnt; + int r; + + ctl->ep_desc[0] = (struct usb_endpoint_descriptor *)&cdb.ep1; + ctl->ep_desc[1] = (struct usb_endpoint_descriptor *)&cdb.ep2; + + cdb.cfg.bLength = USB_DT_CONFIG_SIZE; + cdb.cfg.bDescriptorType = USB_DT_CONFIG; + cdb.cfg.wTotalLength = cpu_to_le16(sizeof(struct cdb)); + cdb.cfg.bNumInterfaces = 1; + cdb.cfg.bConfigurationValue = 1; + cdb.cfg.iConfiguration = 0; + cdb.cfg.bmAttributes = USB_CONFIG_ATT_ONE; + cdb.cfg.bMaxPower = USB_POWER( 500 ); + + cdb.intf.bLength = USB_DT_INTERFACE_SIZE; + cdb.intf.bDescriptorType = USB_DT_INTERFACE; + cdb.intf.bInterfaceNumber = 0; /* unique intf index*/ + cdb.intf.bAlternateSetting = 0; + cdb.intf.bNumEndpoints = 2; + cdb.intf.bInterfaceClass = 0xff; /* vendor specific */ + cdb.intf.bInterfaceSubClass = 0; + cdb.intf.bInterfaceProtocol = 0; + cdb.intf.iInterface = 0; + + cdb.ep1.bLength = USB_DT_INTERFACE_SIZE; + cdb.ep1.bDescriptorType = USB_DT_ENDPOINT; + cdb.ep1.bEndpointAddress = USB_DIR_OUT | 1; + cdb.ep1.bmAttributes = USB_ENDPOINT_XFER_BULK; + cdb.ep1.wMaxPacketSize = cpu_to_le16(64); + cdb.ep1.bInterval = 0; + + cdb.ep2.bLength = USB_DT_INTERFACE_SIZE; + cdb.ep2.bDescriptorType = USB_DT_ENDPOINT; + cdb.ep2.bEndpointAddress = USB_DIR_IN | 2; + cdb.ep2.bmAttributes = USB_ENDPOINT_XFER_BULK; + cdb.ep2.wMaxPacketSize = cpu_to_le16(64); + cdb.ep2.bInterval = 0; + + ctl->dev_desc->bLength = USB_DT_DEVICE_SIZE; + ctl->dev_desc->bDescriptorType = USB_DT_DEVICE; + ctl->dev_desc->bcdUSB = cpu_to_le16(0x100); /* 1.0 */ + ctl->dev_desc->bDeviceClass = clnt->class; + ctl->dev_desc->bDeviceSubClass = clnt->subclass; + ctl->dev_desc->bDeviceProtocol = clnt->protocol; + ctl->dev_desc->bMaxPacketSize0 = 8; /* ep0 max fifo size */ + ctl->dev_desc->idVendor = cpu_to_le16(clnt->vendor); + ctl->dev_desc->idProduct = cpu_to_le16(clnt->product); + ctl->dev_desc->bcdDevice = cpu_to_le16(clnt->version); + ctl->dev_desc->bNumConfigurations = 1; + + /* set language */ + /* See: http://www.usb.org/developers/data/USB_LANGIDs.pdf */ + r = sa1100_usb_add_language(ctl, 0x409); + if (r < 0) + printk(KERN_ERR "usbc: couldn't add language\n"); + + r = sa1100_usb_add_string(ctl, clnt->manufacturer_str); + if (r < 0) + printk(KERN_ERR "usbc: couldn't add manufacturer string\n"); + + ctl->dev_desc->iManufacturer = r > 0 ? r : 0; + + r = sa1100_usb_add_string(ctl, clnt->product_str); + if (r < 0) + printk(KERN_ERR "usbc: couldn't add product string\n"); + + ctl->dev_desc->iProduct = r > 0 ? r : 0; + + r = sa1100_usb_add_string(ctl, clnt->serial_str); + if (r < 0) + printk(KERN_ERR "usbc: couldn't add serial string\n"); + + ctl->dev_desc->iSerialNumber = r > 0 ? r : 0; +} + + +/*==================================================== + * Descriptor Manipulation. + * Use these between open() and start() above to setup + * the descriptors for your device. + */ + +/* get pointer to static default descriptor */ +struct cdb *sa1100_usb_get_descriptor_ptr(void) +{ + return &cdb; +} + +EXPORT_SYMBOL(sa1100_usb_get_descriptor_ptr); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,12 @@ +# +# Makefile for the USB client +# + +usbdevcore-objs := buffer.o control.o strings.o usb_ctl.o + +sa1100-objs := sa1100usb.o usb_recv.o usb_send.o + +obj-$(CONFIG_SA1100_USB) += usbdevcore.o sa1100.o +obj-$(CONFIG_SA1100_USB_NETLINK) += usb-eth.o +obj-$(CONFIG_SA1100_USB_CHAR) += usb-char.o + --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/usb/usbdev.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,91 @@ +#ifndef USBDEV_H +#define USBDEV_H + +#include "strings.h" + +struct usb_buf; +struct module; +struct cdb; +struct usb_client; + +struct usbc_driver { + struct module *owner; + const char *name; + void *priv; + int (*start)(void *); + int (*stop)(void *); + + int (*ep0_queue)(void *, struct usb_buf *buf, unsigned int req_len); + void (*set_address)(void *, unsigned int addr); + void (*set_config)(void *, struct cdb *config); + + /* + * Get specified endpoint status, as defined in 9.4.5. + */ + unsigned int (*ep_get_status)(void *, unsigned int ep); + void (*ep_halt)(void *, unsigned int ep, int halt); + + /* + * Client + */ + int (*ep_queue)(void *, unsigned int, char *, unsigned int); + void (*ep_reset)(void *, unsigned int); + void (*ep_callback)(void *, unsigned int, void (*)(void *, int, int), void *); + int (*ep_idle)(void *, unsigned int); +}; + +struct usbc_endpoint { + struct usb_endpoint_descriptor *desc; +}; + +struct usbc_interface { + struct usb_interface_descriptor *desc; + unsigned int nr_ep; + struct usbc_endpoint *ep[0]; +}; + +struct usbc_config { + struct usb_config_descriptor *desc; + unsigned int nr_interface; + struct usbc_interface *interface[0]; +}; + +struct usbctl { + struct usb_client *clnt; + const struct usbc_driver *driver; + + /* Internal state */ + unsigned int address; /* host assigned address */ + unsigned int state; /* our device state */ + unsigned int sm_state; /* state machine state */ + + struct usbc_config *config; /* active configuration */ + struct usbc_strs strings; + + /* Descriptors */ + struct usb_device_descriptor *dev_desc; /* device descriptor */ + struct usb_buf *dev_desc_buf; /* device descriptor buffer */ + + + int nr_ep; + struct usb_endpoint_descriptor *ep_desc[2]; +}; + +/* + * Function Prototypes + */ + +#define RET_ERROR (-1) +#define RET_NOACTION (0) +#define RET_QUEUED (1) +#define RET_ACK (2) +#define RET_REQERROR (3) + +int usbctl_parse_request(struct usbctl *ctl, struct usb_ctrlrequest *req); + +int usbctl_reset(struct usbctl *ctl); +void usbctl_suspend(struct usbctl *ctl); +void usbctl_resume(struct usbctl *ctl); + +#endif + --- linux-2.6.5/arch/arm/mach-sa1100/pm.c~heh 2004-04-03 22:36:27.000000000 -0500 +++ linux-2.6.5/arch/arm/mach-sa1100/pm.c 2004-04-30 20:57:36.000000000 -0400 @@ -150,6 +150,7 @@ */ static int sa11x0_pm_prepare(u32 state) { + nmi_watchdog_disable(); return 0; } @@ -158,6 +159,7 @@ */ static int sa11x0_pm_finish(u32 state) { + nmi_watchdog_enable(); return 0; } --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/arch/arm/mach-sa1100/nmi-oopser.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void *nmi_stack; + +asm(" \n\ +nmi_start: \n\ + mrs r8, spsr \n\ + ldr r9, .Lstack \n\ + ldr sp, [r9] \n\ + sub sp, sp, #18 * 4 \n\ + str r8, [sp, #16 * 4] \n\ + str lr, [sp, #15 * 4] \n\ + stmia sp, {r0 - r7} \n\ + add r0, sp, #8 * 4 \n\ + mrs r2, cpsr \n\ + bic r1, r2, #0x1f \n\ + orr r1, r1, #0x13 \n\ + msr cpsr_c, r1 \n\ + mov r0, r0 \n\ + stmia r0, {r8 - lr} \n\ + mov r0, r0 \n\ + msr cpsr_c, r2 \n\ + mov r0, r0 \n\ + mov r0, sp \n\ + mov lr, pc \n\ + ldr pc, .Lfn \n\ + ldmia sp, {r0 - r7} \n\ + ldr r8, [sp, #16 * 4] \n\ + ldr lr, [sp, #15 * 4] \n\ + add sp, sp, #18 * 4 \n\ + msr spsr, r8 \n\ + movs pc, lr \n\ + \n\ +.Lstack: .long nmi_stack \n\ +.Lfn: .long nmi_fn \n\ +nmi_end:"); + +extern unsigned char nmi_start, nmi_end; + +static void __attribute__((unused)) nmi_fn(struct pt_regs *regs) +{ + struct thread_info *thread; + unsigned long osmr0, osmr1, oscr, ossr, icmr, icip; + + oscr = OSCR; + osmr0 = OSMR0; + osmr1 = OSMR1; + ossr = OSSR; + icmr = ICMR; + icip = ICIP; + + OSSR = OSSR_M1; + ICMR &= ~IC_OST1; + + thread = (struct thread_info *)(regs->ARM_sp & ~8191); + + bust_spinlocks(1); + printk("OSMR0:%08lx OSMR1:%08lx OSCR:%08lx OSSR:%08lx ICMR:%08lx ICIP:%08lx\n", + osmr0, osmr1, oscr, ossr, icmr, icip); + nmi_watchdog(thread, regs); + bust_spinlocks(0); + + OSSR = OSSR_M1; + OSMR1 = OSSR + 36864000; + ICMR |= IC_OST1; +} + +static int nmi_init(void) +{ + unsigned char *vec_base = (unsigned char *)vectors_base(); +return 0; + nmi_stack = (void *)__get_free_page(GFP_KERNEL); + if (!nmi_stack) + return -ENOMEM; + + nmi_stack += PAGE_SIZE; + + modify_domain(DOMAIN_USER, DOMAIN_MANAGER); + memcpy(vec_base + 0x1c, &nmi_start, &nmi_end - &nmi_start); + modify_domain(DOMAIN_USER, DOMAIN_CLIENT); + + /* + * Ensure timer 1 is set to FIQ, and enabled. + */ + OSMR1 = OSCR - 1; + OSSR = OSSR_M1; + OIER |= OIER_E1; + ICLR |= IC_OST1; + ICMR |= IC_OST1; + + return 0; +} + +__initcall(nmi_init); --- linux-2.6.5/drivers/media/Kconfig~heh 2004-04-03 22:36:51.000000000 -0500 +++ linux-2.6.5/drivers/media/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -32,6 +32,8 @@ source "drivers/media/common/Kconfig" +source "drivers/media/mmc/Kconfig" + config VIDEO_TUNER tristate default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_MXB=y || VIDEO_CX88=y --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,52 @@ +# +# MMC subsystem configuration +# + +menu "MMC/SD Card support" + +config MMC + tristate "MMC support" + help + MMC is the "multi-media card" bus protocol. + + If you want MMC support, you should say Y here and also + to the specific driver for your MMC interface. + +config MMC_DEBUG + bool "MMC debugging" + depends on MMC != n + help + This is an option for use by developers; most people should + say N here. This enables MMC core and driver debugging. + +config MMC_BLOCK + tristate "MMC block device driver" + depends on MMC + default y + help + Say Y here to enable the MMC block device driver support. + This provides a block device driver, which you can use to + mount the filesystem. Almost everyone wishing MMC support + should say Y or M here. + +config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA && MMC + help + This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card + Interface (PL180 and PL181) support. If you have an ARM(R) + platform with a Multimedia Card slot, say Y or M here. + + If unsure, say N. + +config MMC_PXA + tristate "Intel PXA255 Multimedia Card Interface support" + depends on ARCH_PXA && MMC + help + This selects the Intel(R) PXA(R) Multimedia card Interface. + If you have a PXA(R) platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +endmenu --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/mmc_queue.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,171 @@ +/* + * linux/drivers/media/mmc/mmc_queue.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include + +#include +#include +#include "mmc_queue.h" + +/* + * Prepare a MMC request. Essentially, this means passing the + * preparation off to the media driver. The media driver will + * create a mmc_io_request in req->special. + */ +static int mmc_prep_request(struct request_queue *q, struct request *req) +{ + struct mmc_queue *mq = q->queuedata; + int ret = BLKPREP_KILL; + + if (req->flags & REQ_SPECIAL) { + /* + * Special commands already have the command + * blocks already setup in req->special. + */ + BUG_ON(!req->special); + + ret = BLKPREP_OK; + } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) { + /* + * Block I/O requests need translating according + * to the protocol. + */ + ret = mq->prep_fn(mq, req); + } else { + /* + * Everything else is invalid. + */ + blk_dump_rq_flags(req, "MMC bad request"); + } + + if (ret == BLKPREP_OK) + req->flags |= REQ_DONTPREP; + + return ret; +} + +static int mmc_queue_thread(void *d) +{ + struct mmc_queue *mq = d; + struct request_queue *q = mq->queue; + DECLARE_WAITQUEUE(wait, current); + int ret; + + /* + * Set iothread to ensure that we aren't put to sleep by + * the process freezing. We handle suspension ourselves. + */ + current->flags |= PF_MEMALLOC|PF_IOTHREAD; + + daemonize("mmcqd"); + + spin_lock_irq(¤t->sighand->siglock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + mq->thread = current; + complete(&mq->thread_complete); + + add_wait_queue(&mq->thread_wq, &wait); + spin_lock_irq(q->queue_lock); + do { + struct request *req = NULL; + + set_current_state(TASK_INTERRUPTIBLE); + if (!blk_queue_plugged(q)) + mq->req = req = elv_next_request(q); + spin_unlock(q->queue_lock); + + if (!req) { + if (!mq->thread) + break; + schedule(); + continue; + } + set_current_state(TASK_RUNNING); + + ret = mq->issue_fn(mq, req); + + spin_lock_irq(q->queue_lock); + end_request(req, ret); + } while (1); + remove_wait_queue(&mq->thread_wq, &wait); + + complete_and_exit(&mq->thread_complete, 0); + return 0; +} + +/* + * Generic MMC request handler. This is called for any queue on a + * particular host. When the host is not busy, we look for a request + * on any queue on this host, and attempt to issue it. This may + * not be the queue we were asked to process. + */ +static void mmc_request(request_queue_t *q) +{ + struct mmc_queue *mq = q->queuedata; + + if (!mq->req && !blk_queue_plugged(q)) + wake_up(&mq->thread_wq); +} + +/** + * mmc_init_queue - initialise a queue structure. + * @mq: mmc queue + * @card: mmc card to attach this queue + * @lock: queue lock + * + * Initialise a MMC card request queue. + */ +int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) +{ + u64 limit = BLK_BOUNCE_HIGH; + int ret; + + if (card->host->dev->dma_mask) + limit = *card->host->dev->dma_mask; + + mq->card = card; + mq->queue = blk_init_queue(mmc_request, lock); + blk_queue_prep_rq(mq->queue, mmc_prep_request); + blk_queue_bounce_limit(mq->queue, limit); + + mq->queue->queuedata = mq; + mq->req = NULL; + + init_completion(&mq->thread_complete); + init_waitqueue_head(&mq->thread_wq); + + ret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL); + if (ret < 0) { + blk_cleanup_queue(mq->queue); + } else { + wait_for_completion(&mq->thread_complete); + init_completion(&mq->thread_complete); + } + + return ret; +} + +EXPORT_SYMBOL(mmc_init_queue); + +void mmc_cleanup_queue(struct mmc_queue *mq) +{ + mq->thread = NULL; + wake_up(&mq->thread_wq); + wait_for_completion(&mq->thread_complete); + blk_cleanup_queue(mq->queue); + + mq->card = NULL; +} + +EXPORT_SYMBOL(mmc_cleanup_queue); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/pxamci.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,587 @@ +/* + * linux/drivers/media/mmc/pxa.c - PXA MMCI driver + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This hardware is really sick. No way to clear interrupts. Have + * to turn off the clock whenever we touch the device. Yuck! + * + * 1 and 3 byte data transfers not supported + * max block length up to 1023 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pxamci.h" + +#ifdef CONFIG_MMC_DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) do { } while (0) +#endif + +struct pxamci_host { + struct mmc_host mmc; + spinlock_t lock; + struct resource *res; + void *base; + int irq; + int dma; + unsigned int clkrt; + unsigned int cmdat; + unsigned int imask; + unsigned int power_mode; + + struct mmc_request *req; + struct mmc_command *cmd; + struct mmc_data *data; + + dma_addr_t sg_dma; + struct pxa_dma_desc *sg_cpu; + + dma_addr_t dma_buf; + unsigned int dma_size; + unsigned int dma_dir; +}; + +#define to_pxamci_host(x) container_of(x, struct pxamci_host, mmc) + +/* + * The base MMC clock rate + */ +#define CLOCKRATE 20000000 + +static inline unsigned int ns_to_clocks(unsigned int ns) +{ + return (ns * (CLOCKRATE / 1000000) + 999) / 1000; +} + +static void pxamci_stop_clock(struct pxamci_host *host) +{ + if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { + unsigned long flags; + unsigned int v; + + writel(STOP_CLOCK, host->base + MMC_STRPCL); + + /* + * Wait for the "clock has stopped" interrupt. + * We need to unmask the interrupt to receive + * the notification. Sigh. + */ + spin_lock_irqsave(&host->lock, flags); + writel(host->imask & ~CLK_IS_OFF, host->base + MMC_I_MASK); + do { + v = readl(host->base + MMC_I_REG); + } while (!(v & CLK_IS_OFF)); + writel(host->imask, host->base + MMC_I_MASK); + spin_unlock_irqrestore(&host->lock, flags); + } +} + +static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask &= ~mask; + writel(host->imask, host->base + MMC_I_MASK); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask |= mask; + writel(host->imask, host->base + MMC_I_MASK); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) +{ + unsigned int nob = data->blocks; + unsigned int timeout, size; + dma_addr_t dma; + u32 dcmd; + int i; + + host->data = data; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + writel(nob, host->base + MMC_NOB); + writel(1 << data->blksz_bits, host->base + MMC_BLKLEN); + + timeout = ns_to_clocks(data->timeout_ns) + data->timeout_clks; + writel((timeout + 255) / 256, host->base + MMC_RDTO); + + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; + DRCMRTXMMC = 0; + DRCMRRXMMC = host->dma | DRCMR_MAPVLD; + } else { + host->dma_dir = DMA_TO_DEVICE; + dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; + DRCMRRXMMC = 0; + DRCMRTXMMC = host->dma | DRCMR_MAPVLD; + } + + dcmd |= DCMD_BURST32 | DCMD_WIDTH1; + + host->dma_size = data->blocks << data->blksz_bits; + host->dma_buf = dma_map_single(host->mmc.dev, data->rq->buffer, + host->dma_size, host->dma_dir); + + for (i = 0, size = host->dma_size, dma = host->dma_buf; size; i++) { + u32 len = size; + + if (len > DCMD_LENGTH) + len = 0x1000; + + if (data->flags & MMC_DATA_READ) { + host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; + host->sg_cpu[i].dtadr = dma; + } else { + host->sg_cpu[i].dsadr = dma; + host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; + } + host->sg_cpu[i].dcmd = dcmd | len; + + dma += len; + size -= len; + + if (size) { + host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * + sizeof(struct pxa_dma_desc); + } else { + host->sg_cpu[i].ddadr = DDADR_STOP; + } + } + wmb(); + + DDADR(host->dma) = host->sg_dma; + DCSR(host->dma) = DCSR_RUN; +} + +static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) +{ + WARN_ON(host->cmd != NULL); + host->cmd = cmd; + + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= CMDAT_BUSY; + + switch (cmd->flags & (MMC_RSP_MASK | MMC_RSP_CRC)) { + case MMC_RSP_SHORT | MMC_RSP_CRC: + cmdat |= CMDAT_RESP_SHORT; + break; + case MMC_RSP_SHORT: + cmdat |= CMDAT_RESP_R3; + break; + case MMC_RSP_LONG | MMC_RSP_CRC: + cmdat |= CMDAT_RESP_R2; + break; + default: + break; + } + + writel(cmd->opcode, host->base + MMC_CMD); + writel(cmd->arg >> 16, host->base + MMC_ARGH); + writel(cmd->arg & 0xffff, host->base + MMC_ARGL); + writel(cmdat, host->base + MMC_CMDAT); + writel(host->clkrt, host->base + MMC_CLKRT); + + writel(START_CLOCK, host->base + MMC_STRPCL); + + pxamci_enable_irq(host, END_CMD_RES); +} + +static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *req) +{ + DBG("PXAMCI: request done\n"); + host->req = NULL; + host->cmd = NULL; + host->data = NULL; + mmc_request_done(&host->mmc, req); +} + +static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) +{ + struct mmc_command *cmd = host->cmd; + int i; + u32 v; + + if (!cmd) + return 0; + + host->cmd = NULL; + + /* + * Did I mention this is Sick. We always need to + * discard the upper 8 bits of the first 16-bit word. + */ + v = readl(host->base + MMC_RES) & 0xffff; + for (i = 0; i < 4; i++) { + u32 w1 = readl(host->base + MMC_RES) & 0xffff; + u32 w2 = readl(host->base + MMC_RES) & 0xffff; + cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; + v = w2; + } + + if (stat & STAT_TIME_OUT_RESPONSE) { + cmd->error = MMC_ERR_TIMEOUT; + } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) { + cmd->error = MMC_ERR_BADCRC; + } + + pxamci_disable_irq(host, END_CMD_RES); + if (host->data && cmd->error == MMC_ERR_NONE) { + pxamci_enable_irq(host, DATA_TRAN_DONE); + } else { + pxamci_finish_request(host, host->req); + } + + return 1; +} + +static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + + if (!data) + return 0; + + DCSR(host->dma) = 0; + dma_unmap_single(host->mmc.dev, host->dma_buf, host->dma_size, + host->dma_dir); + + if (stat & STAT_READ_TIME_OUT) + data->error = MMC_ERR_TIMEOUT; + else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) + data->error = MMC_ERR_BADCRC; + + data->bytes_xfered = (data->blocks - readl(host->base + MMC_NOB)) + << data->blksz_bits; + + pxamci_disable_irq(host, DATA_TRAN_DONE); + + host->data = NULL; + if (host->req->stop && data->error == MMC_ERR_NONE) { + pxamci_stop_clock(host); + pxamci_start_cmd(host, host->req->stop, 0); + } else { + pxamci_finish_request(host, host->req); + } + + return 1; +} + +static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs) +{ + struct pxamci_host *host = devid; + unsigned int ireg; + int handled = 0; + + ireg = readl(host->base + MMC_I_REG); + + DBG("PXAMCI: irq %08x\n", ireg); + + if (ireg) { + unsigned stat = readl(host->base + MMC_STAT); + + DBG("PXAMCI: stat %08x\n", stat); + + if (ireg & END_CMD_RES) + handled |= pxamci_cmd_done(host, stat); + if (ireg & DATA_TRAN_DONE) + handled |= pxamci_data_done(host, stat); + } + + return IRQ_RETVAL(handled); +} + +static void pxamci_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct pxamci_host *host = to_pxamci_host(mmc); + unsigned int cmdat; + + WARN_ON(host->req != NULL); + + host->req = req; + + pxamci_stop_clock(host); + + cmdat = host->cmdat; + host->cmdat &= ~CMDAT_INIT; + + if (req->data) { + pxamci_setup_data(host, req->data); + + cmdat &= ~CMDAT_BUSY; + cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; + if (req->data->flags & MMC_DATA_WRITE) + cmdat |= CMDAT_WRITE; + + if (req->data->flags & MMC_DATA_STREAM) + cmdat |= CMDAT_STREAM; + } + + pxamci_start_cmd(host, req->cmd, cmdat); +} + +static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct pxamci_host *host = to_pxamci_host(mmc); + + DBG("pxamci_set_ios: clock %u power %u vdd %u.%02u\n", + ios->clock, ios->power_mode, ios->vdd / 100, + ios->vdd % 100); + + if (ios->clock) { + unsigned int clk = CLOCKRATE / ios->clock; + if (CLOCKRATE / clk > ios->clock) + clk <<= 1; + host->clkrt = fls(clk) - 1; + + /* + * we write clkrt on the next command + */ + } else if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { + /* + * Ensure that the clock is off. + */ + writel(STOP_CLOCK, host->base + MMC_STRPCL); + } + + if (host->power_mode != ios->power_mode) { + host->power_mode = ios->power_mode; + + /* + * power control? none on the lubbock. + */ + + if (ios->power_mode == MMC_POWER_ON) + host->cmdat |= CMDAT_INIT; + } + + DBG("pxamci_set_ios: clkrt = %x cmdat = %x\n", + host->clkrt, host->cmdat); +} + +static struct mmc_host_ops pxamci_ops = { + .request = pxamci_request, + .set_ios = pxamci_set_ios, +}; + +static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) + if (dev->resource[i].flags == mask && nr-- == 0) + return &dev->resource[i]; + return NULL; +} + +static int platform_device_irq(struct platform_device *dev, int nr) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) + if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0) + return dev->resource[i].start; + return NO_IRQ; +} + +static void pxamci_dma_irq(int dma, void *devid, struct pt_regs *regs) +{ + printk(KERN_ERR "DMA%d: IRQ???\n", dma); + DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; +} + +static int pxamci_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pxamci_host *host; + struct resource *r; + int ret, irq; + + r = platform_device_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_device_irq(pdev, 0); + if (!r || irq == NO_IRQ) + return -ENXIO; + + r = request_mem_region(r->start, SZ_4K, "PXAMCI"); + if (!r) + return -EBUSY; + + host = kmalloc(sizeof(struct pxamci_host), GFP_KERNEL); + if (!host) { + ret = -ENOMEM; + goto out; + } + + memset(host, 0, sizeof(struct pxamci_host)); + host->dma = -1; + + host->sg_cpu = dma_alloc_coherent(dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); + if (!host->sg_cpu) { + ret = -ENOMEM; + goto out; + } + + ret = mmc_init_host(&host->mmc); + if (ret) + goto out; + + spin_lock_init(&host->lock); + host->res = r; + host->irq = irq; + host->imask = TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| + END_CMD_RES|PRG_DONE|DATA_TRAN_DONE; + host->mmc.dev = dev; + host->mmc.ops = &pxamci_ops; + host->mmc.f_min = 312500; + host->mmc.f_max = 20000000; + host->mmc.ocr_avail = MMC_VDD_32_33; + + host->base = ioremap(r->start, SZ_4K); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + /* + * Ensure that the host controller is shut down, and setup + * with our defaults. + */ + pxamci_stop_clock(host); + writel(0, host->base + MMC_SPI); + writel(64, host->base + MMC_RESTO); + +#ifdef CONFIG_PREEMPT +#error Not Preempt-safe +#endif + pxa_gpio_mode(GPIO6_MMCCLK_MD); + pxa_gpio_mode(GPIO8_MMCCS0_MD); + CKEN |= CKEN12_MMC; + + host->dma = pxa_request_dma("PXAMCI", DMA_PRIO_LOW, pxamci_dma_irq, host); + if (host->dma < 0) + goto out; + + ret = request_irq(host->irq, pxamci_irq, 0, "PXAMCI", host); + if (ret) + goto out; + + dev_set_drvdata(dev, host); + + mmc_add_host(&host->mmc); + + return 0; + + out: + if (host) { + if (host->dma >= 0) + pxa_free_dma(host->dma); + if (host->base) + iounmap(host->base); + if (host->sg_cpu) + dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + kfree(host); + } + release_resource(r); + return ret; +} + +static int pxamci_remove(struct device *dev) +{ + struct pxamci_host *host = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + if (host) { + mmc_remove_host(&host->mmc); + + pxamci_stop_clock(host); + writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| + END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, + host->base + MMC_I_MASK); + + free_irq(host->irq, host); + pxa_free_dma(host->dma); + iounmap(host->base); + dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + + release_resource(host->res); + + kfree(host); + } + return 0; +} + +static int pxamci_suspend(struct device *dev, u32 state, u32 level) +{ + struct pxamci_host *host = dev_get_drvdata(dev); + int ret = 0; + + if (host && level == SUSPEND_DISABLE) + ret = mmc_suspend_host(&host->mmc, state); + return ret; +} + +static int pxamci_resume(struct device *dev, u32 level) +{ + struct pxamci_host *host = dev_get_drvdata(dev); + int ret = 0; + + if (host && level == RESUME_ENABLE) + ret = mmc_resume_host(&host->mmc); + return ret; +} + +static struct device_driver pxamci_driver = { + .name = "pxamci", + .bus = &platform_bus_type, + .probe = pxamci_probe, + .remove = pxamci_remove, + .suspend = pxamci_suspend, + .resume = pxamci_resume, +}; + +static int __init pxamci_init(void) +{ + return driver_register(&pxamci_driver); +} + +static void __exit pxamci_exit(void) +{ + driver_unregister(&pxamci_driver); +} + +module_init(pxamci_init); +module_exit(pxamci_exit); + +MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/mmc.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,15 @@ +/* + * linux/drivers/media/mmc/mmc.h + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _MMC_H +/* core-internal functions */ +void mmc_init_card(struct mmc_card *card, struct mmc_host *host); +int mmc_register_card(struct mmc_card *card); +void mmc_remove_card(struct mmc_card *card); +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/mmc_sysfs.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,231 @@ +/* + * linux/drivers/media/mmc/mmc_sysfs.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * MMC sysfs/driver model support. + */ +#include +#include +#include + +#include +#include + +#include "mmc.h" + +#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) +#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) + +static void mmc_release_card(struct device *dev) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + + kfree(card); +} + +/* + * This currently matches any MMC driver to any MMC card - drivers + * themselves make the decision whether to drive this card in their + * probe method. + */ +static int mmc_bus_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +static int +mmc_bus_hotplug(struct device *dev, char **envp, int num_envp, char *buf, + int buf_size) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + char ccc[13]; + int i = 0; + +#define add_env(fmt,val) \ + ({ \ + int len, ret = -ENOMEM; \ + if (i < num_envp) { \ + envp[i++] = buf; \ + len = snprintf(buf, buf_size, fmt, val) + 1; \ + buf_size -= len; \ + buf += len; \ + if (buf_size >= 0) \ + ret = 0; \ + } \ + ret; \ + }) + + for (i = 0; i < 12; i++) + ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; + ccc[12] = '\0'; + + i = 0; + add_env("MMC_CCC=%s", ccc); + add_env("MMC_MANFID=%03x", card->cid.manfid); + add_env("MMC_SLOT_NAME=%s", card->dev.bus_id); + + return 0; +} + +static int mmc_bus_suspend(struct device *dev, u32 state) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + int ret = 0; + + if (dev->driver && drv->suspend) + ret = drv->suspend(card, state); + return ret; +} + +static int mmc_bus_resume(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + int ret = 0; + + if (dev->driver && drv->resume) + ret = drv->resume(card); + return ret; +} + +static struct bus_type mmc_bus_type = { + .name = "mmc", + .match = mmc_bus_match, + .hotplug = mmc_bus_hotplug, + .suspend = mmc_bus_suspend, + .resume = mmc_bus_resume, +}; + + +static int mmc_drv_probe(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + + return drv->probe(card); +} + +static int mmc_drv_remove(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + + drv->remove(card); + + return 0; +} + + +/** + * mmc_register_driver - register a media driver + * @drv: MMC media driver + */ +int mmc_register_driver(struct mmc_driver *drv) +{ + drv->drv.bus = &mmc_bus_type; + drv->drv.probe = mmc_drv_probe; + drv->drv.remove = mmc_drv_remove; + return driver_register(&drv->drv); +} + +EXPORT_SYMBOL(mmc_register_driver); + +/** + * mmc_unregister_driver - unregister a media driver + * @drv: MMC media driver + */ +void mmc_unregister_driver(struct mmc_driver *drv) +{ + drv->drv.bus = &mmc_bus_type; + driver_unregister(&drv->drv); +} + +EXPORT_SYMBOL(mmc_unregister_driver); + + +#define MMC_ATTR(name, fmt, args...) \ +static ssize_t mmc_dev_show_##name (struct device *dev, char *buf) \ +{ \ + struct mmc_card *card = dev_to_mmc_card(dev); \ + return sprintf(buf, fmt, args); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, mmc_dev_show_##name, NULL) + +MMC_ATTR(date, "%02d/%04d\n", card->cid.month, 1997 + card->cid.year); +MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); +MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); +MMC_ATTR(manfid, "0x%03x\n", card->cid.manfid); +MMC_ATTR(serial, "0x%06x\n", card->cid.serial); +MMC_ATTR(name, "%s\n", card->cid.prod_name); + +static struct device_attribute *mmc_dev_attributes[] = { + &dev_attr_date, + &dev_attr_fwrev, + &dev_attr_hwrev, + &dev_attr_manfid, + &dev_attr_serial, + &dev_attr_name, +}; + +/* + * Internal function. Initialise a MMC card structure. + */ +void mmc_init_card(struct mmc_card *card, struct mmc_host *host) +{ + memset(card, 0, sizeof(struct mmc_card)); + card->host = host; + device_initialize(&card->dev); + card->dev.parent = card->host->dev; + card->dev.bus = &mmc_bus_type; + card->dev.release = mmc_release_card; +} + +/* + * Internal function. Register a new MMC card with the driver model. + */ +int mmc_register_card(struct mmc_card *card) +{ + int ret, i; + + snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), + "mmc%02x:%04x", card->host->host_num, card->rca); + + ret = device_add(&card->dev); + if (ret == 0) + for (i = 0; i < ARRAY_SIZE(mmc_dev_attributes); i++) + device_create_file(&card->dev, mmc_dev_attributes[i]); + + return ret; +} + +/* + * Internal function. Unregister a new MMC card with the + * driver model, and (eventually) free it. + */ +void mmc_remove_card(struct mmc_card *card) +{ + if (mmc_card_present(card)) + device_del(&card->dev); + + put_device(&card->dev); +} + + +static int __init mmc_init(void) +{ + return bus_register(&mmc_bus_type); +} + +static void __exit mmc_exit(void) +{ + bus_unregister(&mmc_bus_type); +} + +module_init(mmc_init); +module_exit(mmc_exit); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/mmci.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,445 @@ +/* + * linux/drivers/media/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mmci.h" + +#define DRIVER_NAME "mmci-pl18x" + +#ifdef CONFIG_MMC_DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) do { } while (0) +#endif + +static int fmax = 515633; + +static void +mmci_request_end(struct mmci_host *host, struct mmc_request *req) +{ + writel(0, host->base + MMCICOMMAND); + host->req = NULL; + host->cmd = NULL; + host->data = NULL; + + if (req->data) + req->data->bytes_xfered = host->data_xfered; + + mmc_request_done(&host->mmc, req); +} + +static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) +{ + unsigned int datactrl; + + DBG("MMCI: data: blksz %04x blks %04x flags %08x\n", + 1 << data->blksz_bits, data->blocks, data->flags); + + datactrl = MCI_DPSM_ENABLE | data->blksz_bits << 4; + + if (data->flags & MMC_DATA_READ) + datactrl |= MCI_DPSM_DIRECTION; + + host->data = data; + host->buffer = data->rq->buffer; + host->size = data->blocks << data->blksz_bits; + host->data_xfered = 0; + + writel(0x800000, host->base + MMCIDATATIMER); + writel(host->size, host->base + MMCIDATALENGTH); + writel(datactrl, host->base + MMCIDATACTRL); +} + +static void +mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) +{ + DBG("MMCI: cmd: op %02x arg %08x flags %08x\n", + cmd->opcode, cmd->arg, cmd->flags); + + if (readl(host->base + MMCICOMMAND) & MCI_CPSM_ENABLE) { + writel(0, host->base + MMCICOMMAND); + udelay(1); + } + + c |= cmd->opcode | MCI_CPSM_ENABLE; + switch (cmd->flags & MMC_RSP_MASK) { + case MMC_RSP_NONE: + default: + break; + case MMC_RSP_LONG: + c |= MCI_CPSM_LONGRSP; + case MMC_RSP_SHORT: + c |= MCI_CPSM_RESPONSE; + break; + } + if (/*interrupt*/0) + c |= MCI_CPSM_INTERRUPT; + + host->cmd = cmd; + + writel(cmd->arg, host->base + MMCIARGUMENT); + writel(c, host->base + MMCICOMMAND); +} + +static void +mmci_data_irq(struct mmci_host *host, struct mmc_data *data, + unsigned int status) +{ + if (status & MCI_DATABLOCKEND) { + host->data_xfered += 1 << data->blksz_bits; + } + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { + if (status & MCI_DATACRCFAIL) + data->error = MMC_ERR_BADCRC; + else if (status & MCI_DATATIMEOUT) + data->error = MMC_ERR_TIMEOUT; + else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) + data->error = MMC_ERR_FIFO; + status |= MCI_DATAEND; + } + if (status & MCI_DATAEND) { + host->data = NULL; + if (!data->stop) { + mmci_request_end(host, data->req); + } else /*if (readl(host->base + MMCIDATACNT) > 6)*/ { + mmci_start_command(host, data->stop, 0); + } + } +} + +static void +mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + unsigned int status) +{ + host->cmd = NULL; + + cmd->resp[0] = readl(host->base + MMCIRESPONSE0); + cmd->resp[1] = readl(host->base + MMCIRESPONSE1); + cmd->resp[2] = readl(host->base + MMCIRESPONSE2); + cmd->resp[3] = readl(host->base + MMCIRESPONSE3); + + if (status & MCI_CMDTIMEOUT) { + cmd->error = MMC_ERR_TIMEOUT; + } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { + cmd->error = MMC_ERR_BADCRC; + } + + if (!cmd->data || cmd->error != MMC_ERR_NONE) { + mmci_request_end(host, cmd->req); + } else if (!(cmd->data->flags & MMC_DATA_READ)) { + mmci_start_data(host, cmd->data); + } +} + +static irqreturn_t mmci_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mmci_host *host = dev_id; + u32 status; + int ret = 0; + + do { + struct mmc_command *cmd; + struct mmc_data *data; + + status = readl(host->base + MMCISTATUS); + writel(status, host->base + MMCICLEAR); + + if (!(status & MCI_IRQMASK)) + break; + + DBG("MMCI: irq %08x\n", status); + + if (status & (MCI_RXDATAAVLBL|MCI_RXFIFOHALFFULL)) { + int count = host->size - (readl(host->base + MMCIFIFOCNT) << 2); + if (count < 0) + count = 0; + if (count && host->buffer) { + readsl(host->base + MMCIFIFO, host->buffer, count >> 2); + host->buffer += count; + host->size -= count; + if (host->size == 0) + host->buffer = NULL; + } else { + static int first = 1; + if (first) { + first = 0; + printk(KERN_ERR "MMCI: sinking excessive data\n"); + } + readl(host->base + MMCIFIFO); + } + } + if (status & (MCI_TXFIFOEMPTY|MCI_TXFIFOHALFEMPTY)) { + int count = host->size; + if (count > MCI_FIFOHALFSIZE) + count = MCI_FIFOHALFSIZE; + if (count && host->buffer) { + writesl(host->base + MMCIFIFO, host->buffer, count >> 2); + host->buffer += count; + host->size -= count; + if (host->size == 0) + host->buffer = NULL; + } else { + static int first = 1; + if (first) { + first = 0; + printk(KERN_ERR "MMCI: ran out of source data\n"); + } + } + } + + data = host->data; + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| + MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND)) + mmci_data_irq(host, data, status); + + cmd = host->cmd; + if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd) + mmci_cmd_irq(host, cmd, status); + + ret = 1; + } while (status); + + return IRQ_RETVAL(ret); +} + +static void mmci_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct mmci_host *host = to_mmci_host(mmc); + + WARN_ON(host->req != NULL); + + host->req = req; + + if (req->data && req->data->flags & MMC_DATA_READ) + mmci_start_data(host, req->data); + + mmci_start_command(host, req->cmd, 0); +} + +static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmci_host *host = to_mmci_host(mmc); + u32 clk = 0, pwr = 0; + + DBG("MMCI: set_ios: clock %dHz busmode %d powermode %d Vdd %d.%02d\n", + ios->clock, ios->bus_mode, ios->power_mode, + ios->vdd / 100, ios->vdd % 100); + + if (ios->clock) { + clk = host->mclk / (2 * ios->clock) - 1; + if (clk > 256) + clk = 255; + clk |= MCI_CLK_ENABLE; + } + + switch (ios->power_mode) { + case MMC_POWER_OFF: + break; + case MMC_POWER_UP: + pwr |= MCI_PWR_UP; + break; + case MMC_POWER_ON: + pwr |= MCI_PWR_ON; + break; + } + + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + pwr |= MCI_ROD; + + writel(clk, host->base + MMCICLOCK); + + if (host->pwr != pwr) { + host->pwr = pwr; + writel(pwr, host->base + MMCIPOWER); + } +} + +static struct mmc_host_ops mmci_ops = { + .request = mmci_request, + .set_ios = mmci_set_ios, +}; + +static int mmci_probe(struct amba_device *dev, void *id) +{ + struct mmci_host *host; + int ret; +// void *tmp; + + /* enable the interrupt via the VIC */ +// tmp = ioremap(0xc3000000, SZ_4K); +// if (tmp) { +// u32 val = readl(tmp + 0x10); +// writel(val | 0x180, tmp + 0x10); +// iounmap(tmp); +// } + + if (!request_mem_region(dev->res.start, SZ_4K, DRIVER_NAME)) + return -EBUSY; + + host = kmalloc(sizeof(struct mmci_host), GFP_KERNEL); + if (!host) { + ret = -ENOMEM; + goto out; + } + + memset(host, 0, sizeof(struct mmci_host)); + + ret = mmc_init_host(&host->mmc); + if (ret) + goto out; + + host->base = ioremap(dev->res.start, SZ_4K); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + host->irq = dev->irq; + host->mclk = 33000000; /* impd1 */ + host->mmc.dev = &dev->dev; + host->mmc.ops = &mmci_ops; + host->mmc.f_min = (host->mclk + 511) / 512; + host->mmc.f_max = host->mclk / 2; + if (host->mmc.f_max > fmax) + host->mmc.f_max = fmax; + + host->mmc.ocr_avail = MMC_VDD_35_36; + + writel(0, host->base + MMCIMASK0); + writel(0, host->base + MMCIMASK1); + writel(0xfff, host->base + MMCICLEAR); + + ret = request_irq(host->irq, mmci_irq, SA_SHIRQ, DRIVER_NAME, host); + if (ret) + goto out; + + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + + amba_set_drvdata(dev, host); + + mmc_add_host(&host->mmc); + + return 0; + + out: + if (host) { + if (host->base) + iounmap(host->base); + kfree(host); + } + release_mem_region(dev->res.start, SZ_4K); + return ret; +} + +static int mmci_remove(struct amba_device *dev) +{ + struct mmci_host *host = amba_get_drvdata(dev); + + amba_set_drvdata(dev, NULL); + + if (host) { + mmc_remove_host(&host->mmc); + + writel(0, host->base + MMCIMASK0); + writel(0, host->base + MMCIMASK1); + + writel(0, host->base + MMCICOMMAND); + writel(0, host->base + MMCIDATACTRL); + + free_irq(host->irq, host); + + iounmap(host->base); + + kfree(host); + + release_mem_region(dev->res.start, SZ_4K); + } + + return 0; +} + +#ifdef CONFIG_PM +static int mmci_suspend(struct amba_device *dev, u32 state) +{ + struct mmci_host *host = amba_get_drvdata(dev); + + return host ? mmc_suspend_host(&host->mmc, state) : 0; +} + +static int mmci_resume(struct amba_device *dev) +{ + struct mmci_host *host = amba_get_drvdata(dev); + int ret = 0; + + if (host) { + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + ret = mmc_resume_host(&host->mmc); + } + + return ret; +} +#else +#define mmci_suspend NULL +#define mmci_resume NULL +#endif + +static struct amba_id mmci_ids[] = { + { + .id = 0x00041180, + .mask = 0x000fffff, + }, + { + .id = 0x00041181, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver mmci_driver = { + .drv = { + .name = DRIVER_NAME, + }, + .probe = mmci_probe, + .remove = mmci_remove, + .suspend = mmci_suspend, + .resume = mmci_resume, + .id_table = mmci_ids, +}; + +static int __init mmci_init(void) +{ + return amba_driver_register(&mmci_driver); +} + +static void __exit mmci_exit(void) +{ + amba_driver_unregister(&mmci_driver); +} + +module_init(mmci_init); +module_exit(mmci_exit); +module_param(fmax, int, 0444); + +MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/mmc_queue.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,29 @@ +#ifndef MMC_QUEUE_H +#define MMC_QUEUE_H + +struct request; +struct task_struct; + +struct mmc_queue { + struct mmc_card *card; + struct completion thread_complete; + wait_queue_head_t thread_wq; + struct task_struct *thread; + struct request *req; + int (*prep_fn)(struct mmc_queue *, struct request *); + int (*issue_fn)(struct mmc_queue *, struct request *); + void *data; + struct request_queue *queue; +}; + +struct mmc_io_request { + struct request *rq; + int num; + struct mmc_command selcmd; /* mmc_queue private */ + struct mmc_command cmd[4]; /* max 4 commands */ +}; + +extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); +extern void mmc_cleanup_queue(struct mmc_queue *); + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/mmc_block.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,482 @@ +/* + * Block driver for media (i.e., flash cards) + * + * Copyright 2002 Hewlett-Packard Company + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Many thanks to Alessandro Rubini and Jonathan Corbet! + * + * Author: Andrew Christian + * 28 May 2002 + */ +#include +#include +#include + +#include +#include /* printk() */ +#include /* everything... */ +#include /* error codes */ +#include /* HDIO_GETGEO */ +#include +#include +#include + +#include +#include + +#include +#include + +#include "mmc_queue.h" + +#define MMC_SHIFT 3 /* max 8 partitions per card */ + +static int mmc_major; +static int maxsectors = 8; + +/* + * There is one mmc_blk_data per slot. + */ +struct mmc_blk_data { + spinlock_t lock; + struct gendisk *disk; + struct mmc_queue queue; + + unsigned int usage; + unsigned int block_bits; + unsigned int suspended; +}; + +static DECLARE_MUTEX(open_lock); + +static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) +{ + struct mmc_blk_data *md; + + down(&open_lock); + md = disk->private_data; + if (md && md->usage == 0) + md = NULL; + if (md) + md->usage++; + up(&open_lock); + + return md; +} + +static void mmc_blk_put(struct mmc_blk_data *md) +{ + down(&open_lock); + md->usage--; + if (md->usage == 0) { + put_disk(md->disk); + mmc_cleanup_queue(&md->queue); + kfree(md); + } + up(&open_lock); +} + +static int mmc_blk_open(struct inode *inode, struct file *filp) +{ + struct mmc_blk_data *md; + int ret = -ENXIO; + + md = mmc_blk_get(inode->i_bdev->bd_disk); + if (md) { + if (md->usage == 2) + check_disk_change(inode->i_bdev); + ret = 0; + } + + return ret; +} + +static int mmc_blk_release(struct inode *inode, struct file *filp) +{ + struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data; + + mmc_blk_put(md); + return 0; +} + +static int +mmc_blk_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + + if (cmd == HDIO_GETGEO) { + struct hd_geometry geo; + + memset(&geo, 0, sizeof(struct hd_geometry)); + + geo.cylinders = get_capacity(bdev->bd_disk) / (4 * 16); + geo.heads = 4; + geo.sectors = 16; + geo.start = get_start_sect(bdev); + + return copy_to_user((void *)arg, &geo, sizeof(geo)) + ? -EFAULT : 0; + } + + return -ENOTTY; +} + +static struct block_device_operations mmc_bdops = { + .open = mmc_blk_open, + .release = mmc_blk_release, + .ioctl = mmc_blk_ioctl, + .owner = THIS_MODULE, +}; + +struct mmc_blk_request { + struct mmc_request req; + struct mmc_command cmd; + struct mmc_command stop; + struct mmc_data data; +}; + +static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + + /* + * If we have no device, we haven't finished initialising. + */ + if (!md || !mq->card) { + printk("killing request - no device/host\n"); + goto kill; + } + + if (md->suspended) { + blk_plug_device(md->queue.queue); + goto defer; + } + + /* + * Check for excessive requests. + */ + if (req->sector + req->nr_sectors > get_capacity(req->rq_disk)) { + printk("bad request size\n"); + goto kill; + } + + return BLKPREP_OK; + + defer: + return BLKPREP_DEFER; + kill: + return BLKPREP_KILL; +} + +static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + int err, sz = 0; + + err = mmc_card_claim_host(card); + if (err) + goto cmd_err; + + do { + struct mmc_blk_request rq; + struct mmc_command cmd; + + memset(&rq, 0, sizeof(struct mmc_blk_request)); + rq.req.cmd = &rq.cmd; + rq.req.data = &rq.data; + + rq.cmd.arg = req->sector << 9; + rq.cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC; + rq.data.rq = req; + rq.data.timeout_ns = card->csd.tacc_ns * 10; + rq.data.timeout_clks = card->csd.tacc_clks * 10; + rq.data.blksz_bits = md->block_bits; + rq.data.blocks = req->current_nr_sectors >> (md->block_bits - 9); + rq.stop.opcode = MMC_STOP_TRANSMISSION; + rq.stop.arg = 0; + rq.stop.flags = MMC_RSP_SHORT | MMC_RSP_CRC | MMC_RSP_BUSY; + + if (rq_data_dir(req) == READ) { + rq.cmd.opcode = rq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK; + rq.data.flags |= MMC_DATA_READ; + } else { + rq.cmd.opcode = MMC_WRITE_BLOCK; + rq.cmd.flags |= MMC_RSP_BUSY; + rq.data.flags |= MMC_DATA_WRITE; + rq.data.blocks = 1; + } + rq.req.stop = rq.data.blocks > 1 ? &rq.stop : NULL; + + mmc_wait_for_req(card->host, &rq.req); + if (rq.cmd.error) { + err = rq.cmd.error; + printk("error %d sending read/write command\n", err); + goto cmd_err; + } + + if (rq_data_dir(req) == READ) { + sz = rq.data.bytes_xfered; + } else { + sz = 0; + } + + if (rq.data.error) { + err = rq.data.error; + printk("error %d transferring data\n", err); + goto cmd_err; + } + + if (rq.stop.error) { + err = rq.stop.error; + printk("error %d sending stop command\n", err); + goto cmd_err; + } + + do { + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + if (err) { + printk("error %d requesting status\n", err); + goto cmd_err; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); + +#if 0 + if (cmd.resp[0] & ~0x00000900) + printk("status = %08x\n", cmd.resp[0]); + err = mmc_decode_status(cmd.resp); + if (err) + goto cmd_err; +#endif + + sz = rq.data.bytes_xfered; + } while (end_that_request_chunk(req, 1, sz)); + + mmc_card_release_host(card); + + return 1; + + cmd_err: + mmc_card_release_host(card); + + end_that_request_chunk(req, 1, sz); + req->errors = err; + + return 0; +} + +#define MMC_NUM_MINORS (256 >> MMC_SHIFT) + +static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; + +static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) +{ + struct mmc_blk_data *md; + int devidx; + + devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); + if (devidx >= MMC_NUM_MINORS) + return NULL; + __set_bit(devidx, dev_use); + + md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); + if (md) { + memset(md, 0, sizeof(struct mmc_blk_data)); + + md->disk = alloc_disk(1 << MMC_SHIFT); + if (md->disk == NULL) { + kfree(md); + md = NULL; + goto out; + } + + spin_lock_init(&md->lock); + md->usage = 1; + + mmc_init_queue(&md->queue, card, &md->lock); + md->queue.prep_fn = mmc_blk_prep_rq; + md->queue.issue_fn = mmc_blk_issue_rq; + md->queue.data = md; + + md->disk->major = mmc_major; + md->disk->first_minor = devidx << MMC_SHIFT; + md->disk->fops = &mmc_bdops; + md->disk->private_data = md; + md->disk->queue = md->queue.queue; + md->disk->driverfs_dev = &card->dev; + + sprintf(md->disk->disk_name, "mmcblk%d", devidx); + sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); + + md->block_bits = md->queue.card->csd.read_blkbits; + + blk_queue_max_sectors(md->queue.queue, maxsectors); + blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); + set_capacity(md->disk, md->queue.card->csd.capacity); + } + out: + return md; +} + +static int +mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) +{ + struct mmc_command cmd; + int err; + + mmc_card_claim_host(card); + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = 1 << card->csd.read_blkbits; + cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + mmc_card_release_host(card); + + if (err) { + printk(KERN_ERR "%s: unable to set block size to %d: %d\n", + md->disk->disk_name, cmd.arg, err); + return -EINVAL; + } + + return 0; +} + +static int mmc_blk_probe(struct mmc_card *card) +{ + struct mmc_blk_data *md; + int err; + + if (card->csd.cmdclass & ~0x1ff) + return -ENODEV; + + if (card->csd.read_blkbits < 9) { + printk(KERN_WARNING "%s: read blocksize too small (%u)\n", + mmc_card_id(card), 1 << card->csd.read_blkbits); + return -ENODEV; + } + + md = mmc_blk_alloc(card); + if (md == NULL) + return -ENOMEM; + + err = mmc_blk_set_blksize(md, card); + if (err) + goto out; + + printk(KERN_INFO "%s: %s %s %dKiB\n", + md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), + (card->csd.capacity << card->csd.read_blkbits) / 1024); + + mmc_set_drvdata(card, md); + add_disk(md->disk); + return 0; + + out: + mmc_blk_put(md); + + return err; +} + +static void mmc_blk_remove(struct mmc_card *card) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + int devidx; + + del_gendisk(md->disk); + + /* + * I think this is needed. + */ + md->disk->queue = NULL; + + devidx = md->disk->first_minor >> MMC_SHIFT; + __clear_bit(devidx, dev_use); + + mmc_blk_put(md); + } + mmc_set_drvdata(card, NULL); +} + +#ifdef CONFIG_PM +static int mmc_blk_suspend(struct mmc_card *card, u32 state) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + blk_stop_queue(md->queue.queue); + } + return 0; +} + +static int mmc_blk_resume(struct mmc_card *card) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + mmc_blk_set_blksize(md, md->queue.card); + blk_start_queue(md->queue.queue); + } + return 0; +} +#else +#define mmc_blk_suspend NULL +#define mmc_blk_resume NULL +#endif + +static struct mmc_driver mmc_driver = { + .drv = { + .name = "mmcblk", + }, + .probe = mmc_blk_probe, + .remove = mmc_blk_remove, + .suspend = mmc_blk_suspend, + .resume = mmc_blk_resume, +}; + +static int __init mmc_blk_init(void) +{ + int res = -ENOMEM; + + res = register_blkdev(mmc_major, "mmc"); + if (res < 0) { + printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", + mmc_major, res); + goto out; + } + if (mmc_major == 0) + mmc_major = res; + + devfs_mk_dir("mmc"); + return mmc_register_driver(&mmc_driver); + + out: + return res; +} + +static void __exit mmc_blk_exit(void) +{ + mmc_unregister_driver(&mmc_driver); + devfs_remove("mmc"); + unregister_blkdev(mmc_major, "mmc"); +} + +module_init(mmc_blk_init); +module_exit(mmc_blk_exit); +module_param(maxsectors, int, 0444); + +MODULE_PARM_DESC(maxsectors, "Maximum number of sectors for a single request"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/pxamci.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,94 @@ +#undef MMC_STRPCL +#undef MMC_STAT +#undef MMC_CLKRT +#undef MMC_SPI +#undef MMC_CMDAT +#undef MMC_RESTO +#undef MMC_RDTO +#undef MMC_BLKLEN +#undef MMC_NOB +#undef MMC_PRTBUF +#undef MMC_I_MASK +#undef END_CMD_RES +#undef PRG_DONE +#undef DATA_TRAN_DONE +#undef MMC_I_REG +#undef MMC_CMD +#undef MMC_ARGH +#undef MMC_ARGL +#undef MMC_RES +#undef MMC_RXFIFO +#undef MMC_TXFIFO + +#define MMC_STRPCL 0x0000 +#define STOP_CLOCK (1 << 0) +#define START_CLOCK (2 << 0) + +#define MMC_STAT 0x0004 +#define STAT_END_CMD_RES (1 << 13) +#define STAT_PRG_DONE (1 << 12) +#define STAT_DATA_TRAN_DONE (1 << 11) +#define STAT_CLK_EN (1 << 8) +#define STAT_RECV_FIFO_FULL (1 << 7) +#define STAT_XMIT_FIFO_EMPTY (1 << 6) +#define STAT_RES_CRC_ERR (1 << 5) +#define STAT_SPI_READ_ERROR_TOKEN (1 << 4) +#define STAT_CRC_READ_ERROR (1 << 3) +#define STAT_CRC_WRITE_ERROR (1 << 2) +#define STAT_TIME_OUT_RESPONSE (1 << 1) +#define STAT_READ_TIME_OUT (1 << 0) + +#define MMC_CLKRT 0x0008 /* 3 bit */ + +#define MMC_SPI 0x000c +#define SPI_CS_ADDRESS (1 << 3) +#define SPI_CS_EN (1 << 2) +#define CRC_ON (1 << 1) +#define SPI_EN (1 << 0) + +#define MMC_CMDAT 0x0010 +#define CMDAT_DMAEN (1 << 7) +#define CMDAT_INIT (1 << 6) +#define CMDAT_BUSY (1 << 5) +#define CMDAT_STREAM (1 << 4) /* 1 = stream */ +#define CMDAT_WRITE (1 << 3) /* 1 = write */ +#define CMDAT_DATAEN (1 << 2) +#define CMDAT_RESP_NONE (0 << 0) +#define CMDAT_RESP_SHORT (1 << 0) +#define CMDAT_RESP_R2 (2 << 0) +#define CMDAT_RESP_R3 (3 << 0) + +#define MMC_RESTO 0x0014 /* 7 bit */ + +#define MMC_RDTO 0x0018 /* 16 bit */ + +#define MMC_BLKLEN 0x001c /* 10 bit */ + +#define MMC_NOB 0x0020 /* 16 bit */ + +#define MMC_PRTBUF 0x0024 +#define BUF_PART_FULL (1 << 0) + +#define MMC_I_MASK 0x0028 +#define TXFIFO_WR_REQ (1 << 6) +#define RXFIFO_RD_REQ (1 << 5) +#define CLK_IS_OFF (1 << 4) +#define STOP_CMD (1 << 3) +#define END_CMD_RES (1 << 2) +#define PRG_DONE (1 << 1) +#define DATA_TRAN_DONE (1 << 0) + +#define MMC_I_REG 0x002c +/* same as MMC_I_MASK */ + +#define MMC_CMD 0x0030 + +#define MMC_ARGH 0x0034 /* 16 bit */ + +#define MMC_ARGL 0x0038 /* 16 bit */ + +#define MMC_RES 0x003c /* 16 bit */ + +#define MMC_RXFIFO 0x0040 /* 8 bit */ + +#define MMC_TXFIFO 0x0044 /* 8 bit */ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/mmci.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,147 @@ +/* + * linux/drivers/media/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define MMCIPOWER 0x000 +#define MCI_PWR_OFF 0x00 +#define MCI_PWR_UP 0x02 +#define MCI_PWR_ON 0x03 +#define MCI_OD (1 << 6) +#define MCI_ROD (1 << 7) + +#define MMCICLOCK 0x004 +#define MCI_CLK_ENABLE (1 << 8) +#define MCI_PWRSAVE (1 << 9) +#define MCI_BYPASS (1 << 10) + +#define MMCIARGUMENT 0x008 +#define MMCICOMMAND 0x00c +#define MCI_CPSM_RESPONSE (1 << 6) +#define MCI_CPSM_LONGRSP (1 << 7) +#define MCI_CPSM_INTERRUPT (1 << 8) +#define MCI_CPSM_PENDING (1 << 9) +#define MCI_CPSM_ENABLE (1 << 10) + +#define MMCIRESPCMD 0x010 +#define MMCIRESPONSE0 0x014 +#define MMCIRESPONSE1 0x018 +#define MMCIRESPONSE2 0x01c +#define MMCIRESPONSE3 0x020 +#define MMCIDATATIMER 0x024 +#define MMCIDATALENGTH 0x028 +#define MMCIDATACTRL 0x02c +#define MCI_DPSM_ENABLE (1 << 0) +#define MCI_DPSM_DIRECTION (1 << 1) +#define MCI_DPSM_MODE (1 << 2) +#define MCI_DPSM_DMAENABLE (1 << 3) + +#define MMCIDATACNT 0x030 +#define MMCISTATUS 0x034 +#define MCI_CMDCRCFAIL (1 << 0) +#define MCI_DATACRCFAIL (1 << 1) +#define MCI_CMDTIMEOUT (1 << 2) +#define MCI_DATATIMEOUT (1 << 3) +#define MCI_TXUNDERRUN (1 << 4) +#define MCI_RXOVERRUN (1 << 5) +#define MCI_CMDRESPEND (1 << 6) +#define MCI_CMDSENT (1 << 7) +#define MCI_DATAEND (1 << 8) +#define MCI_DATABLOCKEND (1 << 10) +#define MCI_CMDACTIVE (1 << 11) +#define MCI_TXACTIVE (1 << 12) +#define MCI_RXACTIVE (1 << 13) +#define MCI_TXFIFOHALFEMPTY (1 << 14) +#define MCI_RXFIFOHALFFULL (1 << 15) +#define MCI_TXFIFOFULL (1 << 16) +#define MCI_RXFIFOFULL (1 << 17) +#define MCI_TXFIFOEMPTY (1 << 18) +#define MCI_RXFIFOEMPTY (1 << 19) +#define MCI_TXDATAAVLBL (1 << 20) +#define MCI_RXDATAAVLBL (1 << 21) + +#define MMCICLEAR 0x038 +#define MCI_CMDCRCFAILCLR (1 << 0) +#define MCI_DATACRCFAILCLR (1 << 1) +#define MCI_CMDTIMEOUTCLR (1 << 2) +#define MCI_DATATIMEOUTCLR (1 << 3) +#define MCI_TXUNDERRUNCLR (1 << 4) +#define MCI_RXOVERRUNCLR (1 << 5) +#define MCI_CMDRESPENDCLR (1 << 6) +#define MCI_CMDSENTCLR (1 << 7) +#define MCI_DATAENDCLR (1 << 8) +#define MCI_DATABLOCKENDCLR (1 << 10) + +#define MMCIMASK0 0x03c +#define MCI_CMDCRCFAILMASK (1 << 0) +#define MCI_DATACRCFAILMASK (1 << 1) +#define MCI_CMDTIMEOUTMASK (1 << 2) +#define MCI_DATATIMEOUTMASK (1 << 3) +#define MCI_TXUNDERRUNMASK (1 << 4) +#define MCI_RXOVERRUNMASK (1 << 5) +#define MCI_CMDRESPENDMASK (1 << 6) +#define MCI_CMDSENTMASK (1 << 7) +#define MCI_DATAENDMASK (1 << 8) +#define MCI_DATABLOCKENDMASK (1 << 10) +#define MCI_CMDACTIVEMASK (1 << 11) +#define MCI_TXACTIVEMASK (1 << 12) +#define MCI_RXACTIVEMASK (1 << 13) +#define MCI_TXFIFOHALFEMPTYMASK (1 << 14) +#define MCI_RXFIFOHALFFULLMASK (1 << 15) +#define MCI_TXFIFOFULLMASK (1 << 16) +#define MCI_RXFIFOFULLMASK (1 << 17) +#define MCI_TXFIFOEMPTYMASK (1 << 18) +#define MCI_RXFIFOEMPTYMASK (1 << 19) +#define MCI_TXDATAAVLBLMASK (1 << 20) +#define MCI_RXDATAAVLBLMASK (1 << 21) + +#define MMCIMASK1 0x040 +#define MMCIFIFOCNT 0x048 +#define MMCIFIFO 0x080 /* to 0x0bc */ + +#define MCI_IRQMASK \ + (MCI_CMDCRCFAIL|MCI_DATACRCFAIL|MCI_CMDTIMEOUT|MCI_DATATIMEOUT| \ + MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_CMDRESPEND|MCI_CMDSENT| \ + MCI_DATAEND|MCI_DATABLOCKEND| \ + MCI_TXFIFOHALFEMPTY|MCI_RXFIFOHALFFULL| \ + MCI_TXFIFOEMPTY|MCI_RXDATAAVLBL) + +#define MCI_IRQENABLE \ + (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \ + MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ + MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK| \ + MCI_DATABLOCKENDMASK|MCI_TXFIFOHALFEMPTYMASK| \ + MCI_RXFIFOHALFFULLMASK) + +#define MCI_FIFOSIZE 16 + +#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) + +struct mmci_host { + struct mmc_host mmc; + void *base; + int irq; + unsigned int mclk; + u32 pwr; + + struct mmc_request *req; + struct mmc_command *cmd; + struct mmc_data *data; + + unsigned int data_xfered; + + /* pio stuff */ + void *buffer; + unsigned int size; + + /* dma stuff */ +// struct scatterlist *sg_list; +// int sg_len; +// int sg_dir; +}; + +#define to_mmci_host(mmc) container_of(mmc, struct mmci_host, mmc) --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/mmc.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,801 @@ +/* + * linux/drivers/media/mmc/mmc.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mmc.h" + +#ifdef CONFIG_MMC_DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) do { } while (0) +#endif + +#define CMD_RETRIES 3 + +/* + * OCR Bit positions to 10s of Vdd mV. + */ +static const unsigned short mmc_ocr_bit_to_vdd[] = { + 150, 155, 160, 165, 170, 180, 190, 200, + 210, 220, 230, 240, 250, 260, 270, 280, + 290, 300, 310, 320, 330, 340, 350, 360 +}; + +static const unsigned int tran_exp[] = { + 10000, 100000, 1000000, 10000000, + 0, 0, 0, 0 +}; + +static const unsigned char tran_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +static const unsigned int tacc_exp[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, +}; + +static const unsigned int tacc_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + + +/** + * mmc_request_done - finish processing an MMC command + * @host: MMC host which completed command + * @cmd: MMC command which completed + * @err: MMC error code + * + * MMC drivers should call this function when they have completed + * their processing of a command. This should be called before the + * data part of the command has completed. + */ +void mmc_request_done(struct mmc_host *host, struct mmc_request *req) +{ + struct mmc_command *cmd = req->cmd; + int err = req->cmd->error; + DBG("MMC: req done (%02x): %d: %08x %08x %08x %08x\n", cmd->opcode, + err, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + + if (err && cmd->retries) { + cmd->retries--; + cmd->error = 0; + host->ops->request(host, req); + } else if (req->done) { + req->done(req); + } +} + +EXPORT_SYMBOL(mmc_request_done); + +/** + * mmc_start_request - start a command on a host + * @host: MMC host to start command on + * @cmd: MMC command to start + * + * Queue a command on the specified host. We expect the + * caller to be holding the host lock with interrupts disabled. + */ +void +mmc_start_request(struct mmc_host *host, struct mmc_request *req) +{ + DBG("MMC: starting cmd %02x arg %08x flags %08x\n", + req->cmd->opcode, req->cmd->arg, req->cmd->flags); + + req->cmd->error = 0; + req->cmd->req = req; + if (req->data) { + req->cmd->data = req->data; + req->data->error = 0; + req->data->req = req; + if (req->stop) { + req->data->stop = req->stop; + req->stop->error = 0; + req->stop->req = req; + } + } + host->ops->request(host, req); +} + +EXPORT_SYMBOL(mmc_start_request); + +static void mmc_wait_done(struct mmc_request *req) +{ + complete(req->done_data); +} + +int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *req) +{ + DECLARE_COMPLETION(complete); + + req->done_data = &complete; + req->done = mmc_wait_done; + + mmc_start_request(host, req); + + wait_for_completion(&complete); + + return 0; +} + +EXPORT_SYMBOL(mmc_wait_for_req); + +/** + * mmc_wait_for_cmd - start a command and wait for completion + * @host: MMC host to start command + * @cmd: MMC command to start + * @retries: maximum number of retries + * + * Start a new MMC command for a host, and wait for the command + * to complete. Return any error that occurred while the command + * was executing. Do not attempt to parse the response. + */ +int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) +{ + struct mmc_request req; + + BUG_ON(host->card_busy == NULL); + + memset(&req, 0, sizeof(struct mmc_request)); + + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = retries; + + req.cmd = cmd; + cmd->data = NULL; + + mmc_wait_for_req(host, &req); + + return cmd->error; +} + +EXPORT_SYMBOL(mmc_wait_for_cmd); + + + +/** + * __mmc_claim_host - exclusively claim a host + * @host: mmc host to claim + * @card: mmc card to claim host for + * + * Claim a host for a set of operations. If a valid card + * is passed and this wasn't the last card selected, select + * the card before returning. + * + * Note: you should use mmc_card_claim_host or mmc_claim_host. + */ +int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int err = 0; + + add_wait_queue(&host->wq, &wait); + spin_lock_irqsave(&host->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (host->card_busy == NULL) + break; + spin_unlock_irqrestore(&host->lock, flags); + schedule(); + spin_lock_irqsave(&host->lock, flags); + } + set_current_state(TASK_RUNNING); + host->card_busy = card; + spin_unlock_irqrestore(&host->lock, flags); + remove_wait_queue(&host->wq, &wait); + + if (card != (void *)-1 && host->card_selected != card) { + struct mmc_command cmd; + + host->card_selected = card; + + cmd.opcode = MMC_SELECT_CARD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + } + + return err; +} + +EXPORT_SYMBOL(__mmc_claim_host); + +/** + * mmc_release_host - release a host + * @host: mmc host to release + * + * Release a MMC host, allowing others to claim the host + * for their operations. + */ +void mmc_release_host(struct mmc_host *host) +{ + unsigned long flags; + + BUG_ON(host->card_busy == NULL); + + spin_lock_irqsave(&host->lock, flags); + host->card_busy = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + wake_up(&host->wq); +} + +EXPORT_SYMBOL(mmc_release_host); + +static void mmc_deselect_cards(struct mmc_host *host) +{ + struct mmc_command cmd; + + /* + * Ensure that no card is selected. + */ + if (host->card_selected) { + host->card_selected = NULL; + + cmd.opcode = MMC_SELECT_CARD; + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE; + + mmc_wait_for_cmd(host, &cmd, 0); + } +} + + +static inline void mmc_delay(unsigned int ms) +{ + if (ms < HZ / 1000) { + yield(); + mdelay(ms); + } else { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(ms * HZ / 1000); + } +} + +static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) +{ + int bit; + + /* + * Mask off any voltages we don't support + */ + ocr &= host->ocr_avail; + + /* + * Select the lowest voltage + */ + bit = ffs(ocr); + if (bit) { + bit -= 1; + + ocr = 1 << bit; + + host->ios.vdd = mmc_ocr_bit_to_vdd[bit]; + host->ops->set_ios(host, &host->ios); + } else { + ocr = 0; + } + + return ocr; +} + +static void mmc_decode_cid(struct mmc_cid *cid, u32 *resp) +{ + memset(cid, 0, sizeof(struct mmc_cid)); + + cid->manfid = resp[0] >> 8; + cid->prod_name[0] = resp[0]; + cid->prod_name[1] = resp[1] >> 24; + cid->prod_name[2] = resp[1] >> 16; + cid->prod_name[3] = resp[1] >> 8; + cid->prod_name[4] = resp[1]; + cid->prod_name[5] = resp[2] >> 24; + cid->prod_name[6] = resp[2] >> 16; + cid->prod_name[7] = '\0'; + cid->hwrev = (resp[2] >> 12) & 15; + cid->fwrev = (resp[2] >> 8) & 15; + cid->serial = (resp[2] & 255) << 16 | (resp[3] >> 16); + cid->month = (resp[3] >> 12) & 15; + cid->year = (resp[3] >> 8) & 15; +} + +static void mmc_decode_csd(struct mmc_csd *csd, u32 *resp) +{ + unsigned int e, m; + + csd->mmc_prot = (resp[0] >> 26) & 15; + m = (resp[0] >> 19) & 15; + e = (resp[0] >> 16) & 7; + csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + csd->tacc_clks = ((resp[0] >> 8) & 255) * 100; + + m = (resp[0] >> 3) & 15; + e = resp[0] & 7; + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = (resp[1] >> 20) & 0xfff; + + e = (resp[2] >> 15) & 7; + m = (resp[1] << 2 | resp[2] >> 30) & 0x3fff; + csd->capacity = (1 + m) << (e + 2); + + csd->read_blkbits = (resp[1] >> 16) & 15; +} + +/* + * Locate a MMC card on this MMC host given a CID. + */ +static struct mmc_card * +mmc_find_card(struct mmc_host *host, struct mmc_cid *cid) +{ + struct mmc_card *card; + + list_for_each_entry(card, &host->cards, node) { + if (memcmp(&card->cid, cid, sizeof(struct mmc_cid)) == 0) + return card; + } + return NULL; +} + +/* + * Allocate a new MMC card, and assign a unique RCA. + */ +static struct mmc_card * +mmc_alloc_card(struct mmc_host *host, struct mmc_cid *cid, unsigned int *frca) +{ + struct mmc_card *card, *c; + unsigned int rca = *frca; + + card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + mmc_init_card(card, host); + memcpy(&card->cid, cid, sizeof(struct mmc_cid)); + + again: + list_for_each_entry(c, &host->cards, node) + if (c->rca == rca) { + rca++; + goto again; + } + + card->rca = rca; + + *frca = rca; + + return card; +} + +/* + * Apply power to the MMC stack. + */ +static void mmc_power_up(struct mmc_host *host) +{ + struct mmc_command cmd; + int bit = fls(host->ocr_avail) - 1; + + host->ios.vdd = mmc_ocr_bit_to_vdd[bit]; + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.power_mode = MMC_POWER_UP; + host->ops->set_ios(host, &host->ios); + + mmc_delay(1); + + host->ios.clock = host->f_min; + host->ios.power_mode = MMC_POWER_ON; + host->ops->set_ios(host, &host->ios); + + mmc_delay(2); + + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE; + + mmc_wait_for_cmd(host, &cmd, 0); +} + +static void mmc_power_off(struct mmc_host *host) +{ + host->ios.clock = 0; + host->ios.vdd = 0; + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.power_mode = MMC_POWER_OFF; + host->ops->set_ios(host, &host->ios); +} + +static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ + struct mmc_command cmd; + int i, err = 0; + + cmd.opcode = MMC_SEND_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_SHORT; + + for (i = 100; i; i--) { + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err != MMC_ERR_NONE) + break; + + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + break; + + err = MMC_ERR_TIMEOUT; + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + +/* + * Discover cards by requesting their CID. If this command + * times out, it is not an error; there are no further cards + * to be discovered. Add new cards to the list. + * + * Create a mmc_card entry for each discovered card, assigning + * it an RCA, and save the CID. + */ +static void mmc_discover_cards(struct mmc_host *host) +{ + struct mmc_card *card; + unsigned int first_rca = 1, err; + + while (1) { + struct mmc_command cmd; + struct mmc_cid cid; + + /* + * Read CID + */ + cmd.opcode = MMC_ALL_SEND_CID; + cmd.arg = 0; + cmd.flags = MMC_RSP_LONG | MMC_RSP_CRC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_TIMEOUT) { + err = MMC_ERR_NONE; + break; + } + if (err != MMC_ERR_NONE) { + printk(KERN_ERR "MMC: mmc%d error requesting CID: %d\n", + host->host_num, err); + break; + } + + mmc_decode_cid(&cid, cmd.resp); + + card = mmc_find_card(host, &cid); + if (!card) { + card = mmc_alloc_card(host, &cid, &first_rca); + if (IS_ERR(card)) { + err = PTR_ERR(card); + break; + } + list_add(&card->node, &host->cards); + } + + card->state &= ~MMC_STATE_DEAD; + + cmd.opcode = MMC_SET_RELATIVE_ADDR; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SHORT | MMC_RSP_CRC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + card->state |= MMC_STATE_DEAD; + } +} + +static void mmc_read_csds(struct mmc_host *host) +{ + struct mmc_card *card; + + list_for_each_entry(card, &host->cards, node) { + struct mmc_command cmd; + int err; + + if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) + continue; + + cmd.opcode = MMC_SEND_CSD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_LONG | MMC_RSP_CRC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + card->state |= MMC_STATE_DEAD; + continue; + } + + mmc_decode_csd(&card->csd, cmd.resp); + } +} + +static unsigned int mmc_calculate_clock(struct mmc_host *host) +{ + struct mmc_card *card; + unsigned int max_dtr = host->f_max; + + list_for_each_entry(card, &host->cards, node) + if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr) + max_dtr = card->csd.max_dtr; + + DBG("MMC: selected %d.%03dMHz transfer rate\n", + max_dtr / 1000000, (max_dtr / 1000) % 1000); + + return max_dtr; +} + +/* + * Check whether cards we already know about are still present. + * We do this by requesting status, and checking whether a card + * responds. + * + * A request for status does not cause a state change in data + * transfer mode. + */ +static void mmc_check_cards(struct mmc_host *host) +{ + struct list_head *l, *n; + + mmc_deselect_cards(host); + + list_for_each_safe(l, n, &host->cards) { + struct mmc_card *card = mmc_list_to_card(l); + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_LONG | MMC_RSP_CRC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_NONE) + continue; + + /* + * Ok, we believe this card has been removed. + */ + card->state |= MMC_STATE_DEAD; + } +} + +static void mmc_setup(struct mmc_host *host) +{ + if (host->ios.power_mode != MMC_POWER_ON) { + int err; + u32 ocr; + + mmc_power_up(host); + + err = mmc_send_op_cond(host, 0, &ocr); + if (err != MMC_ERR_NONE) + return; + + host->ocr = mmc_select_voltage(host, ocr); + } else { + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.clock = host->f_min; + host->ops->set_ios(host, &host->ios); + + /* + * We should remember the OCR mask from the existing + * cards, and detect the new cards OCR mask, combine + * the two and re-select the VDD. However, if we do + * change VDD, we should do an idle, and then do a + * full re-initialisation. We would need to notify + * drivers so that they can re-setup the cards as + * well, while keeping their queues at bay. + * + * For the moment, we take the easy way out - if the + * new cards don't like our currently selected VDD, + * they drop off the bus. + */ + } + + if (host->ocr == 0) + return; + + /* + * Send the selected OCR multiple times... until the cards + * all get the idea that they should be ready for CMD2. + * (My SanDisk card seems to need this.) + */ + mmc_send_op_cond(host, host->ocr, NULL); + + mmc_discover_cards(host); + + /* + * Ok, now switch to push-pull mode. + */ + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; + host->ops->set_ios(host, &host->ios); + + mmc_read_csds(host); +} + + +/** + * mmc_detect_change - process change of state on a MMC socket + * @host: host which changed state. + * + * All we know is that card(s) have been inserted or removed + * from the socket(s). We don't know which socket or cards. + */ +void mmc_detect_change(struct mmc_host *host) +{ + struct list_head *l, *n; + + mmc_claim_host(host); + + if (host->ios.power_mode == MMC_POWER_ON) + mmc_check_cards(host); + + mmc_setup(host); + + if (!list_empty(&host->cards)) { + /* + * (Re-)calculate the fastest clock rate which the + * attached cards and the host support. + */ + host->ios.clock = mmc_calculate_clock(host); + host->ops->set_ios(host, &host->ios); + } + + mmc_release_host(host); + + list_for_each_safe(l, n, &host->cards) { + struct mmc_card *card = mmc_list_to_card(l); + + /* + * If this is a new and good card, register it. + */ + if (!mmc_card_present(card) && !mmc_card_dead(card)) { + if (mmc_register_card(card)) + card->state |= MMC_STATE_DEAD; + else + card->state |= MMC_STATE_PRESENT; + } + + /* + * If this card is dead, destroy it. + */ + if (mmc_card_dead(card)) { + list_del(&card->node); + mmc_remove_card(card); + } + } + + /* + * If we discover that there are no cards on the + * bus, turn off the clock and power down. + */ + if (list_empty(&host->cards)) + mmc_power_off(host); +} + +EXPORT_SYMBOL(mmc_detect_change); + + +/** + * mmc_init_host - initialise the per-host structure. + * @host: mmc host + * + * Initialise the per-host structure. + */ +int mmc_init_host(struct mmc_host *host) +{ + static unsigned int host_num; + + memset(host, 0, sizeof(struct mmc_host)); + + host->host_num = host_num++; + + spin_lock_init(&host->lock); + init_waitqueue_head(&host->wq); + INIT_LIST_HEAD(&host->cards); + + return 0; +} + +EXPORT_SYMBOL(mmc_init_host); + +/** + * mmc_add_host - initialise host hardware + * @host: mmc host + */ +int mmc_add_host(struct mmc_host *host) +{ + mmc_power_off(host); + mmc_detect_change(host); + + return 0; +} + +EXPORT_SYMBOL(mmc_add_host); + +/** + * mmc_remove_host - remove host hardware + * @host: mmc host + * + * Unregister and remove all cards associated with this host, + * and power down the MMC bus. + */ +void mmc_remove_host(struct mmc_host *host) +{ + struct list_head *l, *n; + + list_for_each_safe(l, n, &host->cards) { + struct mmc_card *card = mmc_list_to_card(l); + + mmc_remove_card(card); + } + + mmc_power_off(host); +} + +EXPORT_SYMBOL(mmc_remove_host); + +#ifdef CONFIG_PM + +/** + * mmc_suspend_host - suspend a host + * @host: mmc host + * @state: suspend mode (PM_SUSPEND_xxx) + */ +int mmc_suspend_host(struct mmc_host *host, u32 state) +{ + mmc_claim_host(host); + mmc_deselect_cards(host); + mmc_power_off(host); + mmc_release_host(host); + + return 0; +} + +/** + * mmc_resume_host - resume a previously suspended host + * @host: mmc host + */ +int mmc_resume_host(struct mmc_host *host) +{ + mmc_detect_change(host); + + return 0; +} + + +#else /* CONFIG_PM is not set */ + +int mmc_suspend_host(struct mmc_host *host, u32 state) { return 0; } +int mmc_resume_host(struct mmc_host *host) { return 0; } + +#endif + +EXPORT_SYMBOL(mmc_suspend_host); +EXPORT_SYMBOL(mmc_resume_host); + +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/media/mmc/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,21 @@ +# +# Makefile for the kernel mmc device drivers. +# + +# +# Core +# +obj-$(CONFIG_MMC) += mmc_core.o + +# +# Media drivers +# +obj-$(CONFIG_MMC_BLOCK) += mmc_block.o + +# +# Host drivers +# +obj-$(CONFIG_MMC_ARMMMCI) += mmci.o +obj-$(CONFIG_MMC_PXA) += pxamci.o + +mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o --- linux-2.6.5/drivers/media/Makefile~heh 2004-04-03 22:38:15.000000000 -0500 +++ linux-2.6.5/drivers/media/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -3,3 +3,6 @@ # obj-y := video/ radio/ dvb/ common/ + +obj-$(CONFIG_MMC) += mmc/ + --- linux-2.6.5/drivers/serial/Kconfig~heh 2004-04-03 22:38:15.000000000 -0500 +++ linux-2.6.5/drivers/serial/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -315,6 +315,29 @@ your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_PXA + bool "PXA serial port support" + depends on ARM && ARCH_PXA + select SERIAL_CORE + help + If you have a machine based on an Intel XScale PXA2xx CPU you + can enable its onboard serial ports by enabling this option. + +config SERIAL_PXA_CONSOLE + bool "Console on PXA serial port" + depends on SERIAL_PXA + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the Intel XScale PXA + CPU you can make it the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySA0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + config SERIAL_SA1100 bool "SA1100 serial port support" depends on ARM && ARCH_SA1100 --- linux-2.6.5/drivers/serial/pxa.c~heh 2004-04-03 22:38:16.000000000 -0500 +++ linux-2.6.5/drivers/serial/pxa.c 2004-04-30 20:57:36.000000000 -0400 @@ -294,7 +294,6 @@ unsigned char status; unsigned int ret; -return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; spin_lock_irqsave(&up->port.lock, flags); status = serial_in(up, UART_MSR); spin_unlock_irqrestore(&up->port.lock, flags); @@ -800,6 +799,21 @@ .ops = &serial_pxa_pops, .line = 2, }, + }, { /* HWUART */ + .name = "HWUART", + .cken = CKEN4_HWUART, + .port = { + .type = PORT_PXA, + .iotype = SERIAL_IO_MEM, + .membase = (void *)&HWUART, + .mapbase = __PREG(HWUART), + .irq = IRQ_HWUART, + .uartclk = 921600 * 16, + .fifosize = 64, + .flags = ASYNC_SKIP_TEST, + .ops = &serial_pxa_pops, + .line = 3, + }, } }; --- linux-2.6.5/drivers/serial/sa1100.c~heh 2004-04-03 22:36:24.000000000 -0500 +++ linux-2.6.5/drivers/serial/sa1100.c 2004-04-30 20:57:36.000000000 -0400 @@ -448,6 +448,15 @@ unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; /* + * If we don't support modem control lines, don't allow + * these to be set. + */ + if (0) { + termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); + termios->c_cflag |= CLOCAL; + } + + /* * We only support CS7 and CS8. */ while ((termios->c_cflag & CSIZE) != CS7 && @@ -894,6 +903,7 @@ if (sa1100_ports[i].port.mapbase != res->start) continue; + sa1100_ports[i].port.dev = _dev; uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port); dev_set_drvdata(_dev, &sa1100_ports[i]); break; --- linux-2.6.5/drivers/mtd/mtd_blkdevs.c~heh 2004-04-03 22:36:14.000000000 -0500 +++ linux-2.6.5/drivers/mtd/mtd_blkdevs.c 2004-04-30 20:57:36.000000000 -0400 @@ -81,7 +81,7 @@ struct request_queue *rq = tr->blkcore_priv->rq; /* we might get involved when memory gets low, so use PF_MEMALLOC */ - current->flags |= PF_MEMALLOC; + current->flags |= PF_MEMALLOC|PF_IOTHREAD; daemonize("%sd", tr->name); --- linux-2.6.5/drivers/mtd/devices/doc1000.c~heh 2004-04-03 22:37:36.000000000 -0500 +++ linux-2.6.5/drivers/mtd/devices/doc1000.c 2004-04-30 20:57:36.000000000 -0400 @@ -1,6 +1,6 @@ /*====================================================================== - $Id: doc1000.c,v 1.15 2001/10/02 15:05:13 dwmw2 Exp $ + $Id: doc1000.c,v 1.16 2001/12/28 22:45:17 dwmw2 Exp $ ======================================================================*/ @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -482,7 +483,7 @@ else priv->devstat[erase->dev] = erase->state = MTD_ERASE_PENDING; } - else if (erase->time + erase_timeout < jiffies) + else if (time_after(jiffies, erase->time + erase_timeout)) { printk("Flash erase timed out. The world is broken.\n"); --- linux-2.6.5/drivers/mtd/mtdconcat.c~heh 2004-04-03 22:37:37.000000000 -0500 +++ linux-2.6.5/drivers/mtd/mtdconcat.c 2004-04-30 20:57:36.000000000 -0400 @@ -7,7 +7,7 @@ * * This code is GPL * - * $Id: mtdconcat.c,v 1.4 2003/03/07 17:44:59 rkaiser Exp $ + * $Id: mtdconcat.c,v 1.7 2003/06/29 21:26:34 dwmw2 Exp $ */ #include @@ -26,7 +26,7 @@ */ struct mtd_concat { struct mtd_info mtd; - int num_subdev; + int num_subdev; struct mtd_info **subdev; }; @@ -37,21 +37,20 @@ #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) - /* * Given a pointer to the MTD object in the mtd_concat structure, * we can retrieve the pointer to that structure with this macro. */ #define CONCAT(x) ((struct mtd_concat *)(x)) - /* * MTD methods which look up the relevant subdevice, translate the * effective address and pass through to the subdevice. */ -static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int +concat_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; @@ -59,43 +58,43 @@ *retlen = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; - if (from >= subdev->size) - { /* Not destined for this subdev */ - size = 0; + if (from >= subdev->size) { + /* Not destined for this subdev */ + size = 0; from -= subdev->size; + continue; } + if (from + len > subdev->size) + /* First part goes into this subdev */ + size = subdev->size - from; else - { - if (from + len > subdev->size) - size = subdev->size - from; /* First part goes into this subdev */ - else - size = len; /* Entire transaction goes into this subdev */ + /* Entire transaction goes into this subdev */ + size = len; - err = subdev->read(subdev, from, size, &retsize, buf); + err = subdev->read(subdev, from, size, &retsize, buf); - if(err) - break; + if (err) + break; - *retlen += retsize; - len -= size; - if(len == 0) - break; + *retlen += retsize; + len -= size; + if (len == 0) + break; - err = -EINVAL; - buf += size; - from = 0; - } + err = -EINVAL; + buf += size; + from = 0; } return err; } -static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int +concat_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; @@ -106,46 +105,44 @@ *retlen = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; - if (to >= subdev->size) - { - size = 0; + if (to >= subdev->size) { + size = 0; to -= subdev->size; + continue; } + if (to + len > subdev->size) + size = subdev->size - to; else - { - if (to + len > subdev->size) - size = subdev->size - to; - else - size = len; + size = len; - if (!(subdev->flags & MTD_WRITEABLE)) - err = -EROFS; - else - err = subdev->write(subdev, to, size, &retsize, buf); + if (!(subdev->flags & MTD_WRITEABLE)) + err = -EROFS; + else + err = subdev->write(subdev, to, size, &retsize, buf); - if(err) - break; + if (err) + break; - *retlen += retsize; - len -= size; - if(len == 0) - break; + *retlen += retsize; + len -= size; + if (len == 0) + break; - err = -EINVAL; - buf += size; - to = 0; - } + err = -EINVAL; + buf += size; + to = 0; } return err; } -static int concat_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel) +static int +concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * eccbuf, + struct nand_oobinfo *oobsel) { struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; @@ -153,53 +150,56 @@ *retlen = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; - - if (from >= subdev->size) - { /* Not destined for this subdev */ - size = 0; + + if (from >= subdev->size) { + /* Not destined for this subdev */ + size = 0; from -= subdev->size; + continue; } + + if (from + len > subdev->size) + /* First part goes into this subdev */ + size = subdev->size - from; else - { - if (from + len > subdev->size) - size = subdev->size - from; /* First part goes into this subdev */ - else - size = len; /* Entire transaction goes into this subdev */ - - if (subdev->read_ecc) - err = subdev->read_ecc(subdev, from, size, &retsize, buf, eccbuf, oobsel); - else - err = -EINVAL; + /* Entire transaction goes into this subdev */ + size = len; - if(err) - break; + if (subdev->read_ecc) + err = subdev->read_ecc(subdev, from, size, + &retsize, buf, eccbuf, oobsel); + else + err = -EINVAL; - *retlen += retsize; - len -= size; - if(len == 0) - break; + if (err) + break; - err = -EINVAL; - buf += size; - if (eccbuf) - { - eccbuf += subdev->oobsize; - /* in nand.c at least, eccbufs are tagged with 2 (int)eccstatus', - we must account for these */ - eccbuf += 2 * (sizeof(int)); - } - from = 0; + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + if (eccbuf) { + eccbuf += subdev->oobsize; + /* in nand.c at least, eccbufs are + tagged with 2 (int)eccstatus'; we + must account for these */ + eccbuf += 2 * (sizeof (int)); } + from = 0; } return err; } -static int concat_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel) +static int +concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * eccbuf, + struct nand_oobinfo *oobsel) { struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; @@ -210,50 +210,48 @@ *retlen = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; - - if (to >= subdev->size) - { - size = 0; + + if (to >= subdev->size) { + size = 0; to -= subdev->size; + continue; } + if (to + len > subdev->size) + size = subdev->size - to; else - { - if (to + len > subdev->size) - size = subdev->size - to; - else - size = len; + size = len; - if (!(subdev->flags & MTD_WRITEABLE)) - err = -EROFS; - else if (subdev->write_ecc) - err = subdev->write_ecc(subdev, to, size, &retsize, buf, eccbuf, oobsel); - else - err = -EINVAL; + if (!(subdev->flags & MTD_WRITEABLE)) + err = -EROFS; + else if (subdev->write_ecc) + err = subdev->write_ecc(subdev, to, size, + &retsize, buf, eccbuf, oobsel); + else + err = -EINVAL; - if(err) - break; + if (err) + break; - *retlen += retsize; - len -= size; - if(len == 0) - break; + *retlen += retsize; + len -= size; + if (len == 0) + break; - err = -EINVAL; - buf += size; - if (eccbuf) - eccbuf += subdev->oobsize; - to = 0; - } + err = -EINVAL; + buf += size; + if (eccbuf) + eccbuf += subdev->oobsize; + to = 0; } return err; } -static int concat_read_oob (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int +concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; @@ -261,46 +259,47 @@ *retlen = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; - - if (from >= subdev->size) - { /* Not destined for this subdev */ - size = 0; + + if (from >= subdev->size) { + /* Not destined for this subdev */ + size = 0; from -= subdev->size; + continue; } + if (from + len > subdev->size) + /* First part goes into this subdev */ + size = subdev->size - from; else - { - if (from + len > subdev->size) - size = subdev->size - from; /* First part goes into this subdev */ - else - size = len; /* Entire transaction goes into this subdev */ - - if (subdev->read_oob) - err = subdev->read_oob(subdev, from, size, &retsize, buf); - else - err = -EINVAL; + /* Entire transaction goes into this subdev */ + size = len; - if(err) - break; + if (subdev->read_oob) + err = subdev->read_oob(subdev, from, size, + &retsize, buf); + else + err = -EINVAL; - *retlen += retsize; - len -= size; - if(len == 0) - break; + if (err) + break; - err = -EINVAL; - buf += size; - from = 0; - } + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + from = 0; } return err; } -static int concat_write_oob (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int +concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; @@ -311,50 +310,46 @@ *retlen = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; - - if (to >= subdev->size) - { - size = 0; + + if (to >= subdev->size) { + size = 0; to -= subdev->size; + continue; } + if (to + len > subdev->size) + size = subdev->size - to; else - { - if (to + len > subdev->size) - size = subdev->size - to; - else - size = len; + size = len; - if (!(subdev->flags & MTD_WRITEABLE)) - err = -EROFS; - else if (subdev->write_oob) - err = subdev->write_oob(subdev, to, size, &retsize, buf); - else - err = -EINVAL; + if (!(subdev->flags & MTD_WRITEABLE)) + err = -EROFS; + else if (subdev->write_oob) + err = subdev->write_oob(subdev, to, size, &retsize, + buf); + else + err = -EINVAL; - if(err) - break; + if (err) + break; - *retlen += retsize; - len -= size; - if(len == 0) - break; + *retlen += retsize; + len -= size; + if (len == 0) + break; - err = -EINVAL; - buf += size; - to = 0; - } + err = -EINVAL; + buf += size; + to = 0; } return err; } - -static void concat_erase_callback (struct erase_info *instr) +static void concat_erase_callback(struct erase_info *instr) { - wake_up((wait_queue_head_t *)instr->priv); + wake_up((wait_queue_head_t *) instr->priv); } static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) @@ -370,18 +365,18 @@ erase->mtd = mtd; erase->callback = concat_erase_callback; - erase->priv = (unsigned long)&waitq; - + erase->priv = (unsigned long) &waitq; + /* * FIXME: Allow INTERRUPTIBLE. Which means * not having the wait_queue head on the stack. */ err = mtd->erase(mtd, erase); - if (!err) - { + if (!err) { set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&waitq, &wait); - if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) + if (erase->state != MTD_ERASE_DONE + && erase->state != MTD_ERASE_FAILED) schedule(); remove_wait_queue(&waitq, &wait); set_current_state(TASK_RUNNING); @@ -391,7 +386,7 @@ return err; } -static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) +static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) { struct mtd_concat *concat = CONCAT(mtd); struct mtd_info *subdev; @@ -402,10 +397,10 @@ if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - if(instr->addr > concat->mtd.size) + if (instr->addr > concat->mtd.size) return -EINVAL; - if(instr->len + instr->addr > concat->mtd.size) + if (instr->len + instr->addr > concat->mtd.size) return -EINVAL; /* @@ -414,23 +409,22 @@ * region info rather than looking at each particular sub-device * in turn. */ - if (!concat->mtd.numeraseregions) - { /* the easy case: device has uniform erase block size */ - if(instr->addr & (concat->mtd.erasesize - 1)) + if (!concat->mtd.numeraseregions) { + /* the easy case: device has uniform erase block size */ + if (instr->addr & (concat->mtd.erasesize - 1)) return -EINVAL; - if(instr->len & (concat->mtd.erasesize - 1)) + if (instr->len & (concat->mtd.erasesize - 1)) return -EINVAL; - } - else - { /* device has variable erase size */ - struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; + } else { + /* device has variable erase size */ + struct mtd_erase_region_info *erase_regions = + concat->mtd.eraseregions; /* * Find the erase region where the to-be-erased area begins: */ - for(i = 0; i < concat->mtd.numeraseregions && - instr->addr >= erase_regions[i].offset; i++) - ; + for (i = 0; i < concat->mtd.numeraseregions && + instr->addr >= erase_regions[i].offset; i++) ; --i; /* @@ -438,25 +432,26 @@ * to-be-erased area begins. Verify that the starting * offset is aligned to this region's erase size: */ - if (instr->addr & (erase_regions[i].erasesize-1)) + if (instr->addr & (erase_regions[i].erasesize - 1)) return -EINVAL; /* * now find the erase region where the to-be-erased area ends: */ - for(; i < concat->mtd.numeraseregions && - (instr->addr + instr->len) >= erase_regions[i].offset ; ++i) - ; + for (; i < concat->mtd.numeraseregions && + (instr->addr + instr->len) >= erase_regions[i].offset; + ++i) ; --i; /* * check if the ending offset is aligned to this region's erase size */ - if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1)) + if ((instr->addr + instr->len) & (erase_regions[i].erasesize - + 1)) return -EINVAL; } /* make a local copy of instr to avoid modifying the caller's struct */ - erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL); + erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); if (!erase) return -ENOMEM; @@ -468,39 +463,40 @@ * find the subdevice where the to-be-erased area begins, adjust * starting offset to be relative to the subdevice start */ - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { subdev = concat->subdev[i]; - if(subdev->size <= erase->addr) + if (subdev->size <= erase->addr) erase->addr -= subdev->size; else break; - } - if(i >= concat->num_subdev) /* must never happen since size */ - BUG(); /* limit has been verified above */ + } + + /* must never happen since size limit has been verified above */ + if (i >= concat->num_subdev) + BUG(); /* now do the erase: */ err = 0; - for(;length > 0; i++) /* loop for all subevices affected by this request */ - { - subdev = concat->subdev[i]; /* get current subdevice */ + for (; length > 0; i++) { + /* loop for all subdevices affected by this request */ + subdev = concat->subdev[i]; /* get current subdevice */ /* limit length to subdevice's size: */ - if(erase->addr + length > subdev->size) + if (erase->addr + length > subdev->size) erase->len = subdev->size - erase->addr; else erase->len = length; - if (!(subdev->flags & MTD_WRITEABLE)) - { + if (!(subdev->flags & MTD_WRITEABLE)) { err = -EROFS; break; } length -= erase->len; - if ((err = concat_dev_erase(subdev, erase))) - { - if(err == -EINVAL) /* sanity check: must never happen since */ - BUG(); /* block alignment has been checked above */ + if ((err = concat_dev_erase(subdev, erase))) { + /* sanity check: should never happen since + * block alignment has been checked above */ + if (err == -EINVAL) + BUG(); break; } /* @@ -523,85 +519,79 @@ return 0; } -static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) { struct mtd_concat *concat = CONCAT(mtd); int i, err = -EINVAL; - if ((len + ofs) > mtd->size) + if ((len + ofs) > mtd->size) return -EINVAL; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size; - if (ofs >= subdev->size) - { - size = 0; + if (ofs >= subdev->size) { + size = 0; ofs -= subdev->size; + continue; } + if (ofs + len > subdev->size) + size = subdev->size - ofs; else - { - if (ofs + len > subdev->size) - size = subdev->size - ofs; - else - size = len; + size = len; - err = subdev->lock(subdev, ofs, size); + err = subdev->lock(subdev, ofs, size); - if(err) - break; + if (err) + break; - len -= size; - if(len == 0) - break; + len -= size; + if (len == 0) + break; - err = -EINVAL; - ofs = 0; - } + err = -EINVAL; + ofs = 0; } + return err; } -static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) { struct mtd_concat *concat = CONCAT(mtd); int i, err = 0; - if ((len + ofs) > mtd->size) + if ((len + ofs) > mtd->size) return -EINVAL; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size; - if (ofs >= subdev->size) - { - size = 0; + if (ofs >= subdev->size) { + size = 0; ofs -= subdev->size; + continue; } + if (ofs + len > subdev->size) + size = subdev->size - ofs; else - { - if (ofs + len > subdev->size) - size = subdev->size - ofs; - else - size = len; + size = len; - err = subdev->unlock(subdev, ofs, size); + err = subdev->unlock(subdev, ofs, size); - if(err) - break; + if (err) + break; - len -= size; - if(len == 0) - break; + len -= size; + if (len == 0) + break; - err = -EINVAL; - ofs = 0; - } + err = -EINVAL; + ofs = 0; } + return err; } @@ -610,8 +600,7 @@ struct mtd_concat *concat = CONCAT(mtd); int i; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; subdev->sync(subdev); } @@ -622,10 +611,9 @@ struct mtd_concat *concat = CONCAT(mtd); int i, rc = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - if((rc = subdev->suspend(subdev)) < 0) + if ((rc = subdev->suspend(subdev)) < 0) return rc; } return rc; @@ -636,8 +624,7 @@ struct mtd_concat *concat = CONCAT(mtd); int i; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; subdev->resume(subdev); } @@ -649,11 +636,10 @@ * stored to *new_dev upon success. This function does _not_ * register any devices: this is the caller's responsibility. */ -struct mtd_info *mtd_concat_create( - struct mtd_info *subdev[], /* subdevices to concatenate */ - int num_devs, /* number of subdevices */ - char *name) /* name for the new device */ -{ +struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ + char *name) +{ /* name for the new device */ int i; size_t size; struct mtd_concat *concat; @@ -661,94 +647,103 @@ int num_erase_region; printk(KERN_NOTICE "Concatenating MTD devices:\n"); - for(i = 0; i < num_devs; i++) + for (i = 0; i < num_devs; i++) printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); printk(KERN_NOTICE "into device \"%s\"\n", name); /* allocate the device structure */ size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); - concat = kmalloc (size, GFP_KERNEL); - if(!concat) - { - printk ("memory allocation error while creating concatenated device \"%s\"\n", - name); - return NULL; + concat = kmalloc(size, GFP_KERNEL); + if (!concat) { + printk + ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; } memset(concat, 0, size); - concat->subdev = (struct mtd_info **)(concat + 1); + concat->subdev = (struct mtd_info **) (concat + 1); /* * Set up the new "super" device's MTD object structure, check for * incompatibilites between the subdevices. */ - concat->mtd.type = subdev[0]->type; - concat->mtd.flags = subdev[0]->flags; - concat->mtd.size = subdev[0]->size; + concat->mtd.type = subdev[0]->type; + concat->mtd.flags = subdev[0]->flags; + concat->mtd.size = subdev[0]->size; concat->mtd.erasesize = subdev[0]->erasesize; - concat->mtd.oobblock = subdev[0]->oobblock; - concat->mtd.oobsize = subdev[0]->oobsize; - concat->mtd.ecctype = subdev[0]->ecctype; - concat->mtd.eccsize = subdev[0]->eccsize; + concat->mtd.oobblock = subdev[0]->oobblock; + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.ecctype = subdev[0]->ecctype; + concat->mtd.eccsize = subdev[0]->eccsize; + if (subdev[0]->read_ecc) + concat->mtd.read_ecc = concat_read_ecc; + if (subdev[0]->write_ecc) + concat->mtd.write_ecc = concat_write_ecc; + if (subdev[0]->read_oob) + concat->mtd.read_oob = concat_read_oob; + if (subdev[0]->write_oob) + concat->mtd.write_oob = concat_write_oob; - concat->subdev[0] = subdev[0]; + concat->subdev[0] = subdev[0]; - for(i = 1; i < num_devs; i++) - { - if(concat->mtd.type != subdev[i]->type) - { + for (i = 1; i < num_devs; i++) { + if (concat->mtd.type != subdev[i]->type) { kfree(concat); - printk ("Incompatible device type on \"%s\"\n", subdev[i]->name); + printk("Incompatible device type on \"%s\"\n", + subdev[i]->name); return NULL; } - if(concat->mtd.flags != subdev[i]->flags) - { /* - * Expect all flags except MTD_WRITEABLE to be equal on - * all subdevices. + if (concat->mtd.flags != subdev[i]->flags) { + /* + * Expect all flags except MTD_WRITEABLE to be + * equal on all subdevices. */ - if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE) - { + if ((concat->mtd.flags ^ subdev[i]-> + flags) & ~MTD_WRITEABLE) { kfree(concat); - printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name); + printk("Incompatible device flags on \"%s\"\n", + subdev[i]->name); return NULL; - } - else /* if writeable attribute differs, make super device writeable */ - concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE; + } else + /* if writeable attribute differs, + make super device writeable */ + concat->mtd.flags |= + subdev[i]->flags & MTD_WRITEABLE; } concat->mtd.size += subdev[i]->size; - if(concat->mtd.oobblock != subdev[i]->oobblock || - concat->mtd.oobsize != subdev[i]->oobsize || - concat->mtd.ecctype != subdev[i]->ecctype || - concat->mtd.eccsize != subdev[i]->eccsize) - { + if (concat->mtd.oobblock != subdev[i]->oobblock || + concat->mtd.oobsize != subdev[i]->oobsize || + concat->mtd.ecctype != subdev[i]->ecctype || + concat->mtd.eccsize != subdev[i]->eccsize || + !concat->mtd.read_ecc != !subdev[i]->read_ecc || + !concat->mtd.write_ecc != !subdev[i]->write_ecc || + !concat->mtd.read_oob != !subdev[i]->read_oob || + !concat->mtd.write_oob != !subdev[i]->write_oob) { kfree(concat); - printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); + printk("Incompatible OOB or ECC data on \"%s\"\n", + subdev[i]->name); return NULL; } concat->subdev[i] = subdev[i]; - + } - concat->num_subdev = num_devs; - concat->mtd.name = name; + concat->num_subdev = num_devs; + concat->mtd.name = name; /* * NOTE: for now, we do not provide any readv()/writev() methods * because they are messy to implement and they are not * used to a great extent anyway. */ - concat->mtd.erase = concat_erase; - concat->mtd.read = concat_read; - concat->mtd.write = concat_write; - concat->mtd.read_ecc = concat_read_ecc; - concat->mtd.write_ecc = concat_write_ecc; - concat->mtd.read_oob = concat_read_oob; - concat->mtd.write_oob = concat_write_oob; - concat->mtd.sync = concat_sync; - concat->mtd.lock = concat_lock; - concat->mtd.unlock = concat_unlock; - concat->mtd.suspend = concat_suspend; - concat->mtd.resume = concat_resume; - + concat->mtd.erase = concat_erase; + concat->mtd.read = concat_read; + concat->mtd.write = concat_write; + concat->mtd.sync = concat_sync; + concat->mtd.lock = concat_lock; + concat->mtd.unlock = concat_unlock; + concat->mtd.suspend = concat_suspend; + concat->mtd.resume = concat_resume; /* * Combine the erase block size info of the subdevices: @@ -758,44 +753,44 @@ */ max_erasesize = curr_erasesize = subdev[0]->erasesize; num_erase_region = 1; - for(i = 0; i < num_devs; i++) - { - if(subdev[i]->numeraseregions == 0) - { /* current subdevice has uniform erase size */ - if(subdev[i]->erasesize != curr_erasesize) - { /* if it differs from the last subdevice's erase size, count it */ + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* if it differs from the last subdevice's erase size, count it */ ++num_erase_region; curr_erasesize = subdev[i]->erasesize; - if(curr_erasesize > max_erasesize) + if (curr_erasesize > max_erasesize) max_erasesize = curr_erasesize; } - } - else - { /* current subdevice has variable erase size */ + } else { + /* current subdevice has variable erase size */ int j; - for(j = 0; j < subdev[i]->numeraseregions; j++) - { /* walk the list of erase regions, count any changes */ - if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) - { + for (j = 0; j < subdev[i]->numeraseregions; j++) { + + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j].erasesize != + curr_erasesize) { ++num_erase_region; - curr_erasesize = subdev[i]->eraseregions[j].erasesize; - if(curr_erasesize > max_erasesize) + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; + if (curr_erasesize > max_erasesize) max_erasesize = curr_erasesize; } } } } - if(num_erase_region == 1) - { /* + if (num_erase_region == 1) { + /* * All subdevices have the same uniform erase size. * This is easy: */ concat->mtd.erasesize = curr_erasesize; concat->mtd.numeraseregions = 0; - } - else - { /* + } else { + /* * erase block size varies across the subdevices: allocate * space to store the data describing the variable erase regions */ @@ -804,13 +799,14 @@ concat->mtd.erasesize = max_erasesize; concat->mtd.numeraseregions = num_erase_region; - concat->mtd.eraseregions = erase_region_p = kmalloc ( - num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL); - if(!erase_region_p) - { + concat->mtd.eraseregions = erase_region_p = + kmalloc(num_erase_region * + sizeof (struct mtd_erase_region_info), GFP_KERNEL); + if (!erase_region_p) { kfree(concat); - printk ("memory allocation error while creating erase region list" - " for device \"%s\"\n", name); + printk + ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); return NULL; } @@ -820,46 +816,53 @@ */ curr_erasesize = subdev[0]->erasesize; begin = position = 0; - for(i = 0; i < num_devs; i++) - { - if(subdev[i]->numeraseregions == 0) - { /* current subdevice has uniform erase size */ - if(subdev[i]->erasesize != curr_erasesize) - { /* + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* * fill in an mtd_erase_region_info structure for the area * we have walked so far: */ - erase_region_p->offset = begin; - erase_region_p->erasesize = curr_erasesize; - erase_region_p->numblocks = (position - begin) / curr_erasesize; + erase_region_p->offset = begin; + erase_region_p->erasesize = + curr_erasesize; + erase_region_p->numblocks = + (position - begin) / curr_erasesize; begin = position; curr_erasesize = subdev[i]->erasesize; ++erase_region_p; } position += subdev[i]->size; - } - else - { /* current subdevice has variable erase size */ + } else { + /* current subdevice has variable erase size */ int j; - for(j = 0; j < subdev[i]->numeraseregions; j++) - { /* walk the list of erase regions, count any changes */ - if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) - { - erase_region_p->offset = begin; - erase_region_p->erasesize = curr_erasesize; - erase_region_p->numblocks = (position - begin) / curr_erasesize; + for (j = 0; j < subdev[i]->numeraseregions; j++) { + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j]. + erasesize != curr_erasesize) { + erase_region_p->offset = begin; + erase_region_p->erasesize = + curr_erasesize; + erase_region_p->numblocks = + (position - + begin) / curr_erasesize; begin = position; - curr_erasesize = subdev[i]->eraseregions[j].erasesize; + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; ++erase_region_p; } - position += subdev[i]->eraseregions[j].numblocks * curr_erasesize; + position += + subdev[i]->eraseregions[j]. + numblocks * curr_erasesize; } } } /* Now write the final entry */ - erase_region_p->offset = begin; + erase_region_p->offset = begin; erase_region_p->erasesize = curr_erasesize; erase_region_p->numblocks = (position - begin) / curr_erasesize; } @@ -874,16 +877,14 @@ void mtd_concat_destroy(struct mtd_info *mtd) { struct mtd_concat *concat = CONCAT(mtd); - if(concat->mtd.numeraseregions) + if (concat->mtd.numeraseregions) kfree(concat->mtd.eraseregions); kfree(concat); } - EXPORT_SYMBOL(mtd_concat_create); EXPORT_SYMBOL(mtd_concat_destroy); - MODULE_LICENSE("GPL"); MODULE_AUTHOR("Robert Kaiser "); MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); --- linux-2.6.5/drivers/mtd/maps/Makefile~heh 2004-04-03 22:36:12.000000000 -0500 +++ linux-2.6.5/drivers/mtd/maps/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -17,12 +17,14 @@ obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o +obj-$(CONFIG_MTD_IQ80321) += iq80321.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o obj-$(CONFIG_MTD_MBX860) += mbx860.o +obj-$(CONFIG_MTD_NORA) += nora.o obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o --- linux-2.6.5/drivers/mtd/maps/sa1100-flash.c~heh 2004-04-03 22:36:51.000000000 -0500 +++ linux-2.6.5/drivers/mtd/maps/sa1100-flash.c 2004-04-30 20:57:36.000000000 -0400 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -887,6 +888,7 @@ int width; void *vbase; void (*set_vpp)(struct map_info *, int); + char name[16]; struct map_info *map; struct mtd_info *mtd; struct resource *res; @@ -925,6 +927,8 @@ } sa[i].map = maps + i; + sa[i].map->name = sa[i].name; + sprintf(sa[i].name, "sa1100-%d", i); sa[i].vbase = ioremap(sa[i].base, sa[i].size); if (!sa[i].vbase) { @@ -986,7 +990,7 @@ */ #ifdef CONFIG_MTD_CONCAT *rmtd = mtd_concat_create(subdev, found, - "sa1100 flash"); + "sa1100"); if (*rmtd == NULL) ret = -ENXIO; #else @@ -1044,13 +1048,9 @@ * - Is the MSC setup for flash (no -> failure) * - Probe for flash */ - -static struct map_info sa1100_probe_map __initdata = { - .name = "SA1100-flash", -}; - -static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys) +static void sa1100_probe_one_cs(unsigned int msc, unsigned long phys) { + struct map_info map; struct mtd_info *mtd; printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ", @@ -1066,19 +1066,23 @@ return; } - sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4; - sa1100_probe_map.size = SZ_1M; - sa1100_probe_map.phys = phys; - sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M); - if (sa1100_probe_map.virt == 0) + memset(&map, 0, sizeof(map)); + + map.name = "Probe"; + map.buswidth = msc & MSC_RBW ? 2 : 4; + map.size = SZ_1M; + map.phys = NO_XIP; + map.virt = (unsigned long)ioremap(phys, SZ_1M); + if (map.virt == 0) goto fail; - simple_map_init(&sa1100_probe_map); + + simple_map_init(&map); /* Shame cfi_probe blurts out kernel messages... */ - mtd = do_map_probe("cfi_probe", &sa1100_probe_map); + mtd = do_map_probe("cfi_probe", &map); if (mtd) map_destroy(mtd); - iounmap((void *)sa1100_probe_map.virt); + iounmap((void *)map.virt); if (!mtd) goto fail; @@ -1090,7 +1094,7 @@ printk("failed\n"); } -static void __init sa1100_probe_flash(void) +static void sa1100_probe_flash(void) { printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n"); sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS); @@ -1321,10 +1325,9 @@ kfree(parsed_parts); } -static struct mtd_info *mymtd; - -static int __init sa1100_mtd_init(void) +static int __init sa1100_mtd_probe(struct device *dev) { + struct mtd_info *mtd; int ret; int nr; @@ -1332,21 +1335,74 @@ if (nr < 0) return nr; - ret = sa1100_setup_mtd(info, nr, &mymtd); - if (ret == 0) - sa1100_locate_partitions(mymtd); + ret = sa1100_setup_mtd(info, nr, &mtd); + if (ret == 0) { + sa1100_locate_partitions(mtd); + dev_set_drvdata(dev, mtd); + } return ret; } -static void __exit sa1100_mtd_cleanup(void) +static int __exit sa1100_mtd_remove(struct device *dev) { - sa1100_destroy_mtd(info, mymtd); + struct mtd_info *mtd = dev_get_drvdata(dev); + sa1100_destroy_mtd(info, mtd); sa1100_destroy_partitions(); + return 0; +} + +static int sa1100_mtd_suspend(struct device *dev, u32 level, u32 state) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + if (level == SUSPEND_SAVE_STATE) + mtd->suspend(mtd); + return 0; +} + +static int sa1100_mtd_resume(struct device *dev, u32 level) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + if (level == RESUME_RESTORE_STATE) + mtd->resume(mtd); + return 0; +} + +static struct platform_device sa1100_mtd_device = { + .name = "flash", + .id = 0, +}; + +static struct device_driver sa1100_mtd_driver = { + .name = "flash", + .bus = &platform_bus_type, + .probe = sa1100_mtd_probe, +#ifdef MODULE + .remove = sa1100_mtd_remove, +#endif + .suspend = sa1100_mtd_suspend, + .resume = sa1100_mtd_resume, +}; + +static int __init sa1100_mtd_init(void) +{ + int ret = driver_register(&sa1100_mtd_driver); + if (ret == 0) { + ret = platform_device_register(&sa1100_mtd_device); + if (ret) + driver_unregister(&sa1100_mtd_driver); + } + return ret; +} + +static void __exit sa1100_mtd_exit(void) +{ + platform_device_unregister(&sa1100_mtd_device); + driver_unregister(&sa1100_mtd_driver); } module_init(sa1100_mtd_init); -module_exit(sa1100_mtd_cleanup); +module_exit(sa1100_mtd_exit); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("SA1100 CFI map driver"); --- linux-2.6.5/drivers/mtd/maps/pcmciamtd.c~heh 2004-04-03 22:36:19.000000000 -0500 +++ linux-2.6.5/drivers/mtd/maps/pcmciamtd.c 2004-04-30 20:57:36.000000000 -0400 @@ -25,10 +25,9 @@ #include #include -#include -#ifdef CONFIG_MTD_DEBUG -static int debug = CONFIG_MTD_DEBUG_VERBOSE; +#if 1 //def CONFIG_MTD_DEBUG +static int debug = 5; //CONFIG_MTD_DEBUG_VERBOSE; MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); #undef DEBUG @@ -88,7 +87,7 @@ static int setvpp; /* Force card to be treated as FLASH, ROM or RAM */ -static int mem_type; +static int mem_type = 1; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Simon Evans "); --- linux-2.6.5/drivers/mtd/maps/pci.c~heh 2004-04-03 22:36:56.000000000 -0500 +++ linux-2.6.5/drivers/mtd/maps/pci.c 2004-04-30 20:57:36.000000000 -0400 @@ -22,6 +22,8 @@ #include #include +#include + struct map_pci_info; struct mtd_pci_info { --- linux-2.6.5/drivers/input/Kconfig~heh 2004-04-03 22:36:18.000000000 -0500 +++ linux-2.6.5/drivers/input/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -80,7 +80,7 @@ module will be called joydev. config INPUT_TSDEV - tristate "Touchscreen interface" + tristate "Compaq Touchscreen interface" depends on INPUT ---help--- Say Y here if you have an application that only can understand the @@ -102,6 +102,10 @@ depends on INPUT_TSDEV default "320" +config INPUT_TSLIBDEV + tristate "TSLIB Touchscreen interface" + depends on INPUT + config INPUT_EVDEV tristate "Event interface" depends on INPUT --- linux-2.6.5/drivers/input/serio/Kconfig~heh 2004-04-03 22:37:07.000000000 -0500 +++ linux-2.6.5/drivers/input/serio/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -80,7 +80,7 @@ config SERIO_RPCKBD tristate "Acorn RiscPC keyboard controller" - depends on ARCH_ACORN && SERIO + depends on (ARCH_ACORN || ARCH_CLPS7500) && SERIO default y help Say Y here if you have the Acorn RiscPC and want to use an AT @@ -89,6 +89,18 @@ To compile this driver as a module, choose M here: the module will be called rpckbd. +config SERIO_CLPS7500 + tristate "CLPS7500 PS/2 mouse port controller" + depends on ARCH_CLPS7500 && SERIO + help + Say Y here if you have CLPS7500 based hardware and want to use + the mouse port. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called clps7500ps2. If you want to compile it + as a module, say M here and read . + config SERIO_AMBAKMI tristate "AMBA KMI keyboard controller" depends on ARCH_INTEGRATOR && SERIO --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/input/serio/sa1100ir.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,90 @@ +/* + * linux/drivers/input/serio/sa1100ir.c + * + * 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. + */ +#include +#include +#include + +#include +#include +#include + + + +struct sa1100_kbd { + struct serio io; + void *base; + int irq; +}; + +static void sa1100ir_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sa1100_kbd *kbd = dev_id; + unsigned int status; + + do { + unsigned int flag, data; + + status = readl(kbd->base + UTSR1); + if (!(status & UTSR1_RNE)) + break; + + flag = (status & UTSR1_FRE ? SERIO_FRAME : 0) | + (status & UTSR1_PRE ? SERIO_PARITY : 0); + + data = readl(kbd->base + UTDR); + + serio_interrupt(&kbd->io, data, flag); + } while (1); + + status = readl(kbd->base + UTSR0) & UTSR0_RID | UTSR0_RBB | UTSR0_REB; + if (status) + writel(status, kbd->base + UTSR0); +} + +static int sa1100ir_kbd_open(struct serio *io) +{ + struct sa1100_kbd *kbd = io->driver; + int ret; + + ret = request_irq(kbd->irq, sa1100ir_int, 0, kbd->io.phys, kbd); + if (ret) + return ret; + + return 0; +} + +static void sa1100ir_kbd_close(struct serio *io) +{ + struct sa1100_kbd *kbd = io->driver; + + free_irq(kbd->irq, kbd); +} + +static struct sa1100_kbd sa1100_kbd = { + .io = { + .type = 0, + .open = sa1100ir_kbd_open, + .close = sa1100ir_kbd_close, + .name = "SA11x0 IR port", + .phys = "sa11x0/ir", + .driver = &sa1100_kbd, + }, +}; + +static int __init sa1100_kbd_init(void) +{ + serio_register_port(&sa1100_kbd.io); +} + +static void __exit sa1100_kbd_exit(void) +{ + serio_unregister_port(&sa1100_kbd.io); +} + +module_init(sa1100_kbd_init); +module_exit(sa1100_kbd_exit); --- linux-2.6.5/drivers/input/serio/Makefile~heh 2004-04-03 22:36:16.000000000 -0500 +++ linux-2.6.5/drivers/input/serio/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -10,6 +10,7 @@ obj-$(CONFIG_SERIO_SERPORT) += serport.o obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o +obj-$(CONFIG_SERIO_CLPS7500) += clps7500ps2.o obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/input/tslibdev.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,358 @@ +/* + * linux/drivers/input/tslibdev.c + * + * Copyright (C) 2002 Russell King + * + * From tsdev.c: + * + * Copyright (c) 2001 "Crazy" james Simmons + * + * Input driver to Touchscreen device driver module. + * + * Sponsored by Transvirtual Technology + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ucb1x00_dev { + int exist; + char name[16]; + wait_queue_head_t wait; + struct list_head list; + struct input_handle handle; + int x; + int y; + int pressure; +}; + +struct ts_event { + u16 pressure; + u16 x; + u16 y; + u16 pad; + struct timeval stamp; +}; + +#define TSDEV_BUFFER_SIZE 64 + +struct ucb1x00_list { + struct list_head list; + struct fasync_struct *fasync; + struct ucb1x00_dev *tsdev; + unsigned int head; + unsigned int tail; + struct ts_event event[TSDEV_BUFFER_SIZE]; +}; + +static struct input_handler ucb1x00_handler; +static struct ucb1x00_dev *ucb1x00_dev; + +static void ucb1x00_remove(struct ucb1x00_dev *tsdev); + + +static int ucb1x00_fasync(int fd, struct file *file, int on) +{ + struct ucb1x00_list *list = file->private_data; + int retval; + + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int ucb1x00_open(struct inode *inode, struct file *file) +{ + struct ucb1x00_list *list; + int empty; + + if (!ucb1x00_dev || !ucb1x00_dev->exist) + return -ENODEV; + + printk(KERN_WARNING + "tslibdev: process %s (%d) uses obsolete tslib device\n", + current->comm, current->pid); + + list = kmalloc(sizeof(struct ucb1x00_list), GFP_KERNEL); + if (!list) + return -ENOMEM; + + memset(list, 0, sizeof(struct ucb1x00_list)); + + empty = list_empty(&ucb1x00_dev->list); + + list->tsdev = ucb1x00_dev; + list_add(&list->list, &list->tsdev->list); + + file->private_data = list; + + if (empty && list->tsdev->exist) + input_open_device(&list->tsdev->handle); + + return 0; +} + +static int ucb1x00_release(struct inode *inode, struct file *file) +{ + struct ucb1x00_list *list = file->private_data; + + ucb1x00_fasync(-1, file, 0); + + list_del(&list->list); + + ucb1x00_remove(list->tsdev); + + kfree(list); + + return 0; +} + +static ssize_t +ucb1x00_read(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct ucb1x00_list *list = file->private_data; + int retval = 0; + + if (list->head == list->tail) { + add_wait_queue(&list->tsdev->wait, &wait); + + while (list->head == list->tail) { + set_current_state(TASK_INTERRUPTIBLE); + + if (!list->tsdev->exist) { + retval = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->tsdev->wait, &wait); + } + + if (retval) + return retval; + + while (list->head != list->tail && count >= sizeof(struct ts_event)) { + if (copy_to_user(buffer, list->event + list->tail, + sizeof(struct ts_event))) + return retval ? retval : -EFAULT; + list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1); + retval += sizeof(struct ts_event); + buffer += sizeof(struct ts_event); + count -= sizeof(struct ts_event); + } + return retval; +} + +/* No kernel lock - fine */ +static unsigned int ucb1x00_poll(struct file *file, poll_table * wait) +{ + struct ucb1x00_list *list = file->private_data; + + poll_wait(file, &list->tsdev->wait, wait); + if (list->head != list->tail || !list->tsdev->exist) + return POLLIN | POLLRDNORM; + return 0; +} + +static int +ucb1x00_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + return -EINVAL; +} + +struct file_operations ucb1x00_fops = { + .owner = THIS_MODULE, + .open = ucb1x00_open, + .release = ucb1x00_release, + .read = ucb1x00_read, + .poll = ucb1x00_poll, + .fasync = ucb1x00_fasync, + .ioctl = ucb1x00_ioctl, +}; + +/* + * The official UCB1x00 touchscreen is a miscdevice: + * 10 char Non-serial mice, misc features + * 14 = /dev/touchscreen/ucb1x00 UCB 1x00 touchscreen + */ +static struct miscdevice ucb1x00_ts_dev = { + .minor = 14, + .name = "touchscreen/ucb1x00", + .fops = &ucb1x00_fops, + .devfs_name = "touchscreen/ucb1x00", +}; + +static void ucb1x00_remove(struct ucb1x00_dev *tsdev) +{ + if (list_empty(&tsdev->list)) { + if (tsdev->exist) { + input_close_device(&tsdev->handle); + wake_up_interruptible(&tsdev->wait); + } else { + misc_deregister(&ucb1x00_ts_dev); + ucb1x00_dev = NULL; + kfree(tsdev); + } + } +} + + +static void +ucb1x00_event(struct input_handle *handle, unsigned int type, unsigned int code, + int value) +{ + struct ucb1x00_dev *tsdev = handle->private; + struct list_head *l; + + /* sorry, we only handle absolute stuff */ + if (type == EV_ABS) { + switch (code) { + case ABS_X: + tsdev->x = value; + break; + case ABS_Y: + tsdev->y = value; + break; + case ABS_PRESSURE: + tsdev->pressure = value; + break; + } + return; + } + + if (type != EV_SYN || code != SYN_REPORT) + return; + + list_for_each(l, &tsdev->list) { + struct ucb1x00_list *list = list_entry(l, struct ucb1x00_list, list); + list->event[list->head].pressure = tsdev->pressure; + if (tsdev->pressure) { + list->event[list->head].x = tsdev->x; + list->event[list->head].y = tsdev->y; + } else { + list->event[list->head].x = 0; + list->event[list->head].y = 0; + } + do_gettimeofday(&list->event[list->head].stamp); + list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1); + kill_fasync(&list->fasync, SIGIO, POLL_IN); + } + wake_up_interruptible(&tsdev->wait); +} + +static struct input_handle * +ucb1x00_connect(struct input_handler *handler, struct input_dev *dev, + struct input_device_id *id) +{ + struct ucb1x00_dev *tsdev; + + if (ucb1x00_dev) + return NULL; + + tsdev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL); + if (!tsdev) + return NULL; + + memset(tsdev, 0, sizeof(struct ucb1x00_dev)); + init_waitqueue_head(&tsdev->wait); + INIT_LIST_HEAD(&tsdev->list); + + ucb1x00_dev = tsdev; + + strcpy(tsdev->name, ucb1x00_ts_dev.name); + + tsdev->handle.dev = dev; + tsdev->handle.name = tsdev->name; + tsdev->handle.handler = handler; + tsdev->handle.private = tsdev; + + misc_register(&ucb1x00_ts_dev); + + tsdev->exist = 1; + + return &tsdev->handle; +} + +static void ucb1x00_disconnect(struct input_handle *handle) +{ + struct ucb1x00_dev *tsdev = handle->private; + + tsdev->exist = 0; + ucb1x00_remove(tsdev); +} + +static struct input_device_id ucb1x00_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT(EV_ABS) }, + .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) }, + }, + + {},/* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, ucb1x00_ids); + +static struct input_handler ucb1x00_handler = { + .event = ucb1x00_event, + .connect = ucb1x00_connect, + .disconnect = ucb1x00_disconnect, + .name = "touchscreen/ucb1x00", + .id_table = ucb1x00_ids, +}; + +static int __init ucb1x00_init(void) +{ + input_register_handler(&ucb1x00_handler); + printk(KERN_INFO "ts: UCB1x00 touchscreen protocol output\n"); + return 0; +} + +static void __exit ucb1x00_exit(void) +{ + input_unregister_handler(&ucb1x00_handler); +} + +module_init(ucb1x00_init); +module_exit(ucb1x00_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Input driver to UCB1x00 touchscreen converter"); --- linux-2.6.5/drivers/input/Makefile~heh 2004-04-03 22:38:17.000000000 -0500 +++ linux-2.6.5/drivers/input/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -8,6 +8,7 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o +obj-$(CONFIG_INPUT_TSLIBDEV) += tslibdev.o obj-$(CONFIG_INPUT_TSDEV) += tsdev.o obj-$(CONFIG_INPUT_POWER) += power.o obj-$(CONFIG_INPUT_EVBUG) += evbug.o --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/char/sa1100-rtc.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,416 @@ +/* + * Real Time Clock interface for Linux on Intel SA11x0/PXA2xx + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * Date/time conversion routines taken from arch/arm/kernel/time.c + * by Linus Torvalds and Russel King + * and the GNU C Library + * ( ... I love the GPL ... just take what you need! ;) + * + * 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. + * + * 1.00 2001-06-08 Nicolas Pitre + * - added periodic timer capability using OSMR1 + * - flag compatibility with other RTC chips + * - permission checks for ioctls + * - major cleanup, partial rewrite + * + * 0.03 2001-03-07 CIH + * - Modify the bug setups RTC clock. + * + * 0.02 2001-02-27 Nils Faerber + * - removed mktime(), added alarm irq clear + * + * 0.01 2000-10-01 Nils Faerber + * - initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TIMER_FREQ 3686400 + +#define RTC_DEF_DIVIDER 32768 - 1 +#define RTC_DEF_TRIM 0 + +/* Those are the bits from a classic RTC we want to mimic */ +#define RTC_IRQF 0x80 /* any of the following 3 is active */ +#define RTC_PF 0x40 +#define RTC_AF 0x20 +#define RTC_UF 0x10 + +static unsigned long rtc_freq = 1024; +static struct rtc_time rtc_alarm = { + .tm_year = 0, + .tm_mon = 0, + .tm_mday = 0, + .tm_hour = 0, + .tm_mon = 0, + .tm_sec = 0, +}; + +extern spinlock_t rtc_lock; + +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 rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int rtsr; + unsigned long events = 0; + + spin_lock(&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(1, events); + + if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm)) + rtc_update_alarm(&rtc_alarm); + + spin_unlock(&rtc_lock); + + return IRQ_HANDLED; +} + +#if 0 +static unsigned long rtc_irq_data; + +static irqreturn_t timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* + * If we match for the first time, the periodic interrupt flag won't + * be set. If it is, then we did wrap around (very unlikely but + * still possible) and 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 */ + if (rtc_irq_data & RTC_PF) { + rtc_irq_data += (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))) << 8; + } else { + rtc_update(1, RTC_PF | RTC_IRQF); + } + + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); + + return IRQ_HANDLED; +} +#endif + +static int sa1100_rtc_open(void) +{ + int ret; + + ret = request_irq(IRQ_RTC1Hz, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTC1Hz); + goto fail_ui; + } + ret = request_irq(IRQ_RTCAlrm, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTCAlrm); + goto fail_ai; + } +#if 0 + ret = request_irq(IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_OST1); + goto fail_pi; + } + rtc_irq_data = 0; +#endif + 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(void) +{ + spin_lock_irq (&rtc_lock); + RTSR = 0; + OIER &= ~OIER_E1; + OSSR = OSSR_M1; + spin_unlock_irq (&rtc_lock); + +// free_irq(IRQ_OST1, NULL); + free_irq(IRQ_RTCAlrm, NULL); + free_irq(IRQ_RTC1Hz, NULL); +} + +#if 0 +ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t retval; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + for (;;) { + spin_lock_irq (&rtc_lock); + data = rtc_irq_data; + if (data != 0) { + rtc_irq_data = 0; + break; + } + spin_unlock_irq (&rtc_lock); + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + if (data & RTC_PF) { + /* interpolate missed periods and set match for the next one */ + 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 */ + while( (signed long)((osmr1 = OSMR1) - OSCR) <= 0 ) { + data += 0x100; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + period; + } + } + spin_unlock_irq (&rtc_lock); + + data -= 0x100; /* the first IRQ wasn't actually missed */ + + retval = put_user(data, (unsigned long *)buf); + if (!retval) + retval = sizeof(unsigned long); + +out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc_wait, &wait); + return retval; +} +#endif + +static int sa1100_rtc_ioctl(unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case RTC_AIE_OFF: + spin_lock_irq(&rtc_lock); + RTSR &= ~RTSR_ALE; +// rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_AIE_ON: + spin_lock_irq(&rtc_lock); + RTSR |= RTSR_ALE; +// rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_OFF: + spin_lock_irq(&rtc_lock); + RTSR &= ~RTSR_HZE; +// rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_ON: + spin_lock_irq(&rtc_lock); + RTSR |= RTSR_HZE; +// rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; +#if 0 + case RTC_PIE_OFF: + spin_lock_irq(&rtc_lock); + OIER &= ~OIER_E1; +// rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_ON: + if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE)) + return -EACCES; + spin_lock_irq(&rtc_lock); + OSMR1 = TIMER_FREQ/rtc_freq + OSCR; + OIER |= OIER_E1; +// rtc_irq_data = 0; + spin_unlock_irq(&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; +#endif + } + return -EINVAL; +} + +static void sa1100_rtc_read_time(struct rtc_time *tm) +{ + rtc_time_to_tm(RCNR, tm); +} + +static int sa1100_rtc_set_time(struct rtc_time *tm) +{ + unsigned long time; + int ret; + + ret = rtc_tm_to_time(tm, &time); + if (ret == 0) + RCNR = time; + return ret; +} + +static void sa1100_rtc_read_alarm(struct rtc_wkalrm *alrm) +{ + memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time)); + alrm->pending = RTSR & RTSR_AL ? 1 : 0; +} + +static int sa1100_rtc_set_alarm(struct rtc_wkalrm *alrm) +{ + int ret; + + spin_lock_irq(&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(&rtc_lock); + + return ret; +} + +static int sa1100_rtc_proc(char *buf) +{ + char *p = buf; + + p += sprintf(p, "trim/divider\t: 0x%08x\n", RTTR); + p += sprintf(p, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" ); + p += sprintf(p, "update_IRQ\t: %s\n", (RTSR & RTSR_HZE) ? "yes" : "no"); + p += sprintf(p, "periodic_IRQ\t: %s\n", (OIER & OIER_E1) ? "yes" : "no"); + p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq); + + return p - buf; +} + +static struct rtc_ops sa1100_rtc_ops = { + .owner = THIS_MODULE, + .open = sa1100_rtc_open, + .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 __init rtc_init(void) +{ + /* + * 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; + } + + register_rtc(&sa1100_rtc_ops); + + return 0; +} + +static void __exit rtc_exit(void) +{ + unregister_rtc(&sa1100_rtc_ops); +} + +module_init(rtc_init); +module_exit(rtc_exit); + +MODULE_AUTHOR("Nils Faerber "); +MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); /* so says the header */ --- linux-2.6.5/drivers/char/Kconfig~heh 2004-04-03 22:36:15.000000000 -0500 +++ linux-2.6.5/drivers/char/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -814,6 +814,10 @@ If unsure, say N. +config SA1100_RTC + tristate "SA1100 or PXA Real Time Clock" + depends on ARCH_SA1100 || ARCH_PXA + config DTLK tristate "Double Talk PC internal speech card support" help --- linux-2.6.5/drivers/char/tty_io.c~heh 2004-04-03 22:37:23.000000000 -0500 +++ linux-2.6.5/drivers/char/tty_io.c 2004-04-30 20:57:36.000000000 -0400 @@ -1535,10 +1535,17 @@ return 0; } +/* + * In the case of pty's, "tty" is the master side + * and "real_tty" is the slave side. + */ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize * arg) { struct winsize tmp_ws; + struct task_struct *p; + struct list_head *l; + struct pid *pid; if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) return -EFAULT; @@ -1558,8 +1565,21 @@ #endif if (tty->pgrp > 0) kill_pg(tty->pgrp, SIGWINCH, 1); - if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0)) - kill_pg(real_tty->pgrp, SIGWINCH, 1); + + /* + * Send SIGWINCH to the whole session on the slave tty. + * However, in the case of non-master pty's, be careful + * not to send two SIGWINCH to the same procress group. + */ + if (real_tty->session > 0) { + read_lock(&tasklist_lock); + for_each_task_pid(real_tty->session, PIDTYPE_SID, p, l, pid) { + if (process_group(p) != tty->pgrp) + group_send_sig_info(SIGWINCH, (void *)1L, p); + } + read_unlock(&tasklist_lock); + } + tty->winsize = tmp_ws; real_tty->winsize = tmp_ws; return 0; --- linux-2.6.5/drivers/Makefile~heh 2004-04-03 22:37:43.000000000 -0500 +++ linux-2.6.5/drivers/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -11,6 +11,8 @@ # PnP must come after ACPI since it will eventually need to check if acpi # was used and do nothing if so obj-$(CONFIG_PNP) += pnp/ +obj-$(CONFIG_I2C) += i2c/ +obj-$(CONFIG_L3) += l3/ # char/ comes before serial/ etc so that the VT console is the boot-time # default. @@ -41,7 +43,6 @@ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_SERIO) += input/serio/ obj-$(CONFIG_I2O) += message/ -obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/l3/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,24 @@ +# +# L3 bus configuration +# + +menu "L3 serial bus support" + +config L3 + tristate "L3 support" + +config L3_ALGOBIT + bool "L3 bit-banging interfaces" + depends on L3=y + +config L3_BIT_SA1100_GPIO + bool "SA11x0 GPIO adapter" + depends on L3_ALGOBIT && ARCH_SA1100 + +# i2c must come before this +config BIT_SA1100_GPIO + bool + depends on L3_BIT_SA1100_GPIO || I2C_BIT_SA1100_GPIO=y + default y + +endmenu --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/l3/l3-core.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,203 @@ +/* + * linux/drivers/l3/l3-core.c + * + * Copyright (C) 2001 Russell King + * + * General structure taken from i2c-core.c by Simon G. Vogl + * + * 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. + * + * See linux/Documentation/l3 for further documentation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static DECLARE_MUTEX(adapter_lock); +static LIST_HEAD(adapter_list); + +static DECLARE_MUTEX(driver_lock); +static LIST_HEAD(driver_list); + +/** + * l3_add_adapter - register a new L3 bus adapter + * @adap: l3_adapter structure for the registering adapter + * + * Make the adapter available for use by clients using name adap->name. + * The adap->adapters list is initialised by this function. + * + * Returns 0; + */ +int l3_add_adapter(struct l3_adapter *adap) +{ + down(&adapter_lock); + list_add(&adap->adapters, &adapter_list); + up(&adapter_lock); + return 0; +} + +/** + * l3_del_adapter - unregister a L3 bus adapter + * @adap: l3_adapter structure to unregister + * + * Remove an adapter from the list of available L3 Bus adapters. + * + * Returns 0; + */ +int l3_del_adapter(struct l3_adapter *adap) +{ + down(&adapter_lock); + list_del(&adap->adapters); + up(&adapter_lock); + return 0; +} + +static struct l3_adapter *__l3_get_adapter(const char *name) +{ + struct list_head *l; + + list_for_each(l, &adapter_list) { + struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters); + + if (strcmp(adap->name, name) == 0) + return adap; + } + + return NULL; +} + +/** + * l3_get_adapter - get a reference to an adapter + * @name: driver name + * + * Obtain a l3_adapter structure for the specified adapter. If the adapter + * is not currently load, then load it. The adapter will be locked in core + * until all references are released via l3_put_adapter. + */ +struct l3_adapter *l3_get_adapter(const char *name) +{ + struct l3_adapter *adap; + int try; + + for (try = 0; try < 2; try ++) { + down(&adapter_lock); + adap = __l3_get_adapter(name); + if (adap && !try_module_get(adap->owner)) + adap = NULL; + up(&adapter_lock); + + if (adap) + break; + + if (try == 0) + request_module(name); + } + + return adap; +} + +/** + * l3_put_adapter - release a reference to an adapter + * @adap: driver to release reference + * + * Indicate to the L3 core that you no longer require the adapter reference. + * The adapter module may be unloaded when there are no references to its + * data structure. + * + * You must not use the reference after calling this function. + */ +void l3_put_adapter(struct l3_adapter *adap) +{ + if (adap && adap->owner) + module_put(adap->owner); +} + +/** + * l3_transfer - transfer information on an L3 bus + * @adap: adapter structure to perform transfer on + * @msgs: array of l3_msg structures describing transfer + * @num: number of l3_msg structures + * + * Transfer the specified messages to/from a device on the L3 bus. + * + * Returns number of messages successfully transferred, otherwise negative + * error code. + */ +int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num) +{ + int ret = -ENOSYS; + + if (adap->algo->xfer) { + down(adap->lock); + ret = adap->algo->xfer(adap, msgs, num); + up(adap->lock); + } + return ret; +} + +/** + * l3_write - send data to a device on an L3 bus + * @adap: L3 bus adapter + * @addr: L3 bus address + * @buf: buffer for bytes to send + * @len: number of bytes to send + * + * Send len bytes pointed to by buf to device address addr on the L3 bus + * described by client. + * + * Returns the number of bytes transferred, or negative error code. + */ +int l3_write(struct l3_adapter *adap, int addr, const char *buf, int len) +{ + struct l3_msg msg; + int ret; + + msg.addr = addr; + msg.flags = 0; + msg.buf = (char *)buf; + msg.len = len; + + ret = l3_transfer(adap, &msg, 1); + return ret == 1 ? len : ret; +} + +/** + * l3_read - receive data from a device on an L3 bus + * @adap: L3 bus adapter + * @addr: L3 bus address + * @buf: buffer for bytes to receive + * @len: number of bytes to receive + * + * Receive len bytes from device address addr on the L3 bus described by + * client to a buffer pointed to by buf. + * + * Returns the number of bytes transferred, or negative error code. + */ +int l3_read(struct l3_adapter *adap, int addr, char *buf, int len) +{ + struct l3_msg msg; + int ret; + + msg.addr = addr; + msg.flags = L3_M_RD; + msg.buf = buf; + msg.len = len; + + ret = l3_transfer(adap, &msg, 1); + return ret == 1 ? len : ret; +} + +EXPORT_SYMBOL(l3_add_adapter); +EXPORT_SYMBOL(l3_del_adapter); +EXPORT_SYMBOL(l3_get_adapter); +EXPORT_SYMBOL(l3_put_adapter); +EXPORT_SYMBOL(l3_transfer); +EXPORT_SYMBOL(l3_write); +EXPORT_SYMBOL(l3_read); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/l3/l3-algo-bit.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,175 @@ +/* + * L3 bus algorithm module. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Note that L3 buses can share the same pins as I2C buses, so we must + * _not_ generate an I2C start condition. An I2C start condition is + * defined as a high-to-low transition of the data line while the clock + * is high. Therefore, we must only change the data line while the + * clock is low. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define setdat(adap,val) adap->setdat(adap->data, val) +#define setclk(adap,val) adap->setclk(adap->data, val) +#define setmode(adap,val) adap->setmode(adap->data, val) +#define setdatin(adap) adap->setdir(adap->data, 1) +#define setdatout(adap) adap->setdir(adap->data, 0) +#define getdat(adap) adap->getdat(adap->data) + +/* + * Send one byte of data to the chip. Data is latched into the chip on + * the rising edge of the clock. + */ +static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte) +{ + int i; + + for (i = 0; i < 8; i++) { + setclk(adap, 0); + udelay(adap->data_hold); + setdat(adap, byte & 1); + udelay(adap->data_setup); + setclk(adap, 1); + udelay(adap->clock_high); + byte >>= 1; + } +} + +/* + * Send a set of bytes to the chip. We need to pulse the MODE line + * between each byte, but never at the start nor at the end of the + * transfer. + */ +static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (i) { + udelay(adap->mode_hold); + setmode(adap, 0); + udelay(adap->mode); + } + setmode(adap, 1); + udelay(adap->mode_setup); + sendbyte(adap, buf[i]); + } +} + +/* + * Read one byte of data from the chip. Data is latched into the chip on + * the rising edge of the clock. + */ +static unsigned int readbyte(struct l3_algo_bit_data *adap) +{ + unsigned int byte = 0; + int i; + + for (i = 0; i < 8; i++) { + setclk(adap, 0); + udelay(adap->data_hold + adap->data_setup); + setclk(adap, 1); + if (getdat(adap)) + byte |= 1 << i; + udelay(adap->clock_high); + } + + return byte; +} + +/* + * Read a set of bytes from the chip. We need to pulse the MODE line + * between each byte, but never at the start nor at the end of the + * transfer. + */ +static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (i) { + udelay(adap->mode_hold); + setmode(adap, 0); + } + setmode(adap, 1); + udelay(adap->mode_setup); + buf[i] = readbyte(adap); + } +} + +static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num) +{ + struct l3_algo_bit_data *adap = l3_adap->algo_data; + int i; + + /* + * If we share an I2C bus, ensure that it is in STOP mode + */ + setclk(adap, 1); + setdat(adap, 1); + setmode(adap, 1); + setdatout(adap); + udelay(adap->mode); + + for (i = 0; i < num; i++) { + struct l3_msg *pmsg = &msgs[i]; + + if (!(pmsg->flags & L3_M_NOADDR)) { + setmode(adap, 0); + udelay(adap->mode_setup); + sendbyte(adap, pmsg->addr); + udelay(adap->mode_hold); + } + + if (pmsg->flags & L3_M_RD) { + setdatin(adap); + readbytes(adap, pmsg->buf, pmsg->len); + } else { + setdatout(adap); + sendbytes(adap, pmsg->buf, pmsg->len); + } + } + + /* + * Ensure that we leave the bus in I2C stop mode. + */ + setclk(adap, 1); + setdat(adap, 1); + setmode(adap, 0); + setdatin(adap); + + return num; +} + +static struct l3_algorithm l3_bit_algo = { + name: "L3 bit-shift algorithm", + xfer: l3_xfer, +}; + +int l3_bit_add_bus(struct l3_adapter *adap) +{ + adap->algo = &l3_bit_algo; + return l3_add_adapter(adap); +} + +int l3_bit_del_bus(struct l3_adapter *adap) +{ + return l3_del_adapter(adap); +} + +EXPORT_SYMBOL(l3_bit_add_bus); +EXPORT_SYMBOL(l3_bit_del_bus); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/l3/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,11 @@ +# +# Makefile for the L3 bus driver. +# + +# Link order: +# (core, adapters, algorithms, drivers) then clients + +l3-$(CONFIG_L3_ALGOBIT) += l3-algo-bit.o +l3-$(CONFIG_BIT_SA1100_GPIO) += l3-bit-sa1100.o + +obj-$(CONFIG_L3) += l3-core.o $(l3-y) $(l3-drv-y) --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/l3/l3-bit-sa1100.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,271 @@ +/* + * linux/drivers/l3/l3-bit-sa1100.c + * + * Copyright (C) 2001 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. + * + * This is a combined I2C and L3 bus driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NAME "l3-bit-sa1100-gpio" + +struct bit_data { + unsigned int sda; + unsigned int scl; + unsigned int l3_mode; +}; + +static int getsda(void *data) +{ + struct bit_data *bits = data; + + return GPLR & bits->sda; +} + +#ifdef CONFIG_I2C_BIT_SA1100_GPIO +static void i2c_setsda(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPDR &= ~bits->sda; + else { + GPCR = bits->sda; + GPDR |= bits->sda; + } + local_irq_restore(flags); +} + +static void i2c_setscl(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPDR &= ~bits->scl; + else { + GPCR = bits->scl; + GPDR |= bits->scl; + } + local_irq_restore(flags); +} + +static int i2c_getscl(void *data) +{ + struct bit_data *bits = data; + + return GPLR & bits->scl; +} + +static struct i2c_algo_bit_data i2c_bit_data = { + .setsda = i2c_setsda, + .setscl = i2c_setscl, + .getsda = getsda, + .getscl = i2c_getscl, + .udelay = 10, + .mdelay = 10, + .timeout = 100, +}; + +static struct i2c_adapter i2c_adapter = { + .algo_data = &i2c_bit_data, +}; + +#define LOCK &i2c_adapter.bus_lock + +static int __init i2c_init(struct bit_data *bits) +{ + i2c_bit_data.data = bits; + return i2c_bit_add_bus(&i2c_adapter); +} + +static void i2c_exit(void) +{ + i2c_bit_del_bus(&i2c_adapter); +} + +#else +static DECLARE_MUTEX(l3_lock); +#define LOCK &l3_lock +#define i2c_init(bits) (0) +#define i2c_exit() do { } while (0) +#endif + +#ifdef CONFIG_L3_BIT_SA1100_GPIO +/* + * iPAQs need the clock line driven hard high and low. + */ +static void l3_setscl(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPSR = bits->scl; + else + GPCR = bits->scl; + GPDR |= bits->scl; + local_irq_restore(flags); +} + +static void l3_setsda(void *data, int state) +{ + struct bit_data *bits = data; + + if (state) + GPSR = bits->sda; + else + GPCR = bits->sda; +} + +static void l3_setdir(void *data, int in) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (in) + GPDR &= ~bits->sda; + else + GPDR |= bits->sda; + local_irq_restore(flags); +} + +static void l3_setmode(void *data, int state) +{ + struct bit_data *bits = data; + + if (state) + GPSR = bits->l3_mode; + else + GPCR = bits->l3_mode; +} + +static struct l3_algo_bit_data l3_bit_data = { + .data = NULL, + .setdat = l3_setsda, + .setclk = l3_setscl, + .setmode = l3_setmode, + .setdir = l3_setdir, + .getdat = getsda, + .data_hold = 1, + .data_setup = 1, + .clock_high = 1, + .mode_hold = 1, + .mode_setup = 1, +}; + +static struct l3_adapter l3_adapter = { + .owner = THIS_MODULE, + .name = NAME, + .algo_data = &l3_bit_data, + .lock = LOCK, +}; + +static int __init l3_init(struct bit_data *bits) +{ + l3_bit_data.data = bits; + return l3_bit_add_bus(&l3_adapter); +} + +static void __exit l3_exit(void) +{ + l3_bit_del_bus(&l3_adapter); +} +#else +#define l3_init(bits) (0) +#define l3_exit() do { } while (0) +#endif + +static struct bit_data bit_data; + +static int __init bus_init(void) +{ + struct bit_data *bit = &bit_data; + unsigned long flags; + int ret; + + if (machine_is_assabet() || machine_is_pangolin()) { + bit->sda = GPIO_GPIO15; + bit->scl = GPIO_GPIO18; + bit->l3_mode = GPIO_GPIO17; + } + + if (machine_is_h3600() || machine_is_h3100()) { + bit->sda = GPIO_GPIO14; + bit->scl = GPIO_GPIO16; + bit->l3_mode = GPIO_GPIO15; + } + + if (machine_is_stork()) { + bit->sda = GPIO_GPIO15; + bit->scl = GPIO_GPIO18; + bit->l3_mode = GPIO_GPIO17; + } + + if (!bit->sda) + return -ENODEV; + + /* + * Default level for L3 mode is low. + * We set SCL and SDA high (i2c idle state). + */ + local_irq_save(flags); + GPDR &= ~(bit->scl | bit->sda); + GPCR = bit->l3_mode | bit->scl | bit->sda; + GPDR |= bit->l3_mode; + local_irq_restore(flags); + + if (machine_is_assabet()) { + /* + * Release reset on UCB1300, ADI7171 and UDA1341. We + * need to do this here so that we can communicate on + * the I2C/L3 buses. + */ + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + mdelay(1); + ASSABET_BCR_clear(ASSABET_BCR_CODEC_RST); + mdelay(1); + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + } + + ret = i2c_init(bit); + if (ret == 0 && bit->l3_mode) { + ret = l3_init(bit); + if (ret) + i2c_exit(); + } + + return ret; +} + +static void __exit bus_exit(void) +{ + l3_exit(); + i2c_exit(); +} + +module_init(bus_init); +module_exit(bus_exit); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/switches.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,22 @@ +/* + * linux/drivers/misc/switches.h + * + * Copyright (C) 2001 John Dorsey + * + * 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. + * + * 19 December 2001 - created. + */ + +#if !defined(_SWITCHES_H) +# define _SWITCHES_H + +#include + +#define SWITCHES_NAME "switches" + +extern int switches_event(switches_mask_t *mask); + +#endif /* !defined(_SWITCHES_H) */ --- linux-2.6.5/drivers/misc/Kconfig~heh 2004-04-03 22:36:26.000000000 -0500 +++ linux-2.6.5/drivers/misc/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -21,5 +21,62 @@ If unsure, say N. +menu "Multimedia Capabilities Port drivers" + +config MCP + tristate "Multimedia drivers" + +# Interface drivers +config MCP_SA1100 + tristate "Support SA1100 MCP interface" + depends on MCP && ARCH_SA1100 + +# Chip drivers +config MCP_UCB1200 + tristate "Support for UCB1200 / UCB1300" + depends on MCP + +config MCP_UCB1200_AUDIO + tristate "Audio / Telephony interface support" + depends on MCP_UCB1200 && SOUND + +config MCP_UCB1200_TS + tristate "Touchscreen interface support" + depends on MCP_UCB1200 && INPUT + +endmenu + + +menu "Console Switches" + +config SWITCHES + tristate "Console Switch Support" + help + Say Y here to include support for simple console momentary switches. + This driver implements a miscellaneous character device (named + `switches' in /proc/misc) which can be read by userland programs + to respond to switch press events. This mechanism is efficient for + systems which may not implement a traditional heavyweight console + server. + + It is also possible to say M to build this driver as a module (named + `switches.o'). + +config SWITCHES_SA1100 + tristate "SA-1100 switches" + depends on SWITCHES && ARCH_SA1100 + help + Say Y here to include support for switches routed directly to + interruptable signals on StrongARM SA-1100 systems. + +config SWITCHES_UCB1X00 + tristate "UCB1x00 switches" + depends on SWITCHES && MCP_UCB1200 + help + Say Y here to include support for switches routed through a + UCB1x00 modem/audio analog front-end device. + +endmenu + endmenu --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/ucb1x00-assabet.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,73 @@ +/* + * linux/drivers/misc/ucb1x00-assabet.c + * + * Copyright (C) 2001-2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * We handle the machine-specific bits of the UCB1x00 driver here. + */ +#include +#include +#include +#include +#include + +#include + +#include "ucb1x00.h" + +#define UCB1X00_ATTR(name,input)\ +static ssize_t name##_show(struct class_device *dev, char *buf) \ +{ \ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \ + int val; \ + ucb1x00_adc_enable(ucb); \ + val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC); \ + ucb1x00_adc_disable(ucb); \ + return sprintf(buf, "%d\n", val); \ +} \ +static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL) + +UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1); +UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD0); +UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2); + +static int ucb1x00_assabet_add(struct class_device *dev) +{ + class_device_create_file(dev, &class_device_attr_vbatt); + class_device_create_file(dev, &class_device_attr_vcharger); + class_device_create_file(dev, &class_device_attr_batt_temp); + return 0; +} + +static void ucb1x00_assabet_remove(struct class_device *dev) +{ + class_device_remove_file(dev, &class_device_attr_batt_temp); + class_device_remove_file(dev, &class_device_attr_vcharger); + class_device_remove_file(dev, &class_device_attr_vbatt); +} + +static struct class_interface ucb1x00_assabet_interface = { + .add = ucb1x00_assabet_add, + .remove = ucb1x00_assabet_remove, +}; + +static int __init ucb1x00_assabet_init(void) +{ + return ucb1x00_register_interface(&ucb1x00_assabet_interface); +} + +static void __exit ucb1x00_assabet_exit(void) +{ + ucb1x00_unregister_interface(&ucb1x00_assabet_interface); +} + +module_init(ucb1x00_assabet_init); +module_exit(ucb1x00_assabet_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Assabet noddy testing only example ADC driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/mcp-pxa.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,57 @@ +/* + * linux/drivers/misc/mcp-pxa.c + * + * 2002-01-10 Jeff Sutherland + * + * 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. + * + * NOTE: This is a quick hack to gain access to the aclink codec's + * touch screen facility. Its audio is handled by a separate + * (non-mcp) driver at the present time. + */ + +#include +#include +#include + +#include "mcp.h" + + +extern int pxa_ac97_get(struct ac97_codec **codec); +extern void pxa_ac97_put(void); + + +struct mcp *mcp_get(void) +{ + struct ac97_codec *codec; + if (pxa_ac97_get(&codec) < 0) + return NULL; + return (struct mcp *)codec; +} + +void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + struct ac97_codec *codec = (struct ac97_codec *)mcp; + codec->codec_write(codec, reg, val); +} + +unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg) +{ + struct ac97_codec *codec = (struct ac97_codec *)mcp; + return codec->codec_read(codec, reg); +} + +void mcp_enable(struct mcp *mcp) +{ + /* + * Should we do something here to make sure the aclink + * codec is alive??? + * A: not for now --NP + */ +} + +void mcp_disable(struct mcp *mcp) +{ +} --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/mcp-core.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,235 @@ +/* + * linux/drivers/misc/mcp-core.c + * + * Copyright (C) 2001 Russell King + * + * 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. + * + * Generic MCP (Multimedia Communications Port) layer. All MCP locking + * is solely held within this file. + */ +#include +#include +#include +#include +#include + +#include +#include + +#include "mcp.h" + +#define to_mcp(d) container_of(d, struct mcp, attached_device) +#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv) + +static int mcp_bus_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +static int mcp_bus_probe(struct device *dev) +{ + struct mcp *mcp = to_mcp(dev); + struct mcp_driver *drv = to_mcp_driver(dev->driver); + + return drv->probe(mcp); +} + +static int mcp_bus_remove(struct device *dev) +{ + struct mcp *mcp = to_mcp(dev); + struct mcp_driver *drv = to_mcp_driver(dev->driver); + + drv->remove(mcp); + return 0; +} + +static int mcp_bus_suspend(struct device *dev, u32 state) +{ + struct mcp *mcp = to_mcp(dev); + int ret = 0; + + if (dev->driver) { + struct mcp_driver *drv = to_mcp_driver(dev->driver); + + ret = drv->suspend(mcp, state); + } + return ret; +} + +static int mcp_bus_resume(struct device *dev) +{ + struct mcp *mcp = to_mcp(dev); + int ret = 0; + + if (dev->driver) { + struct mcp_driver *drv = to_mcp_driver(dev->driver); + + ret = drv->resume(mcp); + } + return ret; +} + +static struct bus_type mcp_bus_type = { + .name = "mcp", + .match = mcp_bus_match, + .suspend = mcp_bus_suspend, + .resume = mcp_bus_resume, +}; + +/** + * mcp_set_telecom_divisor - set the telecom divisor + * @mcp: MCP interface structure + * @div: SIB clock divisor + * + * Set the telecom divisor on the MCP interface. The resulting + * sample rate is SIBCLOCK/div. + */ +void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div) +{ + spin_lock_irq(&mcp->lock); + mcp->set_telecom_divisor(mcp, div); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_set_audio_divisor - set the audio divisor + * @mcp: MCP interface structure + * @div: SIB clock divisor + * + * Set the audio divisor on the MCP interface. + */ +void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div) +{ + spin_lock_irq(&mcp->lock); + mcp->set_audio_divisor(mcp, div); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_reg_write - write a device register + * @mcp: MCP interface structure + * @reg: 4-bit register index + * @val: 16-bit data value + * + * Write a device register. The MCP interface must be enabled + * to prevent this function hanging. + */ +void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); + mcp->reg_write(mcp, reg, val); + spin_unlock_irqrestore(&mcp->lock, flags); +} + +/** + * mcp_reg_read - read a device register + * @mcp: MCP interface structure + * @reg: 4-bit register index + * + * Read a device register and return its value. The MCP interface + * must be enabled to prevent this function hanging. + */ +unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&mcp->lock, flags); + val = mcp->reg_read(mcp, reg); + spin_unlock_irqrestore(&mcp->lock, flags); + + return val; +} + +/** + * mcp_enable - enable the MCP interface + * @mcp: MCP interface to enable + * + * Enable the MCP interface. Each call to mcp_enable will need + * a corresponding call to mcp_disable to disable the interface. + */ +void mcp_enable(struct mcp *mcp) +{ + spin_lock_irq(&mcp->lock); + if (mcp->use_count++ == 0) + mcp->enable(mcp); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_disable - disable the MCP interface + * @mcp: MCP interface to disable + * + * Disable the MCP interface. The MCP interface will only be + * disabled once the number of calls to mcp_enable matches the + * number of calls to mcp_disable. + */ +void mcp_disable(struct mcp *mcp) +{ + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); + if (--mcp->use_count == 0) + mcp->disable(mcp); + spin_unlock_irqrestore(&mcp->lock, flags); +} + +int mcp_host_register(struct mcp *mcp, struct device *parent) +{ + mcp->attached_device.parent = parent; + mcp->attached_device.bus = &mcp_bus_type; + mcp->attached_device.dma_mask = parent->dma_mask; + strcpy(mcp->attached_device.bus_id, "mcp0"); + return device_register(&mcp->attached_device); +} + +void mcp_host_unregister(struct mcp *mcp) +{ + device_unregister_wait(&mcp->attached_device); +} + +int mcp_driver_register(struct mcp_driver *mcpdrv) +{ + mcpdrv->drv.bus = &mcp_bus_type; + mcpdrv->drv.probe = mcp_bus_probe; + mcpdrv->drv.remove = mcp_bus_remove; + return driver_register(&mcpdrv->drv); +} + +void mcp_driver_unregister(struct mcp_driver *mcpdrv) +{ + driver_unregister(&mcpdrv->drv); +} + +static int __init mcp_init(void) +{ + return bus_register(&mcp_bus_type); +} + +static void __exit mcp_exit(void) +{ + bus_unregister(&mcp_bus_type); +} + +module_init(mcp_init); +module_exit(mcp_exit); + +EXPORT_SYMBOL(mcp_set_telecom_divisor); +EXPORT_SYMBOL(mcp_set_audio_divisor); +EXPORT_SYMBOL(mcp_reg_write); +EXPORT_SYMBOL(mcp_reg_read); +EXPORT_SYMBOL(mcp_enable); +EXPORT_SYMBOL(mcp_disable); +EXPORT_SYMBOL(mcp_host_register); +EXPORT_SYMBOL(mcp_host_unregister); +EXPORT_SYMBOL(mcp_driver_register); +EXPORT_SYMBOL(mcp_driver_unregister); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Core multimedia communications port driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/ucb1x00-ts.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,465 @@ +/* + * linux/drivers/misc/ucb1x00-ts.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 21-Jan-2002 : + * + * Added support for synchronous A/D mode. This mode is useful to + * avoid noise induced in the touchpanel by the LCD, provided that + * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. + * It is important to note that the signal connected to the ADCSYNC + * pin should provide pulses even when the LCD is blanked, otherwise + * a pen touch needed to unblank the LCD will never be read. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ucb1x00.h" + + +struct ucb1x00_ts { + struct input_dev idev; + struct ucb1x00 *ucb; + + struct semaphore irq_wait; + struct semaphore sem; + struct completion init_exit; + struct task_struct *rtask; + int use_count; + u16 x_res; + u16 y_res; + + int restart:1; + int adcsync:1; +}; + +static int adcsync = UCB_NOSYNC; + +static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) +{ + input_report_abs(&ts->idev, ABS_X, x); + input_report_abs(&ts->idev, ABS_Y, y); + input_report_abs(&ts->idev, ABS_PRESSURE, pressure); + input_sync(&ts->idev); +} + +static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) +{ + input_report_abs(&ts->idev, ABS_PRESSURE, 0); + input_sync(&ts->idev); +} + +/* + * Switch to interrupt mode. + */ +static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) +{ + if (ts->ucb->id == UCB_ID_1400_BUGGY) + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); + else + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); +} + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); +} + +/* + * Switch to X plate resistance mode. Set MX to ground, PX to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * Switch to Y plate resistance mode. Set MY to ground, PY to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * This is a RT kernel thread that handles the ADC accesses + * (mainly so we can use semaphores in the UCB1200 core code + * to serialise accesses to the ADC, and in the UCB1400 case where + * any register access may sleep). + */ +static int ucb1x00_thread(void *_ts) +{ + struct ucb1x00_ts *ts = _ts; + struct task_struct *tsk = current; + int valid; + + ts->rtask = tsk; + + daemonize("ktsd"); + /* only want to receive SIGKILL */ + allow_signal(SIGKILL); + + /* + * We could run as a real-time thread. However, thus far + * this doesn't seem to be necessary. + */ +// tsk->policy = SCHED_FIFO; +// tsk->rt_priority = 1; + + complete(&ts->init_exit); + + valid = 0; + + for (;;) { + unsigned int x, y, p, val; + + ts->restart = 0; + + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); + y = ucb1x00_ts_read_ypos(ts); + p = ucb1x00_ts_read_pressure(ts); + + /* + * Switch back to interrupt mode. + */ + ucb1x00_ts_mode_int(ts); + ucb1x00_adc_disable(ts->ucb); + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100); + if (signal_pending(tsk)) + break; + + ucb1x00_enable(ts->ucb); + val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); + + if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) { + ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + ucb1x00_disable(ts->ucb); + + /* + * If we spat out a valid sample set last time, + * spit out a "pen off" sample here. + */ + if (valid) { + ucb1x00_ts_event_release(ts); + valid = 0; + } + + /* + * Since ucb1x00_enable_irq() might sleep due + * to the way the UCB1400 regs are accessed, we + * can't use set_task_state() before that call, + * and not changing state before enabling the + * interrupt is racy. A semaphore solves all + * those issues quite nicely. + */ + down_interruptible(&ts->irq_wait); + } else { + ucb1x00_disable(ts->ucb); + + /* + * Filtering is policy. Policy belongs in user + * space. We therefore leave it to user space + * to do any filtering they please. + */ + if (!ts->restart) { + ucb1x00_ts_evt_add(ts, p, x, y); + valid = 1; + } + + set_task_state(tsk, TASK_INTERRUPTIBLE); + } + + schedule_timeout(HZ / 100); + if (signal_pending(tsk)) + break; + } + + ts->rtask = NULL; + complete_and_exit(&ts->init_exit, 0); +} + +/* + * We only detect touch screen _touches_ with this interrupt + * handler, and even then we just schedule our task. + */ +static void ucb1x00_ts_irq(int idx, void *id) +{ + struct ucb1x00_ts *ts = id; + ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + up(&ts->irq_wait); +} + +static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) +{ + input_report_abs(&ts->idev, ABS_PRESSURE, 0); +} + +static int ucb1x00_ts_open(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + int ret = 0; + + if (down_interruptible(&ts->sem)) + return -EINTR; + + if (ts->use_count++ != 0) + goto out; + + if (ts->rtask) + panic("ucb1x00: rtask running?"); + + sema_init(&ts->irq_wait, 0); + ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + if (ret < 0) + goto out; + + /* + * If we do this at all, we should allow the user to + * measure and read the X and Y resistance at any time. + */ + ucb1x00_adc_enable(ts->ucb); + ts->x_res = ucb1x00_ts_read_xres(ts); + ts->y_res = ucb1x00_ts_read_yres(ts); + ucb1x00_adc_disable(ts->ucb); + + init_completion(&ts->init_exit); + ret = kernel_thread(ucb1x00_thread, ts, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion(&ts->init_exit); + ret = 0; + } else { + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + } + + out: + if (ret) + ts->use_count--; + up(&ts->sem); + return ret; +} + +/* + * Release touchscreen resources. Disable IRQs. + */ +static void ucb1x00_ts_close(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + + down(&ts->sem); + if (--ts->use_count == 0) { + if (ts->rtask) { + send_sig(SIGKILL, ts->rtask, 1); + wait_for_completion(&ts->init_exit); + } + + ucb1x00_enable(ts->ucb); + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); + ucb1x00_disable(ts->ucb); + } + up(&ts->sem); +} + +#if 0 +static int ucb1x00_ts_resume(struct device *_dev, u32 level) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00_ts *ts = ucb1x00_get_drvdata(dev); + + if (level == RESUME_ENABLE && ts->rtask != NULL) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ts->restart = 1; + up(&ts->irq_wait); + } + return 0; +} +#endif + + +/* + * Initialisation. + */ +static int ucb1x00_ts_add(struct class_device *dev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); + struct ucb1x00_ts *ts; + + ts = kmalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + memset(ts, 0, sizeof(struct ucb1x00_ts)); + + ts->ucb = ucb; + ts->adcsync = adcsync; + init_MUTEX(&ts->sem); + + ts->idev.name = "Touchscreen panel"; + ts->idev.id.product = ts->ucb->id; + ts->idev.open = ucb1x00_ts_open; + ts->idev.close = ucb1x00_ts_close; + + __set_bit(EV_ABS, ts->idev.evbit); + __set_bit(ABS_X, ts->idev.absbit); + __set_bit(ABS_Y, ts->idev.absbit); + __set_bit(ABS_PRESSURE, ts->idev.absbit); + + input_register_device(&ts->idev); + + ucb->ts_data = ts; + + return 0; +} + +static void ucb1x00_ts_remove(struct class_device *dev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); + struct ucb1x00_ts *ts = ucb->ts_data; + + input_unregister_device(&ts->idev); + kfree(ts); +} + +static struct class_interface ucb1x00_ts_interface = { + .add = ucb1x00_ts_add, + .remove = ucb1x00_ts_remove, +}; + +static int __init ucb1x00_ts_init(void) +{ + return ucb1x00_register_interface(&ucb1x00_ts_interface); +} + +static void __exit ucb1x00_ts_exit(void) +{ + ucb1x00_unregister_interface(&ucb1x00_ts_interface); +} + +#ifndef MODULE + +/* + * Parse kernel command-line options. + * + * syntax : ucbts=[sync|nosync],... + */ +static int __init ucb1x00_ts_setup(char *str) +{ + char *p; + + while ((p = strsep(&str, ",")) != NULL) { + if (strcmp(p, "sync") == 0) + adcsync = UCB_SYNC; + } + + return 1; +} + +__setup("ucbts=", ucb1x00_ts_setup); + +#else + +MODULE_PARM(adcsync, "i"); +MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal"); + +#endif + +module_init(ucb1x00_ts_init); +module_exit(ucb1x00_ts_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/ucb1x00-core.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,624 @@ +/* + * linux/drivers/misc/ucb1x00-core.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * The UCB1x00 core driver provides basic services for handling IO, + * the ADC, interrupts, and accessing registers. It is designed + * such that everything goes through this layer, thereby providing + * a consistent locking methodology, as well as allowing the drivers + * to be used on other non-MCP-enabled hardware platforms. + * + * Note that all locks are private to this file. Nothing else may + * touch them. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ucb1x00.h" + +/** + * ucb1x00_io_set_dir - set IO direction + * @ucb: UCB1x00 structure describing chip + * @in: bitfield of IO pins to be set as inputs + * @out: bitfield of IO pins to be set as outputs + * + * Set the IO direction of the ten general purpose IO pins on + * the UCB1x00 chip. The @in bitfield has priority over the + * @out bitfield, in that if you specify a pin as both input + * and output, it will end up as an input. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_dir |= out; + ucb->io_dir &= ~in; + + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +/** + * ucb1x00_io_write - set or clear IO outputs + * @ucb: UCB1x00 structure describing chip + * @set: bitfield of IO pins to set to logic '1' + * @clear: bitfield of IO pins to set to logic '0' + * + * Set the IO output state of the specified IO pins. The value + * is retained if the pins are subsequently configured as inputs. + * The @clear bitfield has priority over the @set bitfield - + * outputs will be cleared. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_out |= set; + ucb->io_out &= ~clear; + + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +/** + * ucb1x00_io_read - read the current state of the IO pins + * @ucb: UCB1x00 structure describing chip + * + * Return a bitfield describing the logic state of the ten + * general purpose IO pins. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function does not take any semaphores or spinlocks. + */ +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) +{ + return ucb1x00_reg_read(ucb, UCB_IO_DATA); +} + +/* + * UCB1300 data sheet says we must: + * 1. enable ADC => 5us (including reference startup time) + * 2. select input => 51*tsibclk => 4.3us + * 3. start conversion => 102*tsibclk => 8.5us + * (tsibclk = 1/11981000) + * Period between SIB 128-bit frames = 10.7us + */ + +/** + * ucb1x00_adc_enable - enable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. + * Any code wishing to use the ADC converter must call this + * function prior to using it. + * + * This function takes the ADC semaphore to prevent two or more + * concurrent uses, and therefore may sleep. As a result, it + * can only be called from process context, not interrupt + * context. + * + * You should release the ADC as soon as possible using + * ucb1x00_adc_disable. + */ +void ucb1x00_adc_enable(struct ucb1x00 *ucb) +{ + down(&ucb->adc_sem); + + ucb->adc_cr |= UCB_ADC_ENA; + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); +} + +/** + * ucb1x00_adc_read - read the specified ADC channel + * @ucb: UCB1x00 structure describing chip + * @adc_channel: ADC channel mask + * @sync: wait for syncronisation pulse. + * + * Start an ADC conversion and wait for the result. Note that + * synchronised ADC conversions (via the ADCSYNC pin) must wait + * until the trigger is asserted and the conversion is finished. + * + * This function currently spins waiting for the conversion to + * complete (2 frames max without sync). + * + * If called for a synchronised ADC conversion, it may sleep + * with the ADC semaphore held. + * + * See ucb1x00.h for definition of the UCB_ADC_DAT macro. It + * addresses a bug in the ucb1200/1300 which, of course, Philips + * decided to finally fix in the ucb1400 ;-) -jws + */ +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) +{ + unsigned int val; + + if (sync) + adc_channel |= UCB_ADC_SYNC_ENA; + + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); + + for (;;) { + val = ucb1x00_reg_read(ucb, UCB_ADC_DATA); + if (val & UCB_ADC_DAT_VAL) + break; + /* yield to other processes */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + + return UCB_ADC_DAT(val); +} + +/** + * ucb1x00_adc_disable - disable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Disable the ADC converter and release the ADC semaphore. + */ +void ucb1x00_adc_disable(struct ucb1x00 *ucb) +{ + ucb->adc_cr &= ~UCB_ADC_ENA; + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); + ucb1x00_disable(ucb); + + up(&ucb->adc_sem); +} + +/* + * UCB1x00 Interrupt handling. + * + * The UCB1x00 can generate interrupts when the SIBCLK is stopped. + * Since we need to read an internal register, we must re-enable + * SIBCLK to talk to the chip. We leave the clock running until + * we have finished processing all interrupts from the chip. + * + * A restriction with interrupts exists when using the ucb1400, as + * the codec read/write routines may sleep while waiting for codec + * access completion and uses semaphores for access control to the + * AC97 bus. A complete codec read cycle could take anywhere from + * 60 to 100uSec so we *definitely* don't want to spin inside the + * interrupt handler waiting for codec access. So, we handle the + * interrupt by scheduling a RT kernel thread to run in process + * context instead of interrupt context. + */ + +static int ucb1x00_thread(void *_ucb) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + struct ucb1x00 *ucb = _ucb; + struct ucb1x00_irq *irq; + unsigned int isr, i; + + ucb->rtask = tsk; + + daemonize(); + reparent_to_init(); + tsk->tty = NULL; + tsk->policy = SCHED_FIFO; + tsk->rt_priority = 1; + strcpy(tsk->comm, "kUCB1x00d"); + + /* only want to receive SIGKILL */ + spin_lock_irq(&tsk->sigmask_lock); + siginitsetinv(&tsk->blocked, sigmask(SIGKILL)); + recalc_sigpending(); + spin_unlock_irq(&tsk->sigmask_lock); + + add_wait_queue(&ucb->irq_wait, &wait); + set_task_state(tsk, TASK_INTERRUPTIBLE); + complete(&ucb->complete); + + for (;;) { + if (signal_pending(tsk)) + break; + enable_irq(ucb->irq); + schedule(); + + ucb1x00_enable(ucb); + isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + for (i = 0, irq = ucb->irq_handler; + i < 16 && isr; + i++, isr >>= 1, irq++) + if (isr & 1 && irq->fn) + irq->fn(i, irq->devid); + ucb1x00_disable(ucb); + + set_task_state(tsk, TASK_INTERRUPTIBLE); + } + + remove_wait_queue(&ucb->irq_wait, &wait); + ucb->rtask = NULL; + complete_and_exit(&ucb->complete, 0); +} + +static irqreturn_t ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs) +{ + struct ucb1x00 *ucb = devid; + disable_irq(irqnr); + wake_up(&ucb->irq_wait); + return IRQ_HANDLED; +} + +/** + * ucb1x00_hook_irq - hook a UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @fn: function to call when interrupt is triggered + * @devid: device id to pass to interrupt handler + * + * Hook the specified interrupt. You can only register one handler + * for each interrupt source. The interrupt source is not enabled + * by this function; use ucb1x00_enable_irq instead. + * + * Interrupt handlers will be called with other interrupts enabled. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -EBUSY if the interrupt has already been hooked + */ +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) +{ + struct ucb1x00_irq *irq; + int ret = -EINVAL; + + if (idx < 16) { + irq = ucb->irq_handler + idx; + ret = -EBUSY; + + spin_lock_irq(&ucb->lock); + if (irq->fn == NULL) { + irq->devid = devid; + irq->fn = fn; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + } + return ret; +} + +/** + * ucb1x00_enable_irq - enable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @edges: interrupt edges to enable + * + * Enable the specified interrupt to trigger on %UCB_RISING, + * %UCB_FALLING or both edges. The interrupt should have been + * hooked by ucb1x00_hook_irq. + */ +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + ucb1x00_enable(ucb); + + /* This prevents spurious interrupts on the UCB1400 */ + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + if (edges & UCB_RISING) { + ucb->irq_ris_enbl |= 1 << idx; + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl |= 1 << idx; + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + } + ucb1x00_disable(ucb); + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/** + * ucb1x00_disable_irq - disable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @edges: interrupt edges to disable + * + * Disable the specified interrupt triggering on the specified + * (%UCB_RISING, %UCB_FALLING or both) edges. + */ +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + ucb1x00_enable(ucb); + if (edges & UCB_RISING) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl &= ~(1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + } + ucb1x00_disable(ucb); + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/** + * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @devid: device id. + * + * Disable the interrupt source and remove the handler. devid must + * match the devid passed when hooking the interrupt. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -ENOENT if devid does not match + */ +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) +{ + struct ucb1x00_irq *irq; + int ret; + + if (idx >= 16) + goto bad; + + irq = ucb->irq_handler + idx; + ret = -ENOENT; + + spin_lock_irq(&ucb->lock); + if (irq->devid == devid) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb->irq_fal_enbl &= ~(1 << idx); + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + ucb1x00_disable(ucb); + + irq->fn = NULL; + irq->devid = NULL; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + return ret; + +bad: + printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx); + return -EINVAL; +} + +/* + * Try to probe our interrupt, rather than relying on lots of + * hard-coded machine dependencies. For reference, the expected + * IRQ mappings are: + * + * Machine Default IRQ + * adsbitsy IRQ_GPCIN4 + * cerf IRQ_GPIO_UCB1200_IRQ + * flexanet IRQ_GPIO_GUI + * freebird IRQ_GPIO_FREEBIRD_UCB1300_IRQ + * graphicsclient ADS_EXT_IRQ(8) + * graphicsmaster ADS_EXT_IRQ(8) + * lart LART_IRQ_UCB1200 + * omnimeter IRQ_GPIO23 + * pfs168 IRQ_GPIO_UCB1300_IRQ + * simpad IRQ_GPIO_UCB1300_IRQ + * shannon SHANNON_IRQ_GPIO_IRQ_CODEC + * yopy IRQ_GPIO_UCB1200_IRQ + */ +static int ucb1x00_detect_irq(struct ucb1x00 *ucb) +{ + unsigned long mask; + + mask = probe_irq_on(); + if (!mask) + return NO_IRQ; + + /* + * Enable the ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Cause an ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + + /* + * Wait for the conversion to complete. + */ + while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0); + ucb1x00_reg_write(ucb, UCB_ADC_CR, 0); + + /* + * Disable and clear interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, 0); + ucb1x00_reg_write(ucb, UCB_IE_FAL, 0); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Read triggered interrupt. + */ + return probe_irq_off(mask); +} + +static int ucb1x00_probe(struct mcp *mcp) +{ + struct ucb1x00 *ucb; + unsigned int id; + int ret = -ENODEV; + + mcp_enable(mcp); + id = mcp_reg_read(mcp, UCB_ID); + + if (id != UCB_ID_1200 && id != UCB_ID_1300) { + printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); + goto err_disable; + } + + ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL); + ret = -ENOMEM; + if (!ucb) + goto err_disable; + + memset(ucb, 0, sizeof(struct ucb1x00)); + + ucb->cdev.class = &ucb1x00_class; + ucb->cdev.dev = &mcp->attached_device; + strlcpy(ucb->cdev.class_id, "ucb1x00", sizeof(ucb->cdev.class_id)); + + spin_lock_init(&ucb->lock); + spin_lock_init(&ucb->io_lock); + sema_init(&ucb->adc_sem, 1); + + ucb->id = id; + ucb->mcp = mcp; + ucb->irq = ucb1x00_detect_irq(ucb); + if (ucb->irq == NO_IRQ) { + printk(KERN_ERR "UCB1x00: IRQ probe failed\n"); + ret = -ENODEV; + goto err_free; + } + + ret = request_irq(ucb->irq, ucb1x00_irq, 0, "UCB1x00", ucb); + if (ret) { + printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", + ucb->irq, ret); + goto err_free; + } + + set_irq_type(ucb->irq, IRQT_RISING); + mcp_set_drvdata(mcp, ucb); + + ret = class_device_register(&ucb->cdev); + if (ret) { + free_irq(ucb->irq, ucb); + err_free: + kfree(ucb); + } + err_disable: + mcp_disable(mcp); + return ret; +} + +static void ucb1x00_remove(struct mcp *mcp) +{ + struct ucb1x00 *ucb = mcp_get_drvdata(mcp); + + class_device_unregister(&ucb->cdev); + free_irq(ucb->irq, ucb); +} + +static void ucb1x00_release(struct class_device *dev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); + kfree(ucb); +} + +static struct class ucb1x00_class = { + .name = "ucb1x00", + .release = ucb1x00_release, +}; + +int ucb1x00_register_interface(struct class_interface *intf) +{ + intf->class = &ucb1x00_class; + return class_interface_register(intf); +} + +void ucb1x00_unregister_interface(struct class_interface *intf) +{ + class_interface_unregister(intf); +} + +static struct mcp_driver ucb1x00_driver = { + .drv = { + .name = "ucb1x00", + }, + .probe = ucb1x00_probe, + .remove = ucb1x00_remove, +}; + +static int __init ucb1x00_init(void) +{ + int ret = class_register(&ucb1x00_class); + if (ret == 0) { + ret = mcp_driver_register(&ucb1x00_driver); + if (ret) + class_unregister(&ucb1x00_class); + } + return ret; +} + +static void __exit ucb1x00_exit(void) +{ + mcp_driver_unregister(&ucb1x00_driver); + class_unregister(&ucb1x00_class); +} + +module_init(ucb1x00_init); +module_exit(ucb1x00_exit); + +EXPORT_SYMBOL(ucb1x00_class); + +EXPORT_SYMBOL(ucb1x00_io_set_dir); +EXPORT_SYMBOL(ucb1x00_io_write); +EXPORT_SYMBOL(ucb1x00_io_read); + +EXPORT_SYMBOL(ucb1x00_adc_enable); +EXPORT_SYMBOL(ucb1x00_adc_read); +EXPORT_SYMBOL(ucb1x00_adc_disable); + +EXPORT_SYMBOL(ucb1x00_hook_irq); +EXPORT_SYMBOL(ucb1x00_free_irq); +EXPORT_SYMBOL(ucb1x00_enable_irq); +EXPORT_SYMBOL(ucb1x00_disable_irq); + +EXPORT_SYMBOL(ucb1x00_register_interface); +EXPORT_SYMBOL(ucb1x00_unregister_interface); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 core driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/switches-ucb1x00.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,214 @@ +/* + * linux/drivers/misc/switches-ucb1x00.c + * + * Copyright (C) 2001 John Dorsey + * + * 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. + * + * 19 December 2001 - created from sa1100_switches.c. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_SA1100_ASSABET +#include +#endif + +#include "switches.h" +#include "ucb1x00.h" + + +static void switches_ucb1x00_handler(int irq, void *devid); + + +#ifdef CONFIG_SA1100_ASSABET + +/* Assabet + * ^^^^^^^ + * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8. + * This code sets bits in the range [3, 8] in the mask that we + * return to userland. Note that we transpose signals SW7 and SW8; + * see assabet_switches_ucb1x00_handler(). + */ + +static int assabet_switches_ucb1x00_init(struct ucb1x00 *ucb) +{ + int i; + + ucb1x00_enable(ucb); + + ucb1x00_io_set_dir(ucb, + UCB_IO_0 | UCB_IO_1 | UCB_IO_2 | + UCB_IO_3 | UCB_IO_4 | UCB_IO_5, 0); + + for (i = 0; i < 6; ++i) { + ucb1x00_enable_irq(ucb, i, UCB_RISING | UCB_FALLING); + + if (ucb1x00_hook_irq(ucb, i, + switches_ucb1x00_handler, ucb) < 0) { + printk(KERN_ERR "%s: unable to hook IRQ for " + "UCB1300 IO_%d\n", SWITCHES_NAME, i); + + /* FIXME: BUGGY ERROR HANDLING */ + return -EBUSY; + } + + } + + ucb1x00_disable(ucb); + + return 0; + +} + +static void assabet_switches_ucb1x00_shutdown(struct ucb1x00 *ucb) +{ + int i; + + ucb1x00_enable(ucb); + + for (i = 5; i >= 0; --i) { + ucb1x00_disable_irq(ucb, i, UCB_RISING | UCB_FALLING); + + /* Only error conditions are ENOENT and EINVAL; silently + * ignore: + */ + ucb1x00_free_irq(ucb, i, ucb); + } + + ucb1x00_disable(ucb); +} + +static void assabet_switches_ucb1x00_handler(struct ucb1x00 *ucb, int irq, switches_mask_t *mask) +{ + unsigned int last, this; + static unsigned int states = 0; + + last = ((states & (1 << irq)) != 0); + this = ((ucb1x00_io_read(ucb) & (1 << irq)) != 0); + + if (last == this) /* debounce */ + return; + + /* Intel StrongARM SA-1110 Development Board + * Schematics Figure 5, Sheet 5 of 12 + * + * See switches S8 and S7. Notice their + * relationship to signals SW7 and SW8. Hmmm. + */ + + switch (irq) { + + case 4: + + SWITCHES_SET(mask, 8, this); + break; + + case 5: + + SWITCHES_SET(mask, 7, this); + break; + + default: + + SWITCHES_SET(mask, irq + 3, this); + + } + + states = this ? (states | (1 << irq)) : (states & ~(1 << irq)); + +} +#endif /* CONFIG_SA1100_ASSABET */ + + +/* switches_ucb1x00_handler() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * This routine is a generalized handler for UCB1x00 GPIO switches + * which calls a board-specific service routine and passes an event + * mask to the core event handler. This routine is appropriate for + * systems which use the ucb1x00 framework, and can be registered + * using ucb1x00_hook_irq(). + */ +static void switches_ucb1x00_handler(int irq, void *devid) +{ + struct ucb1x00 *ucb = devid; + switches_mask_t mask; + + SWITCHES_ZERO(&mask); + + /* Porting note: call a board-specific UCB1x00 switch handler here. + * The handler can assume that sufficient storage for `mask' has + * been allocated, and that the corresponding switches_mask_t + * structure has been zeroed. + */ + +#ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { + assabet_switches_ucb1x00_handler(ucb, irq, &mask); + } +#endif + + switches_event(&mask); +} + +static int switches_add(struct class_device *dev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); + int ret = -ENODEV; + +#ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { + ret = assabet_switches_ucb1x00_init(ucb); + } +#endif + /* Porting note: call a board-specific init routine here. */ + + return ret; +} + +static void switches_remove(struct class_device *dev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); + + /* Porting note: call a board-specific shutdown routine here. */ + +#ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { + assabet_switches_ucb1x00_shutdown(ucb); + } +#endif +} + +static struct class_interface ucb1x00_switches_interface = { + .add = switches_add, + .remove = switches_remove, +}; + +static int __init switches_ucb1x00_init(void) +{ + return ucb1x00_register_interface(&ucb1x00_switches_interface); +} + +static void __exit switches_ucb1x00_exit(void) +{ + ucb1x00_unregister_interface(&ucb1x00_switches_interface); +} + +module_init(switches_ucb1x00_init); +module_exit(switches_ucb1x00_exit); + +MODULE_DESCRIPTION("ucb1x00 switches driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/switches-core.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,200 @@ +/* + * linux/drivers/misc/switches-core.c + * + * Copyright (C) 2000-2001 John Dorsey + * + * 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. + * + * 5 October 2000 - created. + * + * 25 October 2000 - userland file interface added. + * + * 13 January 2001 - added support for Spot. + * + * 11 September 2001 - UCB1200 driver framework support added. + * + * 19 December 2001 - separated out SA-1100 and UCB1x00 code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "switches.h" + + +MODULE_AUTHOR("John Dorsey"); +MODULE_DESCRIPTION("Console switch support"); +MODULE_LICENSE("GPL"); + + +struct switches_action { + struct list_head list; + switches_mask_t mask; +}; + + +static int switches_users = 0; + +static spinlock_t switches_lock = SPIN_LOCK_UNLOCKED; + +DECLARE_WAIT_QUEUE_HEAD(switches_wait); +LIST_HEAD(switches_event_queue); + + +static ssize_t switches_read(struct file *file, char *buffer, + size_t count, loff_t *pos) +{ + unsigned long flags; + struct list_head *event; + struct switches_action *action; + + if (count < sizeof(struct switches_mask_t)) + return -EINVAL; + + while (list_empty(&switches_event_queue)) { + + if (file->f_flags & O_NDELAY) + return -EAGAIN; + + interruptible_sleep_on(&switches_wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + } + + if (verify_area(VERIFY_WRITE, buffer, sizeof(struct switches_mask_t))) + return -EFAULT; + + spin_lock_irqsave(&switches_lock, flags); + + event = switches_event_queue.next; + action = list_entry(event, struct switches_action, list); + copy_to_user(buffer, &(action->mask), sizeof(struct switches_mask_t)); + list_del(event); + kfree(action); + + spin_unlock_irqrestore(&switches_lock, flags); + + return 0; + +} + +static ssize_t switches_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static unsigned int switches_poll(struct file *file, poll_table *wait) +{ + + poll_wait(file, &switches_wait, wait); + + if (!list_empty(&switches_event_queue)) + return POLLIN | POLLRDNORM; + + return 0; + +} + +static int switches_open(struct inode *inode, struct file *file) +{ + + if (switches_users > 0) + return -EBUSY; + + ++switches_users; + return 0; + +} + +static int switches_release(struct inode *inode, struct file *file) +{ + + --switches_users; + return 0; + +} + +static struct file_operations switches_ops = { + .owner = THIS_MODULE, + .read = switches_read, + .write = switches_write, + .poll = switches_poll, + .open = switches_open, + .release = switches_release, +}; + +static struct miscdevice switches_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = SWITCHES_NAME, + .fops = &switches_ops, +}; + +int switches_event(switches_mask_t *mask) +{ + struct switches_action *action; + + if ((switches_users > 0) && (SWITCHES_COUNT(mask) > 0)) { + + if ((action = (struct switches_action *) + kmalloc(sizeof(struct switches_action), + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "%s: unable to allocate action " + "descriptor\n", SWITCHES_NAME); + return -1; + } + + action->mask = *mask; + + spin_lock(&switches_lock); + list_add_tail(&action->list, &switches_event_queue); + spin_unlock(&switches_lock); + + wake_up_interruptible(&switches_wait); + + } + + return 0; + +} + +EXPORT_SYMBOL(switches_event); + + +static int __init switches_init(void) +{ + if (misc_register(&switches_misc) < 0) { + printk(KERN_ERR "%s: unable to register misc device\n", + SWITCHES_NAME); + return -EIO; + } + + printk(KERN_INFO "Console switches initialized\n"); + + return 0; +} + +static void __exit switches_exit(void) +{ + if (misc_deregister(&switches_misc) < 0) + printk(KERN_ERR "%s: unable to deregister misc device\n", + SWITCHES_NAME); +} + +module_init(switches_init); +module_exit(switches_exit); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/ucb1x00-audio.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,439 @@ +/* + * linux/drivers/misc/ucb1x00-audio.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ucb1x00.h" + +#include "../sound/oss/sa1100-audio.h" + +#define MAGIC 0x41544154 + +struct ucb1x00_audio { + struct file_operations fops; + struct file_operations mops; + struct ucb1x00 *ucb; + audio_stream_t output_stream; + audio_stream_t input_stream; + audio_state_t state; + unsigned int rate; + int dev_id; + int mix_id; + unsigned int daa_oh_bit; + unsigned int telecom; + unsigned int magic; + unsigned int ctrl_a; + unsigned int ctrl_b; + + /* mixer info */ + unsigned int mod_cnt; + unsigned short output_level; + unsigned short input_level; +}; + +#define REC_MASK (SOUND_MASK_VOLUME | SOUND_MASK_MIC) +#define DEV_MASK REC_MASK + +static int +ucb1x00_mixer_ioctl(struct inode *ino, struct file *filp, uint cmd, ulong arg) +{ + struct ucb1x00_audio *ucba; + unsigned int val, gain; + int ret = 0; + + ucba = list_entry(filp->f_op, struct ucb1x00_audio, mops); + + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + if (cmd == SOUND_MIXER_INFO) { + struct mixer_info mi; + + strncpy(mi.id, "UCB1x00", sizeof(mi.id)); + strncpy(mi.name, "Philips UCB1x00", sizeof(mi.name)); + mi.modify_counter = ucba->mod_cnt; + return copy_to_user((void *)arg, &mi, sizeof(mi)) ? -EFAULT : 0; + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + unsigned int left, right; + + ret = get_user(val, (unsigned int *)arg); + if (ret) + goto out; + + left = val & 255; + right = val >> 8; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + gain = (left + right) / 2; + + ret = -EINVAL; + if (!ucba->telecom) { + switch(_IOC_NR(cmd)) { + case SOUND_MIXER_VOLUME: + ucba->output_level = gain | gain << 8; + ucba->mod_cnt++; + ucba->ctrl_b = (ucba->ctrl_b & 0xff00) | + ((gain * 31) / 100); + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, + ucba->ctrl_b); + ret = 0; + break; + + case SOUND_MIXER_MIC: + ucba->input_level = gain | gain << 8; + ucba->mod_cnt++; + ucba->ctrl_a = (ucba->ctrl_a & 0x7f) | + (((gain * 31) / 100) << 7); + ucb1x00_reg_write(ucba->ucb, UCB_AC_A, + ucba->ctrl_a); + ret = 0; + break; + } + } + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_VOLUME: + val = ucba->output_level; + break; + + case SOUND_MIXER_MIC: + val = ucba->input_level; + break; + + case SOUND_MIXER_RECSRC: + case SOUND_MIXER_RECMASK: + val = ucba->telecom ? 0 : REC_MASK; + break; + + case SOUND_MIXER_DEVMASK: + val = ucba->telecom ? 0 : DEV_MASK; + break; + + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + + default: + val = 0; + ret = -EINVAL; + break; + } + + if (ret == 0) + ret = put_user(val, (int *)arg); + } + out: + return ret; +} + +static int ucb1x00_audio_setrate(struct ucb1x00_audio *ucba, int rate) +{ + unsigned int div_rate = ucb1x00_clkrate(ucba->ucb) / 32; + unsigned int div; + + div = (div_rate + (rate / 2)) / rate; + if (div < 6) + div = 6; + if (div > 127) + div = 127; + + ucba->ctrl_a = (ucba->ctrl_a & ~0x7f) | div; + + if (ucba->telecom) { + ucb1x00_reg_write(ucba->ucb, UCB_TC_B, 0); + ucb1x00_set_telecom_divisor(ucba->ucb, div * 32); + ucb1x00_reg_write(ucba->ucb, UCB_TC_A, ucba->ctrl_a); + ucb1x00_reg_write(ucba->ucb, UCB_TC_B, ucba->ctrl_b); + } else { + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, 0); + ucb1x00_set_audio_divisor(ucba->ucb, div * 32); + ucb1x00_reg_write(ucba->ucb, UCB_AC_A, ucba->ctrl_a); + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, ucba->ctrl_b); + } + + ucba->rate = div_rate / div; + + return ucba->rate; +} + +static int ucb1x00_audio_getrate(struct ucb1x00_audio *ucba) +{ + return ucba->rate; +} + +static void ucb1x00_audio_startup(void *data) +{ + struct ucb1x00_audio *ucba = data; + + ucb1x00_enable(ucba->ucb); + ucb1x00_audio_setrate(ucba, ucba->rate); + + ucb1x00_reg_write(ucba->ucb, UCB_MODE, UCB_MODE_DYN_VFLAG_ENA); + + /* + * Take off-hook + */ + if (ucba->daa_oh_bit) + ucb1x00_io_write(ucba->ucb, 0, ucba->daa_oh_bit); +} + +static void ucb1x00_audio_shutdown(void *data) +{ + struct ucb1x00_audio *ucba = data; + + /* + * Place on-hook + */ + if (ucba->daa_oh_bit) + ucb1x00_io_write(ucba->ucb, ucba->daa_oh_bit, 0); + + ucb1x00_reg_write(ucba->ucb, ucba->telecom ? UCB_TC_B : UCB_AC_B, 0); + ucb1x00_disable(ucba->ucb); +} + +static int +ucb1x00_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + struct ucb1x00_audio *ucba; + int val, ret = 0; + + ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); + + /* + * Make sure we have our magic number + */ + if (ucba->magic != MAGIC) + return -ENODEV; + + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + if (val != 0) + return -EINVAL; + val = 0; + break; + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + val = 1; + break; + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + val = ucb1x00_audio_setrate(ucba, val); + break; + + case SOUND_PCM_READ_RATE: + val = ucb1x00_audio_getrate(ucba); + break; + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + val = AFMT_S16_LE; + break; + + default: + return ucb1x00_mixer_ioctl(inode, file, cmd, arg); + } + + return put_user(val, (int *)arg); +} + +static int ucb1x00_audio_open(struct inode *inode, struct file *file) +{ + struct ucb1x00_audio *ucba; + + ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); + + return sa1100_audio_attach(inode, file, &ucba->state); +} + +static struct ucb1x00_audio *ucb1x00_audio_alloc(struct ucb1x00 *ucb) +{ + struct ucb1x00_audio *ucba; + + ucba = kmalloc(sizeof(*ucba), GFP_KERNEL); + if (ucba) { + memset(ucba, 0, sizeof(*ucba)); + + ucba->magic = MAGIC; + ucba->ucb = ucb; + ucba->fops.owner = THIS_MODULE; + ucba->fops.open = ucb1x00_audio_open; + ucba->mops.owner = THIS_MODULE; + ucba->mops.ioctl = ucb1x00_mixer_ioctl; + ucba->state.output_stream = &ucba->output_stream; + ucba->state.input_stream = &ucba->input_stream; + ucba->state.data = ucba; + ucba->state.hw_init = ucb1x00_audio_startup; + ucba->state.hw_shutdown = ucb1x00_audio_shutdown; + ucba->state.client_ioctl = ucb1x00_audio_ioctl; + + /* There is a bug in the StrongARM causes corrupt MCP data to be sent to + * the codec when the FIFOs are empty and writes are made to the OS timer + * match register 0. To avoid this we must make sure that data is always + * sent to the codec. + */ + ucba->state.need_tx_for_rx = 1; + + init_MUTEX(&ucba->state.sem); + ucba->rate = 8000; + } + return ucba; +} + +static struct ucb1x00_audio *ucb1x00_audio_add_one(struct ucb1x00 *ucb, int telecom) +{ + struct ucb1x00_audio *a; + + a = ucb1x00_audio_alloc(ucb); + if (a) { + a->telecom = telecom; + + a->input_stream.dev = ucb->cdev.dev; + a->output_stream.dev = ucb->cdev.dev; + a->ctrl_a = 0; + + if (a->telecom) { + a->input_stream.dma_dev = ucb->mcp->dma_telco_rd; + a->input_stream.id = "UCB1x00 telco in"; + a->output_stream.dma_dev = ucb->mcp->dma_telco_wr; + a->output_stream.id = "UCB1x00 telco out"; + a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA; +#if 0 + a->daa_oh_bit = UCB_IO_8; + + ucb1x00_enable(ucb); + ucb1x00_io_write(ucb, a->daa_oh_bit, 0); + ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit); + ucb1x00_disable(ucb); +#endif + } else { + a->input_stream.dma_dev = ucb->mcp->dma_audio_rd; + a->input_stream.id = "UCB1x00 audio in"; + a->output_stream.dma_dev = ucb->mcp->dma_audio_wr; + a->output_stream.id = "UCB1x00 audio out"; + a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA; + } + + a->dev_id = register_sound_dsp(&a->fops, -1); + a->mix_id = register_sound_mixer(&a->mops, -1); + + printk("Sound: UCB1x00 %s: dsp id %d mixer id %d\n", + a->telecom ? "telecom" : "audio", + a->dev_id, a->mix_id); + } + + return a; +} + +static void ucb1x00_audio_remove_one(struct ucb1x00_audio *a) +{ + unregister_sound_dsp(a->dev_id); + unregister_sound_mixer(a->mix_id); + kfree(a); +} + +static int ucb1x00_audio_add(struct class_device *cdev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(cdev); + + if (ucb->cdev.dev == NULL || ucb->cdev.dev->dma_mask == NULL) + return -ENXIO; + + ucb->audio_data = ucb1x00_audio_add_one(ucb, 0); + ucb->telecom_data = ucb1x00_audio_add_one(ucb, 1); + + return 0; +} + +static void ucb1x00_audio_remove(struct class_device *cdev) +{ + struct ucb1x00 *ucb = classdev_to_ucb1x00(cdev); + + ucb1x00_audio_remove_one(ucb->audio_data); + ucb1x00_audio_remove_one(ucb->telecom_data); +} + +#if 0 //def CONFIG_PM +static int ucb1x00_audio_suspend(struct ucb1x00 *ucb, u32 state) +{ + struct ucb1x00_audio *a; + + a = ucb->audio_data; + sa1100_audio_suspend(&a->state, state); + a = ucb->telecom_data; + sa1100_audio_suspend(&a->state, state); + + return 0; +} + +static int ucb1x00_audio_resume(struct ucb1x00 *ucb) +{ + struct ucb1x00_audio *a; + + a = ucb->audio_data; + sa1100_audio_resume(&a->state); + a = ucb->telecom_data; + sa1100_audio_resume(&a->state); + + return 0; +} +#else +#define ucb1x00_audio_suspend NULL +#define ucb1x00_audio_resume NULL +#endif + +static struct class_interface ucb1x00_audio_interface = { + .add = ucb1x00_audio_add, + .remove = ucb1x00_audio_remove, +}; + +static int __init ucb1x00_audio_init(void) +{ + return ucb1x00_register_interface(&ucb1x00_audio_interface); +} + +static void __exit ucb1x00_audio_exit(void) +{ + ucb1x00_unregister_interface(&ucb1x00_audio_interface); +} + +module_init(ucb1x00_audio_init); +module_exit(ucb1x00_audio_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 telecom/audio driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/mcp-sa1100.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,275 @@ +/* + * linux/drivers/misc/mcp-sa1100.c + * + * Copyright (C) 2001 Russell King + * + * 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. + * + * SA1100 MCP (Multimedia Communications Port) driver. + * + * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mcp.h" + +static void +mcp_sa1100_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x00007f00; + mccr0 |= divisor << 8; + Ser4MCCR0 = mccr0; +} + +static void +mcp_sa1100_set_audio_divisor(struct mcp *mcp, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x0000007f; + mccr0 |= divisor; + Ser4MCCR0 = mccr0; +} + +/* + * Write data to the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static void +mcp_sa1100_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + int ret = -ETIME; + int i; + + Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); + + for (i = 0; i < 2; i++) { + udelay(mcp->rw_timeout); + if (Ser4MCSR & MCSR_CWC) { + ret = 0; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: write timed out\n"); +} + +/* + * Read data from the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static unsigned int +mcp_sa1100_read(struct mcp *mcp, unsigned int reg) +{ + int ret = -ETIME; + int i; + + Ser4MCDR2 = reg << 17 | MCDR2_Rd; + + for (i = 0; i < 2; i++) { + udelay(mcp->rw_timeout); + if (Ser4MCSR & MCSR_CRC) { + ret = Ser4MCDR2 & 0xffff; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: read timed out\n"); + + return ret; +} + +static void mcp_sa1100_enable(struct mcp *mcp) +{ + Ser4MCSR = -1; + Ser4MCCR0 |= MCCR0_MCE; +} + +static void mcp_sa1100_disable(struct mcp *mcp) +{ + Ser4MCCR0 &= ~MCCR0_MCE; +} + +/* + * Our methods. + */ +static struct mcp mcp_sa1100 = { + .owner = THIS_MODULE, + .lock = SPIN_LOCK_UNLOCKED, + .sclk_rate = 11981000, + .dma_audio_rd = DMA_Ser4MCP0Rd, + .dma_audio_wr = DMA_Ser4MCP0Wr, + .dma_telco_rd = DMA_Ser4MCP1Rd, + .dma_telco_wr = DMA_Ser4MCP1Wr, + .set_telecom_divisor = mcp_sa1100_set_telecom_divisor, + .set_audio_divisor = mcp_sa1100_set_audio_divisor, + .reg_write = mcp_sa1100_write, + .reg_read = mcp_sa1100_read, + .enable = mcp_sa1100_enable, + .disable = mcp_sa1100_disable, +}; + +static int mcp_sa1100_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mcp *mcp = &mcp_sa1100; + int ret; + + if (!machine_is_adsbitsy() && !machine_is_assabet() && + !machine_is_cerf() && !machine_is_flexanet() && + !machine_is_freebird() && !machine_is_graphicsclient() && + !machine_is_graphicsmaster() && !machine_is_lart() && + !machine_is_omnimeter() && !machine_is_pfs168() && + !machine_is_shannon() && !machine_is_simpad() && + !machine_is_yopy()) + return -ENODEV; + + if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) + return -EBUSY; + + mcp->me = dev; + dev_set_drvdata(dev, mcp); + + if (machine_is_assabet()) { + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + } + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + + Ser4MCSR = -1; + Ser4MCCR1 = 0; + Ser4MCCR0 = 0x00007f7f | MCCR0_ADM; + + /* + * Calculate the read/write timeout (us) from the bit clock + * rate. This is the period for 3 64-bit frames. Always + * round this time up. + */ + mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / + mcp->sclk_rate; + + ret = mcp_host_register(mcp, &pdev->dev); + if (ret != 0) { + release_mem_region(0x80060000, 0x60); + dev_set_drvdata(dev, NULL); + } + + return ret; +} + +static int mcp_sa1100_remove(struct device *dev) +{ + struct mcp *mcp = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + mcp_host_unregister(mcp); + release_mem_region(0x80060000, 0x60); + + return 0; +} + +struct mcp_sa1100_state { + u32 mccr0; + u32 mccr1; +}; + +static int mcp_sa1100_suspend(struct device *dev, u32 state, u32 level) +{ + struct mcp_sa1100_state *s = (struct mcp_sa1100_state *)dev->saved_state; + + if (!s) { + s = kmalloc(sizeof(struct mcp_sa1100_state), GFP_KERNEL); + dev->saved_state = (unsigned char *)s; + } + + if (s) { + s->mccr0 = Ser4MCCR0; + s->mccr1 = Ser4MCCR1; + } + + if (level == SUSPEND_DISABLE) + Ser4MCCR0 &= ~MCCR0_MCE; + return 0; +} + +static int mcp_sa1100_resume(struct device *dev, u32 level) +{ + struct mcp_sa1100_state *s = (struct mcp_sa1100_state *)dev->saved_state; + + if (s && level == RESUME_RESTORE_STATE) { + Ser4MCCR1 = s->mccr1; + Ser4MCCR0 = s->mccr0; + + dev->saved_state = NULL; + kfree(s); + } + return 0; +} + +/* + * The driver for the SA11x0 MCP port. + */ +static struct device_driver mcp_sa1100_driver = { + .name = "sa11x0-mcp", + .bus = &platform_bus_type, + .probe = mcp_sa1100_probe, + .remove = mcp_sa1100_remove, + .suspend = mcp_sa1100_suspend, + .resume = mcp_sa1100_resume, +}; + +/* + * This needs re-working + */ +static int __init mcp_sa1100_init(void) +{ + return driver_register(&mcp_sa1100_driver); +} + +static void __exit mcp_sa1100_exit(void) +{ + driver_unregister(&mcp_sa1100_driver); +} + +module_init(mcp_sa1100_init); +module_exit(mcp_sa1100_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/mcp.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,58 @@ +/* + * linux/drivers/misc/mcp.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#ifndef MCP_H +#define MCP_H + +struct mcp { + struct module *owner; + struct device *me; + spinlock_t lock; + int use_count; + unsigned int sclk_rate; + unsigned int rw_timeout; + dma_device_t dma_audio_rd; + dma_device_t dma_audio_wr; + dma_device_t dma_telco_rd; + dma_device_t dma_telco_wr; + void (*set_telecom_divisor)(struct mcp *, unsigned int); + void (*set_audio_divisor)(struct mcp *, unsigned int); + void (*reg_write)(struct mcp *, unsigned int, unsigned int); + unsigned int (*reg_read)(struct mcp *, unsigned int); + void (*enable)(struct mcp *); + void (*disable)(struct mcp *); + struct device attached_device; +}; + +void mcp_set_telecom_divisor(struct mcp *, unsigned int); +void mcp_set_audio_divisor(struct mcp *, unsigned int); +void mcp_reg_write(struct mcp *, unsigned int, unsigned int); +unsigned int mcp_reg_read(struct mcp *, unsigned int); +void mcp_enable(struct mcp *); +void mcp_disable(struct mcp *); +#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) + +int mcp_host_register(struct mcp *, struct device *); +void mcp_host_unregister(struct mcp *); + +struct mcp_driver { + struct device_driver drv; + int (*probe)(struct mcp *); + void (*remove)(struct mcp *); + int (*suspend)(struct mcp *, u32); + int (*resume)(struct mcp *); +}; + +int mcp_driver_register(struct mcp_driver *); +void mcp_driver_unregister(struct mcp_driver *); + +#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device) +#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d) + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/ucb1x00-input.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,233 @@ +/* + * linux/drivers/misc/ucb1x00.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#ifndef UCB1200_H +#define UCB1200_H + +#define UCB_IO_DATA 0x00 +#define UCB_IO_DIR 0x01 + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_RIS 0x02 +#define UCB_IE_FAL 0x03 +#define UCB_IE_STATUS 0x04 +#define UCB_IE_CLEAR 0x04 +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_IRQ_TSPX 12 + +#define UCB_TC_A 0x05 +#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ + +#define UCB_TC_B 0x06 +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_A 0x07 +#define UCB_AC_B 0x08 +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR 0x09 +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_CR 0x0a +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DATA 0x0b +#define UCB_ADC_DAT_VAL (1 << 15) +#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) + +#define UCB_ID 0x0c +#define UCB_ID_1200 0x1004 +#define UCB_ID_1300 0x1005 +#define UCB_ID_1400 0x4304 +#define UCB_ID_1400_BUGGY 0x4303 /* fake ID */ + +#define UCB_MODE 0x0d +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +#include "mcp.h" + +struct ucb1x00; + +struct ucb1x00_irq { + void *devid; + void (*fn)(int, void *); +}; + +struct ucb1x00 { + spinlock_t lock; + struct mcp *mcp; + unsigned int irq; + struct semaphore adc_sem; + spinlock_t io_lock; + u16 id; + u16 io_dir; + u16 io_out; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + struct ucb1x00_irq irq_handler[16]; +}; + +/** + * ucb1x00_clkrate - return the UCB1x00 SIB clock rate + * @ucb: UCB1x00 structure describing chip + * + * Return the SIB clock rate in Hz. + */ +static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) +{ + return mcp_get_sclk_rate(ucb->mcp); +} + +/** + * ucb1x00_enable - enable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Enable the SIB clock. This can be called multiple times. + */ +static inline void ucb1x00_enable(struct ucb1x00 *ucb) +{ + mcp_enable(ucb->mcp); +} + +/** + * ucb1x00_disable - disable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Disable the SIB clock. The SIB clock will only be disabled + * when the number of ucb1x00_enable calls match the number of + * ucb1x00_disable calls. + */ +static inline void ucb1x00_disable(struct ucb1x00 *ucb) +{ + mcp_disable(ucb->mcp); +} + +/** + * ucb1x00_reg_write - write a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * @val: UCB1x00 16-bit value to write + * + * Write the UCB1x00 register @reg with value @val. The SIB + * clock must be running for this function to return. + */ +static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) +{ + mcp_reg_write(ucb->mcp, reg, val); +} + +/** + * ucb1x00_reg_read - read a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * + * Read the UCB1x00 register @reg and return its value. The SIB + * clock must be running for this function to return. + */ +static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) +{ + return mcp_reg_read(ucb->mcp, reg); +} +/** + * ucb1x00_set_audio_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_audio_divisor(ucb->mcp, div); +} + +/** + * ucb1x00_set_telecom_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_telecom_divisor(ucb->mcp, div); +} + +struct ucb1x00 *ucb1x00_get(void); + +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); + +#define UCB_NOSYNC (0) +#define UCB_SYNC (1) + +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); +void ucb1x00_adc_enable(struct ucb1x00 *ucb); +void ucb1x00_adc_disable(struct ucb1x00 *ucb); + +/* + * Which edges of the IRQ do you want to control today? + */ +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/ucb1x00.h 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,271 @@ +/* + * linux/drivers/misc/ucb1x00.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#ifndef UCB1200_H +#define UCB1200_H + +#ifdef CONFIG_ARCH_PXA + +/* ucb1400 aclink register mappings: */ + +#define UCB_IO_DATA 0x5a +#define UCB_IO_DIR 0x5c +#define UCB_IE_RIS 0x5e +#define UCB_IE_FAL 0x60 +#define UCB_IE_STATUS 0x62 +#define UCB_IE_CLEAR 0x62 +#define UCB_TS_CR 0x64 +#define UCB_ADC_CR 0x66 +#define UCB_ADC_DATA 0x68 +#define UCB_ID 0x7e /* 7c is mfr id, 7e part id (from aclink spec) */ + +#define UCB_ADC_DAT(x) ((x) & 0x3ff) + +#else + +/* ucb1x00 SIB register mappings: */ + +#define UCB_IO_DATA 0x00 +#define UCB_IO_DIR 0x01 +#define UCB_IE_RIS 0x02 +#define UCB_IE_FAL 0x03 +#define UCB_IE_STATUS 0x04 +#define UCB_IE_CLEAR 0x04 +#define UCB_TC_A 0x05 +#define UCB_TC_B 0x06 +#define UCB_AC_A 0x07 +#define UCB_AC_B 0x08 +#define UCB_TS_CR 0x09 +#define UCB_ADC_CR 0x0a +#define UCB_ADC_DATA 0x0b +#define UCB_ID 0x0c +#define UCB_MODE 0x0d + +#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) + +#endif + + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_IRQ_TSPX 12 + +#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ + +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DAT_VAL (1 << 15) + +#define UCB_ID_1200 0x1004 +#define UCB_ID_1300 0x1005 +#define UCB_ID_1400 0x4304 +#define UCB_ID_1400_BUGGY 0x4303 /* fake ID */ + +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +#include +#include "mcp.h" + +struct ucb1x00_irq { + void *devid; + void (*fn)(int, void *); +}; + +extern struct class ucb1x00_class; + +struct ucb1x00 { + spinlock_t lock; + struct mcp *mcp; + unsigned int irq; + struct semaphore adc_sem; + spinlock_t io_lock; + wait_queue_head_t irq_wait; + struct completion complete; + struct task_struct *rtask; + u16 id; + u16 io_dir; + u16 io_out; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + struct ucb1x00_irq irq_handler[16]; + struct class_device cdev; + void *audio_data; + void *telecom_data; + void *ts_data; +}; + +#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, cdev) + +int ucb1x00_register_interface(struct class_interface *intf); +void ucb1x00_unregister_interface(struct class_interface *intf); + +/** + * ucb1x00_clkrate - return the UCB1x00 SIB clock rate + * @ucb: UCB1x00 structure describing chip + * + * Return the SIB clock rate in Hz. + */ +static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) +{ + return mcp_get_sclk_rate(ucb->mcp); +} + +/** + * ucb1x00_enable - enable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Enable the SIB clock. This can be called multiple times. + */ +static inline void ucb1x00_enable(struct ucb1x00 *ucb) +{ + mcp_enable(ucb->mcp); +} + +/** + * ucb1x00_disable - disable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Disable the SIB clock. The SIB clock will only be disabled + * when the number of ucb1x00_enable calls match the number of + * ucb1x00_disable calls. + */ +static inline void ucb1x00_disable(struct ucb1x00 *ucb) +{ + mcp_disable(ucb->mcp); +} + +/** + * ucb1x00_reg_write - write a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * @val: UCB1x00 16-bit value to write + * + * Write the UCB1x00 register @reg with value @val. The SIB + * clock must be running for this function to return. + */ +static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) +{ + mcp_reg_write(ucb->mcp, reg, val); +} + +/** + * ucb1x00_reg_read - read a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * + * Read the UCB1x00 register @reg and return its value. The SIB + * clock must be running for this function to return. + */ +static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) +{ + return mcp_reg_read(ucb->mcp, reg); +} +/** + * ucb1x00_set_audio_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_audio_divisor(ucb->mcp, div); +} + +/** + * ucb1x00_set_telecom_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_telecom_divisor(ucb->mcp, div); +} + +#define ucb1x00_get() NULL + +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); + +#define UCB_NOSYNC (0) +#define UCB_SYNC (1) + +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); +void ucb1x00_adc_enable(struct ucb1x00 *ucb); +void ucb1x00_adc_disable(struct ucb1x00 *ucb); + +/* + * Which edges of the IRQ do you want to control today? + */ +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); + +#endif --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/misc/switches-sa1100.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,323 @@ +/* + * linux/drivers/misc/switches-sa1100.c + * + * Copyright (C) 2001 John Dorsey + * + * 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. + * + * 19 December 2001 - created from sa1100_switches.c. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "switches.h" + + +static irqreturn_t switches_sa1100_handler(int irq, void *dev_id, + struct pt_regs *regs); + + +#ifdef CONFIG_SA1100_ASSABET + +/* Assabet + * ^^^^^^^ + * We have two general-purpose switches, S1 and S2, available via GPIO + * on Assabet. This code sets bits in the range [1, 2] in the mask that + * we return to userland. + */ + +static int assabet_switches_sa1100_init(void) +{ + + if (machine_has_neponset()) + NCR_0 |= NCR_GP01_OFF; + + if (request_irq(IRQ_GPIO0, switches_sa1100_handler, SA_INTERRUPT, + SWITCHES_NAME, NULL) < 0) { + printk(KERN_ERR "%s: unable to register IRQ for GPIO 0\n", + SWITCHES_NAME); + return -EIO; + } + + if (request_irq(IRQ_GPIO1, switches_sa1100_handler, SA_INTERRUPT, + SWITCHES_NAME, NULL) < 0) { + printk(KERN_ERR "%s: unable to register IRQ for GPIO 1\n", + SWITCHES_NAME); + free_irq(IRQ_GPIO0, NULL); + return -EIO; + } + + set_irq_type(IRQ_GPIO0, IRQT_BOTHEDGE); + set_irq_type(IRQ_GPIO1, IRQT_BOTHEDGE); + + return 0; + +} + +static void assabet_switches_sa1100_shutdown(void) +{ + + free_irq(IRQ_GPIO1, NULL); + free_irq(IRQ_GPIO0, NULL); + +} + +static irqreturn_t assabet_switches_sa1100_handler(int irq, switches_mask_t *mask) +{ + unsigned int s, last, this; + static unsigned int states = 0; + + switch (irq) { + + case IRQ_GPIO0: s = 0; break; + + case IRQ_GPIO1: s = 1; break; + + default: return IRQ_NONE; + + } + + last = ((states & (1 << s)) != 0); + this = ((GPLR & GPIO_GPIO(s)) != 0); + + if (last == this) /* debounce */ + return IRQ_HANDLED; + + SWITCHES_SET(mask, s + 1, this); + + states = this ? (states | (1 << s)) : (states & ~(1 << s)); + + return IRQ_HANDLED; +} +#endif /* CONFIG_SA1100_ASSABET */ + +#ifdef CONFIG_SA1100_BADGE4 + +/* BadgePAD 4 + * ^^^^^^^^^^ + * + * Here we use test point J6 (BADGE4_GPIO_TESTPT_J6 aka GPIO 23) as a + * general purpose switch input. We map this to switch #0. + */ + +#define BADGE4_SW0_GPIO GPIO_GPIO23 /* aka BADGE4_GPIO_TESTPT_J6 */ +#define BADGE4_SW0_IRQ IRQ_GPIO23 + +static int badge4_switches_sa1100_init(void) +{ + if (request_irq(BADGE4_SW0_IRQ, switches_sa1100_handler, SA_INTERRUPT, + SWITCHES_NAME, NULL) < 0) { + printk(KERN_ERR "%s: unable to register IRQ for SW0\n", + SWITCHES_NAME); + return -EIO; + } + + set_irq_type(BADGE4_SW0_IRQ, IRQT_BOTHEDGE); + + return 0; +} + +static void badge4_switches_sa1100_shutdown(void) +{ + free_irq(BADGE4_SW0_IRQ, NULL); +} + +static irqreturn_t badge4_switches_sa1100_handler(int irq, switches_mask_t *mask) +{ + unsigned int swno, last, this, gpio; + static unsigned int states = 0; + + switch (irq) { + case BADGE4_SW0_IRQ: + swno = 0; + gpio = BADGE4_SW0_GPIO; + break; + default: + return IRQ_NONE; + } + + last = ((states & gpio) != 0); + this = ((GPLR & gpio) != 0); + + if (last == this) /* debounce */ + return IRQ_HANDLED; + + SWITCHES_SET(mask, swno, this); + + states = this ? (states | gpio) : (states & ~gpio); + + return IRQ_HANDLED; +} +#endif /* CONFIG_SA1100_BADGE4 */ + + +#ifdef CONFIG_SA1100_SPOT + +/* Spot + * ^^^^ + * Spot (R2, R3) has a single general-purpose switch (S1), which is + * also the power-on switch. We set bit [1] in the mask we return to + * userland. + */ + +static int spot_switches_sa1100_init(void) +{ + + set_GPIO_IRQ_edge(GPIO_SW1, GPIO_BOTH_EDGES); + + if (request_irq(IRQ_GPIO_SW1, switches_sa1100_handler, SA_INTERRUPT, + SWITCHES_NAME, NULL) < 0) { + printk(KERN_ERR "%s: unable to register IRQ for SW1\n", + SWITCHES_NAME); + return -EIO; + } + + return 0; + +} + +static void spot_switches_sa1100_shutdown(void) +{ + + free_irq(IRQ_GPIO_SW1, NULL); + +} + +static irqreturn_t spot_switches_sa1100_handler(int irq, switches_mask_t *mask) +{ + unsigned int s, last, this; + static unsigned int states = 0; + + switch (irq) { + + case IRQ_GPIO_SW1: s = 0; break; + + default: return IRQ_NONE; + + } + + last = ((states & (1 << s)) != 0); + this = ((GPLR & GPIO_GPIO(s)) != 0); + + if (last == this) /* debounce */ + return IRQ_HANDLED; + + SWITCHES_SET(mask, s + 1, this); + + states = this ? (states | (1 << s)) : (states & ~(1 << s)); + + return IRQ_HANDLED; + +} +#endif /* CONFIG_SA1100_SPOT */ + + +/* switches_sa1100_handler() + * ^^^^^^^^^^^^^^^^^^^^^^^^^ + * This routine is a generalized handler for SA-1100 switches + * which manages action descriptors and calls a board-specific + * service routine. This routine is appropriate for GPIO switches + * or other primary interrupt sources, and can be registered as a + * first-class IRQ handler using request_irq(). + */ +static irqreturn_t switches_sa1100_handler(int irq, void *dev_id, + struct pt_regs *regs) +{ + switches_mask_t mask; + irqreturn_t ret = IRQ_NONE; + + SWITCHES_ZERO(&mask); + + /* Porting note: call a board-specific switch interrupt handler + * here. The handler can assume that sufficient storage for + * `mask' has been allocated, and that the corresponding + * switches_mask_t structure has been zeroed. + */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + ret = assabet_switches_sa1100_handler(irq, &mask); +#endif + } else if (machine_is_badge4()) { +#ifdef CONFIG_SA1100_BADGE4 + ret = badge4_switches_sa1100_handler(irq, &mask); +#endif + } else if (machine_is_spot()) { +#ifdef CONFIG_SA1100_SPOT + ret = spot_switches_sa1100_handler(irq, &mask); +#endif + } + + switches_event(&mask); + + return ret; +} + +int __init switches_sa1100_init(void) +{ + + /* Porting note: call a board-specific init routine here. */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + if (assabet_switches_sa1100_init() < 0) + return -EIO; +#endif + } else if (machine_is_badge4()) { +#ifdef CONFIG_SA1100_BADGE4 + if (badge4_switches_sa1100_init() < 0) + return -EIO; +#endif + } else if (machine_is_spot()) { +#ifdef CONFIG_SA1100_SPOT + if (spot_switches_sa1100_init() < 0) + return -EIO; +#endif + } + + return 0; + +} + +void __exit switches_sa1100_exit(void) +{ + + /* Porting note: call a board-specific shutdown routine here. */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + assabet_switches_sa1100_shutdown(); +#endif + } else if (machine_is_badge4()) { +#ifdef CONFIG_SA1100_BADGE4 + badge4_switches_sa1100_shutdown(); +#endif + } else if (machine_is_spot()) { +#ifdef CONFIG_SA1100_SPOT + spot_switches_sa1100_shutdown(); +#endif + } + +} + +module_init(switches_sa1100_init); +module_exit(switches_sa1100_exit); + +MODULE_DESCRIPTION("SA-1100 switches driver"); +MODULE_LICENSE("GPL"); --- linux-2.6.5/drivers/misc/Makefile~heh 2004-04-03 22:38:17.000000000 -0500 +++ linux-2.6.5/drivers/misc/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -4,3 +4,21 @@ obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ + +obj-$(CONFIG_MCP) += mcp-core.o +obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o +obj-$(CONFIG_MCP_UCB1200_AUDIO) += ucb1x00-audio.o +obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o + +ifeq ($(CONFIG_SA1100_ASSABET),y) +obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o +endif + +obj-$(CONFIG_SWITCHES) += switches-core.o +obj-$(CONFIG_SWITCHES_SA1100) += switches-sa1100.o +obj-$(CONFIG_SWITCHES_UCB1X00) += switches-ucb1x00.o + +obj-$(CONFIG_MCP_SA1100) += mcp-sa1100.o + +obj-$(CONFIG_UCB1400_TS) += mcp-pxa.o ucb1x00-core.o ucb1x00-ts.o + --- linux-2.6.5/drivers/i2c/busses/Kconfig~heh 2004-04-03 22:38:10.000000000 -0500 +++ linux-2.6.5/drivers/i2c/busses/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -70,6 +70,16 @@ This support is also available as a module. If so, the module will be called i2c-hydra. +config I2C_BIT_SA1100_GPIO + bool "SA1100 I2C GPIO adapter" + depends on ARCH_SA1100 && I2C_ALGOBIT + help + This supports I2C on the SA11x0 processor GPIO pins. This + shares support with the L3 driver. + + This support is also available as a module. If so, the module + will be called l3-bit-sa1100. + config I2C_I801 tristate "Intel 801" depends on I2C && PCI && EXPERIMENTAL @@ -241,6 +251,18 @@ This support is also available as a module. If so, the module will be called i2c-prosavage. +config I2C_PXA2XX + tristate "PXA I2C Interface" + depends on I2C && ARCH_PXA + select I2C_ALGOPXA + help + This supports the use of the PXA I2C interface found on the Intel + PXA 25x and PXA 26x systems. Say Y if you have one of these. + + This support is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + The module will be called i2c-adap-pxa. + config I2C_RPXLITE tristate "Embedded Planet RPX Lite/Classic support" depends on (RPXLITE || RPXCLASSIC) && I2C --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/i2c/busses/pxa2xx_i2c.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,388 @@ +/* + * i2c_adap_pxa.c + * + * I2C adapter for the PXA I2C bus access. + * + * Copyright (C) 2002 Intrinsyc 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. + * + * History: + * Apr 2002: Initial version [CS] + * Jun 2002: Properly seperated algo/adap [FB] + * Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem] + * Jan 2003: added limited signal handling [Kai-Uwe Bloem] + * Jun 2003: updated for 2.5 [Dustin McIntire] + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* for IRQ_I2C */ + +#include + +/* + * Set this to zero to remove all debug statements via dead code elimination. + */ +//#define DEBUG 1 + +#if DEBUG +static unsigned int i2c_debug = DEBUG; +#else +#define i2c_debug 0 +#endif + +static int irq = 0; +static volatile int i2c_pending = 0; /* interrupt pending when 1 */ +static volatile int bus_error = 0; +static volatile int tx_finished = 0; +static volatile int rx_finished = 0; + +static wait_queue_head_t i2c_wait; +static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte); + +/* place a byte in the transmit register */ +static void i2c_pxa_write_byte(u8 value) +{ + IDBR = value; +} + +/* read byte in the receive register */ +static u8 i2c_pxa_read_byte(void) +{ + return (u8) (0xff & IDBR); +} + +static void i2c_pxa_start(void) +{ + unsigned long icr = ICR; + icr |= ICR_START; + icr &= ~(ICR_STOP | ICR_ALDIE | ICR_ACKNAK); + ICR = icr; + + bus_error=0; /* clear any bus_error from previous txfers */ + tx_finished=0; /* clear rx and tx interrupts from previous txfers */ + rx_finished=0; + i2c_pending = 0; +} + +static void i2c_pxa_repeat_start(void) +{ + unsigned long icr = ICR; + icr |= ICR_START; + icr &= ~(ICR_STOP | ICR_ALDIE); + ICR = icr; + + bus_error=0; /* clear any bus_error from previous txfers */ + tx_finished=0; /* clear rx and tx interrupts from previous txfers */ + rx_finished=0; + i2c_pending = 0; +} + +static void i2c_pxa_stop(void) +{ + unsigned long icr = ICR; + icr |= ICR_STOP; + icr &= ~(ICR_START); + ICR = icr; +} + +static void i2c_pxa_midbyte(void) +{ + unsigned long icr = ICR; + icr &= ~(ICR_START | ICR_STOP); + ICR = icr; +} + +static void i2c_pxa_abort(void) +{ + unsigned long timeout = jiffies + HZ/4; + +#ifdef PXA_ABORT_MA + while ((long)(timeout - jiffies) > 0 && (ICR & ICR_TB)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + + ICR |= ICR_MA; + udelay(100); +#else + while ((long)(timeout - jiffies) > 0 && (IBMR & 0x1) == 0) { + i2c_pxa_transfer( 1, I2C_RECEIVE, 1); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } +#endif + ICR &= ~(ICR_MA | ICR_START | ICR_STOP); +} + +static int i2c_pxa_wait_bus_not_busy( void) +{ + int timeout = DEF_TIMEOUT; + + while (timeout-- && (ISR & ISR_IBB)) { + udelay(100); /* wait for 100 us */ + } + + return (timeout<=0); +} + +spinlock_t i2c_pxa_irqlock = SPIN_LOCK_UNLOCKED; + +static void i2c_pxa_wait_for_ite(void){ + unsigned long flags; + if (irq > 0) { + spin_lock_irqsave(&i2c_pxa_irqlock, flags); + if (i2c_pending == 0) { + interruptible_sleep_on_timeout(&i2c_wait, I2C_SLEEP_TIMEOUT ); + } + i2c_pending = 0; + spin_unlock_irqrestore(&i2c_pxa_irqlock, flags); + } else { + udelay(100); + } +} + +static int i2c_pxa_wait_for_int( int wait_type) +{ + int timeout = DEF_TIMEOUT; +#ifdef DEBUG + if (bus_error) + printk(KERN_INFO"i2c_pxa_wait_for_int: Bus error on enter\n"); + if (rx_finished) + printk(KERN_INFO"i2c_pxa_wait_for_int: Receive interrupt on enter\n"); + if (tx_finished) + printk(KERN_INFO"i2c_pxa_wait_for_int: Transmit interrupt on enter\n"); +#endif + + if (wait_type == I2C_RECEIVE){ /* wait on receive */ + + do { + i2c_pxa_wait_for_ite(); + } while (!(rx_finished) && timeout-- && !signal_pending(current)); + +#ifdef DEBUG + if (timeout<0){ + if (tx_finished) + printk("Error: i2c-algo-pxa.o: received a tx" + " interrupt while waiting on a rx in wait_for_int"); + } +#endif + } else { /* wait on transmit */ + + do { + i2c_pxa_wait_for_ite(); + } while (!(tx_finished) && timeout-- && !signal_pending(current)); + +#ifdef DEBUG + if (timeout<0){ + if (rx_finished) + printk("Error: i2c-algo-pxa.o: received a rx" + " interrupt while waiting on a tx in wait_for_int"); + } +#endif + } + + udelay(ACK_DELAY); /* this is needed for the bus error */ + + tx_finished=0; + rx_finished=0; + + if (bus_error){ + bus_error=0; + if( i2c_debug > 2)printk("wait_for_int: error - no ack.\n"); + return BUS_ERROR; + } + + if (signal_pending(current)) { + return (-ERESTARTSYS); + } else if (timeout < 0) { + if( i2c_debug > 2)printk("wait_for_int: timeout.\n"); + return(-EIO); + } else + return(0); +} + +static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte) +{ + if( lastbyte) + { + if( receive==I2C_RECEIVE) ICR |= ICR_ACKNAK; + i2c_pxa_stop(); + } + else if( midbyte) + { + i2c_pxa_midbyte(); + } + ICR |= ICR_TB; +} + +static void i2c_pxa_reset( void) +{ +#ifdef DEBUG + printk("Resetting I2C Controller Unit\n"); +#endif + + /* abort any transfer currently under way */ + i2c_pxa_abort(); + + /* reset according to 9.8 */ + ICR = ICR_UR; + ISR = I2C_ISR_INIT; + ICR &= ~ICR_UR; + + /* set the global I2C clock on */ + CKEN |= CKEN14_I2C; + + /* set our slave address */ + ISAR = I2C_PXA_SLAVE_ADDR; + + /* set control register values */ + ICR = I2C_ICR_INIT; + + /* clear any leftover states from prior transmissions */ + i2c_pending = rx_finished = tx_finished = bus_error = 0; + + /* enable unit */ + ICR |= ICR_IUE; + udelay(100); +} + +static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + int status, wakeup = 0; + status = (ISR); + + if (status & ISR_BED){ + (ISR) |= ISR_BED; + bus_error=ISR_BED; + wakeup = 1; + } + if (status & ISR_ITE){ + (ISR) |= ISR_ITE; + tx_finished=ISR_ITE; + wakeup = 1; + } + if (status & ISR_IRF){ + (ISR) |= ISR_IRF; + rx_finished=ISR_IRF; + wakeup = 1; + } + if (wakeup) { + spin_lock_irqsave(&i2c_pxa_irqlock, flags); + i2c_pending = 1; + spin_unlock_irqrestore(&i2c_pxa_irqlock, flags); + wake_up_interruptible(&i2c_wait); + } + return IRQ_HANDLED; +} + +static int i2c_pxa_resource_init( void) +{ + init_waitqueue_head(&i2c_wait); + + if (request_irq(IRQ_I2C, &i2c_pxa_handler, SA_INTERRUPT, "I2C", 0) < 0) { + irq = 0; + if(i2c_debug) + printk(KERN_INFO "I2C: Failed to register I2C irq %i\n", IRQ_I2C); + return -ENODEV; + } + return 0; +} + +static void i2c_pxa_resource_release( void) +{ + if( irq > 0) + { + disable_irq(irq); + free_irq(irq,0); + irq=0; + } +} + +static int i2c_pxa_client_register(struct i2c_client *client) +{ + return 0; +} + +static int i2c_pxa_client_unregister(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_algo_pxa_data i2c_pxa_data = { + write_byte: i2c_pxa_write_byte, + read_byte: i2c_pxa_read_byte, + + start: i2c_pxa_start, + repeat_start: i2c_pxa_repeat_start, + stop: i2c_pxa_stop, + abort: i2c_pxa_abort, + + wait_bus_not_busy: i2c_pxa_wait_bus_not_busy, + wait_for_interrupt: i2c_pxa_wait_for_int, + transfer: i2c_pxa_transfer, + reset: i2c_pxa_reset, + + udelay: 10, + timeout: DEF_TIMEOUT, +}; + +static struct i2c_adapter i2c_pxa_ops = { + .owner = THIS_MODULE, + .id = I2C_ALGO_PXA, + .algo_data = &i2c_pxa_data, + .dev = { + .name = "PXA I2C Adapter", + }, + .client_register = i2c_pxa_client_register, + .client_unregister = i2c_pxa_client_unregister, + .retries = 2, +}; + +extern int i2c_pxa_add_bus(struct i2c_adapter *); +extern int i2c_pxa_del_bus(struct i2c_adapter *); + +static int __init i2c_adap_pxa_init(void) +{ + if( i2c_pxa_resource_init() == 0) { + + if (i2c_pxa_add_bus(&i2c_pxa_ops) < 0) { + i2c_pxa_resource_release(); + printk(KERN_INFO "I2C: Failed to add bus\n"); + return -ENODEV; + } + } else { + return -ENODEV; + } + + printk(KERN_INFO "I2C: Successfully added bus\n"); + + return 0; +} + +static void i2c_adap_pxa_exit(void) +{ + i2c_pxa_del_bus( &i2c_pxa_ops); + i2c_pxa_resource_release(); + + printk(KERN_INFO "I2C: Successfully removed bus\n"); +} + +module_init(i2c_adap_pxa_init); +module_exit(i2c_adap_pxa_exit); --- linux-2.6.5/drivers/i2c/busses/Makefile~heh 2004-04-03 22:36:17.000000000 -0500 +++ linux-2.6.5/drivers/i2c/busses/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -21,6 +21,7 @@ obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o +obj-$(CONFIG_I2C_PXA) += pxa2xx_i2c.o obj-$(CONFIG_I2C_RPXLITE) += i2c-rpx.o obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o --- linux-2.6.5/drivers/i2c/algos/Kconfig~heh 2004-04-03 22:37:59.000000000 -0500 +++ linux-2.6.5/drivers/i2c/algos/Kconfig 2004-04-30 20:57:36.000000000 -0400 @@ -38,6 +38,18 @@ This support is also available as a module. If so, the module will be called i2c-algo-ite. +config I2C_ALGOPXA + tristate "PXA I2C Algorithm" + depends on ARCH_PXA && I2C + help + This supports the use of the PXA I2C interface found on the Intel + PXA 25x and PXA 26x systems. Say Y if you have one of these. + You should also say Y for the PXA I2C peripheral driver support below. + + This support is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + The module will be called i2c-algo-pxa. + config I2C_ALGO8XX tristate "MPC8xx CPM I2C interface" depends on 8xx && I2C --- /dev/null 2003-09-23 18:19:32.000000000 -0400 +++ linux-2.6.5/drivers/i2c/algos/i2c-algo-pxa.c 2004-04-30 20:57:36.000000000 -0400 @@ -0,0 +1,384 @@ +/* + * i2c-algo-pxa.c + * + * I2C algorithm for the PXA I2C bus access. + * Byte driven algorithm similar to pcf. + * + * Copyright (C) 2002 Intrinsyc 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. + * + * History: + * Apr 2002: Initial version [CS] + * Jun 2002: Properly seperated algo/adap [FB] + * Jan 2003: added limited signal handling [Kai-Uwe Bloem] + * Jan 2003: allow SMBUS_QUICK as valid msg [FB] + * Jun 2003: updated for 2.5 [Dustin McIntire] + * + */ +#include +#include + +#include +#include +#include +#include /* struct i2c_msg and others */ +#include + +#include + +/* + * Set this to zero to remove all the debug statements via dead code elimination. + */ +//#define DEBUG 1 + +#if DEBUG +static unsigned int i2c_debug = DEBUG; +#else +#define i2c_debug 0 +#endif + +static int pxa_scan = 1; + +static int i2c_pxa_valid_messages( struct i2c_msg msgs[], int num) +{ + int i; + if (num < 1 || num > MAX_MESSAGES){ + if( i2c_debug) + printk(KERN_INFO "Invalid number of messages (max=%d, num=%d)\n", + MAX_MESSAGES, num); + return -EINVAL; + } + + /* check consistency of our messages */ + for (i=0;ialgo_data; + + /* increment number of bytes to read by one -- read dummy byte */ + for (i = 0; i <= count; i++) { + if (i!=0){ + /* set ACK to NAK for last received byte ICR[ACKNAK] = 1 + only if not a repeated start */ + + if ((i == count) && last) { + adap->transfer( last, I2C_RECEIVE, 0); + }else{ + adap->transfer( 0, I2C_RECEIVE, 1); + } + + timeout = adap->wait_for_interrupt(I2C_RECEIVE); + +#ifdef DEBUG + if (timeout==BUS_ERROR){ + printk(KERN_INFO "i2c_pxa_readbytes: bus error -> forcing reset\n"); + adap->reset(); + return I2C_RETRY; + } else +#endif + if (timeout == -ERESTARTSYS) { + adap->abort(); + return timeout; + } else + if (timeout){ +#ifdef DEBUG + printk(KERN_INFO "i2c_pxa_readbytes: timeout -> forcing reset\n"); +#endif + adap->reset(); + return I2C_RETRY; + } + + } + + if (i) { + buf[i - 1] = adap->read_byte(); + } else { + adap->read_byte(); /* dummy read */ + } + } + return (i - 1); +} + +static int i2c_pxa_sendbytes(struct i2c_adapter *i2c_adap, const char *buf, + int count, int last) +{ + + struct i2c_algo_pxa_data *adap = i2c_adap->algo_data; + int wrcount, timeout; + + for (wrcount=0; wrcountwrite_byte(buf[wrcount]); + if ((wrcount==(count-1)) && last) { + adap->transfer( last, I2C_TRANSMIT, 0); + }else{ + adap->transfer( 0, I2C_TRANSMIT, 1); + } + + timeout = adap->wait_for_interrupt(I2C_TRANSMIT); + +#ifdef DEBUG + if (timeout==BUS_ERROR) { + printk(KERN_INFO "i2c_pxa_sendbytes: bus error -> forcing reset.\n"); + adap->reset(); + return I2C_RETRY; + } else +#endif + if (timeout == -ERESTARTSYS) { + adap->abort(); + return timeout; + } else + if (timeout) { +#ifdef DEBUG + printk(KERN_INFO "i2c_pxa_sendbytes: timeout -> forcing reset\n"); +#endif + adap->reset(); + return I2C_RETRY; + } + } + return (wrcount); +} + + +static inline int i2c_pxa_set_ctrl_byte(struct i2c_algo_pxa_data * adap, struct i2c_msg *msg) +{ + u16 flags = msg->flags; + u8 addr; + addr = (u8) ( (0x7f & msg->addr) << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + adap->write_byte(addr); + return 0; +} + +static int i2c_pxa_do_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct i2c_algo_pxa_data * adap; + struct i2c_msg *pmsg=NULL; + int i; + int ret=0, timeout; + + adap = i2c_adap->algo_data; + + timeout = adap->wait_bus_not_busy(); + + if (timeout) { + return I2C_RETRY; + } + + for (i = 0;ret >= 0 && i < num; i++) { + int last = i + 1 == num; + pmsg = &msgs[i]; + + ret = i2c_pxa_set_ctrl_byte(adap,pmsg); + + /* Send START */ + if (i == 0) { + adap->start(); + }else{ + adap->repeat_start(); + } + + adap->transfer(0, I2C_TRANSMIT, 0); + + /* Wait for ITE (transmit empty) */ + timeout = adap->wait_for_interrupt(I2C_TRANSMIT); + +#ifdef DEBUG + /* Check for ACK (bus error) */ + if (timeout==BUS_ERROR){ + printk(KERN_INFO "i2c_pxa_do_xfer: bus error -> forcing reset\n"); + adap->reset(); + return I2C_RETRY; + } else +#endif + if (timeout == -ERESTARTSYS) { + adap->abort(); + return timeout; + } else + if (timeout) { +#ifdef DEBUG + printk(KERN_INFO "i2c_pxa_do_xfer: timeout -> forcing reset\n"); +#endif + adap->reset(); + return I2C_RETRY; + } +/* FIXME: handle arbitration... */ +#if 0 + /* Check for bus arbitration loss */ + if (adap->arbitration_loss()){ + printk("Arbitration loss detected \n"); + adap->reset(); + return I2C_RETRY; + } +#endif + + /* Read */ + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer*/ + ret = i2c_pxa_readbytes(i2c_adap, pmsg->buf, pmsg->len, last); +#if DEBUG > 2 + if (ret != pmsg->len) { + printk(KERN_INFO"i2c_pxa_do_xfer: read %d/%d bytes.\n", + ret, pmsg->len); + } else { + printk(KERN_INFO"i2c_pxa_do_xfer: read %d bytes.\n",ret); + } +#endif + } else { /* Write */ + ret = i2c_pxa_sendbytes(i2c_adap, pmsg->buf, pmsg->len, last); +#if DEBUG > 2 + if (ret != pmsg->len) { + printk(KERN_INFO"i2c_pxa_do_xfer: wrote %d/%d bytes.\n", + ret, pmsg->len); + } else { + printk(KERN_INFO"i2c_pxa_do_xfer: wrote %d bytes.\n",ret); + } +#endif + } + } + + if (ret<0){ + return ret; + }else{ + return i; + } +} + +static int i2c_pxa_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + int retval = i2c_pxa_valid_messages( msgs, num); + if( retval > 0) + { + int i; + for (i=i2c_adap->retries; i>=0; i--){ + int retval = i2c_pxa_do_xfer(i2c_adap,msgs,num); + if (retval!=I2C_RETRY){ + return retval; + } + if( i2c_debug)printk(KERN_INFO"Retrying transmission \n"); + udelay(100); + } + if( i2c_debug)printk(KERN_INFO"Retried %i times\n",i2c_adap->retries); + return -EREMOTEIO; + + } + return retval; +} + +static u32 i2c_pxa_functionality(struct i2c_adapter * adapter) +{ + /* Emulate the SMBUS functions */ + return I2C_FUNC_SMBUS_EMUL; +} + +struct i2c_algorithm i2c_pxa_algorithm = { + name: "PXA I2C Algorithm", + id: I2C_ALGO_PXA, + master_xfer: i2c_pxa_xfer, + smbus_xfer: NULL, + slave_send: NULL, + slave_recv: NULL, + algo_control: NULL, + functionality: i2c_pxa_functionality, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pxa_add_bus(struct i2c_adapter *i2c_adap) +{ + struct i2c_algo_pxa_data *adap = i2c_adap->algo_data; + + printk(KERN_INFO"I2C: Adding %s.\n", i2c_adap->dev.name); + + i2c_adap->algo = &i2c_pxa_algorithm; + + MOD_INC_USE_COUNT; + + /* register new adapter to i2c module... */ + i2c_add_adapter(i2c_adap); + + adap->reset(); + + /* scan bus */ + if (pxa_scan) { + int i; + printk(KERN_INFO "I2C: Scanning bus "); + for (i = 0x02; i < 0xff; i+=2) { + if( i==(I2C_PXA_SLAVE_ADDR<<1)) continue; + + if (adap->wait_bus_not_busy()) { + printk(KERN_INFO "I2C: scanning bus %s - TIMEOUT.\n", + i2c_adap->dev.name); + return -EIO; + } + adap->write_byte(i); + adap->start(); + adap->transfer(0, I2C_TRANSMIT, 0); + + if ((adap->wait_for_interrupt(I2C_TRANSMIT) != BUS_ERROR)) { + printk("(%02x)",i>>1); + adap->abort(); + } else { +// printk("."); + adap->stop(); + } + udelay(adap->udelay); + } + printk("\n"); + } + return 0; +} + +int i2c_pxa_del_bus(struct i2c_adapter *i2c_adap) +{ + int res; + if ((res = i2c_del_adapter(i2c_adap)) < 0) + return res; + + MOD_DEC_USE_COUNT; + + printk(KERN_INFO "I2C: Removing %s.\n", i2c_adap->dev.name); + + return 0; +} + +static int __init i2c_algo_pxa_init (void) +{ + printk(KERN_INFO "I2C: PXA algorithm module loaded.\n"); + return 0; +} + +EXPORT_SYMBOL(i2c_pxa_add_bus); +EXPORT_SYMBOL(i2c_pxa_del_bus); + +MODULE_PARM(pxa_scan, "i"); +MODULE_PARM_DESC(pxa_scan, "Scan for active chips on the bus"); + +MODULE_AUTHOR("Intrinsyc Software Inc."); +MODULE_LICENSE("GPL"); + +module_init(i2c_algo_pxa_init); --- linux-2.6.5/drivers/i2c/algos/Makefile~heh 2004-04-03 22:37:37.000000000 -0500 +++ linux-2.6.5/drivers/i2c/algos/Makefile 2004-04-30 20:57:36.000000000 -0400 @@ -4,6 +4,7 @@ obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o +obj-$(CONFIG_I2C_ALGOPXA) += i2c-algo-pxa.o obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o ifeq ($(CONFIG_I2C_DEBUG_ALGO),y) --- linux-2.6.5/Makefile~heh 2004-04-03 22:37:36.000000000 -0500 +++ linux-2.6.5/Makefile 2004-04-30 20:57:58.000000000 -0400 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 5 -EXTRAVERSION = +EXTRAVERSION = -gnalm1 NAME=Zonked Quokka # *DOCUMENTATION*