diff options
Diffstat (limited to 'packages/linux/gumstix-2.6.5-gnalm1-gum0/linux-2.6.5-gnalm1.patch')
-rw-r--r-- | packages/linux/gumstix-2.6.5-gnalm1-gum0/linux-2.6.5-gnalm1.patch | 20991 |
1 files changed, 20991 insertions, 0 deletions
diff --git a/packages/linux/gumstix-2.6.5-gnalm1-gum0/linux-2.6.5-gnalm1.patch b/packages/linux/gumstix-2.6.5-gnalm1-gum0/linux-2.6.5-gnalm1.patch index e69de29bb2..4da2b88285 100644 --- a/packages/linux/gumstix-2.6.5-gnalm1-gum0/linux-2.6.5-gnalm1.patch +++ b/packages/linux/gumstix-2.6.5-gnalm1-gum0/linux-2.6.5-gnalm1.patch @@ -0,0 +1,20991 @@ +--- 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 <linux/sysrq.h> ++ ++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 <linux/mmc/mmc.h> ++ ++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 <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/device.h> ++ ++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 <linux/mmc/mmc.h> ++ ++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 <tolkien@mizi.com> ++ * 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 <Complex> 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 <linux/l3/l3.h> ++ ++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 <linux/types.h> ++#include <linux/list.h> ++ ++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 <nico@cam.org> ++ * ++ * 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 <asm/hardware/serial_amba.h> ++#include <asm/hardware/amba_serial.h> + + .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 <mail@peter-teichmann.de> ++ ++ 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 <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/signal.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++ ++#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 <mail@peter-teichmann.de>"); ++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 <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/time.h> ++#include <linux/rtc.h> ++#include <linux/poll.h> ++#include <linux/proc_fs.h> ++#include <linux/miscdevice.h> ++#include <linux/spinlock.h> ++#include <linux/device.h> ++ ++#include <asm/rtc.h> ++#include <asm/semaphore.h> ++ ++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 <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/cpufreq.h> ++ ++#include <asm/hardware.h> ++ ++#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<NUM_RUN_FREQS;i++) { ++ pxa255_run_freq_table[i].frequency = pxa255_run_freqs[i].khz; ++ pxa255_run_freq_table[i].index = i; ++ } ++ pxa255_run_freq_table[i].frequency = CPUFREQ_TABLE_END; ++ /* Generate the turbo cpufreq_frequency_table struct */ ++ for(i=0;i<NUM_TURBO_FREQS;i++) { ++ pxa255_turbo_freq_table[i].frequency = pxa255_turbo_freqs[i].khz; ++ pxa255_turbo_freq_table[i].index = i; ++ } ++ pxa255_turbo_freq_table[i].frequency = CPUFREQ_TABLE_END; ++ ++ set_cpus_allowed(current, cpus_allowed); ++ printk(KERN_INFO "PXA CPU frequency change support initialized\n"); ++ ++ return 0; ++} ++ ++static struct cpufreq_driver pxa_cpufreq_driver = { ++ .verify = pxa_verify_policy, ++ .target = pxa_set_target, ++ .init = pxa_cpufreq_init, ++ .name = "PXA25x", ++}; ++ ++static int __init pxa_cpu_init(void) ++{ ++ return cpufreq_register_driver(&pxa_cpufreq_driver); ++} ++ ++static void __exit pxa_cpu_exit(void) ++{ ++ cpufreq_unregister_driver(&pxa_cpufreq_driver); ++} ++ ++ ++MODULE_AUTHOR ("Intrinsyc Software Inc."); ++MODULE_DESCRIPTION ("CPU frequency changing driver for the PXA architecture"); ++MODULE_LICENSE("GPL"); ++module_init(pxa_cpu_init); ++module_exit(pxa_cpu_exit); ++ +--- linux-2.6.5/arch/arm/boot/compressed/misc.c~heh 2004-04-03 22:37:37.000000000 -0500 ++++ linux-2.6.5/arch/arm/boot/compressed/misc.c 2004-04-30 20:57:36.000000000 -0400 +@@ -113,7 +113,7 @@ + * gzip delarations + */ + #define OF(args) args +-#define STATIC static ++#define STATIC + + typedef unsigned char uch; + typedef unsigned short ush; +@@ -122,12 +122,12 @@ + #define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +-static uch *inbuf; /* input buffer */ +-static uch window[WSIZE]; /* Sliding window buffer */ ++unsigned char *inbuf; /* input buffer */ ++unsigned char window[WSIZE]; /* Sliding window buffer */ + +-static unsigned insize; /* valid bytes in inbuf */ +-static unsigned inptr; /* index of next byte to be processed in inbuf */ +-static unsigned outcnt; /* bytes in output buffer */ ++unsigned int insize; /* valid bytes in inbuf */ ++unsigned int inptr; /* index of next byte to be processed in inbuf */ ++unsigned int outcnt; /* bytes in output buffer */ + + /* gzip flag byte */ + #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +@@ -166,9 +166,9 @@ + extern char input_data[]; + extern char input_data_end[]; + +-static uch *output_data; +-static ulg output_ptr; +-static ulg bytes_out; ++unsigned char *output_data; ++unsigned long output_ptr; ++unsigned long bytes_out; + + static void *malloc(int size); + static void free(void *where); +@@ -179,8 +179,8 @@ + static void puts(const char *); + + extern int end; +-static ulg free_mem_ptr; +-static ulg free_mem_ptr_end; ++unsigned long free_mem_ptr; ++unsigned long free_mem_ptr_end; + + #define HEAP_SIZE 0x2000 + +--- linux-2.6.5/arch/arm/mm/ioremap.c~heh 2004-04-03 22:37:36.000000000 -0500 ++++ linux-2.6.5/arch/arm/mm/ioremap.c 2004-04-30 20:57:36.000000000 -0400 +@@ -13,15 +13,18 @@ + * virtual space. One should *only* use readl, writel, memcpy_toio and + * so on with such remapped areas. + * +- * Because the ARM only has a 32-bit address space we can't address the +- * whole of the (physical) PCI space at once. PCI huge-mode addressing +- * allows us to circumvent this restriction by splitting PCI space into +- * two 2GB chunks and mapping only one at a time into processor memory. +- * We use MMU protection domains to trap any attempt to access the bank +- * that is not currently mapped. (This isn't fully implemented yet.) ++ * ioremap support tweaked to allow support for large page mappings. We ++ * have several issues that needs to be resolved first however: ++ * ++ * 1. We need set_pte, or something like set_pte to understand large ++ * page mappings. ++ * ++ * 2. we need the unmap_* functions to likewise understand large page ++ * mappings. + */ + #include <linux/errno.h> + #include <linux/mm.h> ++#include <linux/slab.h> + #include <linux/vmalloc.h> + + #include <asm/page.h> +@@ -29,31 +32,162 @@ + #include <asm/io.h> + #include <asm/tlbflush.h> + +-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 <linux/spinlock.h> ++ ++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 <asm/dma.h> /* 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 <linux/module.h> ++#include <linux/pci.h> ++#include <linux/errno.h> ++#include <linux/delay.h> // for the massive_attack hack 28Feb01ww ++#include <linux/usb_ch9.h> ++ ++#include <asm/dma.h> ++ ++#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 <linux/module.h> ++#include <linux/config.h> ++#include <linux/miscdevice.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/cache.h> ++#include <linux/poll.h> ++#include <linux/circ_buf.h> ++#include <linux/timer.h> ++#include <linux/usb_ch9.h> ++ ++#include <asm/io.h> ++#include <asm/semaphore.h> ++#include <asm/page.h> ++#include <asm/mach-types.h> ++ ++#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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/skbuff.h> ++#include <linux/random.h> ++#include <linux/usb_ch9.h> ++ ++#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 <linux/module.h> ++#include <linux/pci.h> ++#include <linux/errno.h> ++#include <linux/usb_ch9.h> ++ ++#include <asm/byteorder.h> ++#include <asm/dma.h> ++ ++#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 <linux/module.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++ ++#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 <linux/list.h> ++ ++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 <linux/module.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/usb_ch9.h> ++#include <linux/init.h> ++#include <linux/proc_fs.h> ++#include <linux/spinlock.h> ++#include <linux/device.h> ++ ++#include <asm/mach-types.h> ++#include <asm/io.h> ++#include <asm/dma.h> ++#include <asm/irq.h> ++ ++#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 <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/usb_ch9.h> ++#include <linux/gfp.h> ++#include <linux/init.h> ++ ++#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 <linux/module.h> ++#include <linux/slab.h> ++#include <linux/errno.h> ++#include <linux/spinlock.h> ++#include <linux/string.h> ++#include <linux/usb_ch9.h> ++ ++#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 <linux/module.h> ++#include <linux/errno.h> ++#include <linux/usb.h> ++ ++#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 <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/errno.h> ++#include <linux/gfp.h> ++#include <linux/module.h> ++#include <linux/delay.h> ++ ++#include <asm/ptrace.h> ++#include <asm/domain.h> ++#include <asm/hardware.h> ++ ++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 <linux/module.h> ++#include <linux/blkdev.h> ++ ++#include <linux/mmc/card.h> ++#include <linux/mmc/host.h> ++#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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/ioport.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/blkdev.h> ++#include <linux/dma-mapping.h> ++#include <linux/mmc/host.h> ++#include <linux/mmc/protocol.h> ++ ++#include <asm/dma.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/sizes.h> ++ ++#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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/device.h> ++ ++#include <linux/mmc/card.h> ++#include <linux/mmc/host.h> ++ ++#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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/ioport.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/blkdev.h> ++#include <linux/delay.h> ++#include <linux/mmc/host.h> ++#include <linux/mmc/protocol.h> ++ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/hardware/amba.h> ++ ++#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 <linux/moduleparam.h> ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#include <linux/sched.h> ++#include <linux/kernel.h> /* printk() */ ++#include <linux/fs.h> /* everything... */ ++#include <linux/errno.h> /* error codes */ ++#include <linux/hdreg.h> /* HDIO_GETGEO */ ++#include <linux/kdev_t.h> ++#include <linux/blkdev.h> ++#include <linux/devfs_fs_kernel.h> ++ ++#include <linux/mmc/card.h> ++#include <linux/mmc/protocol.h> ++ ++#include <asm/system.h> ++#include <asm/uaccess.h> ++ ++#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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/completion.h> ++#include <linux/device.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++ ++#include <linux/mmc/card.h> ++#include <linux/mmc/host.h> ++#include <linux/mmc/protocol.h> ++ ++#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 <linux/ioctl.h> + #include <asm/io.h> + #include <asm/system.h> ++#include <asm/segment.h> + #include <stdarg.h> + #include <linux/delay.h> + #include <linux/init.h> +@@ -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 <linux/module.h> +@@ -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 <rkaiser@sysgo.de>"); + 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 <linux/init.h> + #include <linux/errno.h> + #include <linux/slab.h> ++#include <linux/device.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -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 <pcmcia/ds.h> + + #include <linux/mtd/map.h> +-#include <linux/mtd/mtd.h> + +-#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 <spse@secret.org.uk>"); +--- 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 <linux/mtd/map.h> + #include <linux/mtd/partitions.h> + ++#include <asm/io.h> ++ + 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 <file:Documentation/modules.txt>. ++ + 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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/serio.h> ++ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/hardware.h> ++ ++ ++ ++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 <jsimmons@transvirtual.com>. ++ */ ++ ++#include <linux/slab.h> ++#include <linux/poll.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/config.h> ++#include <linux/smp_lock.h> ++#include <linux/random.h> ++#include <linux/time.h> ++#include <linux/list.h> ++#include <linux/miscdevice.h> ++ ++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 <rmk@arm.linux.org.uk>"); ++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 <nico@cam.org> ++ * - 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 <cih@coventive.com> ++ * - Modify the bug setups RTC clock. ++ * ++ * 0.02 2001-02-27 Nils Faerber <nils@@kernelconcepts.de> ++ * - removed mktime(), added alarm irq clear ++ * ++ * 0.01 2000-10-01 Nils Faerber <nils@@kernelconcepts.de> ++ * - initial release ++ */ ++ ++#include <linux/module.h> ++#include <linux/fs.h> ++#include <linux/miscdevice.h> ++#include <linux/string.h> ++#include <linux/init.h> ++#include <linux/poll.h> ++#include <linux/proc_fs.h> ++#include <linux/interrupt.h> ++#include <linux/rtc.h> ++ ++#include <asm/bitops.h> ++#include <asm/hardware.h> ++#include <asm/irq.h> ++#include <asm/rtc.h> ++ ++#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 <nils@@kernelconcepts.de>"); ++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 <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/proc_fs.h> ++#include <linux/kmod.h> ++#include <linux/init.h> ++#include <linux/l3/l3.h> ++ ++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 <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/sched.h> ++#include <linux/l3/l3.h> ++#include <linux/l3/algo-bit.h> ++ ++#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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/ioport.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/i2c.h> ++#include <linux/i2c-algo-bit.h> ++#include <linux/l3/algo-bit.h> ++ ++#include <asm/system.h> ++#include <asm/hardware.h> ++#include <asm/mach-types.h> ++#include <asm/arch/assabet.h> ++ ++#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 <linux/switches.h> ++ ++#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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/proc_fs.h> ++#include <linux/device.h> ++ ++#include <asm/dma.h> ++ ++#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 <rmk@arm.linux.org.uk>"); ++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 <jeffs@accelent.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ * ++ * 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 <linux/module.h> ++#include <linux/types.h> ++#include <linux/ac97_codec.h> ++ ++#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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/smp.h> ++#include <linux/device.h> ++ ++#include <asm/dma.h> ++#include <asm/system.h> ++ ++#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 <rmk@arm.linux.org.uk>"); ++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 <jco@ict.es> : ++ * ++ * 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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/smp.h> ++#include <linux/smp_lock.h> ++#include <linux/sched.h> ++#include <linux/completion.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/input.h> ++#include <linux/device.h> ++#include <linux/slab.h> ++ ++#include <asm/dma.h> ++#include <asm/semaphore.h> ++ ++#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 <rmk@arm.linux.org.uk>"); ++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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/device.h> ++ ++#include <asm/dma.h> ++#include <asm/hardware.h> ++#include <asm/irq.h> ++ ++#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 <rmk@arm.linux.org.uk>"); ++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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/errno.h> ++#include <linux/device.h> ++ ++#include <asm/dma.h> ++#include <asm/hardware.h> ++#include <asm/irq.h> ++#include <asm/mach-types.h> ++ ++#ifdef CONFIG_SA1100_ASSABET ++#include <asm/arch/assabet.h> ++#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 <linux/config.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/kernel.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/mm.h> ++#include <linux/poll.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/wait.h> ++ ++#include <asm/uaccess.h> ++ ++#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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/sound.h> ++#include <linux/soundcard.h> ++#include <linux/list.h> ++#include <linux/device.h> ++ ++#include <asm/dma.h> ++#include <asm/hardware.h> ++#include <asm/semaphore.h> ++#include <asm/uaccess.h> ++ ++#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 <rmk@arm.linux.org.uk>"); ++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 <linux/module.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++#include <linux/slab.h> ++#include <linux/device.h> ++ ++#include <asm/dma.h> ++#include <asm/hardware.h> ++#include <asm/mach-types.h> ++#include <asm/system.h> ++ ++#include <asm/arch/assabet.h> ++ ++#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 <rmk@arm.linux.org.uk>"); ++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 <linux/completion.h> ++#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 <linux/config.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++ ++#include <asm/hardware.h> ++#include <asm/mach-types.h> ++#include <asm/irq.h> ++ ++#include <asm/arch/assabet.h> ++#include <asm/arch/neponset.h> ++#include <asm/arch/badge4.h> ++ ++#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 <linux/kernel.h> ++#include <linux/module.h> ++ ++#include <linux/i2c.h> ++#include <linux/i2c-id.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/sched.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++ ++#include <asm/hardware.h> ++#include <asm/irq.h> ++#include <asm/arch/irqs.h> /* for IRQ_I2C */ ++ ++#include <linux/i2c-pxa.h> ++ ++/* ++ * 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 <linux/kernel.h> ++#include <linux/module.h> ++ ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/i2c.h> /* struct i2c_msg and others */ ++#include <linux/i2c-id.h> ++ ++#include <linux/i2c-pxa.h> ++ ++/* ++ * 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;i<num;i++){ ++ if (&msgs[i]==NULL){ ++ if( i2c_debug) printk(KERN_INFO "Msgs is NULL\n"); ++ return -EINVAL; ++ } else { ++ if (msgs[i].buf == NULL){ ++ if( i2c_debug)printk(KERN_INFO "Length is less than zero"); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ return 1; ++} ++ ++static int i2c_pxa_readbytes(struct i2c_adapter *i2c_adap, char *buf, ++ int count, int last) ++{ ++ ++ int i, timeout=0; ++ struct i2c_algo_pxa_data *adap = i2c_adap->algo_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; wrcount<count; ++wrcount) { ++ ++ adap->write_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* |