diff options
author | Petr Štetiar <ynezz@true.cz> | 2010-10-03 14:01:57 +0200 |
---|---|---|
committer | Eric Bénard <eric@eukrea.com> | 2010-10-04 21:57:25 +0200 |
commit | 88ff1fbba53e8a1226ada1140b800b4ffb26a717 (patch) | |
tree | 3b950b6649db8708e6848c69b0aaee8732aa50f2 /recipes/linux/linux-2.6.34 | |
parent | 5786f08ba6066e9af60104c5d5606c106e8df7d2 (diff) | |
download | openembedded-88ff1fbba53e8a1226ada1140b800b4ffb26a717.tar.gz |
linux-2.6.34: add support for ts72xx and make it default kernel
Signed-off-by: Petr Štetiar <ynezz@true.cz>
Signed-off-by: Eric Bénard <eric@eukrea.com>
Diffstat (limited to 'recipes/linux/linux-2.6.34')
19 files changed, 11139 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.34/ts72xx/0001-ts72xx_base.patch b/recipes/linux/linux-2.6.34/ts72xx/0001-ts72xx_base.patch new file mode 100644 index 0000000000..247601b527 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0001-ts72xx_base.patch @@ -0,0 +1,445 @@ +From 9a60df856b54ba2f09dc0bbe4229627372f0a122 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Thu, 10 Jun 2010 10:43:24 +0200 +Subject: [PATCH 01/18] ts72xx_base + +- patch: allow to force nF bit in control reg +- register pwm1 +--- + arch/arm/Kconfig | 1 + + arch/arm/mach-ep93xx/Kconfig | 9 ++ + arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 8 ++ + arch/arm/mach-ep93xx/include/mach/memory.h | 28 +++++ + arch/arm/mach-ep93xx/include/mach/ts72xx.h | 147 ++++++++++++++++++++--- + arch/arm/mach-ep93xx/ts72xx.c | 40 ++++++- + arch/arm/mm/proc-arm920.S | 5 +- + 7 files changed, 218 insertions(+), 20 deletions(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 92622eb..ab19836 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -321,6 +321,7 @@ config ARCH_EP93XX + select CPU_ARM920T + select ARM_AMBA + select ARM_VIC ++ select ARCH_SPARSEMEM_ENABLE + select GENERIC_GPIO + select HAVE_CLK + select COMMON_CLKDEV +diff --git a/arch/arm/mach-ep93xx/Kconfig b/arch/arm/mach-ep93xx/Kconfig +index 3a08b18..b6be37e 100644 +--- a/arch/arm/mach-ep93xx/Kconfig ++++ b/arch/arm/mach-ep93xx/Kconfig +@@ -7,6 +7,15 @@ config CRUNCH + help + Enable kernel support for MaverickCrunch. + ++config CR1_NFBIT ++ bool "Turn on nF bit in ControlRegister 1" ++ help ++ Say 'Y' here to force the nF bit on. Usually this is set ++ by the bootrom. If it is not set, then the CPU core will ++ run from HCLK instead of FCLK, and performance will suffer. ++ If you see BogoMIPS of about 1/4 of your CPU clock, try ++ turning this on; your performance should double. ++ + comment "EP93xx Platforms" + + choice +diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +index 93e2ecc..f100f7f 100644 +--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h ++++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +@@ -83,6 +83,7 @@ + + #define EP93XX_BOOT_ROM_BASE EP93XX_AHB_IOMEM(0x00090000) + ++#define EP93XX_IDE_PHYS_BASE EP93XX_AHB_PHYS(0x000a0000) + #define EP93XX_IDE_BASE EP93XX_AHB_IOMEM(0x000a0000) + + #define EP93XX_VIC1_BASE EP93XX_AHB_IOMEM(0x000b0000) +@@ -96,16 +97,22 @@ + #define EP93XX_I2S_BASE EP93XX_APB_IOMEM(0x00020000) + + #define EP93XX_SECURITY_BASE EP93XX_APB_IOMEM(0x00030000) ++#define EP93XX_SECURITY_REG(x) (EP93XX_SECURITY_BASE + (x)) ++#define EP93XX_SECURITY_UNIQID EP93XX_SECURITY_REG(0x2440) + + #define EP93XX_GPIO_BASE EP93XX_APB_IOMEM(0x00040000) + #define EP93XX_GPIO_REG(x) (EP93XX_GPIO_BASE + (x)) + #define EP93XX_GPIO_F_INT_STATUS EP93XX_GPIO_REG(0x5c) ++#define EP93XX_GPIO_F_INT_DEBOUNCE EP93XX_GPIO_REG(0x64) + #define EP93XX_GPIO_A_INT_STATUS EP93XX_GPIO_REG(0xa0) ++#define EP93XX_GPIO_A_INT_DEBOUNCE EP93XX_GPIO_REG(0xa8) + #define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc) ++#define EP93XX_GPIO_B_INT_DEBOUNCE EP93XX_GPIO_REG(0xc4) + #define EP93XX_GPIO_EEDRIVE EP93XX_GPIO_REG(0xc8) + + #define EP93XX_AAC_BASE EP93XX_APB_IOMEM(0x00080000) + ++#define EP93XX_SPI_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000a0000) + #define EP93XX_SPI_BASE EP93XX_APB_IOMEM(0x000a0000) + + #define EP93XX_IRDA_BASE EP93XX_APB_IOMEM(0x000b0000) +@@ -209,6 +216,7 @@ + #define EP93XX_SYSCON_SYSCFG_LEECLK (1<<3) + #define EP93XX_SYSCON_SYSCFG_LCSN2 (1<<1) + #define EP93XX_SYSCON_SYSCFG_LCSN1 (1<<0) ++#define EP93XX_SYSCON_CHIPID EP93XX_SYSCON_REG(0x94) + #define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0) + + #define EP93XX_WATCHDOG_BASE EP93XX_APB_IOMEM(0x00140000) +diff --git a/arch/arm/mach-ep93xx/include/mach/memory.h b/arch/arm/mach-ep93xx/include/mach/memory.h +index 554064e..78eaacf 100644 +--- a/arch/arm/mach-ep93xx/include/mach/memory.h ++++ b/arch/arm/mach-ep93xx/include/mach/memory.h +@@ -19,4 +19,32 @@ + #error "Kconfig bug: No EP93xx PHYS_OFFSET set" + #endif + ++/* ++ * Non-linear mapping like so: ++ * phys => virt ++ * 0x00000000 => 0xc0000000 ++ * 0x01000000 => 0xc1000000 ++ * 0x04000000 => 0xc4000000 ++ * 0x05000000 => 0xc5000000 ++ * 0xe0000000 => 0xc8000000 ++ * 0xe1000000 => 0xc9000000 ++ * 0xe4000000 => 0xcc000000 ++ * 0xe5000000 => 0xcd000000 ++ * ++ * As suggested here: http://marc.info/?l=linux-arm&m=122754446724900&w=2 ++ * ++ * Note that static inline functions won't work here because ++ * arch/arm/include/asm/memory.h uses "#ifndef __virt_to_phys" to check whether to ++ * use generic functions or not. ++ */ ++ ++#define __phys_to_virt(p) \ ++ (((p) & 0x07ffffff) | (((p) & 0xe0000000) ? 0x08000000 : 0) | PAGE_OFFSET) ++ ++#define __virt_to_phys(v) \ ++ (((v) & 0x07ffffff) | (((v) & 0x08000000) ? 0xe0000000 : 0 )) ++ ++#define SECTION_SIZE_BITS 24 ++#define MAX_PHYSMEM_BITS 32 ++ + #endif +diff --git a/arch/arm/mach-ep93xx/include/mach/ts72xx.h b/arch/arm/mach-ep93xx/include/mach/ts72xx.h +index 93107d8..83ed0e6 100644 +--- a/arch/arm/mach-ep93xx/include/mach/ts72xx.h ++++ b/arch/arm/mach-ep93xx/include/mach/ts72xx.h +@@ -8,38 +8,40 @@ + * virt phys size + * febff000 22000000 4K model number register + * febfe000 22400000 4K options register +- * febfd000 22800000 4K options register #2 ++ * febfd000 22800000 4K options register #2 (JP6 and TS-9420 flags) + * febfc000 [67]0000000 4K NAND data register + * febfb000 [67]0400000 4K NAND control register + * febfa000 [67]0800000 4K NAND busy register + * febf9000 10800000 4K TS-5620 RTC index register + * febf8000 11700000 4K TS-5620 RTC data register ++ * febf7000 23400000 4K PLD version (3 bits) ++ * febf6000 22c00000 4K RS-485 control register ++ * febf5000 23000000 4K RS-485 mode register + */ + + #define TS72XX_MODEL_PHYS_BASE 0x22000000 + #define TS72XX_MODEL_VIRT_BASE 0xfebff000 + #define TS72XX_MODEL_SIZE 0x00001000 + +-#define TS72XX_MODEL_TS7200 0x00 +-#define TS72XX_MODEL_TS7250 0x01 +-#define TS72XX_MODEL_TS7260 0x02 ++#define TS7XXX_MODEL_TS7200 0x00 ++#define TS7XXX_MODEL_TS7250 0x01 ++#define TS7XXX_MODEL_TS7260 0x02 ++#define TS7XXX_MODEL_TS7300 0x03 ++#define TS7XXX_MODEL_TS7400 0x04 ++#define TS7XXX_MODEL_MASK 0x07 + + + #define TS72XX_OPTIONS_PHYS_BASE 0x22400000 + #define TS72XX_OPTIONS_VIRT_BASE 0xfebfe000 + #define TS72XX_OPTIONS_SIZE 0x00001000 +- +-#define TS72XX_OPTIONS_COM2_RS485 0x02 +-#define TS72XX_OPTIONS_MAX197 0x01 +- ++#define TS72XX_OPTIONS_COM2_RS485 0x02 ++#define TS72XX_OPTIONS_MAX197 0x01 + + #define TS72XX_OPTIONS2_PHYS_BASE 0x22800000 + #define TS72XX_OPTIONS2_VIRT_BASE 0xfebfd000 + #define TS72XX_OPTIONS2_SIZE 0x00001000 +- +-#define TS72XX_OPTIONS2_TS9420 0x04 +-#define TS72XX_OPTIONS2_TS9420_BOOT 0x02 +- ++#define TS72XX_OPTIONS2_TS9420 0x04 ++#define TS72XX_OPTIONS2_TS9420_BOOT 0x02 + + #define TS72XX_NAND1_DATA_PHYS_BASE 0x60000000 + #define TS72XX_NAND2_DATA_PHYS_BASE 0x70000000 +@@ -50,12 +52,13 @@ + #define TS72XX_NAND2_CONTROL_PHYS_BASE 0x70400000 + #define TS72XX_NAND_CONTROL_VIRT_BASE 0xfebfb000 + #define TS72XX_NAND_CONTROL_SIZE 0x00001000 ++#define TS72XX_NAND_CONTROL_OFFSET 0x00400000 + + #define TS72XX_NAND1_BUSY_PHYS_BASE 0x60800000 + #define TS72XX_NAND2_BUSY_PHYS_BASE 0x70800000 + #define TS72XX_NAND_BUSY_VIRT_BASE 0xfebfa000 + #define TS72XX_NAND_BUSY_SIZE 0x00001000 +- ++#define TS72XX_NAND_BUSY_OFFSET 0x00800000 + + #define TS72XX_RTC_INDEX_VIRT_BASE 0xfebf9000 + #define TS72XX_RTC_INDEX_PHYS_BASE 0x10800000 +@@ -68,32 +71,140 @@ + #define TS72XX_WDT_CONTROL_PHYS_BASE 0x23800000 + #define TS72XX_WDT_FEED_PHYS_BASE 0x23c00000 + ++#define TS72XX_PLD_VERSION_VIRT_BASE 0xfebf7000 ++#define TS72XX_PLD_VERSION_PHYS_BASE 0x23400000 ++#define TS72XX_PLD_VERSION_SIZE 0x00001000 ++ ++#define TS72XX_JUMPERS_MAX197_PHYS_BASE 0x10800000 // jumpers/max197 busy bit/COM1 dcd register (8-bit, read only) ++#define TS72XX_MAX197_SAMPLE_PHYS_BASE 0x10f00000 // max197 sample/control register (16-bit read/8-bit write) ++ ++/* ++ * RS485 option ++ */ ++#define TS72XX_RS485_CONTROL_VIRT_BASE 0xfebf6000 ++#define TS72XX_RS485_CONTROL_PHYS_BASE 0x22c00000 ++#define TS72XX_RS485_CONTROL_SIZE 0x00001000 ++ ++#define TS72XX_RS485_MODE_VIRT_BASE 0xfebf5000 ++#define TS72XX_RS485_MODE_PHYS_BASE 0x23000000 ++#define TS72XX_RS485_MODE_SIZE 0x00001000 ++ ++#define TS72XX_RS485_AUTO485FD 1 ++#define TS72XX_RS485_AUTO485HD 2 ++#define TS72XX_RS485_MODE_RS232 0x00 ++#define TS72XX_RS485_MODE_FD 0x01 ++#define TS72XX_RS485_MODE_9600_HD 0x04 ++#define TS72XX_RS485_MODE_19200_HD 0x05 ++#define TS72XX_RS485_MODE_57600_HD 0x06 ++#define TS72XX_RS485_MODE_115200_HD 0x07 ++ ++/* ++ * PC/104 8-bit & 16-bit bus ++ * ++ * virt phys size ++ * febf0000 11e00000 4K PC/104 8-bit I/O ++ * febef000 21e00000 4K PC/104 16-bit I/O ++ * fea00000 11a00000 1MB PC/104 8-bit memory ++ * fe900000 21a00000 1MB PC/104 16-bit memory ++ */ ++#define TS72XX_PC104_8BIT_IO_VIRT_BASE 0xfebf0000 ++#define TS72XX_PC104_8BIT_IO_PHYS_BASE 0x11e00000 ++#define TS72XX_PC104_8BIT_IO_SIZE 0x00001000 ++#define TS72XX_PC104_8BIT_MEM_VIRT_BASE 0xfea00000 ++#define TS72XX_PC104_8BIT_MEM_PHYS_BASE 0x11a00000 ++#define TS72XX_PC104_8BIT_MEM_SIZE 0x00100000 ++ ++#define TS72XX_PC104_16BIT_IO_VIRT_BASE 0xfebef000 ++#define TS72XX_PC104_16BIT_IO_PHYS_BASE 0x21e00000 ++#define TS72XX_PC104_16BIT_IO_SIZE 0x00001000 ++#define TS72XX_PC104_16BIT_MEM_VIRT_BASE 0xfe900000 ++#define TS72XX_PC104_16BIT_MEM_PHYS_BASE 0x21a00000 ++#define TS72XX_PC104_16BIT_MEM_SIZE 0x00100000 ++ ++/* ++ * TS7200 specific : CompactFlash memory map ++ * ++ * phys size description ++ * 11000000 7 CF registers (8-bit each), starting at 11000001 ++ * 10400006 2 CF aux registers (8-bit) ++ * 21000000 2 CF data register (16-bit) ++ */ ++#define TS7200_CF_CMD_PHYS_BASE 0x11000000 ++#define TS7200_CF_AUX_PHYS_BASE 0x10400006 ++#define TS7200_CF_DATA_PHYS_BASE 0x21000000 ++ ++/* ++ * TS7260 specific : SD card & Power Management ++ * ++ * phys size description ++ * 12000000 4K Power management register (8-bit) ++ * 13000000 4K SD card registers (4 x 8-bit) ++ */ ++#define TS7260_POWER_MANAGEMENT_PHYS_BASE 0x12000000 ++#define TS7260_PM_RS232_LEVEL_CONVERTER 0x01 ++#define TS7260_PM_USB 0x02 ++#define TS7260_PM_LCD 0x04 ++#define TS7260_PM_5V_SWITCHER 0x08 ++#define TS7260_PM_PC104_CLOCK 0x10 ++#define TS7260_PM_PC104_FAST_STROBES 0x20 ++#define TS7260_PM_TTL_UART_ENABLE 0x40 ++#define TS7260_PM_SCRATCH_BIT 0x80 ++ ++#define TS7260_SDCARD_PHYS_BASE 0x13000000 ++ + #ifndef __ASSEMBLY__ + + static inline int board_is_ts7200(void) + { +- return __raw_readb(TS72XX_MODEL_VIRT_BASE) == TS72XX_MODEL_TS7200; ++ return (__raw_readb(TS72XX_MODEL_VIRT_BASE) & ++ TS7XXX_MODEL_MASK) == TS7XXX_MODEL_TS7200; + } + + static inline int board_is_ts7250(void) + { +- return __raw_readb(TS72XX_MODEL_VIRT_BASE) == TS72XX_MODEL_TS7250; ++ return (__raw_readb(TS72XX_MODEL_VIRT_BASE) & ++ TS7XXX_MODEL_MASK) == TS7XXX_MODEL_TS7250; + } + + static inline int board_is_ts7260(void) + { +- return __raw_readb(TS72XX_MODEL_VIRT_BASE) == TS72XX_MODEL_TS7260; ++ return (__raw_readb(TS72XX_MODEL_VIRT_BASE) & ++ TS7XXX_MODEL_MASK) == TS7XXX_MODEL_TS7260; ++} ++ ++static inline int board_is_ts7400(void) ++{ ++ return (__raw_readb(TS72XX_MODEL_VIRT_BASE) & ++ TS7XXX_MODEL_MASK) == TS7XXX_MODEL_TS7400; + } + + static inline int is_max197_installed(void) + { + return !!(__raw_readb(TS72XX_OPTIONS_VIRT_BASE) & +- TS72XX_OPTIONS_MAX197); ++ TS72XX_OPTIONS_MAX197); + } + + static inline int is_ts9420_installed(void) + { + return !!(__raw_readb(TS72XX_OPTIONS2_VIRT_BASE) & +- TS72XX_OPTIONS2_TS9420); ++ TS72XX_OPTIONS2_TS9420); ++} ++ ++static inline int is_rs485_installed(void) ++{ ++ return !!(__raw_readb(TS72XX_OPTIONS_VIRT_BASE) & ++ TS72XX_OPTIONS_COM2_RS485); ++} ++ ++static inline int get_ts72xx_pld_version(void) ++{ ++ return (__raw_readb(TS72XX_PLD_VERSION_VIRT_BASE) & 0x7); + } ++ ++/* User jumper */ ++static inline int is_jp6_set(void) ++{ ++ return (__raw_readb(TS72XX_OPTIONS2_VIRT_BASE) & 0x1); ++} ++ + #endif +diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c +index fac1ec7..37325c4 100644 +--- a/arch/arm/mach-ep93xx/ts72xx.c ++++ b/arch/arm/mach-ep93xx/ts72xx.c +@@ -51,7 +51,36 @@ static struct map_desc ts72xx_io_desc[] __initdata = { + .pfn = __phys_to_pfn(TS72XX_RTC_DATA_PHYS_BASE), + .length = TS72XX_RTC_DATA_SIZE, + .type = MT_DEVICE, +- } ++ }, ++ /* Use this for debug only. Each device will map its own PC/104 address space */ ++ ///* PC/104 (8-bit) I/O bus */ ++ //{ ++ // .virtual = TS72XX_PC104_8BIT_IO_VIRT_BASE, ++ // .pfn = __phys_to_pfn(TS72XX_PC104_8BIT_IO_PHYS_BASE), ++ // .length = TS72XX_PC104_8BIT_IO_SIZE, ++ // .type = MT_DEVICE, ++ //}, ++ ///* PC/104 (16-bit) I/O bus */ ++ //{ ++ // .virtual = TS72XX_PC104_16BIT_IO_VIRT_BASE, ++ // .pfn = __phys_to_pfn(TS72XX_PC104_16BIT_IO_PHYS_BASE), ++ // .length = TS72XX_PC104_16BIT_IO_SIZE, ++ // .type = MT_DEVICE, ++ //}, ++ ///* PC/104 (8-bit) MEM bus */ ++ //{ ++ // .virtual = TS72XX_PC104_8BIT_MEM_VIRT_BASE, ++ // .pfn = __phys_to_pfn(TS72XX_PC104_8BIT_MEM_PHYS_BASE), ++ // .length = TS72XX_PC104_8BIT_MEM_SIZE, ++ // .type = MT_DEVICE, ++ //}, ++ ///* PC/104 (16-bit) MEM bus */ ++ //{ ++ // .virtual = TS72XX_PC104_16BIT_MEM_VIRT_BASE, ++ // .pfn = __phys_to_pfn(TS72XX_PC104_16BIT_MEM_PHYS_BASE), ++ // .length = TS72XX_PC104_16BIT_MEM_SIZE, ++ // .type = MT_DEVICE, ++ //} + }; + + static struct map_desc ts72xx_nand_io_desc[] __initdata = { +@@ -140,6 +169,9 @@ static void __init ts72xx_register_flash(void) + platform_device_register(&ts72xx_flash); + } + ++/************************************************************************* ++ * RTC ++ *************************************************************************/ + static unsigned char ts72xx_rtc_readbyte(unsigned long addr) + { + __raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE); +@@ -186,6 +218,9 @@ static struct platform_device ts72xx_wdt_device = { + .resource = ts72xx_wdt_resources, + }; + ++/************************************************************************* ++ * Ethernet ++ *************************************************************************/ + static struct ep93xx_eth_data ts72xx_eth_data = { + .phy_id = 1, + }; +@@ -198,6 +233,9 @@ static void __init ts72xx_init_machine(void) + platform_device_register(&ts72xx_wdt_device); + + ep93xx_register_eth(&ts72xx_eth_data, 1); ++ ++ /* PWM1 is DIO_6 on TS-72xx header */ ++ ep93xx_register_pwm(0, 1); + } + + MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC") +diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S +index 8be8199..a4f123d 100644 +--- a/arch/arm/mm/proc-arm920.S ++++ b/arch/arm/mm/proc-arm920.S +@@ -198,7 +198,7 @@ ENTRY(arm920_coherent_kern_range) + */ + ENTRY(arm920_coherent_user_range) + bic r0, r0, #CACHE_DLINESIZE - 1 +-1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry + mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry + add r0, r0, #CACHE_DLINESIZE + cmp r0, r1 +@@ -398,6 +398,9 @@ __arm920_setup: + mrc p15, 0, r0, c1, c0 @ get control register v4 + bic r0, r0, r5 + orr r0, r0, r6 ++#ifdef CONFIG_CR1_NFBIT ++ orr r0, r0, #0x40000000 @ set nF ++#endif + mov pc, lr + .size __arm920_setup, . - __arm920_setup + +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0002-ts72xx_force_machine-id.patch b/recipes/linux/linux-2.6.34/ts72xx/0002-ts72xx_force_machine-id.patch new file mode 100644 index 0000000000..e59bb4e122 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0002-ts72xx_force_machine-id.patch @@ -0,0 +1,45 @@ +From 453060da6afa4c32d2e2704e69ad32f200d4d240 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Thu, 10 Jun 2010 10:51:39 +0200 +Subject: [PATCH 02/18] ts72xx_force_machine-id + +--- + arch/arm/kernel/head.S | 3 +++ + arch/arm/mach-ep93xx/Kconfig | 7 +++++++ + 2 files changed, 10 insertions(+), 0 deletions(-) + +diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S +index eb62bf9..543eccf 100644 +--- a/arch/arm/kernel/head.S ++++ b/arch/arm/kernel/head.S +@@ -82,6 +82,9 @@ ENTRY(stext) + bl __lookup_processor_type @ r5=procinfo r9=cpuid + movs r10, r5 @ invalid processor (r5=0)? + beq __error_p @ yes, error 'p' ++#ifdef CONFIG_MACH_TS72XX_FORCE_MACHINEID ++ ldr r1, =0x2a1 ++#endif + bl __lookup_machine_type @ r5=machinfo + movs r8, r5 @ invalid machine (r5=0)? + beq __error_a @ yes, error 'a' +diff --git a/arch/arm/mach-ep93xx/Kconfig b/arch/arm/mach-ep93xx/Kconfig +index b6be37e..bd463a0 100644 +--- a/arch/arm/mach-ep93xx/Kconfig ++++ b/arch/arm/mach-ep93xx/Kconfig +@@ -205,6 +205,13 @@ config EP93XX_EARLY_UART3 + + endchoice + ++config MACH_TS72XX_FORCE_MACHINEID ++ bool "Force Machine ID" ++ depends on MACH_TS72XX ++ help ++ Say 'Y' here to force Machine ID to 0x2A1 (MACH_TYPE_TS72XX legacy value) ++ In early days Technologic Systems fixed the 0x163 value in redboot. ++ + endmenu + + endif +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0003-ep93xx_cpuinfo.patch b/recipes/linux/linux-2.6.34/ts72xx/0003-ep93xx_cpuinfo.patch new file mode 100644 index 0000000000..83519d6336 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0003-ep93xx_cpuinfo.patch @@ -0,0 +1,32 @@ +From cbb613e31ddb5f27e7bffa95be458c260fa4de11 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Thu, 10 Jun 2010 10:59:31 +0200 +Subject: [PATCH 03/18] ep93xx_cpuinfo + +--- + arch/arm/kernel/setup.c | 9 +++++++++ + 1 files changed, 9 insertions(+), 0 deletions(-) + +diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c +index c91c77b..867387c 100644 +--- a/arch/arm/kernel/setup.c ++++ b/arch/arm/kernel/setup.c +@@ -844,6 +844,15 @@ static int c_show(struct seq_file *m, void *v) + seq_puts(m, "\n"); + + seq_printf(m, "Hardware\t: %s\n", machine_name); ++ ++ #if defined(CONFIG_ARCH_EP93XX) ++ #include <mach/io.h> ++ #include <mach/ep93xx-regs.h> ++ system_rev = *((unsigned int *)EP93XX_SYSCON_CHIPID) >> 28; ++ system_serial_low = *((unsigned int *)EP93XX_SECURITY_UNIQID); ++ system_serial_high = 0; ++ #endif ++ + seq_printf(m, "Revision\t: %04x\n", system_rev); + seq_printf(m, "Serial\t\t: %08x%08x\n", + system_serial_high, system_serial_low); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0004-ep93xx_eth.patch b/recipes/linux/linux-2.6.34/ts72xx/0004-ep93xx_eth.patch new file mode 100644 index 0000000000..4e3caf4de2 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0004-ep93xx_eth.patch @@ -0,0 +1,551 @@ +From 9f020e91022926d1fe36b8a6e28b16bcca2d37e5 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Thu, 10 Jun 2010 13:34:14 +0200 +Subject: [PATCH 04/18] ep93xx_eth + +--- + drivers/net/arm/Kconfig | 1 + + drivers/net/arm/ep93xx_eth.c | 346 ++++++++++++++++++++++++++++++++++-------- + 2 files changed, 280 insertions(+), 67 deletions(-) + +diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig +index 39e1c0d..55c0dd4 100644 +--- a/drivers/net/arm/Kconfig ++++ b/drivers/net/arm/Kconfig +@@ -52,6 +52,7 @@ config EP93XX_ETH + tristate "EP93xx Ethernet support" + depends on ARM && ARCH_EP93XX + select MII ++ select PHYLIB + help + This is a driver for the ethernet hardware included in EP93xx CPUs. + Say Y if you are building a kernel for EP93xx based devices. +diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c +index cd17d09..d18824b 100644 +--- a/drivers/net/arm/ep93xx_eth.c ++++ b/drivers/net/arm/ep93xx_eth.c +@@ -2,6 +2,7 @@ + * EP93xx ethernet network device driver + * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> + * Dedicated to Marija Kulikova. ++ * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> + * + * 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 +@@ -16,6 +17,7 @@ + #include <linux/kernel.h> + #include <linux/netdevice.h> + #include <linux/mii.h> ++#include <linux/phy.h> + #include <linux/etherdevice.h> + #include <linux/ethtool.h> + #include <linux/init.h> +@@ -27,8 +29,8 @@ + + #include <mach/hardware.h> + +-#define DRV_MODULE_NAME "ep93xx-eth" +-#define DRV_MODULE_VERSION "0.1" ++#define DRV_NAME "ep93xx-eth" ++#define DRV_VERSION "0.13" + + #define RX_QUEUE_ENTRIES 64 + #define TX_QUEUE_ENTRIES 8 +@@ -40,6 +42,8 @@ + #define REG_RXCTL_DEFAULT 0x00073800 + #define REG_TXCTL 0x0004 + #define REG_TXCTL_ENABLE 0x00000001 ++#define REG_TESTCTL 0x0008 ++#define REG_TESTCTL_MFDX 0x00000040 + #define REG_MIICMD 0x0010 + #define REG_MIICMD_READ 0x00008000 + #define REG_MIICMD_WRITE 0x00004000 +@@ -48,6 +52,9 @@ + #define REG_MIISTS_BUSY 0x00000001 + #define REG_SELFCTL 0x0020 + #define REG_SELFCTL_RESET 0x00000001 ++#define REG_SELFCTL_MDCDIV_MSK 0x00007e00 ++#define REG_SELFCTL_MDCDIV_OFS 9 ++#define REG_SELFCTL_PSPRS 0x00000100 + #define REG_INTEN 0x0024 + #define REG_INTEN_TX 0x00000008 + #define REG_INTEN_RX 0x00000007 +@@ -177,8 +184,14 @@ struct ep93xx_priv + + struct net_device_stats stats; + +- struct mii_if_info mii; + u8 mdc_divisor; ++ int phy_supports_mfps:1; ++ ++ struct mii_bus mii_bus; ++ struct phy_device *phy_dev; ++ int speed; ++ int duplex; ++ int link; + }; + + #define rdb(ep, off) __raw_readb((ep)->base_addr + (off)) +@@ -188,46 +201,78 @@ struct ep93xx_priv + #define wrw(ep, off, val) __raw_writew((val), (ep)->base_addr + (off)) + #define wrl(ep, off, val) __raw_writel((val), (ep)->base_addr + (off)) + +-static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg) ++/* common MII transactions should take < 100 iterations */ ++#define EP93XX_PHY_TIMEOUT 2000 ++ ++static int ep93xx_mdio_wait(struct mii_bus *bus) + { +- struct ep93xx_priv *ep = netdev_priv(dev); +- int data; +- int i; ++ struct ep93xx_priv *ep = bus->priv; ++ unsigned int timeout = EP93XX_PHY_TIMEOUT; + +- wrl(ep, REG_MIICMD, REG_MIICMD_READ | (phy_id << 5) | reg); ++ while ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) ++ && timeout--) ++ cpu_relax(); + +- for (i = 0; i < 10; i++) { +- if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0) +- break; +- msleep(1); ++ if (timeout <= 0) { ++ dev_err(&bus->dev, "MII operation timed out\n"); ++ return -ETIMEDOUT; + } + +- if (i == 10) { +- pr_info("mdio read timed out\n"); +- data = 0xffff; +- } else { +- data = rdl(ep, REG_MIIDATA); +- } ++ return 0; ++} ++ ++static int ep93xx_mdio_read(struct mii_bus *bus, int mii_id, int reg) ++{ ++ struct ep93xx_priv *ep = bus->priv; ++ u32 selfctl; ++ u32 data; ++ ++ if (ep93xx_mdio_wait(bus) < 0) ++ return -ETIMEDOUT; ++ ++ selfctl = rdl(ep, REG_SELFCTL); + +- return data; ++ if (ep->phy_supports_mfps) ++ wrl(ep, REG_SELFCTL, selfctl | REG_SELFCTL_PSPRS); ++ else ++ wrl(ep, REG_SELFCTL, selfctl & ~REG_SELFCTL_PSPRS); ++ ++ wrl(ep, REG_MIICMD, REG_MIICMD_READ | (mii_id << 5) | reg); ++ ++ if (ep93xx_mdio_wait(bus) < 0) ++ return -ETIMEDOUT; ++ ++ data = rdl(ep, REG_MIIDATA); ++ ++ wrl(ep, REG_SELFCTL, selfctl); ++ ++ return data; + } + +-static void ep93xx_mdio_write(struct net_device *dev, int phy_id, int reg, int data) ++static int ep93xx_mdio_write(struct mii_bus *bus, int mii_id, int reg, u16 data) + { +- struct ep93xx_priv *ep = netdev_priv(dev); +- int i; ++ struct ep93xx_priv *ep = bus->priv; ++ u32 selfctl; ++ ++ if (ep93xx_mdio_wait(bus) < 0) ++ return -ETIMEDOUT; ++ ++ selfctl = rdl(ep, REG_SELFCTL); ++ ++ if (ep->phy_supports_mfps) ++ wrl(ep, REG_SELFCTL, selfctl | REG_SELFCTL_PSPRS); ++ else ++ wrl(ep, REG_SELFCTL, selfctl & ~REG_SELFCTL_PSPRS); + + wrl(ep, REG_MIIDATA, data); +- wrl(ep, REG_MIICMD, REG_MIICMD_WRITE | (phy_id << 5) | reg); ++ wrl(ep, REG_MIICMD, REG_MIICMD_WRITE | (mii_id << 5) | reg); + +- for (i = 0; i < 10; i++) { +- if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0) +- break; +- msleep(1); +- } ++ if (ep93xx_mdio_wait(bus) < 0) ++ return -ETIMEDOUT; + +- if (i == 10) +- pr_info("mdio write timed out\n"); ++ wrl(ep, REG_SELFCTL, selfctl); ++ ++ return 0; + } + + static struct net_device_stats *ep93xx_get_stats(struct net_device *dev) +@@ -559,6 +604,22 @@ err: + return 1; + } + ++static int ep93xx_mdio_reset(struct mii_bus *bus) ++{ ++ struct ep93xx_priv *ep = bus->priv; ++ ++ u32 selfctl = rdl(ep, REG_SELFCTL); ++ ++ selfctl &= ~(REG_SELFCTL_MDCDIV_MSK | REG_SELFCTL_PSPRS); ++ ++ selfctl |= (ep->mdc_divisor - 1) << REG_SELFCTL_MDCDIV_OFS; ++ selfctl |= REG_SELFCTL_PSPRS; ++ ++ wrl(ep, REG_SELFCTL, selfctl); ++ ++ return 0; ++} ++ + static int ep93xx_start_hw(struct net_device *dev) + { + struct ep93xx_priv *ep = netdev_priv(dev); +@@ -577,11 +638,8 @@ static int ep93xx_start_hw(struct net_device *dev) + return 1; + } + +- wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9)); +- +- /* Does the PHY support preamble suppress? */ +- if ((ep93xx_mdio_read(dev, ep->mii.phy_id, MII_BMSR) & 0x0040) != 0) +- wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9) | (1 << 8)); ++ /* The reset cleared REG_SELFCTL, so set the MDC divisor again */ ++ ep93xx_mdio_reset(&ep->mii_bus); + + /* Receive descriptor ring. */ + addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rdesc); +@@ -690,6 +748,8 @@ static int ep93xx_open(struct net_device *dev) + + wrl(ep, REG_GIINTMSK, REG_GIINTMSK_ENABLE); + ++ phy_start(ep->phy_dev); ++ + netif_start_queue(dev); + + return 0; +@@ -702,6 +762,9 @@ static int ep93xx_close(struct net_device *dev) + napi_disable(&ep->napi); + netif_stop_queue(dev); + ++ if (ep->phy_dev) ++ phy_stop(ep->phy_dev); ++ + wrl(ep, REG_GIINTMSK, 0); + free_irq(ep->irq, dev); + ep93xx_stop_hw(dev); +@@ -713,47 +776,44 @@ static int ep93xx_close(struct net_device *dev) + static int ep93xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) + { + struct ep93xx_priv *ep = netdev_priv(dev); +- struct mii_ioctl_data *data = if_mii(ifr); + +- return generic_mii_ioctl(&ep->mii, data, cmd, NULL); ++ return phy_mii_ioctl(ep->phy_dev, if_mii(ifr), cmd); + } + + static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) + { +- strcpy(info->driver, DRV_MODULE_NAME); +- strcpy(info->version, DRV_MODULE_VERSION); ++ strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); ++ strlcpy(info->version, DRV_VERSION, sizeof(info->version)); ++ strlcpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); + } + + static int ep93xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) + { + struct ep93xx_priv *ep = netdev_priv(dev); +- return mii_ethtool_gset(&ep->mii, cmd); ++ struct phy_device *phydev = ep->phy_dev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_gset(phydev, cmd); + } + + static int ep93xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) + { + struct ep93xx_priv *ep = netdev_priv(dev); +- return mii_ethtool_sset(&ep->mii, cmd); +-} ++ struct phy_device *phydev = ep->phy_dev; + +-static int ep93xx_nway_reset(struct net_device *dev) +-{ +- struct ep93xx_priv *ep = netdev_priv(dev); +- return mii_nway_restart(&ep->mii); +-} ++ if (!phydev) ++ return -ENODEV; + +-static u32 ep93xx_get_link(struct net_device *dev) +-{ +- struct ep93xx_priv *ep = netdev_priv(dev); +- return mii_link_ok(&ep->mii); ++ return phy_ethtool_sset(phydev, cmd); + } + + static const struct ethtool_ops ep93xx_ethtool_ops = { + .get_drvinfo = ep93xx_get_drvinfo, + .get_settings = ep93xx_get_settings, + .set_settings = ep93xx_set_settings, +- .nway_reset = ep93xx_nway_reset, +- .get_link = ep93xx_get_link, ++ .get_link = ethtool_op_get_link, + }; + + static const struct net_device_ops ep93xx_netdev_ops = { +@@ -815,6 +875,113 @@ static int ep93xx_eth_remove(struct platform_device *pdev) + return 0; + } + ++static void ep93xx_adjust_link(struct net_device *dev) ++{ ++ struct ep93xx_priv *ep = netdev_priv(dev); ++ struct phy_device *phydev = ep->phy_dev; ++ ++ int status_change = 0; ++ ++ if (phydev->link) { ++ if ((ep->speed != phydev->speed) || ++ (ep->duplex != phydev->duplex)) { ++ /* speed and/or duplex state changed */ ++ u32 testctl = rdl(ep, REG_TESTCTL); ++ ++ if (DUPLEX_FULL == phydev->duplex) ++ testctl |= REG_TESTCTL_MFDX; ++ else ++ testctl &= ~(REG_TESTCTL_MFDX); ++ ++ wrl(ep, REG_TESTCTL, testctl); ++ ++ ep->speed = phydev->speed; ++ ep->duplex = phydev->duplex; ++ status_change = 1; ++ } ++ } ++ ++ /* test for online/offline link transition */ ++ if (phydev->link != ep->link) { ++ if (phydev->link) /* link went online */ ++ netif_tx_schedule_all(dev); ++ else { /* link went offline */ ++ ep->speed = 0; ++ ep->duplex = -1; ++ } ++ ep->link = phydev->link; ++ ++ status_change = 1; ++ } ++ ++ if (status_change) ++ phy_print_status(phydev); ++} ++ ++static int ep93xx_mii_probe(struct net_device *dev, int phy_addr) ++{ ++ struct ep93xx_priv *ep = netdev_priv(dev); ++ struct phy_device *phydev = NULL; ++ int val; ++ ++ if (phy_addr >= 0 && phy_addr < PHY_MAX_ADDR) ++ phydev = ep->mii_bus.phy_map[phy_addr]; ++ ++ if (!phydev) { ++ pr_info("PHY not found at specified address," ++ " trying autodetection\n"); ++ ++ /* find the first phy */ ++ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { ++ if (ep->mii_bus.phy_map[phy_addr]) { ++ phydev = ep->mii_bus.phy_map[phy_addr]; ++ break; ++ } ++ } ++ } ++ ++ if (!phydev) { ++ pr_err("no PHY found\n"); ++ return -ENODEV; ++ } ++ ++ phydev = phy_connect(dev, dev_name(&phydev->dev), ++ ep93xx_adjust_link, 0, PHY_INTERFACE_MODE_MII); ++ ++ if (IS_ERR(phydev)) { ++ pr_err("Could not attach to PHY\n"); ++ return PTR_ERR(phydev); ++ } ++ ++ ep->phy_supports_mfps = 0; ++ ++ val = phy_read(phydev, MII_BMSR); ++ if (val < 0) { ++ pr_err("failed to read MII register\n"); ++ return val; ++ } ++ ++ if (val & 0x0040) { ++ pr_info("PHY supports MII frame preamble suppression\n"); ++ ep->phy_supports_mfps = 1; ++ } ++ ++ phydev->supported &= PHY_BASIC_FEATURES; ++ ++ phydev->advertising = phydev->supported; ++ ++ ep->link = 0; ++ ep->speed = 0; ++ ep->duplex = -1; ++ ep->phy_dev = phydev; ++ ++ pr_info("attached PHY driver [%s] " ++ "(mii_bus:phy_addr=%s, irq=%d)\n", ++ phydev->drv->name, dev_name(&phydev->dev), phydev->irq); ++ ++ return 0; ++} ++ + static int ep93xx_eth_probe(struct platform_device *pdev) + { + struct ep93xx_eth_data *data; +@@ -822,7 +989,7 @@ static int ep93xx_eth_probe(struct platform_device *pdev) + struct ep93xx_priv *ep; + struct resource *mem; + int irq; +- int err; ++ int err, i; + + if (pdev == NULL) + return -ENODEV; +@@ -849,24 +1016,43 @@ static int ep93xx_eth_probe(struct platform_device *pdev) + if (ep->res == NULL) { + dev_err(&pdev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; +- goto err_out; ++ goto err_out_request_mem_region; + } + + ep->base_addr = ioremap(mem->start, resource_size(mem)); + if (ep->base_addr == NULL) { + dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n"); + err = -EIO; +- goto err_out; ++ goto err_out_ioremap; + } + ep->irq = irq; + +- ep->mii.phy_id = data->phy_id; +- ep->mii.phy_id_mask = 0x1f; +- ep->mii.reg_num_mask = 0x1f; +- ep->mii.dev = dev; +- ep->mii.mdio_read = ep93xx_mdio_read; +- ep->mii.mdio_write = ep93xx_mdio_write; ++ /* mdio/mii bus */ ++ ep->mii_bus.state = MDIOBUS_ALLOCATED; /* see mdiobus_alloc */ ++ ep->mii_bus.name = "ep93xx_mii_bus"; ++ snprintf(ep->mii_bus.id, MII_BUS_ID_SIZE, "0"); ++ ++ ep->mii_bus.read = ep93xx_mdio_read; ++ ep->mii_bus.write = ep93xx_mdio_write; ++ ep->mii_bus.reset = ep93xx_mdio_reset; ++ ++ ep->mii_bus.phy_mask = 0; ++ ++ ep->mii_bus.priv = ep; ++ ep->mii_bus.dev = dev->dev; ++ ++ ep->mii_bus.irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); ++ if (NULL == ep->mii_bus.irq) { ++ dev_err(&pdev->dev, "Could not allocate memory\n"); ++ err = -ENOMEM; ++ goto err_out_mii_bus_irq_kmalloc; ++ } ++ ++ for (i = 0; i < PHY_MAX_ADDR; i++) ++ ep->mii_bus.irq[i] = PHY_POLL; ++ + ep->mdc_divisor = 40; /* Max HCLK 100 MHz, min MDIO clk 2.5 MHz. */ ++ ep->phy_supports_mfps = 0; /* probe without preamble suppression */ + + if (is_zero_ether_addr(dev->dev_addr)) + random_ether_addr(dev->dev_addr); +@@ -874,14 +1060,39 @@ static int ep93xx_eth_probe(struct platform_device *pdev) + err = register_netdev(dev); + if (err) { + dev_err(&pdev->dev, "Failed to register netdev\n"); +- goto err_out; ++ goto err_out_register_netdev; ++ } ++ ++ err = mdiobus_register(&ep->mii_bus); ++ if (err) { ++ dev_err(&dev->dev, "Could not register MII bus\n"); ++ goto err_out_mdiobus_register; ++ } ++ ++ err = ep93xx_mii_probe(dev, data->phy_id); ++ if (err) { ++ dev_err(&dev->dev, "failed to probe MII bus\n"); ++ goto err_out_mii_probe; + } + +- printk(KERN_INFO "%s: ep93xx on-chip ethernet, IRQ %d, %pM\n", +- dev->name, ep->irq, dev->dev_addr); ++ dev_info(&dev->dev, "ep93xx on-chip ethernet, IRQ %d, %pM\n", ++ ep->irq, dev->dev_addr); + + return 0; + ++err_out_mii_probe: ++ mdiobus_unregister(&ep->mii_bus); ++err_out_mdiobus_register: ++ unregister_netdev(dev); ++err_out_register_netdev: ++ kfree(ep->mii_bus.irq); ++err_out_mii_bus_irq_kmalloc: ++ iounmap(ep->base_addr); ++err_out_ioremap: ++ release_resource(ep->res); ++ kfree(ep->res); ++err_out_request_mem_region: ++ free_netdev(dev); + err_out: + ep93xx_eth_remove(pdev); + return err; +@@ -899,7 +1110,6 @@ static struct platform_driver ep93xx_eth_driver = { + + static int __init ep93xx_eth_init_module(void) + { +- printk(KERN_INFO DRV_MODULE_NAME " version " DRV_MODULE_VERSION " loading\n"); + return platform_driver_register(&ep93xx_eth_driver); + } + +@@ -910,5 +1120,7 @@ static void __exit ep93xx_eth_cleanup_module(void) + + module_init(ep93xx_eth_init_module); + module_exit(ep93xx_eth_cleanup_module); ++ + MODULE_LICENSE("GPL"); +-MODULE_ALIAS("platform:ep93xx-eth"); ++MODULE_DESCRIPTION("EP93XX Ethernet driver"); ++MODULE_ALIAS("platform:" DRV_NAME); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0005-ep93xx-m2m-DMA-support.patch b/recipes/linux/linux-2.6.34/ts72xx/0005-ep93xx-m2m-DMA-support.patch new file mode 100644 index 0000000000..5adc450a85 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0005-ep93xx-m2m-DMA-support.patch @@ -0,0 +1,882 @@ +From 62a8847f18ac66120bf1dc07a497a4d74e099036 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Thu, 10 Jun 2010 16:40:16 +0200 +Subject: [PATCH 05/18] ep93xx: m2m DMA support + +--- + arch/arm/mach-ep93xx/Makefile | 2 +- + arch/arm/mach-ep93xx/dma-m2m.c | 753 +++++++++++++++++++++++++++++++ + arch/arm/mach-ep93xx/include/mach/dma.h | 65 +++ + 3 files changed, 819 insertions(+), 1 deletions(-) + create mode 100644 arch/arm/mach-ep93xx/dma-m2m.c + +diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile +index 33ee2c8..ea652c2 100644 +--- a/arch/arm/mach-ep93xx/Makefile ++++ b/arch/arm/mach-ep93xx/Makefile +@@ -1,7 +1,7 @@ + # + # Makefile for the linux kernel. + # +-obj-y := core.o clock.o dma-m2p.o gpio.o ++obj-y := core.o clock.o dma-m2p.o dma-m2m.o gpio.o + obj-m := + obj-n := + obj- := +diff --git a/arch/arm/mach-ep93xx/dma-m2m.c b/arch/arm/mach-ep93xx/dma-m2m.c +new file mode 100644 +index 0000000..8b0d720 +--- /dev/null ++++ b/arch/arm/mach-ep93xx/dma-m2m.c +@@ -0,0 +1,753 @@ ++/* ++ * arch/arm/mach-ep93xx/dma-m2m.c ++ * M2M DMA handling for Cirrus EP93xx chips. ++ * Copyright (C) 2007 Metasoft <prylowski@xxxxxxxxxxx> ++ * ++ * Based on dma-m2p.c by: ++ * Copyright (C) 2006 Lennert Buytenhek <buytenh@xxxxxxxxxxxxxx> ++ * Copyright (C) 2006 Applied Data Systems ++ * ++ * 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. ++ */ ++ ++#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt ++ ++#include <linux/kernel.h> ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/string.h> ++#include <linux/io.h> ++ ++#include <mach/dma.h> ++#include <mach/hardware.h> ++ ++/* TEMP */ ++#define DPRINTK(fmt, args...) ++ ++#define M2M_CONTROL 0x00 ++#define M2M_INTERRUPT 0x04 ++#define M2M_STATUS 0x0c ++#define M2M_BCR0 0x10 ++#define M2M_BCR1 0x14 ++#define M2M_SAR_BASE0 0x18 ++#define M2M_SAR_BASE1 0x1c ++#define M2M_SAR_CURR0 0x24 ++#define M2M_SAR_CURR1 0x28 ++#define M2M_DAR_BASE0 0x2c ++#define M2M_DAR_BASE1 0x30 ++#define M2M_DAR_CURR0 0x34 ++#define M2M_DAR_CURR1 0x3c ++ ++ ++/* control register bits */ ++#define CTRL_STALL_INT_EN 0x00000001 /* stall interrupt enable */ ++#define CTRL_SCT 0x00000002 /* source copy transfer ++ (1 elem. from source fills ++ destination block */ ++#define CTRL_DONE_INT_EN 0x00000004 /* done interrupt enable */ ++#define CTRL_ENABLE 0x00000008 /* channel enable / disable, ++ should be set after ++ write to SAR/DAR/BCR ++ registers */ ++#define CTRL_NFB_INT_EN 0x00200000 /* nfb (next frame buffer) ++ interrupt enable */ ++ ++ ++#define CTRL_START 0x00000010 /* software triggered ++ dma start, not used ++ for M2P/P2M/IDE/SSP */ ++#define CTRL_BWC_MASK 0x000001e0 /* bandwidth control (number ++ of bytes in a block ++ transfer, only M2M */ ++#define CTRL_BWC_SHIFT 5 ++ ++#define BWC_FULL 0x0 /* full bandwidth utilized */ ++#define BWC_16 0x1 /* 16 bytes per block */ ++#define BWC_32 0x5 ++#define BWC_64 0x6 ++#define BWC_128 0x7 ++#define BWC_256 0x8 ++#define BWC_512 0x9 ++#define BWC_1024 0xa ++#define BWC_2048 0xb ++#define BWC_4096 0xc ++#define BWC_8192 0xd ++#define BWC_16384 0xe ++#define BWC_32768 0xf ++ ++#define CTRL_PW_MASK 0x00000600 /* peripheral width, ++ only M2P/P2M */ ++#define CTRL_PW_SHIFT 9 ++ ++#define PW_BYTE 0x0 /* one byte width */ ++#define PW_HALFWORD 0x1 /* 16 bits */ ++#define PW_WORD 0x2 /* 32 bits */ ++#define PW_NOT_USED 0x3 ++ ++#define CTRL_DAH 0x00000800 /* destination address ++ hold, for M2P */ ++#define CTRL_SAH 0x00001000 /* source address ++ hold, for P2M */ ++#define CTRL_TM_MASK 0x00006000 /* transfer mode */ ++#define CTRL_TM_SHIFT 13 ++ ++#define TM_M2M 0x0 /* software initiated M2M transfer */ ++#define TM_M2P 0x1 /* memory to ext. peripheral ++ or IDE/SSP */ ++#define TM_P2M 0x2 /* ext. peripheral or IDE/SSP ++ to memory */ ++#define TM_NOT_USED 0x3 ++ ++#define CTRL_ETDP_MASK 0x00018000 /* end of transfer/terminal ++ count pin direction ++ & polarity */ ++#define CTRL_ETDP_SHIFT 15 ++ ++#define ETDP_ACT_LOW_EOT 0x0 /* pin programmed as active ++ * low end-of-transfer input */ ++#define ETDP_ACT_HIGH_EOT 0x1 /* active high eot input */ ++#define ETDP_ACT_LOW_TC 0x2 /* active low terminal count output */ ++#define ETDP_ACT_HIGH_TC 0x3 /* active high tc output */ ++ ++#define CTRL_DACKP 0x00020000 /* dma acknowledge pin ++ polarity */ ++#define CTRL_DREQP_MASK 0x00180000 /* dma request pin polarity */ ++#define CTRL_DREQP_SHIFT 19 ++ ++#define DREQP_ACT_LOW_LEVEL 0x0 /* DREQ is active low, level ++ sensitive */ ++#define DREQP_ACT_HIGH_LEVEL 0x1 /* active high, level sensitive */ ++#define DREQP_ACT_LOW_EDGE 0x2 /* active low, edge sensitive */ ++#define DREQP_ACT_HIGH_EDGE 0x3 /* active high, edge sensitive */ ++ ++ ++#define CTRL_RSS_MASK 0x00c00000 /* request source selection */ ++#define CTRL_RSS_SHIFT 22 ++ ++#define RSS_EXT 0x0 /* external dma request */ ++#define RSS_SSP_RX 0x1 /* internal SSPRx */ ++#define RSS_SSP_TX 0x2 /* internal SSPTx */ ++#define RSS_IDE 0x3 /* internal IDE */ ++ ++#define CTRL_NO_HDSK 0x01000000 /* no handshake, required for ++ SSP/IDE, optional for ++ ext. M2P/P2M */ ++ ++/* interrupt register bits */ ++#define INTR_STALL 0x1 ++#define INTR_DONE 0x2 ++#define INTR_NFB 0x4 ++#define INTR_ALL 0x7 ++ ++/* status register bits */ ++#define STAT_STALL 0x0001 /* waiting for software start ++ or device request */ ++#define STAT_CTL_STATE_MASK 0x000e /* control fsm state */ ++#define STAT_CTL_STATE_SHIFT 1 ++ ++#define CTL_STATE_IDLE 0x0 ++#define CTL_STATE_STALL 0x1 ++#define CTL_STATE_MEM_RD 0x2 ++#define CTL_STATE_MEM_WR 0x3 ++#define CTL_STATE_BWC_WAIT 0x4 ++ ++#define STAT_BUF_STATE_MASK 0x0030 /* buffer fsm state */ ++#define STAT_BUF_STATE_SHIFT 4 ++ ++#define BUF_STATE_NO_BUF 0x0 ++#define BUF_STATE_BUF_ON 0x1 ++#define BUF_STATE_BUF_NEXT 0x2 ++ ++#define STAT_DONE 0x0040 /* transfer completed successfully ++ (by device or BCR is 0) */ ++ ++#define STAT_TCS_MASK 0x0018 /* terminal count status */ ++#define STAT_TCS_SHIFT 7 ++ ++#define TCS_NONE 0x0 /* terminal count not reached ++ for buffer0 and buffer1 */ ++#define TCS_BUF0 0x1 /* terminal count reached ++ for buffer0 */ ++#define TCS_BUF1 0x2 ++#define TCS_BOTH 0x3 /* terminal count reached ++ for both buffers */ ++ ++#define STAT_EOTS_MASK 0x0060 /* end of transfer status */ ++#define STAT_EOTS_SHIFT 9 ++ ++#define EOTS_NONE 0x0 /* end of transfer has not been ++ requested by ext. periph. for +++ any buffer */ ++#define EOTS_BUF0 0x1 /* eot requested for buffer0 */ ++#define EOTS_BUF1 0x2 ++#define EOTS_BOTH 0x3 /* eot requested for both buffers */ ++ ++#define STAT_NFB 0x0800 /* next frame buffer interrupt */ ++#define STAT_NB 0x1000 /* next buffer status, inform which ++ buffer is free for update */ ++#define STAT_DREQS 0x2000 /* status of dma request signal from ++ ext. periph or IDE/SSP request */ ++ ++/* IDE/SSP support */ ++#define IDE_UDMA_DATAOUT 0x20 ++#define IDE_UDMA_DATAIN 0x24 ++ ++#ifndef SSPDR ++#define SSPDR 0x08 ++#endif ++ ++struct m2m_channel { ++ char *name; ++ void __iomem *base; ++ int irq; ++ ++ struct clk *clk; ++ spinlock_t lock; ++ ++ void *client; ++ unsigned next_slot:1; ++ struct ep93xx_dma_buffer *buffer_xfer; ++ struct ep93xx_dma_buffer *buffer_next; ++ struct list_head buffers_pending; ++}; ++ ++static struct m2m_channel m2m_rxtx[] = { ++ {"m2m0", EP93XX_DMA_BASE + 0x0100, IRQ_EP93XX_DMAM2M0}, ++ {"m2m1", EP93XX_DMA_BASE + 0x0140, IRQ_EP93XX_DMAM2M1}, ++ {NULL}, ++}; ++ ++ ++static void feed_buf(struct m2m_channel *ch, struct ep93xx_dma_buffer *buf) ++{ ++ struct ep93xx_dma_m2m_client *cl = ch->client; ++ u32 src_addr, dst_addr; ++ ++ if ((cl->flags & EP93XX_DMA_M2M_DIR_MASK) == EP93XX_DMA_M2M_TX) { ++ src_addr = buf->bus_addr; ++ switch (cl->flags & EP93XX_DMA_M2M_DEV_MASK) { ++ case EP93XX_DMA_M2M_DEV_IDE: ++ dst_addr = EP93XX_IDE_PHYS_BASE + IDE_UDMA_DATAOUT; ++ break; ++ case EP93XX_DMA_M2M_DEV_SSP: ++ dst_addr = EP93XX_SPI_PHYS_BASE + SSPDR; ++ break; ++ default: ++ dst_addr = buf->bus_addr2; ++ break; ++ } ++ } else { ++ switch (cl->flags & EP93XX_DMA_M2M_DEV_MASK) { ++ case EP93XX_DMA_M2M_DEV_IDE: ++ src_addr = EP93XX_IDE_PHYS_BASE + IDE_UDMA_DATAIN; ++ break; ++ case EP93XX_DMA_M2M_DEV_SSP: ++ src_addr = EP93XX_SPI_PHYS_BASE + SSPDR; ++ break; ++ default: ++ src_addr = buf->bus_addr2; ++ break; ++ } ++ dst_addr = buf->bus_addr; ++ } ++ ++ if (ch->next_slot == 0) { ++ DPRINTK("Writing src_addr: %08x\n", src_addr); ++ DPRINTK("Writing dest_addr: %08x\n", dst_addr); ++ DPRINTK("Writing size: %08x\n", buf->size); ++ writel(src_addr, ch->base + M2M_SAR_BASE0); ++ writel(dst_addr, ch->base + M2M_DAR_BASE0); ++ writel(buf->size, ch->base + M2M_BCR0); ++ } else { ++ writel(src_addr, ch->base + M2M_SAR_BASE1); ++ writel(dst_addr, ch->base + M2M_DAR_BASE1); ++ writel(buf->size, ch->base + M2M_BCR1); ++ } ++ ch->next_slot ^= 1; ++ DPRINTK("data size = %d, slot %d\n", buf->size, ch->next_slot ^ 1); ++} ++ ++static void choose_buffer_xfer(struct m2m_channel *ch) ++{ ++ struct ep93xx_dma_buffer *buf; ++ ++ ch->buffer_xfer = NULL; ++ if (!list_empty(&ch->buffers_pending)) { ++ buf = list_entry(ch->buffers_pending.next, ++ struct ep93xx_dma_buffer, list); ++ list_del(&buf->list); ++ feed_buf(ch, buf); ++ ch->buffer_xfer = buf; ++ } ++} ++ ++static void choose_buffer_next(struct m2m_channel *ch) ++{ ++ struct ep93xx_dma_buffer *buf; ++ ++ ch->buffer_next = NULL; ++ if (!list_empty(&ch->buffers_pending)) { ++ buf = list_entry(ch->buffers_pending.next, ++ struct ep93xx_dma_buffer, list); ++ list_del(&buf->list); ++ feed_buf(ch, buf); ++ ch->buffer_next = buf; ++ } ++} ++ ++static irqreturn_t m2m_irq(int irq, void *dev_id) ++{ ++ struct m2m_channel *ch = dev_id; ++ struct ep93xx_dma_m2m_client *cl; ++ u32 irq_status, dma_state, buf_state, ctl_state; ++ ++ spin_lock(&ch->lock); ++ irq_status = readl(ch->base + M2M_INTERRUPT); ++ /*if ((irq_status & INTR_ALL) == 0) { ++ spin_unlock(&ch->lock); ++ return IRQ_NONE; ++ }*/ ++ dma_state = readl(ch->base + M2M_STATUS); ++ cl = ch->client; ++ ++ //printk("intr status: %08x, dma state: %08x\n", irq_status, dma_state); ++ ++ DPRINTK("intr status %d, dma state %x\n", ++ irq_status, dma_state); ++ ++ buf_state = (dma_state & STAT_BUF_STATE_MASK) >> STAT_BUF_STATE_SHIFT; ++ ctl_state = (dma_state & STAT_CTL_STATE_MASK) >> STAT_CTL_STATE_SHIFT; ++ /*printk("STAT_CTL_STATE: %d, STAT_BUF_STATE: %d\n", ++ * ctl_state, buf_state);*/ ++ if (ctl_state == CTL_STATE_STALL && ++ buf_state == BUF_STATE_NO_BUF && ++ dma_state & STAT_DONE) { ++ /* transfer completed successfully (done) */ ++ ++ ++ /* send client the done command */ ++ if (cl->buffer_finished) { ++ cl->buffer_finished(cl->cookie, ch->buffer_xfer, ch->buffer_xfer->size, 0); ++ } ++ ++ writel(0, ch->base + M2M_INTERRUPT); ++ choose_buffer_xfer(ch); ++ choose_buffer_next(ch); ++ if (ch->buffer_xfer != NULL) { ++ /* retrigger if more buffers exist */ ++ if ((cl->flags & EP93XX_DMA_M2M_DEV_MASK) == ++ EP93XX_DMA_M2M_DEV_MEM) { ++ DPRINTK("Writing start1 to M2M control\n"); ++ writel(readl(ch->base + M2M_CONTROL) | ++ CTRL_START, ch->base + M2M_CONTROL); ++ readl(ch->base + M2M_CONTROL); ++ } ++ } else { ++ DPRINTK("DISABLING DMA: dreqs state: %d\n", dma_state & STAT_DREQS); ++ ++ writel(readl(ch->base + M2M_CONTROL) ++ & ~CTRL_ENABLE, ch->base + M2M_CONTROL); ++ readl(ch->base + M2M_CONTROL); ++ } ++ } else if (ctl_state == CTL_STATE_MEM_RD && ++ buf_state == BUF_STATE_BUF_ON && ++ dma_state & STAT_NFB) { ++ /* next frame buffer */ ++ if (cl->buffer_finished) { ++ cl->buffer_finished(cl->cookie, ch->buffer_xfer, 0, 0); ++ } ++ ch->buffer_xfer = ch->buffer_next; ++ choose_buffer_next(ch); ++ } ++ ++ if (cl->buffer_started && ch->buffer_xfer != NULL) { ++ cl->buffer_started(cl->cookie, ch->buffer_xfer); ++ } ++ ++ spin_unlock(&ch->lock); ++ return IRQ_HANDLED; ++} ++ ++static struct m2m_channel *find_free_channel(struct ep93xx_dma_m2m_client *cl, int channel_spec) ++{ ++ struct m2m_channel *ch = m2m_rxtx; ++ int i; ++ ++#if 0 ++ /* BMS: This code isn't particularly clear; look like it asserts ++ * that a requested channel must not share the same data direction ++ * as a previously requested channel - which makes sense for the SSP, ++ * but not at all for direct hardware transferrs ++ */ ++ for (i = 0; ch[i].base; i++) { ++ struct ep93xx_dma_m2m_client *cl2; ++ ++ cl2 = ch[i].client; ++ if (cl2 != NULL) { ++ int port; ++ ++ /* two the same devices in the same direction ++ are not allowed ++ (two "memory devices" should be allowed) */ ++ port = cl2->flags & (EP93XX_DMA_M2M_DEV_MASK | ++ EP93XX_DMA_M2M_DIR_MASK); ++ if (port == (cl->flags & (EP93XX_DMA_M2M_DEV_MASK | ++ EP93XX_DMA_M2M_DIR_MASK))) ++ return NULL; ++ } ++ } ++#endif ++ ++ if (channel_spec == EP93XX_DMA_M2M_REQUIRES_CH_ANY) { ++ for (i = 0; ch[i].base; i++) { ++ if (ch[i].client == NULL) ++ return ch + i; ++ } ++ } else if (channel_spec == EP93XX_DMA_M2M_REQUIRES_CH_0) { ++ if (ch[0].client == NULL) { ++ return &(ch[0]); ++ } ++ } else if (channel_spec == EP93XX_DMA_M2M_REQUIRES_CH_1) { ++ if (ch[1].client == NULL) { ++ return &(ch[1]); ++ } ++ } else { ++ printk(KERN_ERR "ep93xx-m2m dma channel request: unknown channel spec\n"); ++ } ++ return NULL; ++} ++ ++static u32 set_direction_reg(u32 outv, u32 flags) ++{ ++ switch (flags & EP93XX_DMA_M2M_DEV_MASK) { ++ case EP93XX_DMA_M2M_DEV_EXT: ++ outv &= ~(CTRL_SAH | CTRL_DAH | CTRL_TM_MASK); ++ ++ if (flags & EP93XX_DMA_M2M_EXT_FIFO) ++ outv |= (flags & EP93XX_DMA_M2M_DIR_MASK) == ++ EP93XX_DMA_M2M_TX ? CTRL_DAH : CTRL_SAH; ++ ++ outv |= (((flags & EP93XX_DMA_M2M_DIR_MASK) == ++ EP93XX_DMA_M2M_TX) ? TM_M2P : TM_P2M) << ++ CTRL_TM_SHIFT; ++ ++ break; ++ case EP93XX_DMA_M2M_DEV_IDE: ++ outv &= ~(CTRL_SAH | CTRL_DAH | CTRL_TM_MASK | CTRL_PWSC_MASK); ++ if ((flags & EP93XX_DMA_M2M_DIR_MASK) == EP93XX_DMA_M2M_TX) { ++ outv |= (2 << CTRL_PWSC_SHIFT) & CTRL_PWSC_MASK; ++ outv |= CTRL_DAH; ++ outv |= TM_M2P << CTRL_TM_SHIFT; ++ } else { ++ outv |= (1 << CTRL_PWSC_SHIFT) & CTRL_PWSC_MASK; ++ outv |= CTRL_SAH; ++ outv |= TM_P2M << CTRL_TM_SHIFT; ++ } ++ break; ++ case EP93XX_DMA_M2M_DEV_SSP: ++ outv &= ~(CTRL_SAH | CTRL_DAH | CTRL_TM_MASK | CTRL_RSS_MASK); ++ if ((flags & EP93XX_DMA_M2M_DIR_MASK) == EP93XX_DMA_M2M_TX) { ++ outv |= TM_M2P << CTRL_TM_SHIFT; ++ outv |= CTRL_DAH; ++ outv |= RSS_SSP_TX << CTRL_RSS_SHIFT; ++ } else { ++ outv |= TM_P2M << CTRL_TM_SHIFT; ++ outv |= CTRL_SAH; ++ outv |= RSS_SSP_RX << CTRL_RSS_SHIFT; ++ } ++ break; ++ case EP93XX_DMA_M2M_DEV_MEM: ++ break; ++ } ++ return outv; ++} ++ ++static void channel_enable(struct m2m_channel *ch) ++{ ++ struct ep93xx_dma_m2m_client *cl = ch->client; ++ u32 outv = 0; ++ ++ clk_enable(ch->clk); ++ ++ /* set peripheral wait state mask - IFF specified in control word */ ++ outv |= (cl->flags & CTRL_PWSC_MASK); ++ outv |= (cl->flags & EP93XX_DREQ_MASK); ++ ++ DPRINTK("Set outv to: %08x\n",outv); ++ ++ switch (cl->flags & EP93XX_DMA_M2M_DEV_MASK) { ++ case EP93XX_DMA_M2M_DEV_EXT: ++ switch (cl->flags & EP93XX_DMA_M2M_EXT_WIDTH_MASK) { ++ case EP93XX_DMA_M2M_EXT_WIDTH_BYTE: ++ outv |= PW_BYTE << CTRL_PW_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_EXT_WIDTH_2BYTES: ++ outv |= PW_HALFWORD << CTRL_PW_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_EXT_WIDTH_4BYTES: ++ outv |= PW_WORD << CTRL_PW_SHIFT; ++ break; ++ } ++ /* if NO_HDSK then PWSC, if not, then DREQ, DACK, TC/DEOT */ ++ if (cl->flags & EP93XX_DMA_M2M_EXT_NO_HDSK) { ++ outv |= CTRL_NO_HDSK; ++ /* TODO: wait states */ ++ } else { ++ /* TODO: regular handshaking */ ++ } ++ outv |= RSS_EXT << CTRL_RSS_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_DEV_IDE: ++ /* NO_HDSK, PWSC, PW, SAH, DAH */ ++ outv |= CTRL_NO_HDSK; ++ outv |= PW_WORD << CTRL_PW_SHIFT; ++ /* PWSC = 1 for read, PWSC = 2 for write in UDMA */ ++ outv |= RSS_IDE << CTRL_RSS_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_DEV_SSP: ++ outv |= CTRL_NO_HDSK; ++ outv |= PW_HALFWORD << CTRL_PW_SHIFT; ++ outv |= (8 << CTRL_PWSC_SHIFT) & CTRL_PWSC_MASK; ++ break; ++ case EP93XX_DMA_M2M_DEV_MEM: ++ switch (cl->flags & EP93XX_DMA_M2M_MEM_SPEED_MASK) { ++ case EP93XX_DMA_M2M_MEM_SPEED_FULL: ++ outv |= BWC_FULL << CTRL_BWC_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_MEM_SPEED_HALF: ++ outv |= BWC_32768 << CTRL_BWC_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_MEM_SPEED_QUART: ++ outv |= BWC_16384 << CTRL_BWC_SHIFT; ++ break; ++ case EP93XX_DMA_M2M_MEM_SPEED_SLOW: ++ outv |= BWC_16 << CTRL_BWC_SHIFT; ++ break; ++ } ++ outv |= (cl->flags & EP93XX_DMA_M2M_MEM_FILL) ? CTRL_SCT : 0; ++ outv |= TM_M2M << CTRL_TM_SHIFT; ++ break; ++ } ++ ++ // debug code ++ DPRINTK("PRE-Enable, status is: %08x\n", readl(ch->base+M2M_STATUS)); ++ ++ outv = set_direction_reg(outv, cl->flags); ++ /* STALL interrupt must be enabled */ ++ outv |= CTRL_NFB_INT_EN | CTRL_DONE_INT_EN | CTRL_STALL_INT_EN; ++ ++ writel(outv, ch->base + M2M_CONTROL); ++ outv = readl(ch->base + M2M_CONTROL); ++ DPRINTK("channel enable, writing control reg = %08x\n", outv); ++} ++ ++static void channel_disable(struct m2m_channel *ch) ++{ ++ u32 v; ++ ++ DPRINTK("Disabling channel\n"); ++ v = readl(ch->base + M2M_CONTROL); ++ ++ writel(v & ~(CTRL_NFB_INT_EN | CTRL_DONE_INT_EN | CTRL_STALL_INT_EN), ++ ch->base + M2M_CONTROL); ++ ++ v = readl(ch->base + M2M_CONTROL); ++ ++ while (readl(ch->base + M2M_STATUS) & STAT_NFB) { ++ cpu_relax(); ++ } ++ ++ writel(0, ch->base + M2M_CONTROL); ++ ++ v = readl(ch->base + M2M_CONTROL); ++ ++ while (readl(ch->base + M2M_STATUS) & STAT_STALL) { ++ cpu_relax(); ++ } ++ ++ clk_disable(ch->clk); ++} ++ ++void ep93xx_dma_m2m_set_direction(struct ep93xx_dma_m2m_client *cl, ++ int direction) ++{ ++ struct m2m_channel *ch = cl->channel; ++ u32 outv; ++ unsigned long flags; ++ ++ direction &= EP93XX_DMA_M2M_DIR_MASK; ++ ++ spin_lock_irqsave(&ch->lock, flags); ++ ++ cl->flags &= ~EP93XX_DMA_M2M_DIR_MASK; ++ cl->flags |= direction; ++ ++ outv = readl(ch->base + M2M_CONTROL); ++ outv = set_direction_reg(outv, cl->flags); ++ writel(outv, ch->base + M2M_CONTROL); ++ outv = readl(ch->base + M2M_CONTROL); ++ DPRINTK("set_direction: configured control reg = %08x\n", outv); ++ ++ spin_unlock_irqrestore(&ch->lock, flags); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_set_direction); ++ ++int ep93xx_dma_m2m_client_register(struct ep93xx_dma_m2m_client *cl, int channel_spec) ++{ ++ struct m2m_channel *ch; ++ int err; ++ ++ ch = find_free_channel(cl, channel_spec); ++ if (ch == NULL) ++ return -1; ++ ++ err = request_irq(ch->irq, m2m_irq, IRQF_DISABLED, cl->name ? : "dma-m2m", ch); ++ if (err) ++ return err; ++ ++ ch->client = cl; ++ ch->next_slot = 0; ++ ch->buffer_xfer = NULL; ++ ch->buffer_next = NULL; ++ INIT_LIST_HEAD(&ch->buffers_pending); ++ ++ cl->channel = ch; ++ ++ channel_enable(ch); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_client_register); ++ ++void ep93xx_dma_m2m_client_unregister(struct ep93xx_dma_m2m_client *cl) ++{ ++ struct m2m_channel *ch = cl->channel; ++ ++ channel_disable(ch); ++ free_irq(ch->irq, ch); ++ ch->client = NULL; ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_client_unregister); ++ ++void ep93xx_dma_m2m_submit(struct ep93xx_dma_m2m_client *cl, ++ struct ep93xx_dma_buffer *buf) ++{ ++ struct m2m_channel *ch = cl->channel; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ch->lock, flags); ++ ++ if (ch->buffer_xfer == NULL) { ++ ch->buffer_xfer = buf; ++ feed_buf(ch, buf); ++ if (readl(ch->base + M2M_CONTROL) & CTRL_ENABLE) { ++ DPRINTK("CTRL_ENABLE\n"); ++ if ((cl->flags & EP93XX_DMA_M2M_DEV_MASK) == ++ EP93XX_DMA_M2M_DEV_MEM) { ++ DPRINTK("WRITING START2 TO M2M control\n"); ++ writel(readl(ch->base + M2M_CONTROL) | ++ CTRL_START, ch->base + M2M_CONTROL); ++ readl(ch->base + M2M_CONTROL); ++ } ++ } ++ } else if (ch->buffer_next == NULL) { ++ ch->buffer_next = buf; ++ feed_buf(ch, buf); ++ } else ++ list_add_tail(&buf->list, &ch->buffers_pending); ++ spin_unlock_irqrestore(&ch->lock, flags); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_submit); ++ ++void ep93xx_dma_m2m_start(struct ep93xx_dma_m2m_client *cl) ++{ ++ struct m2m_channel *ch = cl->channel; ++ u32 v; ++ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ch->lock, flags); ++ ++ writel(readl(ch->base + M2M_STATUS), ch->base+M2M_STATUS); ++ //printk("At start, status is: %08x\n", readl(ch->base + M2M_STATUS)); ++ ++ v = readl(ch->base + M2M_CONTROL) | CTRL_ENABLE; ++ writel(v, ch->base + M2M_CONTROL); ++ v = readl(ch->base + M2M_CONTROL); ++ if (ch->buffer_xfer != NULL) { ++ if (((cl->flags & EP93XX_DMA_M2M_DEV_MASK) == ++ EP93XX_DMA_M2M_DEV_MEM)) { ++ DPRINTK("WRITING START3 to M2M controller\n"); ++ v |= CTRL_START; ++ writel(v, ch->base + M2M_CONTROL); ++ v = readl(ch->base + M2M_CONTROL); ++ } ++ } ++ ++ spin_unlock_irqrestore(&ch->lock, flags); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_start); ++ ++void ep93xx_dma_m2m_stop(struct ep93xx_dma_m2m_client *cl) ++{ ++ struct m2m_channel *ch = cl->channel; ++ u32 v; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ch->lock, flags); ++ ++ DPRINTK("Stopping DMA by disabling CTRL_ENABLE\n"); ++ v = readl(ch->base + M2M_CONTROL) & ~CTRL_ENABLE; ++ writel(v, ch->base + M2M_CONTROL); ++ readl(ch->base + M2M_CONTROL); ++ DPRINTK("configured control reg = %08x\n", v); ++ ++ spin_unlock_irqrestore(&ch->lock, flags); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_stop); ++ ++void ep93xx_dma_m2m_flush(struct ep93xx_dma_m2m_client *cl) ++{ ++ struct m2m_channel *ch = cl->channel; ++ ++ channel_disable(ch); ++ ch->next_slot = 0; ++ ch->buffer_xfer = NULL; ++ ch->buffer_next = NULL; ++ INIT_LIST_HEAD(&ch->buffers_pending); ++ channel_enable(ch); ++} ++EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_flush); ++ ++static int init_channel(struct m2m_channel *ch) ++{ ++ ch->clk = clk_get(NULL, ch->name); ++ if (IS_ERR(ch->clk)) ++ return PTR_ERR(ch->clk); ++ ++ spin_lock_init(&ch->lock); ++ ch->client = NULL; ++ ++ return 0; ++} ++ ++static int __init ep93xx_dma_m2m_init(void) ++{ ++ int i; ++ int ret; ++ ++ for (i = 0; m2m_rxtx[i].base; i++) { ++ ret = init_channel(m2m_rxtx + i); ++ if (ret) ++ return ret; ++ } ++ ++ pr_info("M2M DMA subsystem initialized\n"); ++ return 0; ++} ++arch_initcall(ep93xx_dma_m2m_init); +diff --git a/arch/arm/mach-ep93xx/include/mach/dma.h b/arch/arm/mach-ep93xx/include/mach/dma.h +index 3a5961d..6a3552b 100644 +--- a/arch/arm/mach-ep93xx/include/mach/dma.h ++++ b/arch/arm/mach-ep93xx/include/mach/dma.h +@@ -11,6 +11,7 @@ + struct ep93xx_dma_buffer { + struct list_head list; + u32 bus_addr; ++ u32 bus_addr2; /* only used by M2M */ + u16 size; + }; + +@@ -28,6 +29,7 @@ struct ep93xx_dma_m2p_client { + void *channel; + }; + ++/* flags (m2p client) */ + #define EP93XX_DMA_M2P_PORT_I2S1 0x00 + #define EP93XX_DMA_M2P_PORT_I2S2 0x01 + #define EP93XX_DMA_M2P_PORT_AAC1 0x02 +@@ -45,6 +47,58 @@ struct ep93xx_dma_m2p_client { + #define EP93XX_DMA_M2P_IGNORE_ERROR 0x40 + #define EP93XX_DMA_M2P_ERROR_MASK 0x60 + ++ ++struct ep93xx_dma_m2m_client { ++ char *name; ++ u32 flags; ++ void *cookie; ++ void (*buffer_started)(void *cookie, ++ struct ep93xx_dma_buffer *buf); ++ void (*buffer_finished)(void *cookie, ++ struct ep93xx_dma_buffer *buf, ++ int bytes, int error); ++ ++ /* Internal to the DMA code. */ ++ void *channel; ++}; ++ ++/* flags (m2m client) */ ++#define EP93XX_DMA_M2M_RX 0x000 /* read from periph./memory */ ++#define EP93XX_DMA_M2M_TX 0x004 /* write to periph./memory */ ++#define EP93XX_DMA_M2M_DIR_MASK 0x004 /* direction mask */ ++#define EP93XX_DMA_M2M_DEV_EXT 0x000 /* external peripheral */ ++#define EP93XX_DMA_M2M_DEV_SSP 0x001 /* internal SSP */ ++#define EP93XX_DMA_M2M_DEV_IDE 0x002 /* internal IDE */ ++#define EP93XX_DMA_M2M_DEV_MEM 0x003 /* memory to memory transfer */ ++#define EP93XX_DMA_M2M_DEV_MASK 0x003 /* device mask */ ++#define EP93XX_DMA_M2M_EXT_FIFO 0x008 /* external peripheral is one location fifo */ ++#define EP93XX_DMA_M2M_EXT_NO_HDSK 0x010 /* external peripheral doesn't require regular handshaking protocol */ ++#define EP93XX_DMA_M2M_EXT_WIDTH_MASK 0x300 ++#define EP93XX_DMA_M2M_EXT_WIDTH_BYTE 0x000 /* external peripheral transfer is one byte width */ ++#define EP93XX_DMA_M2M_EXT_WIDTH_2BYTES 0x100 ++#define EP93XX_DMA_M2M_EXT_WIDTH_4BYTES 0x200 ++#define EP93XX_DMA_M2M_MEM_SPEED_FULL 0x000 /* M2M bandwidth control */ ++#define EP93XX_DMA_M2M_MEM_SPEED_HALF 0x040 /* half bus bandwidth */ ++#define EP93XX_DMA_M2M_MEM_SPEED_QUART 0x080 /* quarter bus bandwidth */ ++#define EP93XX_DMA_M2M_MEM_SPEED_SLOW 0x0c0 /* slowest speed */ ++#define EP93XX_DMA_M2M_MEM_SPEED_MASK 0x0c0 /* memory speed mask */ ++#define EP93XX_DMA_M2M_MEM_FILL 0x020 /* M2M is one location to block fill */ ++ ++/* FIXME */ ++#define CTRL_PWSC_MASK 0xfe000000 /* peripheral wait states count */ ++#define CTRL_PWSC_SHIFT 25 ++#define EP93XX_DREQ_SHIFT 19 ++#define EP93XX_DREQ_MASK 0x00180000 ++#define EP93XX_DMA_M2M_DREQ_LS_L (00 << EP93XX_DREQ_SHIFT) ++#define EP93XX_DMA_M2M_DREQ_LS_H (01 << EP93XX_DREQ_SHIFT) ++#define EP93XX_DMA_M2M_DREQ_ES_L (10 << EP93XX_DREQ_SHIFT) ++#define EP93XX_DMA_M2M_DREQ_ES_H (11 << EP93XX_DREQ_SHIFT) ++ ++/* See ep93xx_dma_m2m_client_register (channel_spec) */ ++#define EP93XX_DMA_M2M_REQUIRES_CH_ANY 0 ++#define EP93XX_DMA_M2M_REQUIRES_CH_0 1 ++#define EP93XX_DMA_M2M_REQUIRES_CH_1 2 ++ + int ep93xx_dma_m2p_client_register(struct ep93xx_dma_m2p_client *m2p); + void ep93xx_dma_m2p_client_unregister(struct ep93xx_dma_m2p_client *m2p); + void ep93xx_dma_m2p_submit(struct ep93xx_dma_m2p_client *m2p, +@@ -53,4 +107,15 @@ void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *m2p, + struct ep93xx_dma_buffer *buf); + void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *m2p); + ++int ep93xx_dma_m2m_client_register(struct ep93xx_dma_m2m_client *m2m, ++ int channel_spec); ++void ep93xx_dma_m2m_client_unregister(struct ep93xx_dma_m2m_client *m2m); ++void ep93xx_dma_m2m_submit(struct ep93xx_dma_m2m_client *m2m, ++ struct ep93xx_dma_buffer *buf); ++void ep93xx_dma_m2m_flush(struct ep93xx_dma_m2m_client *m2m); ++void ep93xx_dma_m2m_start(struct ep93xx_dma_m2m_client *m2m); ++void ep93xx_dma_m2m_stop(struct ep93xx_dma_m2m_client *m2m); ++void ep93xx_dma_m2m_set_direction(struct ep93xx_dma_m2m_client *m2m, ++ int direction); ++ + #endif /* __ASM_ARCH_DMA_H */ +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0006-ts72xx_rs485.patch b/recipes/linux/linux-2.6.34/ts72xx/0006-ts72xx_rs485.patch new file mode 100644 index 0000000000..ae7fefe1ad --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0006-ts72xx_rs485.patch @@ -0,0 +1,218 @@ +From 6f231c654139e014473ccf0e79725dd313435c8c Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Thu, 10 Jun 2010 17:00:12 +0200 +Subject: [PATCH 06/18] ts72xx_rs485 + +Crude hack... +--- + arch/arm/include/asm/ioctls.h | 3 + + drivers/serial/Kconfig | 8 +++ + drivers/serial/amba-pl010.c | 124 ++++++++++++++++++++++++++++++++++++++++- + 3 files changed, 134 insertions(+), 1 deletions(-) + +diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h +index a91d8a1..a4b60ae 100644 +--- a/arch/arm/include/asm/ioctls.h ++++ b/arch/arm/include/asm/ioctls.h +@@ -70,6 +70,9 @@ + #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ + #define FIOQSIZE 0x545E + ++#define TIOC_SBCC485 0x545F /* TS72xx RTS/485 mode clear */ ++#define TIOC_SBCS485 0x5460 /* TS72xx RTS/485 mode set */ ++ + /* Used for packet mode */ + #define TIOCPKT_DATA 0 + #define TIOCPKT_FLUSHREAD 1 +diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig +index f55c494..e5702d1 100644 +--- a/drivers/serial/Kconfig ++++ b/drivers/serial/Kconfig +@@ -304,6 +304,14 @@ config SERIAL_AMBA_PL010_CONSOLE + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + ++config SERIAL_AMBA_PL010_TS72XX ++ bool "Support for RS-485 on AMBA serial port (for TS-72XX SBC)" ++ depends on SERIAL_AMBA_PL010 != n && MACH_TS72XX ++ help ++ This add support for RS-485 on some Technologic System SBC. ++ ++ If unsure, say N. ++ + config SERIAL_AMBA_PL011 + tristate "ARM AMBA PL011 serial port support" + depends on ARM_AMBA +diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c +index b09a638..a12922a 100644 +--- a/drivers/serial/amba-pl010.c ++++ b/drivers/serial/amba-pl010.c +@@ -51,6 +51,10 @@ + + #include <asm/io.h> + ++#if defined(CONFIG_SERIAL_AMBA_PL010_TS72XX) ++#include <mach/ts72xx.h> ++#endif ++ + #define UART_NR 8 + + #define SERIAL_AMBA_MAJOR 204 +@@ -65,6 +69,11 @@ + #define UART_DUMMY_RSR_RX 256 + #define UART_PORT_SIZE 64 + ++#if defined(CONFIG_SERIAL_AMBA_PL010_TS72XX) ++static void __iomem *ts_rs485_data9_register; ++static void __iomem *ts_rs485_control_register; ++#endif ++ + /* + * We wrap our port structure around the generic uart_port. + */ +@@ -386,7 +395,7 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, + /* + * Ask the core to calculate the divisor for us. + */ +- baud = uart_get_baud_rate(port, termios, old, 0, uap->port.uartclk/16); ++ baud = uart_get_baud_rate(port, termios, old, 0, uap->port.uartclk/16); + quot = uart_get_divisor(port, baud); + + switch (termios->c_cflag & CSIZE) { +@@ -534,6 +543,105 @@ static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) + return ret; + } + ++ ++#if defined(CONFIG_SERIAL_AMBA_PL010_TS72XX) ++static int ts72xx_rs485_init(void) ++{ ++ ts_rs485_data9_register = ioremap(TS72XX_RS485_MODE_PHYS_BASE, 4096); ++ if (ts_rs485_data9_register == NULL) { ++ return -1; ++ } ++ ++ ts_rs485_control_register = ioremap(TS72XX_RS485_CONTROL_PHYS_BASE, 4096); ++ if (ts_rs485_control_register == NULL) { ++ iounmap(ts_rs485_data9_register); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ts72xx_auto485(struct uart_port *port, unsigned int cmd, unsigned long *arg) ++{ ++ int baud, cflag, mode; ++ int datalength; ++ ++ mode = (int)*arg; ++ if (!is_rs485_installed()) { ++ printk("amba-pl010.c: this board does not support RS485 auto mode\n"); ++ return -EINVAL; ++ } ++ ++ if (port->line != 1) { ++ printk("amba-pl010.c: auto RS485 mode is only supported on second port (/dev/ttyAM1)\n"); ++ return -EINVAL; ++ } ++ ++ datalength = 8; ++ cflag = port->state->port.tty->termios->c_cflag; ++ if (cflag & PARENB) ++ datalength++; ++ ++ if (cflag & CSTOPB) ++ datalength++; ++ ++ baud = tty_get_baud_rate(port->state->port.tty); ++ ++ switch (cmd) { ++ case TIOC_SBCC485: ++ if ((mode & TS72XX_RS485_AUTO485FD) || (mode & TS72XX_RS485_AUTO485HD)) { ++ printk("amba-pl010.c: unsetting auto RS485 mode\n"); ++ __raw_writew(TS72XX_RS485_MODE_RS232, ts_rs485_control_register); ++ __raw_writew(TS72XX_RS485_MODE_RS232, ts_rs485_data9_register); ++ } ++ break; ++ case TIOC_SBCS485: ++ if (mode & TS72XX_RS485_AUTO485FD) { ++ printk ("amba-pl010.c: setting FULL duplex auto RS485 mode\n"); ++ __raw_writew(TS72XX_RS485_MODE_FD, ts_rs485_control_register); ++ if (datalength > 8) ++ __raw_writew(TS72XX_RS485_MODE_FD, ts_rs485_data9_register); ++ } else if (mode & TS72XX_RS485_AUTO485HD) { ++ printk("amba-pl010.c: setting HALF DUPLEX auto RS485 mode\n"); ++ switch (baud) { ++ case 9600: ++ __raw_writew(TS72XX_RS485_MODE_9600_HD, ts_rs485_control_register); ++ break; ++ case 19200: ++ __raw_writew(TS72XX_RS485_MODE_19200_HD, ts_rs485_control_register); ++ break; ++ case 57600: ++ __raw_writew(TS72XX_RS485_MODE_57600_HD, ts_rs485_control_register); ++ break; ++ case 115200: ++ __raw_writew(TS72XX_RS485_MODE_115200_HD, ts_rs485_control_register); ++ break; ++ default: ++ printk("amba-pl010.c: %d baud rate is not supported for auto RS485 mode\n", baud); ++ return -1; ++ } ++ if (datalength > 8) ++ __raw_writew(TS72XX_RS485_MODE_FD, ts_rs485_data9_register); ++ } ++ break; ++ } ++ ++ return 0; ++} ++ ++static int pl010_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case TIOC_SBCC485: ++ case TIOC_SBCS485: ++ return ts72xx_auto485(port, cmd, (unsigned long *)arg); ++ } ++ ++ return -ENOIOCTLCMD; ++} ++#endif /* CONFIG_SERIAL_AMBA_PL010_TS72XX */ ++ ++ + static struct uart_ops amba_pl010_pops = { + .tx_empty = pl010_tx_empty, + .set_mctrl = pl010_set_mctrl, +@@ -552,6 +660,9 @@ static struct uart_ops amba_pl010_pops = { + .request_port = pl010_request_port, + .config_port = pl010_config_port, + .verify_port = pl010_verify_port, ++#if defined(CONFIG_SERIAL_AMBA_PL010_TS72XX) ++ .ioctl = pl010_ioctl, ++#endif + }; + + static struct uart_amba_port *amba_ports[UART_NR]; +@@ -810,6 +921,17 @@ static int __init pl010_init(void) + ret = uart_register_driver(&amba_reg); + if (ret == 0) { + ret = amba_driver_register(&pl010_driver); ++ ++ #if defined(CONFIG_SERIAL_AMBA_PL010_TS72XX) ++ if (!ret && is_rs485_installed()) { ++ ret = ts72xx_rs485_init(); ++ if (ret) ++ printk("amba-pl010.c: ts72xx_rs485_init() failed\n"); ++ else ++ printk("amba-pl010.c: auto RS485 mode initialized\n"); ++ } ++ #endif ++ + if (ret) + uart_unregister_driver(&amba_reg); + } +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0007-ts72xx_ts_ser1.patch b/recipes/linux/linux-2.6.34/ts72xx/0007-ts72xx_ts_ser1.patch new file mode 100644 index 0000000000..a6fab759c7 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0007-ts72xx_ts_ser1.patch @@ -0,0 +1,259 @@ +From f5b0dc2b5e15d83cbc51404d2a754ea8ea8961b5 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Wed, 16 Jun 2010 14:44:44 +0200 +Subject: [PATCH 07/18] ts72xx_ts_ser1 + +TS-SER1 - Serial Port PC/104 peripheral +--- + drivers/serial/8250_ts_ser1.c | 197 +++++++++++++++++++++++++++++++++++++++++ + drivers/serial/Kconfig | 17 ++++ + drivers/serial/Makefile | 1 + + 3 files changed, 215 insertions(+), 0 deletions(-) + create mode 100644 drivers/serial/8250_ts_ser1.c + +diff --git a/drivers/serial/8250_ts_ser1.c b/drivers/serial/8250_ts_ser1.c +new file mode 100644 +index 0000000..e5fe616 +--- /dev/null ++++ b/drivers/serial/8250_ts_ser1.c +@@ -0,0 +1,197 @@ ++/* ++ * linux/drivers/serial/8250_ts_ser1.c ++ * Technologic Systems TS-SER1 support. ++ * ++ * (c) Copyright 2006-2008 Matthieu Crapet <mcrapet@gmail.com> ++ * Data taken from include/asm-i386/serial.h ++ * ++ * 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. ++ * ++ * Pin Number: ++ * 1 DCD ++ * 2 Receive data ++ * 3 Trasmit data ++ * 4 DTR ++ * 5 Signal Ground ++ * 6 DSR ++ * 7 RTS ++ * 8 CTS ++ * 9 RI ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/serial_8250.h> ++#include <linux/irq.h> ++#include <linux/io.h> ++#include <mach/hardware.h> ++#include <mach/ts72xx.h> ++#include <mach/gpio.h> ++ ++#define TS72XX_SER1_IO_PHYS_BASE (TS72XX_PC104_8BIT_IO_PHYS_BASE) ++#define TS72XX_SER1_IO_SIZE (TS72XX_PC104_8BIT_IO_SIZE) ++ ++#define TS_SER1_PORT_COM3 0x3E8 ++#define TS_SER1_PORT_COM4 0x2E8 ++#define TS_SER1_PORT_COM5 0x3A8 ++ ++/* Value to write in 16550A scratch register */ ++#define MARKER_BYTE 0xAA /* or 0x55 */ ++ ++#define PORT(_base,_irq) \ ++{ \ ++ .iobase = _base, \ ++ .membase = (void __iomem *)0, \ ++ .irq = _irq, \ ++ .uartclk = 1843200, \ ++ .iotype = UPIO_PORT, \ ++ .flags = UPF_BOOT_AUTOCONF, \ ++} ++/* Note: IRQ can be shared (see CONFIG_SERIAL_8250_SHARE_IRQ) */ ++ ++ ++static struct plat_serial8250_port ts72xx_ser1_data_com3[] = { ++ PORT(TS_SER1_PORT_COM3, 0), ++ { }, ++}; ++ ++static struct plat_serial8250_port ts72xx_ser1_data_com4[] = { ++ PORT(TS_SER1_PORT_COM4, 0), ++ { }, ++}; ++ ++static struct plat_serial8250_port ts72xx_ser1_data_com5[] = { ++ PORT(TS_SER1_PORT_COM5, 0), ++ { }, ++}; ++ ++ ++static int ts_ser1_irq = CONFIG_SERIAL_8250_TS_SER1_IRQ; // 5, 6 or 7 ++static struct platform_device *serial8250_ts_ser1_dev; ++ ++ ++static int __init ts_ser1_init(void) ++{ ++ struct plat_serial8250_port *comX = NULL; ++ void __iomem *iomem; ++ ++ int ret = -ENODEV; ++ int n = 0; // COM number as printed on TS-SER1 pcb ++ ++ iomem = ioremap(TS72XX_SER1_IO_PHYS_BASE, TS72XX_SER1_IO_SIZE); ++ ++ if (iomem != NULL) { ++ __raw_writeb(MARKER_BYTE, iomem + TS_SER1_PORT_COM3 + 7); ++ if (__raw_readb(iomem + TS_SER1_PORT_COM3 + 7) == MARKER_BYTE) { ++ comX = ts72xx_ser1_data_com3; ++ n = 3; ++ } else { ++ __raw_writeb(MARKER_BYTE, iomem + TS_SER1_PORT_COM4 + 7); ++ if (__raw_readb(iomem + TS_SER1_PORT_COM4 + 7) == MARKER_BYTE) { ++ comX = ts72xx_ser1_data_com4; ++ n = 4; ++ } else { ++ __raw_writeb(MARKER_BYTE, iomem + TS_SER1_PORT_COM5 + 7); ++ if (__raw_readb(iomem + TS_SER1_PORT_COM5 + 7) == MARKER_BYTE) { ++ comX = ts72xx_ser1_data_com5; ++ n = 5; ++ } ++ } ++ } ++ ++ if (comX) { ++ switch (ts_ser1_irq) { ++ case 5: ++ ret = gpio_request(EP93XX_GPIO_LINE_F(3), "TS-SER1"); ++ if (ret < 0) { ++ pr_err("gpio_request failed, try another irq\n"); ++ goto init_error; ++ } ++ gpio_direction_input(EP93XX_GPIO_LINE_F(3)); ++ comX->irq = gpio_to_irq(EP93XX_GPIO_LINE_F(3)); ++ set_irq_type(comX->irq, IRQ_TYPE_EDGE_RISING); ++ break; ++ case 6: ++ comX->irq = IRQ_EP93XX_EXT1; ++ break; ++ case 7: ++ comX->irq = IRQ_EP93XX_EXT3; ++ break; ++ default: ++ pr_err("wrong specified irq\n"); ++ goto init_error; ++ } ++ ++ comX->iobase += (unsigned long)iomem; // virtual address ++ ++ } else { ++ pr_err("can't detect COM number\n"); ++ goto init_error; ++ } ++ ++ /* create platform_device structure */ ++ serial8250_ts_ser1_dev = platform_device_alloc("serial8250", n); ++ if (!serial8250_ts_ser1_dev) { ++ ret = -ENOMEM; ++ goto init_error; ++ } ++ ++ ret = platform_device_add_data(serial8250_ts_ser1_dev, comX, ++ 2 * sizeof(struct plat_serial8250_port)); ++ if (ret) { ++ platform_device_put(serial8250_ts_ser1_dev); ++ goto init_error; ++ } ++ ++ ret = platform_device_add(serial8250_ts_ser1_dev); ++ if (ret) { ++ platform_device_put(serial8250_ts_ser1_dev); ++ goto init_error; ++ } ++ ++ platform_set_drvdata(serial8250_ts_ser1_dev, iomem); ++ return 0; ++ } ++ ++init_error: ++ if (iomem) { ++ iounmap(iomem); ++ iomem = NULL; ++ } ++ return ret; ++} ++ ++static void __exit ts_ser1_exit(void) ++{ ++ struct platform_device *pdev = serial8250_ts_ser1_dev; ++ void __iomem *iomem = platform_get_drvdata(pdev); ++ ++ serial8250_ts_ser1_dev = NULL; ++ ++ platform_device_unregister(pdev); ++ ++ iounmap(iomem); ++ if (ts_ser1_irq == 5) ++ gpio_free(EP93XX_GPIO_LINE_F(3)); ++} ++ ++module_init(ts_ser1_init); ++module_exit(ts_ser1_exit); ++ ++module_param(ts_ser1_irq, int, 0); ++MODULE_PARM_DESC(ts_ser1_irq, "TS-SER1 IRQ, default=" __MODULE_STRING(CONFIG_SERIAL_8250_TS_SER1_IRQ) ")"); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("8250 serial probe module for TS-SER1 (TS-72xx)"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.5"); +diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig +index e5702d1..9f38fc5 100644 +--- a/drivers/serial/Kconfig ++++ b/drivers/serial/Kconfig +@@ -275,6 +275,23 @@ config SERIAL_8250_RM9K + port hardware found on MIPS RM9122 and similar processors. + If unsure, say N. + ++config SERIAL_8250_TS_SER1 ++ tristate "Support TS-SER1 (for TS-72XX SBC)" ++ depends on SERIAL_8250 != n && MACH_TS72XX ++ help ++ Say Y here if you have a TS-SER1 PC/104 peripheral. ++ COM number will be configured automaticaly. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called 8250_ts_ser1. ++ ++config SERIAL_8250_TS_SER1_IRQ ++ int "Selected IRQ (5, 6 or 7)" ++ depends on SERIAL_8250_TS_SER1 ++ default "5" ++ help ++ Enter jumper IRQ configuration ++ + comment "Non-8250 serial port support" + + config SERIAL_AMBA_PL010 +diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile +index 6aa4723..a3b1cf2 100644 +--- a/drivers/serial/Makefile ++++ b/drivers/serial/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o + obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o + obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o + obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o ++obj-$(CONFIG_SERIAL_8250_TS_SER1) += 8250_ts_ser1.o + obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o + obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o + obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0008-ts72xx_ts_eth100.patch b/recipes/linux/linux-2.6.34/ts72xx/0008-ts72xx_ts_eth100.patch new file mode 100644 index 0000000000..75d53039da --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0008-ts72xx_ts_eth100.patch @@ -0,0 +1,273 @@ +From 9a9465369f2fdbd22b68e545b4744b8dca1608ff Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Fri, 18 Jun 2010 17:39:09 +0200 +Subject: [PATCH 08/18] ts72xx_ts_eth100 + +TS-ETH100 - 10/100 Ethernet PC/104 peripheral +--- + drivers/net/Kconfig | 10 ++ + drivers/net/Makefile | 1 + + drivers/net/ax88796.c | 8 ++- + drivers/net/ax88796_ts_eth100.c | 190 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 208 insertions(+), 1 deletions(-) + create mode 100644 drivers/net/ax88796_ts_eth100.c + +diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig +index 7b832c7..f812332 100644 +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -248,6 +248,16 @@ config AX88796_93CX6 + help + Select this if your platform comes with an external 93CX6 eeprom. + ++config AX88796_TS_ETH100 ++ tristate "Support for TS-ETH100 (TS-72XX SBC)" ++ depends on AX88796 && MACH_TS72XX ++ help ++ Say Y here if you have a TS-ETH100 PC/104 peripheral. ++ IRQ numbers and I/O address will be configurated automatically. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called ax88796_ts_eth100. ++ + config MACE + tristate "MACE (Power Mac ethernet) support" + depends on PPC_PMAC && PPC32 +diff --git a/drivers/net/Makefile b/drivers/net/Makefile +index 12b280a..cb448fa 100644 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -143,6 +143,7 @@ obj-$(CONFIG_B44) += b44.o + obj-$(CONFIG_FORCEDETH) += forcedeth.o + obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o + obj-$(CONFIG_AX88796) += ax88796.o ++obj-$(CONFIG_AX88796_TS_ETH100) += ax88796_ts_eth100.o + obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o + + obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o +diff --git a/drivers/net/ax88796.c b/drivers/net/ax88796.c +index b718dc6..282fd8e 100644 +--- a/drivers/net/ax88796.c ++++ b/drivers/net/ax88796.c +@@ -809,7 +809,9 @@ static int ax_remove(struct platform_device *_dev) + ax = to_ax_dev(dev); + + unregister_netdev(dev); +- free_irq(dev->irq, dev); ++ if (ax->running) { // already freed in ax_close? ++ free_irq(dev->irq, dev); ++ } + + iounmap(ei_status.mem); + release_resource(ax->mem); +@@ -935,7 +937,11 @@ static int ax_probe(struct platform_device *pdev) + goto exit_mem2; + } + ++ #if defined(CONFIG_AX88796_TS_ETH100) || defined(CONFIG_AX88796_TS_ETH100_MODULE) ++ ei_status.reg_offset[0x10] = ax->map2 - ei_status.mem + 0x10; /* don't know why, but +0x20 works too */ ++ #else + ei_status.reg_offset[0x1f] = ax->map2 - ei_status.mem; ++ #endif + } + + /* got resources, now initialise and register device */ +diff --git a/drivers/net/ax88796_ts_eth100.c b/drivers/net/ax88796_ts_eth100.c +new file mode 100644 +index 0000000..448b3e3 +--- /dev/null ++++ b/drivers/net/ax88796_ts_eth100.c +@@ -0,0 +1,190 @@ ++/* ++ * linux/drivers/net/ax88796_ts_eth100.c ++ * Technologic Systems TS-ETH100 support. ++ * ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.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, 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. ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/irq.h> ++#include <linux/io.h> ++#include <net/ax88796.h> ++#include <mach/ts72xx.h> ++#include <mach/gpio.h> ++ ++#define TS72XX_ETH100_IO8_PHYS_BASE (TS72XX_PC104_8BIT_IO_PHYS_BASE) ++#define TS72XX_ETH100_IO8_SIZE (TS72XX_PC104_8BIT_IO_SIZE) ++#define TS72XX_ETH100_IO16_PHYS_BASE (TS72XX_PC104_16BIT_IO_PHYS_BASE) ++#define TS72XX_ETH100_IO16_SIZE (TS72XX_PC104_16BIT_IO_SIZE) ++ ++/* Technologic systems I/O space */ ++#define TS_ETH100_PLD_0 0x100 ++#define TS_ETH100_PLD_1 0x110 ++#define TS_ETH100_PLD_2 0x120 ++#define TS_ETH100_PLD_3 0x130 ++ ++/* NE2000 I/O space */ ++#define TS_ETH100_MAC_0 0x200 ++#define TS_ETH100_MAC_1 0x240 ++#define TS_ETH100_MAC_2 0x300 ++#define TS_ETH100_MAC_3 0x340 ++ ++/* Board identifier must be 5 ; PLD revision should be 1 */ ++#define is_eth100_present(__iomem, __offset) \ ++ (((__raw_readb(__iomem + __offset) & 0xF) == 0x5) && \ ++ ((__raw_readb(__iomem + __offset + 4) & 0xF) == 0x1)) ++ ++/* Jumpers status (SRAM control register) */ ++#define read_irq(__iomem, __offset) \ ++ (__raw_readb(__iomem + __offset + 8) & 0xE) ++ ++ ++static u32 offsets[0x20] = { ++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, ++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, ++ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, ++ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F ++}; ++ ++static struct ax_plat_data ts72xx_eth100_asix_data = { ++ .flags = AXFLG_HAS_93CX6, ++ .wordlength = 2, ++ .dcr_val = 0x48, ++ .rcr_val = 0x40, ++ .reg_offsets = offsets, ++}; ++ ++static struct resource ts72xx_eth100_resource[] = { ++ [0] = { ++ .start = TS72XX_ETH100_IO8_PHYS_BASE, ++ .end = TS72XX_ETH100_IO8_PHYS_BASE + 0x40 - 1, ++ .flags = IORESOURCE_MEM ++ }, ++ [1] = { /* 0x10 is NE_DATAPORT is 16-bit access */ ++ .start = TS72XX_ETH100_IO16_PHYS_BASE, ++ .end = TS72XX_ETH100_IO16_PHYS_BASE + 0x40 - 1, ++ .flags = IORESOURCE_MEM ++ }, ++ [2] = { ++ .start = IRQ_EP93XX_EXT1, ++ .end = IRQ_EP93XX_EXT1, ++ .flags = IORESOURCE_IRQ ++ } ++}; ++ ++static int ts_eth100_irq; // 2 [IRQ 5], 4 [IRQ 6] or 8 [IRQ 7] (jumper configuration) ++ ++ ++static void ts72xx_eth100_release(struct device *dev) ++{ ++ /* nothing to do (no kfree) because we have static struct */ ++} ++ ++static struct platform_device ts72xx_eth100_device_asix = { ++ .name = "ax88796", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(ts72xx_eth100_resource), ++ .resource = ts72xx_eth100_resource, ++ .dev = { ++ .platform_data = &ts72xx_eth100_asix_data, ++ .release = ts72xx_eth100_release, ++ } ++}; ++ ++static int __init ts_eth100_init(void) ++{ ++ void __iomem *iomem; ++ struct platform_device *ethX = NULL; ++ ++ iomem = ioremap(TS72XX_ETH100_IO8_PHYS_BASE, TS72XX_ETH100_IO8_SIZE); ++ if (iomem != NULL) { ++ ethX = &ts72xx_eth100_device_asix; ++ ++ if (is_eth100_present(iomem, TS_ETH100_PLD_0)) { ++ ethX->resource[0].start += TS_ETH100_MAC_0; ++ ethX->resource[0].end += TS_ETH100_MAC_0; ++ ethX->resource[1].start += TS_ETH100_MAC_0; ++ ethX->resource[1].end += TS_ETH100_MAC_0; ++ ts_eth100_irq = read_irq(iomem, TS_ETH100_PLD_0); ++ } else if(is_eth100_present(iomem, TS_ETH100_PLD_1)) { ++ ethX->resource[0].start += TS_ETH100_MAC_1; ++ ethX->resource[0].end += TS_ETH100_MAC_1; ++ ethX->resource[1].start += TS_ETH100_MAC_1; ++ ethX->resource[1].end += TS_ETH100_MAC_1; ++ ts_eth100_irq = read_irq(iomem, TS_ETH100_PLD_1); ++ } else if(is_eth100_present(iomem, TS_ETH100_PLD_2)) { ++ ethX->resource[0].start += TS_ETH100_MAC_2; ++ ethX->resource[0].end += TS_ETH100_MAC_2; ++ ethX->resource[1].start += TS_ETH100_MAC_2; ++ ethX->resource[1].end += TS_ETH100_MAC_2; ++ ts_eth100_irq = read_irq(iomem, TS_ETH100_PLD_2); ++ } else if(is_eth100_present(iomem, TS_ETH100_PLD_3)) { ++ ethX->resource[0].start += TS_ETH100_MAC_3; ++ ethX->resource[0].end += TS_ETH100_MAC_3; ++ ethX->resource[1].start += TS_ETH100_MAC_3; ++ ethX->resource[1].end += TS_ETH100_MAC_3; ++ ts_eth100_irq = read_irq(iomem, TS_ETH100_PLD_3); ++ } else { ++ ethX = NULL; ++ } ++ ++ /* Translate IRQ number */ ++ if (ethX != NULL) { ++ int ret, irq = 0; ++ switch (ts_eth100_irq) { ++ case 0x2: /* IRQ5 */ ++ irq = gpio_to_irq(EP93XX_GPIO_LINE_F(3)); ++ ret = gpio_request(irq, "TS-ETH100"); ++ if (ret < 0) { ++ ethX = NULL; ++ goto init_error; ++ } else { ++ gpio_direction_input(irq); ++ set_irq_type(irq, IRQ_TYPE_EDGE_RISING); ++ } ++ break; ++ case 0x4: /* IRQ6 */ ++ irq = IRQ_EP93XX_EXT1; ++ break; ++ case 0x8: /* IRQ7 */ ++ default: ++ irq = IRQ_EP93XX_EXT3; ++ } ++ ethX->resource[2].start = irq; ++ ethX->resource[2].end = irq; ++ } ++init_error: ++ iounmap(iomem); ++ } ++ ++ return ((ethX == NULL) ? -ENODEV : ++ platform_device_register(&ts72xx_eth100_device_asix)); ++} ++ ++ ++static void __exit ts_eth100_exit(void) ++{ ++ platform_device_unregister(&ts72xx_eth100_device_asix); ++ if (ts_eth100_irq == 2) ++ gpio_free(EP93XX_GPIO_LINE_F(3)); ++} ++ ++module_init(ts_eth100_init); ++module_exit(ts_eth100_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("Asix 88796 ethernet probe module for TS-ETH100 (TS-72xx)"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.21"); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0009-ts72xx_pata.patch b/recipes/linux/linux-2.6.34/ts72xx/0009-ts72xx_pata.patch new file mode 100644 index 0000000000..1575e0e96c --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0009-ts72xx_pata.patch @@ -0,0 +1,432 @@ +From 2500e39a54c9c37abbddd1ad552575b24420e1a8 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sat, 19 Jun 2010 11:25:31 +0200 +Subject: [PATCH 09/18] ts72xx_pata + +Support: +TS-7200 - Compact flash +TS-9600 - IDE interface PC/104 peripheral +--- + drivers/ata/Kconfig | 20 +++++ + drivers/ata/Makefile | 3 + + drivers/ata/pata_ts7200_cf.c | 92 ++++++++++++++++++++++++ + drivers/ata/pata_ts72xx.c | 161 ++++++++++++++++++++++++++++++++++++++++++ + drivers/ata/pata_ts9600.c | 95 +++++++++++++++++++++++++ + 5 files changed, 371 insertions(+), 0 deletions(-) + create mode 100644 drivers/ata/pata_ts7200_cf.c + create mode 100644 drivers/ata/pata_ts72xx.c + create mode 100644 drivers/ata/pata_ts9600.c + +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index 01c52c4..2d26789 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -799,5 +799,25 @@ config PATA_MACIO + different chipsets, though generally, MacIO is one of them. + + ++config PATA_TS72XX ++ bool "TS72XX ATA support" ++ depends on ARCH_EP93XX && MACH_TS72XX ++ help ++ This option enables support for ATA devices on Technologic Systems SBC. ++ ++config PATA_TS7200_CF ++ tristate "TS-7200 Compact Flash support" ++ depends on PATA_TS72XX ++ help ++ This option enables support for the compact flash control on ++ Technologic System TS-7200 SBC. ++ ++config PATA_TS9600 ++ tristate "TS-9600 IDE interface support" ++ depends on PATA_TS72XX && BLK_DEV_IDE_TS9600 != y ++ help ++ This option enables support for Technologic Systems TS-9600 PC/104 IDE interface. ++ + endif # ATA_SFF ++ + endif # ATA +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index fc936d4..183ce00 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -79,6 +79,9 @@ obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o + obj-$(CONFIG_PATA_AT91) += pata_at91.o + obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o + obj-$(CONFIG_PATA_ICSIDE) += pata_icside.o ++obj-$(CONFIG_PATA_TS72XX) += pata_ts72xx.o ++obj-$(CONFIG_PATA_TS7200_CF) += pata_ts7200_cf.o ++obj-$(CONFIG_PATA_TS9600) += pata_ts9600.o + # Should be last but two libata driver + obj-$(CONFIG_PATA_ACPI) += pata_acpi.o + # Should be last but one libata driver +diff --git a/drivers/ata/pata_ts7200_cf.c b/drivers/ata/pata_ts7200_cf.c +new file mode 100644 +index 0000000..4126682 +--- /dev/null ++++ b/drivers/ata/pata_ts7200_cf.c +@@ -0,0 +1,92 @@ ++/* ++ * Technologic Systems TS-7200 Compact Flash PATA device driver. ++ * ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.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, 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. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/libata.h> ++#include <scsi/scsi_host.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <mach/ts72xx.h> ++ ++#define DRV_NAME "pata_ts7200_cf" ++#define DRV_VERSION "0.21" ++ ++ ++static void pata_ts7200_cf_release(struct device *dev) ++{ ++ /* nothing to do (no kfree) because we have static struct */ ++} ++ ++static struct resource ts7200_cf_resources[] = { ++ [0] = { ++ .start = TS7200_CF_CMD_PHYS_BASE, ++ .end = TS7200_CF_CMD_PHYS_BASE + 8, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = TS7200_CF_AUX_PHYS_BASE, ++ .end = TS7200_CF_AUX_PHYS_BASE + 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [2] = { ++ .start = TS7200_CF_DATA_PHYS_BASE, ++ .end = TS7200_CF_DATA_PHYS_BASE + 2, ++ .flags = IORESOURCE_MEM, ++ }, ++ [3] = { ++ .start = IRQ_EP93XX_EXT0, /* pin 103 of EP9301 */ ++ .end = IRQ_EP93XX_EXT0, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++ ++static struct platform_device ts7200_cf_device = { ++ .name = "ts72xx-ide", ++ .id = 0, ++ .dev = { ++ .dma_mask = &ts7200_cf_device.dev.coherent_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ .release = pata_ts7200_cf_release, ++ }, ++ .num_resources = ARRAY_SIZE(ts7200_cf_resources), ++ .resource = ts7200_cf_resources, ++}; ++ ++ ++static __init int pata_ts7200_cf_init(void) ++{ ++ return (board_is_ts7200()) ? \ ++ platform_device_register(&ts7200_cf_device) : -ENODEV; ++} ++ ++static __exit void pata_ts7200_cf_exit(void) ++{ ++ platform_device_unregister(&ts7200_cf_device); ++} ++ ++module_init(pata_ts7200_cf_init); ++module_exit(pata_ts7200_cf_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("TS-7200 CF PATA device driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); +diff --git a/drivers/ata/pata_ts72xx.c b/drivers/ata/pata_ts72xx.c +new file mode 100644 +index 0000000..d540029 +--- /dev/null ++++ b/drivers/ata/pata_ts72xx.c +@@ -0,0 +1,161 @@ ++/* ++ * TS-72XX PATA driver for Technologic Systems boards. ++ * ++ * Based on pata_platform.c by Paul Mundt & ++ * Alessandro Zummo <a.zummo@towertech.it> ++ * and old pata-ts72xx.c by Alessandro Zummo <a.zummo@towertech.it> ++ * ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.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, 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. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/interrupt.h> ++#include <scsi/scsi_host.h> ++#include <linux/ata.h> ++#include <linux/libata.h> ++ ++#define DRV_NAME "pata_ts72xx" ++#define DRV_VERSION "2.01" ++ ++ ++/* ++ * Provide our own set_mode() as we don't want to change anything that has ++ * already been configured.. ++ */ ++static int ts72xx_set_mode(struct ata_link *link, struct ata_device **unused) ++{ ++ struct ata_device *dev; ++ ++ ata_for_each_dev(dev, link, ENABLED) { ++ if (ata_dev_enabled(dev)) { ++ /* We don't really care */ ++ dev->pio_mode = dev->xfer_mode = XFER_PIO_0; ++ dev->xfer_shift = ATA_SHIFT_PIO; ++ dev->flags |= ATA_DFLAG_PIO; ++ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n"); ++ } ++ } ++ return 0; ++} ++ ++static struct scsi_host_template ts72xx_sht = { ++ ATA_PIO_SHT(DRV_NAME), ++}; ++ ++static struct ata_port_operations ts72xx_port_ops = { ++ .inherits = &ata_sff_port_ops, ++ .set_mode = ts72xx_set_mode, ++}; ++ ++static __devinit int ts72xx_pata_probe(struct platform_device *pdev) ++{ ++ struct ata_host *host; ++ struct ata_port *ap; ++ int irq; ++ ++ struct resource *pata_cmd = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct resource *pata_aux = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ struct resource *pata_data = platform_get_resource(pdev, IORESOURCE_MEM, 2); ++ ++ if (!pata_cmd || !pata_aux || !pata_data) { ++ dev_err(&pdev->dev, "missing resource(s)\n"); ++ return -EINVAL; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ irq = 0; /* no irq */ ++ ++ /* ++ * Now that that's out of the way, wire up the port ++ */ ++ host = ata_host_alloc(&pdev->dev, 1); ++ if (!host) ++ return -ENOMEM; ++ ap = host->ports[0]; ++ ++ ap->ops = &ts72xx_port_ops; ++ ap->pio_mask = 0x1f; /* PIO0-4 */ ++ ap->flags |= ATA_FLAG_SLAVE_POSS; ++ ++ /* ++ * Use polling mode if there's no IRQ ++ */ ++ if (!irq) { ++ ap->flags |= ATA_FLAG_PIO_POLLING; ++ ata_port_desc(ap, "no IRQ, using PIO polling"); ++ } ++ ++ ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, pata_cmd->start, ++ pata_cmd->end - pata_cmd->start + 1); ++ ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, pata_aux->start, ++ pata_aux->end - pata_aux->start + 1); ++ ++ if (!ap->ioaddr.cmd_addr || !ap->ioaddr.ctl_addr) { ++ dev_err(&pdev->dev, "failed to map IO/CTL base\n"); ++ return -ENOMEM; ++ } ++ ++ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; ++ ++ ata_sff_std_ports(&ap->ioaddr); ++ ap->ioaddr.data_addr = devm_ioremap(&pdev->dev, pata_data->start, ++ pata_data->end - pata_data->start + 1); ++ ++ ata_port_desc(ap, "mmio cmd 0x%llx ctl 0x%llx", ++ (unsigned long long)pata_cmd->start, ++ (unsigned long long)pata_aux->start); ++ ++ return ata_host_activate(host, irq, irq ? ata_sff_interrupt : NULL, ++ 0 /* irq flags */, &ts72xx_sht); ++} ++ ++static __devexit int ts72xx_pata_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_host *host = dev_get_drvdata(dev); ++ ++ ata_host_detach(host); ++ ++ return 0; ++} ++ ++static struct platform_driver ts72xx_pata_platform_driver = { ++ .probe = ts72xx_pata_probe, ++ .remove = __devexit_p(ts72xx_pata_remove), ++ .driver = { ++ .name = "ts72xx-ide", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init ts72xx_pata_init(void) ++{ ++ return platform_driver_register(&ts72xx_pata_platform_driver); ++} ++ ++static void __exit ts72xx_pata_exit(void) ++{ ++ platform_driver_unregister(&ts72xx_pata_platform_driver); ++} ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("low-level driver for TS-72xx device PATA"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); ++ ++module_init(ts72xx_pata_init); ++module_exit(ts72xx_pata_exit); +diff --git a/drivers/ata/pata_ts9600.c b/drivers/ata/pata_ts9600.c +new file mode 100644 +index 0000000..7a70550 +--- /dev/null ++++ b/drivers/ata/pata_ts9600.c +@@ -0,0 +1,95 @@ ++/* ++ * Technologic Systems TS-9600 PATA device driver. ++ * ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.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, 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. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/libata.h> ++#include <scsi/scsi_host.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <mach/ts72xx.h> ++ ++#define DRV_NAME "pata_ts9600" ++#define DRV_VERSION "0.21" ++ ++#define TS9600_IDE_IO (TS72XX_PC104_8BIT_IO_PHYS_BASE + 0x1F0) ++#define TS9600_IDE_DATA (TS72XX_PC104_16BIT_IO_PHYS_BASE + 0x1F0) ++#define TS9600_IDE_IRQ IRQ_EP93XX_EXT3 // IRQ7 (no other possibility for arm) ++ ++ ++static void pata_ts9600_release(struct device *dev) ++{ ++ /* nothing to do (no kfree) because we have static struct */ ++} ++ ++static struct resource ts9600_resources[] = { ++ [0] = { ++ .start = TS9600_IDE_IO, ++ .end = TS9600_IDE_IO + 8, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = TS9600_IDE_IO + 0x206, ++ .end = TS9600_IDE_IO + 0x206 + 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [2] = { ++ .start = TS9600_IDE_DATA, ++ .end = TS9600_IDE_DATA + 2, ++ .flags = IORESOURCE_MEM, ++ }, ++ [3] = { ++ .start = TS9600_IDE_IRQ, ++ .end = TS9600_IDE_IRQ, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++ ++static struct platform_device ts9600_device = { ++ .name = "ts72xx-ide", ++ .id = 9600, ++ .dev = { ++ .dma_mask = &ts9600_device.dev.coherent_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ .release = pata_ts9600_release, ++ }, ++ .num_resources = ARRAY_SIZE(ts9600_resources), ++ .resource = ts9600_resources, ++}; ++ ++ ++static __init int pata_ts9600_init(void) ++{ ++ return platform_device_register(&ts9600_device); ++} ++ ++static __exit void pata_ts9600_exit(void) ++{ ++ platform_device_unregister(&ts9600_device); ++} ++ ++module_init(pata_ts9600_init); ++module_exit(pata_ts9600_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("TS-9600 PATA device driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0010-ts72xx_gpio_i2c.patch b/recipes/linux/linux-2.6.34/ts72xx/0010-ts72xx_gpio_i2c.patch new file mode 100644 index 0000000000..b984736ffc --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0010-ts72xx_gpio_i2c.patch @@ -0,0 +1,58 @@ +From 93e23786cb9a055fca63f4bdfdfaeff63bae745b Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sat, 19 Jun 2010 11:45:39 +0200 +Subject: [PATCH 10/18] ts72xx_gpio_i2c + +--- + arch/arm/mach-ep93xx/ts72xx.c | 21 +++++++++++++++++++++ + 1 files changed, 21 insertions(+), 0 deletions(-) + +diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c +index 37325c4..325b7af 100644 +--- a/arch/arm/mach-ep93xx/ts72xx.c ++++ b/arch/arm/mach-ep93xx/ts72xx.c +@@ -16,6 +16,9 @@ + #include <linux/io.h> + #include <linux/m48t86.h> + #include <linux/mtd/physmap.h> ++#include <linux/gpio.h> ++#include <linux/i2c.h> ++#include <linux/i2c-gpio.h> + + #include <mach/hardware.h> + #include <mach/ts72xx.h> +@@ -225,6 +228,21 @@ static struct ep93xx_eth_data ts72xx_eth_data = { + .phy_id = 1, + }; + ++/************************************************************************* ++ * I2C (make access through TS-72XX "DIO" 2x8 header) ++ *************************************************************************/ ++static struct i2c_gpio_platform_data ts72xx_i2c_gpio_data = { ++ .sda_pin = EP93XX_GPIO_LINE_EGPIO14, // DIO_6 ++ .sda_is_open_drain = 0, ++ .scl_pin = EP93XX_GPIO_LINE_EGPIO15, // DIO_7 ++ .scl_is_open_drain = 0, ++ .udelay = 0, /* default is 100 kHz */ ++ .timeout = 0, /* default is 100 ms */ ++}; ++ ++static struct i2c_board_info __initdata ts72xx_i2c_board_info[] = { ++}; ++ + static void __init ts72xx_init_machine(void) + { + ep93xx_init_devices(); +@@ -233,6 +251,9 @@ static void __init ts72xx_init_machine(void) + platform_device_register(&ts72xx_wdt_device); + + ep93xx_register_eth(&ts72xx_eth_data, 1); ++ ep93xx_register_i2c(&ts72xx_i2c_gpio_data, ++ ts72xx_i2c_board_info, ++ ARRAY_SIZE(ts72xx_i2c_board_info)); + + /* PWM1 is DIO_6 on TS-72xx header */ + ep93xx_register_pwm(0, 1); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0011-ts72xx_dio_keypad.patch b/recipes/linux/linux-2.6.34/ts72xx/0011-ts72xx_dio_keypad.patch new file mode 100644 index 0000000000..cced7842bc --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0011-ts72xx_dio_keypad.patch @@ -0,0 +1,306 @@ +From c1515f196bfc70cad5f9a755bf0038f190fd8c22 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sat, 19 Jun 2010 14:44:32 +0200 +Subject: [PATCH 11/18] ts72xx_dio_keypad + +Depends of "matrix-keypad" driver. +--- + drivers/input/keyboard/Kconfig | 30 ++++++++ + drivers/input/keyboard/Makefile | 2 + + drivers/input/keyboard/ts72xx_dio_3x4.c | 110 +++++++++++++++++++++++++++++ + drivers/input/keyboard/ts72xx_dio_4x4.c | 115 +++++++++++++++++++++++++++++++ + 4 files changed, 257 insertions(+), 0 deletions(-) + create mode 100644 drivers/input/keyboard/ts72xx_dio_3x4.c + create mode 100644 drivers/input/keyboard/ts72xx_dio_4x4.c + +diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig +index 64c1023..4ca7d48 100644 +--- a/drivers/input/keyboard/Kconfig ++++ b/drivers/input/keyboard/Kconfig +@@ -201,6 +201,36 @@ config KEYBOARD_MATRIX + To compile this driver as a module, choose M here: the + module will be called matrix_keypad. + ++if KEYBOARD_MATRIX ++ ++choice ++ prompt "Keypad type" ++ default TS72XX_DIO_4X4_KEYPAD ++ ++config TS72XX_DIO_3X4_KEYPAD ++ tristate "TS-72xx 3x4 matrix keypad" ++ depends on MACH_TS72XX ++ help ++ This a 12 keys (4 rows, 3 cols using DIO_0-6) keypad with the following layout: ++ 1 2 3 ++ 4 5 6 ++ 7 8 9 ++ * 0 # ++ ++config TS72XX_DIO_4X4_KEYPAD ++ tristate "TS-72xx 4x4 matrix keypad" ++ depends on MACH_TS72XX ++ help ++ This a 16 keys (4 rows, 4 cols using DIO_0-7) keypad with the following layout: ++ 7 8 9 F ++ 4 5 6 E ++ 1 2 3 D ++ A 0 B C ++ ++endchoice ++ ++endif # KEYBOARD_MATRIX ++ + config KEYBOARD_HIL_OLD + tristate "HP HIL keyboard support (simple driver)" + depends on GSC || HP300 +diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile +index 706c6b5..f81f1c1 100644 +--- a/drivers/input/keyboard/Makefile ++++ b/drivers/input/keyboard/Makefile +@@ -40,3 +40,5 @@ obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o + obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o + obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o + obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o ++obj-$(CONFIG_TS72XX_DIO_3X4_KEYPAD) += ts72xx_dio_3x4.o ++obj-$(CONFIG_TS72XX_DIO_4X4_KEYPAD) += ts72xx_dio_4x4.o +diff --git a/drivers/input/keyboard/ts72xx_dio_3x4.c b/drivers/input/keyboard/ts72xx_dio_3x4.c +new file mode 100644 +index 0000000..e214335 +--- /dev/null ++++ b/drivers/input/keyboard/ts72xx_dio_3x4.c +@@ -0,0 +1,110 @@ ++/* ++ * TS-72xx (3x4) keypad device driver for DIO1 header (DIO_0 thru DIO_6) ++ * ++ * (c) Copyright 2010 Matthieu Crapet <mcrapet@gmail.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, 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. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/input/matrix_keypad.h> ++#include <mach/gpio.h> ++ ++static const uint32_t ts72xx_kbd_keymap[] = { ++ KEY(0, 0, KEY_1), ++ KEY(0, 1, KEY_2), ++ KEY(0, 2, KEY_3), ++ ++ KEY(1, 0, KEY_4), ++ KEY(1, 1, KEY_5), ++ KEY(1, 2, KEY_6), ++ ++ KEY(2, 0, KEY_7), ++ KEY(2, 1, KEY_8), ++ KEY(2, 2, KEY_9), ++ ++ KEY(3, 0, KEY_KPASTERISK), ++ KEY(3, 1, KEY_0), ++ KEY(3, 2, KEY_ENTER), ++}; ++ ++static struct matrix_keymap_data ts72xx_kbd_keymap_data = { ++ .keymap = ts72xx_kbd_keymap, ++ .keymap_size = ARRAY_SIZE(ts72xx_kbd_keymap), ++}; ++ ++static const int ts72xx_kbd_row_gpios[] = { ++ EP93XX_GPIO_LINE_EGPIO14, // DIO_6 (row0) ++ EP93XX_GPIO_LINE_EGPIO13, ++ EP93XX_GPIO_LINE_EGPIO12, ++ EP93XX_GPIO_LINE_EGPIO11, ++}; ++ ++static const int ts72xx_kbd_col_gpios[] = { ++ EP93XX_GPIO_LINE_EGPIO10, // DIO_2 (col0) ++ EP93XX_GPIO_LINE_EGPIO9, ++ EP93XX_GPIO_LINE_EGPIO8, ++}; ++ ++static struct matrix_keypad_platform_data ts72xx_kbd_pdata = { ++ .keymap_data = &ts72xx_kbd_keymap_data, ++ .row_gpios = ts72xx_kbd_row_gpios, ++ .col_gpios = ts72xx_kbd_col_gpios, ++ .num_row_gpios = ARRAY_SIZE(ts72xx_kbd_row_gpios), ++ .num_col_gpios = ARRAY_SIZE(ts72xx_kbd_col_gpios), ++ .col_scan_delay_us = 20, ++ .debounce_ms = 20, ++ .wakeup = 1, ++ .active_low = 1, ++ //.no_autorep = 1, ++}; ++ ++static void ts72xx_kbd_release(struct device *dev) ++{ ++} ++ ++static struct platform_device ts72xx_kbd_device = { ++ .name = "matrix-keypad", ++ .id = -1, ++ .dev = { ++ .platform_data = &ts72xx_kbd_pdata, ++ .release = ts72xx_kbd_release, ++ }, ++}; ++ ++static int __init ts72xx_dio_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ts72xx_kbd_row_gpios); i++) { ++ int irq = gpio_to_irq(ts72xx_kbd_row_gpios[i]); ++ ++ ep93xx_gpio_int_debounce(irq, 1); ++ } ++ ++ return platform_device_register(&ts72xx_kbd_device); ++} ++ ++static void __exit ts72xx_dio_exit(void) ++{ ++ platform_device_unregister(&ts72xx_kbd_device); ++} ++ ++module_init(ts72xx_dio_init); ++module_exit(ts72xx_dio_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("Platform device 3x4 keypad"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/keyboard/ts72xx_dio_4x4.c b/drivers/input/keyboard/ts72xx_dio_4x4.c +new file mode 100644 +index 0000000..790abd5 +--- /dev/null ++++ b/drivers/input/keyboard/ts72xx_dio_4x4.c +@@ -0,0 +1,115 @@ ++/* ++ * TS-72xx (4x4) keypad device driver for DIO1 header (DIO_0 thru DIO_7) ++ * ++ * (c) Copyright 2010 Matthieu Crapet <mcrapet@gmail.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, 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. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/input/matrix_keypad.h> ++#include <mach/gpio.h> ++ ++static const uint32_t ts72xx_kbd_keymap[] = { ++ KEY(0, 0, KEY_7), ++ KEY(0, 1, KEY_8), ++ KEY(0, 2, KEY_9), ++ KEY(0, 3, KEY_F), ++ ++ KEY(1, 0, KEY_4), ++ KEY(1, 1, KEY_5), ++ KEY(1, 2, KEY_6), ++ KEY(1, 3, KEY_E), ++ ++ KEY(2, 0, KEY_1), ++ KEY(2, 1, KEY_2), ++ KEY(2, 2, KEY_3), ++ KEY(2, 3, KEY_D), ++ ++ KEY(3, 0, KEY_A), ++ KEY(3, 1, KEY_0), ++ KEY(3, 2, KEY_B), ++ KEY(3, 3, KEY_C), ++}; ++ ++static struct matrix_keymap_data ts72xx_kbd_keymap_data = { ++ .keymap = ts72xx_kbd_keymap, ++ .keymap_size = ARRAY_SIZE(ts72xx_kbd_keymap), ++}; ++ ++static const int ts72xx_kbd_row_gpios[] = { ++ EP93XX_GPIO_LINE_EGPIO8, // DIO_0 (row0) ++ EP93XX_GPIO_LINE_EGPIO9, ++ EP93XX_GPIO_LINE_EGPIO12, ++ EP93XX_GPIO_LINE_EGPIO14, ++}; ++ ++static const int ts72xx_kbd_col_gpios[] = { ++ EP93XX_GPIO_LINE_EGPIO15, // DIO_7 (col0) ++ EP93XX_GPIO_LINE_EGPIO13, ++ EP93XX_GPIO_LINE_EGPIO11, ++ EP93XX_GPIO_LINE_EGPIO10, ++}; ++ ++static struct matrix_keypad_platform_data ts72xx_kbd_pdata = { ++ .keymap_data = &ts72xx_kbd_keymap_data, ++ .row_gpios = ts72xx_kbd_row_gpios, ++ .col_gpios = ts72xx_kbd_col_gpios, ++ .num_row_gpios = ARRAY_SIZE(ts72xx_kbd_row_gpios), ++ .num_col_gpios = ARRAY_SIZE(ts72xx_kbd_col_gpios), ++ .col_scan_delay_us = 20, ++ .debounce_ms = 20, ++ .wakeup = 1, ++ .active_low = 1, ++ //.no_autorep = 1, ++}; ++ ++static void ts72xx_kbd_release(struct device *dev) ++{ ++} ++ ++static struct platform_device ts72xx_kbd_device = { ++ .name = "matrix-keypad", ++ .id = -1, ++ .dev = { ++ .platform_data = &ts72xx_kbd_pdata, ++ .release = ts72xx_kbd_release, ++ }, ++}; ++ ++static int __init ts72xx_dio_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ts72xx_kbd_row_gpios); i++) { ++ int irq = gpio_to_irq(ts72xx_kbd_row_gpios[i]); ++ ++ ep93xx_gpio_int_debounce(irq, 1); ++ } ++ ++ return platform_device_register(&ts72xx_kbd_device); ++} ++ ++static void __exit ts72xx_dio_exit(void) ++{ ++ platform_device_unregister(&ts72xx_kbd_device); ++} ++ ++module_init(ts72xx_dio_init); ++module_exit(ts72xx_dio_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("Platform device 4x4 keypad"); ++MODULE_LICENSE("GPL"); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0012-ts72xx_sbcinfo.patch b/recipes/linux/linux-2.6.34/ts72xx/0012-ts72xx_sbcinfo.patch new file mode 100644 index 0000000000..8a54f2c0c3 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0012-ts72xx_sbcinfo.patch @@ -0,0 +1,263 @@ +From b663f02d95027d89b42f57072adf6ea83adb78dd Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sat, 19 Jun 2010 15:08:58 +0200 +Subject: [PATCH 12/18] ts72xx_sbcinfo + +--- + arch/arm/mach-ep93xx/Kconfig | 7 + + arch/arm/mach-ep93xx/Makefile | 1 + + arch/arm/mach-ep93xx/ts72xx.c | 5 + + arch/arm/mach-ep93xx/ts72xx_sbcinfo.c | 198 +++++++++++++++++++++++++++++++++ + 4 files changed, 211 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/mach-ep93xx/ts72xx_sbcinfo.c + +diff --git a/arch/arm/mach-ep93xx/Kconfig b/arch/arm/mach-ep93xx/Kconfig +index bd463a0..2bfb01e 100644 +--- a/arch/arm/mach-ep93xx/Kconfig ++++ b/arch/arm/mach-ep93xx/Kconfig +@@ -212,6 +212,13 @@ config MACH_TS72XX_FORCE_MACHINEID + Say 'Y' here to force Machine ID to 0x2A1 (MACH_TYPE_TS72XX legacy value) + In early days Technologic Systems fixed the 0x163 value in redboot. + ++config MACH_TS72XX_SBCINFO ++ tristate "Add procfs /proc/driver/sbcinfo" ++ depends on MACH_TS72XX ++ help ++ Say 'Y' to add a procfs entry containing some information ++ related to Technologic Systems TS-72xx SBC. ++ + endmenu + + endif +diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile +index ea652c2..c38d1e2 100644 +--- a/arch/arm/mach-ep93xx/Makefile ++++ b/arch/arm/mach-ep93xx/Makefile +@@ -13,3 +13,4 @@ obj-$(CONFIG_MACH_MICRO9) += micro9.o + obj-$(CONFIG_MACH_SIM_ONE) += simone.o + obj-$(CONFIG_MACH_SNAPPER_CL15) += snappercl15.o + obj-$(CONFIG_MACH_TS72XX) += ts72xx.o ++obj-$(CONFIG_MACH_TS72XX_SBCINFO) += ts72xx_sbcinfo.o +diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c +index 325b7af..a46b370 100644 +--- a/arch/arm/mach-ep93xx/ts72xx.c ++++ b/arch/arm/mach-ep93xx/ts72xx.c +@@ -35,6 +35,11 @@ static struct map_desc ts72xx_io_desc[] __initdata = { + .length = TS72XX_MODEL_SIZE, + .type = MT_DEVICE, + }, { ++ .virtual = TS72XX_PLD_VERSION_VIRT_BASE, ++ .pfn = __phys_to_pfn(TS72XX_PLD_VERSION_PHYS_BASE), ++ .length = TS72XX_PLD_VERSION_SIZE, ++ .type = MT_DEVICE, ++ }, { + .virtual = TS72XX_OPTIONS_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_OPTIONS_PHYS_BASE), + .length = TS72XX_OPTIONS_SIZE, +diff --git a/arch/arm/mach-ep93xx/ts72xx_sbcinfo.c b/arch/arm/mach-ep93xx/ts72xx_sbcinfo.c +new file mode 100644 +index 0000000..cbb485f +--- /dev/null ++++ b/arch/arm/mach-ep93xx/ts72xx_sbcinfo.c +@@ -0,0 +1,198 @@ ++/* ++ * Technologic Systems TS-72XX sbc /proc/driver/sbcinfo entry. ++ * ++ * Original idea by Liberty Young (Technologic Systems). ++ * ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.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, or (at your option) any later version. ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/proc_fs.h> ++#include <mach/hardware.h> ++#include <mach/ts72xx.h> ++ ++struct infos { ++ const char *cpu_rev; ++ int model, pld, wdt; ++ int option_ad; ++ int option_rs485; ++ unsigned char jumpers[6]; // 0=off,1=on,2=error ++ ++ /* Power management : TS-7260 only */ ++ int pm; ++}; ++ ++static const char *revisions[] = { "A", "B", "C", "D0", "D1", "E0", "E1", "E2", "??" }; ++ ++ ++static void get_sbcinfo(struct infos *data) ++{ ++ void __iomem *p; ++ short rev; ++ ++ /* CPU revision */ ++ rev = __raw_readl(EP93XX_SYSCON_CHIPID) >> 28; ++ if (rev > ARRAY_SIZE(revisions)) ++ rev = ARRAY_SIZE(revisions) - 1; ++ data->cpu_rev = revisions[rev]; ++ ++ /* Board model */ ++ if (board_is_ts7200()) ++ data->model = 7200; ++ else if (board_is_ts7250()) ++ data->model = 7250; ++ else if (board_is_ts7260()) ++ data->model = 7260; ++ else if (board_is_ts7400()) ++ data->model = 7400; ++ else ++ data->model = 0; ++ ++ data->pld = get_ts72xx_pld_version(); ++ ++ /* A/D converter (8 x 12-bit channels) */ ++ if (data->model == 7200 || data->model == 7250) { ++ data->option_ad = is_max197_installed(); ++ } else { ++ data->option_ad = 0; ++ } ++ ++ /* COM2 RS-485 */ ++ if (is_rs485_installed()) { ++ data->option_rs485 = 1; ++ } else { ++ data->option_rs485 = 0; ++ } ++ ++ /* jumpers */ ++ p = ioremap(TS72XX_JUMPERS_MAX197_PHYS_BASE, SZ_4K - 1); ++ if (p) { ++ unsigned char c = __raw_readb(p); ++ ++ data->jumpers[0] = 2; // JP1 (bootstrap) ++ data->jumpers[1] = !!(c & 0x01); // JP2 (enable serial console) ++ data->jumpers[2] = !!(c & 0x02); // JP3 (flash write enable) ++ data->jumpers[3] = !(c & 0x08); // JP4 (console on COM2) ++ data->jumpers[4] = !(c & 0x10); // JP5 (test) ++ data->jumpers[5] = !!(is_jp6_set()); // JP6 (user jumper) ++ ++ iounmap(p); ++ } else { ++ data->jumpers[0] = data->jumpers[1] = data->jumpers[2] = 2; ++ data->jumpers[3] = data->jumpers[4] = data->jumpers[5] = 2; ++ } ++ ++ /* cpld watchdog */ ++ p = ioremap(TS72XX_WDT_CONTROL_PHYS_BASE, SZ_4K - 1); ++ if (p) { ++ data->wdt = __raw_readb(p) & 0x7; ++ iounmap(p); ++ } else { ++ data->wdt = 8; ++ } ++ ++ /* power management */ ++ data->pm = -1; ++ if (data->model == 7260) { ++ p = ioremap(TS7260_POWER_MANAGEMENT_PHYS_BASE, SZ_4K - 1); ++ if (p) { ++ data->pm = __raw_readb(p); ++ iounmap(p); ++ } ++ } ++} ++ ++static char *get_pm_string(int reg, char *buffer, size_t size) ++{ ++ static const char *pm_state = "rs232=%d usb=%d lcd=%d pc104=%d ttl=%d"; ++ ++ if (reg < 0) { ++ strncpy(buffer, "n/a", size); ++ } else { ++ /* 1 means on/enabled */ ++ snprintf(buffer, size, pm_state, ++ reg & TS7260_PM_RS232_LEVEL_CONVERTER, ++ !!(reg & TS7260_PM_USB), ++ !!(reg & TS7260_PM_LCD), ++ !(reg & TS7260_PM_PC104_CLOCK), ++ !!(reg & TS7260_PM_TTL_UART_ENABLE)); ++ } ++ return buffer; ++} ++ ++static int ts72xx_sbcinfo_read_proc(char *buffer, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int len, size = count; ++ char *p = buffer; ++ char temp[64]; ++ struct infos nfo; ++ ++ static const char jpc[3] = { 'n', 'y', '?' }; ++ static const char *wdt[9] = { "disabled", "250ms", "500ms", "1s", "reserved", "2s", "4s", "8s", "n/a" }; ++ ++ get_sbcinfo(&nfo); ++ len = scnprintf(p, size, ++ "Model : TS-%d (CPU rev %s) (PLD rev %c)\n" ++ "Option max197 A/D : %s\n" ++ "Option RS-485 : %s\n" ++ "Jumpers : JP2=%c JP3=%c JP4=%c JP5=%c JP6=%c\n" ++ "CPLD Watchdog : %s\n" ++ "Power management : %s\n", ++ nfo.model, nfo.cpu_rev, nfo.pld + 0x40, ++ (nfo.option_ad ? "yes" : "no"), ++ (nfo.option_rs485 ? "yes" : "no"), ++ jpc[nfo.jumpers[1]], jpc[nfo.jumpers[2]], jpc[nfo.jumpers[3]], jpc[nfo.jumpers[4]], ++ jpc[nfo.jumpers[5]], wdt[nfo.wdt], ++ get_pm_string(nfo.pm, &temp[0], sizeof(temp))); ++ ++ if (len <= offset + count) ++ *eof = 1; ++ ++ *start = buffer + offset; ++ len -= offset; ++ ++ if (len > count) ++ len = count; ++ if (len < 0) ++ len = 0; ++ ++ return len; ++} ++ ++static int __init ts72xx_sbcinfo_init(void) ++{ ++ struct proc_dir_entry *entry; ++ int ret = 0; ++ ++ entry = create_proc_read_entry("driver/sbcinfo", 0, ++ NULL, ts72xx_sbcinfo_read_proc, NULL); ++ ++ if (!entry) { ++ printk(KERN_ERR "sbcinfo: can't create /proc/driver/sbcinfo\n"); ++ ret = -ENOMEM; ++ } ++ ++ return ret; ++} ++ ++static void __exit ts72xx_sbcinfo_exit(void) ++{ ++ remove_proc_entry("driver/sbcinfo", NULL); ++} ++ ++module_init(ts72xx_sbcinfo_init); ++module_exit(ts72xx_sbcinfo_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("Show information of Technologic Systems TS-72XX sbc"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.04"); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0013-ts72xx_max197.patch b/recipes/linux/linux-2.6.34/ts72xx/0013-ts72xx_max197.patch new file mode 100644 index 0000000000..86f52a3fbb --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0013-ts72xx_max197.patch @@ -0,0 +1,347 @@ +From 93aa4fc5d32534422e80bbb7d1929f533c4ce65f Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sat, 19 Jun 2010 15:49:34 +0200 +Subject: [PATCH 13/18] ts72xx_max197 + +--- + arch/arm/mach-ep93xx/ts72xx.c | 30 +++++ + drivers/misc/Kconfig | 21 ++++ + drivers/misc/Makefile | 1 + + drivers/misc/ts72xx_max197.c | 235 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 287 insertions(+), 0 deletions(-) + create mode 100644 drivers/misc/ts72xx_max197.c + +diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c +index a46b370..ba27d9d 100644 +--- a/arch/arm/mach-ep93xx/ts72xx.c ++++ b/arch/arm/mach-ep93xx/ts72xx.c +@@ -227,6 +227,32 @@ static struct platform_device ts72xx_wdt_device = { + }; + + /************************************************************************* ++ * MAX197 (8 * 12-bit A/D converter) option ++ *************************************************************************/ ++static struct resource ts72xx_max197_resources[] = { ++ [0] = { /* sample/control register */ ++ .start = TS72XX_MAX197_SAMPLE_PHYS_BASE, ++ .end = TS72XX_MAX197_SAMPLE_PHYS_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { /* busy bit */ ++ .start = TS72XX_JUMPERS_MAX197_PHYS_BASE, ++ .end = TS72XX_JUMPERS_MAX197_PHYS_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device ts72xx_max197_device = { ++ .name = "ts72xx-max197", ++ .id = -1, ++ .dev = { ++ .platform_data = NULL, ++ }, ++ .num_resources = ARRAY_SIZE(ts72xx_max197_resources), ++ .resource = ts72xx_max197_resources, ++}; ++ ++/************************************************************************* + * Ethernet + *************************************************************************/ + static struct ep93xx_eth_data ts72xx_eth_data = { +@@ -260,6 +286,10 @@ static void __init ts72xx_init_machine(void) + ts72xx_i2c_board_info, + ARRAY_SIZE(ts72xx_i2c_board_info)); + ++ if (is_max197_installed()) { ++ platform_device_register(&ts72xx_max197_device); ++ } ++ + /* PWM1 is DIO_6 on TS-72xx header */ + ep93xx_register_pwm(0, 1); + } +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 0d0d625..45c4be8 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -332,4 +332,25 @@ source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" + source "drivers/misc/iwmc3200top/Kconfig" + ++config TS72XX_MAX197 ++ tristate "TS-72xx MAX197 support" ++ depends on ARCH_EP93XX && MACH_TS72XX && SYSFS ++ help ++ Say Y here if to include support for the MAX197 A/D converter ++ optionally included on Technologic Systems SBCs. ++ Default acquisition range is [0..5V]. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ts72xx_max197. ++ ++if TS72XX_MAX197 ++ ++config TS72XX_MAX197_AVERAGE ++ bool "Average measurement" ++ help ++ Say Y here to enable making average measurement. Default is 1. ++ See /sys/module/ts72xx_max197/parameters/average file. ++ ++endif # TS72XX_MAX197 ++ + endif # MISC_DEVICES +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 7b6f7ee..388f636 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -23,6 +23,7 @@ obj-$(CONFIG_HP_ILO) += hpilo.o + obj-$(CONFIG_ISL29003) += isl29003.o + obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o + obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o ++obj-$(CONFIG_TS72XX_MAX197) += ts72xx_max197.o + obj-$(CONFIG_DS1682) += ds1682.o + obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o + obj-$(CONFIG_C2PORT) += c2port/ +diff --git a/drivers/misc/ts72xx_max197.c b/drivers/misc/ts72xx_max197.c +new file mode 100644 +index 0000000..4121ae5 +--- /dev/null ++++ b/drivers/misc/ts72xx_max197.c +@@ -0,0 +1,235 @@ ++/* ++ * TS-72XX max197 driver for Technologic Systems boards. ++ * ++ * Voltage conversion is taken from adc_logger from Jim Jackson. ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.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, or (at your option) any later version. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <asm/io.h> ++ ++#define DRV_VERSION "0.2" ++#define PFX "ts72xx_max197: " ++ ++#define MAX197_RANGE_5_5 1 // [- 5V + 5V] ++#define MAX197_RANGE_10_10 3 // [-10V +10V] ++#define MAX197_RANGE_0_5 0 // [ 0V + 5V] ++#define MAX197_RANGE_0_10 2 // [ 0V +10V] ++ ++#define MAX197_RESET_CHANNEL_CONF(x) (~(3 << (2*(x)))) ++#define MAX197_SET_CHANNEL_CONF(x, range) ((range) << (2*(x))) ++#define MAX197_GET_CHANNEL_CONF(x, conf) (((conf) >> (2*(x))) & 3) ++ ++struct max197_config ++{ ++ void __iomem *control_and_data_register; ++ void __iomem *busy_bit_register; ++ unsigned int channels; // two bits per channels ++}; ++ ++static struct max197_config conf; ++#ifdef CONFIG_TS72XX_MAX197_AVERAGE ++static ushort average = 1; ++#endif ++ ++static ssize_t max197_acquire(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int range, n; ++ signed short val; ++#ifdef CONFIG_TS72XX_MAX197_AVERAGE ++ int i, total; ++#endif ++ ++ n = attr->attr.name[2] - 0x31; ++ range = MAX197_GET_CHANNEL_CONF(n, conf.channels); ++ ++#ifdef CONFIG_TS72XX_MAX197_AVERAGE ++ val = 0; total = 0; ++ for (i = 0; i < average; i++) { ++#endif ++ ++ __raw_writeb(((range << 3) | n | 0x40) & 0xFF, ++ conf.control_and_data_register); ++ while (__raw_readb(conf.busy_bit_register) & 0x80); ++ val = __raw_readw(conf.control_and_data_register); ++ ++ //printk(PFX "%hd/%hd: 0x%04X\n", i+1, average, val); ++ ++#ifdef CONFIG_TS72XX_MAX197_AVERAGE ++ total += val; ++ } ++ total /= average; ++ val = (signed short)total; ++#endif ++ ++ /* We want three digit precision */ ++ switch (range) { ++ case MAX197_RANGE_0_5: ++ val = ((val * 50000/4096)+5)/10; ++ break; ++ case MAX197_RANGE_5_5: ++ case MAX197_RANGE_0_10: ++ val = ((val * 100000/4096)+5)/10; ++ break; ++ case MAX197_RANGE_10_10: ++ val = ((val * 200000/4096)+5)/10; ++ break; ++ } ++ ++ return sprintf(buf, "%d.%03ld\n", val/1000, abs(val%1000)); ++} ++ ++static ssize_t max197_configure(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t len) ++{ ++ int n = attr->attr.name[2] - 0x31; ++ ++ long val = simple_strtol(buf, NULL, 10); ++ switch (val) { ++ case 10: ++ conf.channels &= MAX197_RESET_CHANNEL_CONF(n); ++ conf.channels |= MAX197_SET_CHANNEL_CONF(n, MAX197_RANGE_0_10); ++ break; ++ case 5: ++ conf.channels &= MAX197_RESET_CHANNEL_CONF(n); ++ conf.channels |= MAX197_SET_CHANNEL_CONF(n, MAX197_RANGE_0_5); ++ break; ++ case -10: ++ conf.channels &= MAX197_RESET_CHANNEL_CONF(n); ++ conf.channels |= MAX197_SET_CHANNEL_CONF(n, MAX197_RANGE_10_10); ++ break; ++ case -5: ++ conf.channels &= MAX197_RESET_CHANNEL_CONF(n); ++ conf.channels |= MAX197_SET_CHANNEL_CONF(n, MAX197_RANGE_5_5); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return len; ++} ++ ++static DEVICE_ATTR(ch1, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); ++static DEVICE_ATTR(ch2, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); ++static DEVICE_ATTR(ch3, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); ++static DEVICE_ATTR(ch4, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); ++static DEVICE_ATTR(ch5, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); ++static DEVICE_ATTR(ch6, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); ++static DEVICE_ATTR(ch7, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); ++static DEVICE_ATTR(ch8, S_IWUSR | S_IRUGO, max197_acquire, max197_configure); ++ ++static struct attribute *max197_attributes[] = { ++ &dev_attr_ch1.attr, ++ &dev_attr_ch2.attr, ++ &dev_attr_ch3.attr, ++ &dev_attr_ch4.attr, ++ &dev_attr_ch5.attr, ++ &dev_attr_ch6.attr, ++ &dev_attr_ch7.attr, ++ &dev_attr_ch8.attr, ++ NULL ++}; ++ ++static struct attribute_group max197_group = { ++ .attrs = max197_attributes, ++ //.name = "channels", ++}; ++ ++static __devinit int ts72xx_max197_probe(struct platform_device *pdev) ++{ ++ int err = 0; ++ struct resource *r_data, *r_busy; ++ ++ r_data = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ r_busy = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ ++ if (!r_data || !r_busy) { ++ dev_err(&pdev->dev, "missing resource(s)\n"); ++ return -EINVAL; ++ } ++ ++ conf.control_and_data_register = ioremap(r_data->start, r_data->end - r_data->start + 1); ++ if (!conf.control_and_data_register) { ++ err = -ENODEV; ++ goto exit; ++ } ++ ++ conf.busy_bit_register = ioremap(r_busy->start, r_busy->end - r_busy->start + 1); ++ if (!conf.busy_bit_register) { ++ err = -ENODEV; ++ goto exit_unmap1; ++ } ++ ++ conf.channels = ++ MAX197_SET_CHANNEL_CONF(0, MAX197_RANGE_0_5) | ++ MAX197_SET_CHANNEL_CONF(1, MAX197_RANGE_0_5) | ++ MAX197_SET_CHANNEL_CONF(2, MAX197_RANGE_0_5) | ++ MAX197_SET_CHANNEL_CONF(3, MAX197_RANGE_0_5) | ++ MAX197_SET_CHANNEL_CONF(4, MAX197_RANGE_0_5) | ++ MAX197_SET_CHANNEL_CONF(5, MAX197_RANGE_0_5) | ++ MAX197_SET_CHANNEL_CONF(6, MAX197_RANGE_0_5) | ++ MAX197_SET_CHANNEL_CONF(7, MAX197_RANGE_0_5); ++ ++ /* Register sysfs hooks */ ++ if ((err = sysfs_create_group(&pdev->dev.kobj, &max197_group))) ++ goto exit_unmap2; ++ ++ printk(PFX "TS-72xx max197 driver, v%s\n", DRV_VERSION); ++ return 0; ++ ++exit_unmap2: ++ iounmap(conf.busy_bit_register); ++exit_unmap1: ++ iounmap(conf.control_and_data_register); ++exit: ++ return err; ++} ++ ++static int __devexit ts72xx_max197_remove(struct platform_device *pdev) ++{ ++ sysfs_remove_group(&pdev->dev.kobj, &max197_group); ++ iounmap(conf.busy_bit_register); ++ iounmap(conf.control_and_data_register); ++ return 0; ++} ++ ++static struct platform_driver ts72xx_max197_platform_driver = { ++ .probe = ts72xx_max197_probe, ++ .remove = __devexit_p(ts72xx_max197_remove), ++ .driver = { ++ .name = "ts72xx-max197", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init ts72xx_max197_init(void) ++{ ++ return platform_driver_register(&ts72xx_max197_platform_driver); ++} ++ ++static void __exit ts72xx_max197_exit(void) ++{ ++ platform_driver_unregister(&ts72xx_max197_platform_driver); ++} ++ ++#ifdef CONFIG_TS72XX_MAX197_AVERAGE ++module_param(average, ushort, S_IWUSR | S_IRUGO); ++MODULE_PARM_DESC(average, "Allow average measurement (default=1)"); ++#endif ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("TS-72xx max197 driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); ++ ++module_init(ts72xx_max197_init); ++module_exit(ts72xx_max197_exit); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0014-ts7200_nor_flash.patch b/recipes/linux/linux-2.6.34/ts72xx/0014-ts7200_nor_flash.patch new file mode 100644 index 0000000000..c76ad361ce --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0014-ts7200_nor_flash.patch @@ -0,0 +1,176 @@ +From 116d99d9f45c30d3b1c80b54fed8bf406aa590f7 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sat, 19 Jun 2010 16:56:48 +0200 +Subject: [PATCH 14/18] ts7200_nor_flash + +Deal with 8mb or 16mb NOR Flash (TS-7200 only) +--- + drivers/mtd/maps/Kconfig | 15 +++++ + drivers/mtd/maps/Makefile | 1 + + drivers/mtd/maps/ts7200_flash.c | 116 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 132 insertions(+), 0 deletions(-) + create mode 100644 drivers/mtd/maps/ts7200_flash.c + +diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig +index aa2807d..5d12903 100644 +--- a/drivers/mtd/maps/Kconfig ++++ b/drivers/mtd/maps/Kconfig +@@ -422,6 +422,21 @@ config MTD_H720X + This enables access to the flash chips on the Hynix evaluation boards. + If you have such a board, say 'Y'. + ++config MTD_TS7200_NOR ++ tristate "CFI Flash device mapped on TS-7200" ++ depends on MTD_CFI && MACH_TS72XX ++ help ++ This provides a map driver for the on-board flash of the Technologic ++ System's TS-7200 board. The 8MB (or 16MB) flash is splitted into 3 partitions ++ which are accessed as separate MTD devices. ++ ++config MTD_TS7200_NOR_SIZE ++ int "Flash size (8 or 16mb)" ++ depends on MTD_TS7200_NOR ++ default "8" ++ help ++ Enter the NOR Flash size of your TS-7200 board. Can be 8 or 16. ++ + # This needs CFI or JEDEC, depending on the cards found. + config MTD_PCI + tristate "PCI MTD driver" +diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile +index bb035cd..775c2e7 100644 +--- a/drivers/mtd/maps/Makefile ++++ b/drivers/mtd/maps/Makefile +@@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_DMV182) += dmv182.o + obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o + obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o + obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o ++obj-$(CONFIG_MTD_TS7200_NOR) += ts7200_flash.o + obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o + obj-$(CONFIG_MTD_VMU) += vmu-flash.o + obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o +diff --git a/drivers/mtd/maps/ts7200_flash.c b/drivers/mtd/maps/ts7200_flash.c +new file mode 100644 +index 0000000..94fb4f4 +--- /dev/null ++++ b/drivers/mtd/maps/ts7200_flash.c +@@ -0,0 +1,116 @@ ++/* ++ * ts7200_flash.c - mapping for TS-7200 SBCs (8mb NOR flash) ++ * No platform_device resource is used here. All is hardcoded. ++ * ++ * (c) Copyright 2006-2010 Matthieu Crapet <mcrapet@gmail.com> ++ * Based on ts5500_flash.c by Sean Young <sean@mess.org> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <asm/io.h> ++#include <asm/sizes.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <mach/ep93xx-regs.h> ++ ++ ++static struct mtd_info *mymtd; ++ ++static struct map_info ts7200nor_map = { ++ .name = "Full TS-7200 NOR flash", ++ .size = 0, /* filled in later */ ++ .bankwidth = 2, ++ .phys = EP93XX_CS6_PHYS_BASE, ++}; ++ ++/* ++ * MTD partitioning stuff ++ */ ++#ifdef CONFIG_MTD_PARTITIONS ++ ++#define TS7200_BOOTROM_PART_SIZE (SZ_128K) ++#define TS7200_REDBOOT_PART_SIZE (15*SZ_128K) ++ ++static struct mtd_partition ts7200_nor_parts[] = ++{ ++ { ++ .name = "TS-BOOTROM", ++ .offset = 0, ++ .size = TS7200_BOOTROM_PART_SIZE, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, ++ { ++ .name = "RootFS", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 0, /* filled in later */ ++ }, ++ { ++ .name = "Redboot", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, /* up to the end */ ++ } ++}; ++#endif ++ ++static int __init ts7200_nor_init(void) ++{ ++ if (CONFIG_MTD_TS7200_NOR_SIZE <= 8) ++ ts7200nor_map.size = SZ_8M; ++ else ++ ts7200nor_map.size = SZ_16M; ++ ++ printk(KERN_NOTICE "TS-7200 flash mapping: %ldmo at 0x%x\n", ++ ts7200nor_map.size / SZ_1M, ts7200nor_map.phys); ++ ++ ts7200nor_map.virt = ioremap(ts7200nor_map.phys, ts7200nor_map.size - 1); ++ if (!ts7200nor_map.virt) { ++ printk("ts7200_flash: failed to ioremap\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ts7200nor_map); ++ mymtd = do_map_probe("cfi_probe", &ts7200nor_map); ++ if (mymtd) { ++ mymtd->owner = THIS_MODULE; ++ add_mtd_device(mymtd); ++ #ifdef CONFIG_MTD_PARTITIONS ++ ts7200_nor_parts[1].size = ts7200nor_map.size - TS7200_REDBOOT_PART_SIZE; ++ return add_mtd_partitions(mymtd, ts7200_nor_parts, ARRAY_SIZE(ts7200_nor_parts)); ++ #else ++ return 0; ++ #endif ++ } ++ ++ iounmap(ts7200nor_map.virt); ++ return -ENXIO; ++} ++ ++static void __exit ts7200_nor_exit(void) ++{ ++ if (mymtd) { ++ del_mtd_device(mymtd); ++ map_destroy(mymtd); ++ mymtd = NULL; ++ } ++ if (ts7200nor_map.virt) { ++ iounmap(ts7200nor_map.virt); ++ ts7200nor_map.virt = 0; ++ } ++} ++ ++module_init(ts7200_nor_init); ++module_exit(ts7200_nor_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("MTD map driver for TS-7200 board"); ++MODULE_VERSION("0.1"); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0015-ts72xx_sdcard.patch b/recipes/linux/linux-2.6.34/ts72xx/0015-ts72xx_sdcard.patch new file mode 100644 index 0000000000..47eaa7a5b8 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0015-ts72xx_sdcard.patch @@ -0,0 +1,3293 @@ +From 3da21c5a4384f9853fd75cb9fed204402072d280 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sun, 20 Jun 2010 10:46:15 +0200 +Subject: [PATCH 15/18] ts72xx_sdcard + +SD Card support for TS-7260. Device name is "tssda". +Patch based on work of Breton Saunders: +http://tech.groups.yahoo.com/group/ts-7000/message/15787 +http://tech.groups.yahoo.com/group/ts-7000/message/16028 +--- + arch/arm/mach-ep93xx/ts72xx.c | 24 + + drivers/block/Kconfig | 7 + + drivers/block/Makefile | 2 + + drivers/block/sdcore2.c | 2391 +++++++++++++++++++++++++++++++++++++++++ + drivers/block/sdcore2.h | 372 +++++++ + drivers/block/tssdcard.c | 415 +++++++ + 6 files changed, 3211 insertions(+), 0 deletions(-) + create mode 100644 drivers/block/sdcore2.c + create mode 100644 drivers/block/sdcore2.h + create mode 100644 drivers/block/tssdcard.c + +diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c +index ba27d9d..7fd8f80 100644 +--- a/arch/arm/mach-ep93xx/ts72xx.c ++++ b/arch/arm/mach-ep93xx/ts72xx.c +@@ -178,6 +178,29 @@ static void __init ts72xx_register_flash(void) + } + + /************************************************************************* ++ * SD Card (TS-7260 only) ++ *************************************************************************/ ++ ++static struct resource ts72xx_sdcard_resource = { ++ .start = TS7260_SDCARD_PHYS_BASE, ++ .end = TS7260_SDCARD_PHYS_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct platform_device ts72xx_sdcard = { ++ .name = "ts72xx-sdcard", ++ .id = 0, ++ .num_resources = 1, ++ .resource = &ts72xx_sdcard_resource, ++}; ++ ++static void __init ts72xx_register_sdcard(void) ++{ ++ if (board_is_ts7260()) ++ platform_device_register(&ts72xx_sdcard); ++} ++ ++/************************************************************************* + * RTC + *************************************************************************/ + static unsigned char ts72xx_rtc_readbyte(unsigned long addr) +@@ -278,6 +301,7 @@ static void __init ts72xx_init_machine(void) + { + ep93xx_init_devices(); + ts72xx_register_flash(); ++ ts72xx_register_sdcard(); + platform_device_register(&ts72xx_rtc_device); + platform_device_register(&ts72xx_wdt_device); + +diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig +index 77bfce5..fc8fae5 100644 +--- a/drivers/block/Kconfig ++++ b/drivers/block/Kconfig +@@ -488,4 +488,11 @@ config BLK_DEV_HD + + If unsure, say N. + ++config BLK_DEV_TS72XX_SDCARD ++ tristate "TS-72XX SD Card support" ++ depends on ARCH_EP93XX && MACH_TS72XX ++ help ++ This option enables support SD Card control on Technologic Systems ++ TS-7260 SBC. ++ + endif # BLK_DEV +diff --git a/drivers/block/Makefile b/drivers/block/Makefile +index aff5ac9..a4d0579 100644 +--- a/drivers/block/Makefile ++++ b/drivers/block/Makefile +@@ -34,8 +34,10 @@ obj-$(CONFIG_VIODASD) += viodasd.o + obj-$(CONFIG_BLK_DEV_SX8) += sx8.o + obj-$(CONFIG_BLK_DEV_UB) += ub.o + obj-$(CONFIG_BLK_DEV_HD) += hd.o ++obj-$(CONFIG_BLK_DEV_TS72XX_SDCARD) += ts72xx_sdcard.o + + obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o + obj-$(CONFIG_BLK_DEV_DRBD) += drbd/ + + swim_mod-objs := swim.o swim_asm.o ++ts72xx_sdcard-objs := tssdcard.o sdcore2.o +diff --git a/drivers/block/sdcore2.c b/drivers/block/sdcore2.c +new file mode 100644 +index 0000000..6dadee1 +--- /dev/null ++++ b/drivers/block/sdcore2.c +@@ -0,0 +1,2391 @@ ++/* ++ * Copyright (c) 2006-2009, Technologic Systems ++ * All rights reserved. ++ */ ++ ++/* ++ * This code is 100% operating system/CPU independent-- not a single global ++ * reference, external symbol, or #include is required. Centric upon one data ++ * structure "struct sdcore". OS-specific callbacks for things like DMA ++ * acceleration and sleeping are defined by function pointers to OS-specific ++ * code in the struct sdcore. Minimally requires the os_sleep() callback to be ++ * implemented for proper SD card initialization and a pointer to start ++ * of SD card registers. Auto-determines TS SD core version. All other ++ * callback functions may be left NULL-- they are only to allow speed/CPU ++ * utilization improvements. ++ * ++ * 3 main public functions - sdreset(), sdread() and sdwrite(). sdreset() ++ * returns card size. OS specific shim is required to turn this ++ * simple API into the complicated, constantly changing, hacker "designed" ++ * horrible excuses for abstraction, block driver APIs required in other ++ * "modern" operating systems. ++ * ++ * Not all SD cards over the years have followed spec perfectly -- many ++ * don't even check CRC's on the CMD or DAT busses and some have problems ++ * (lock up) when reading/writing the last sectors with SD read/write multiple ++ * commands. ++ * ++ * The TS SD hardware cores are not much more than GPIO bit-bang cores with ++ * a few well-placed hardware optimizations to achieve reasonable ++ * performance goals. In the roughly 2000 lines of code that follow, there ++ * is support for all distinct TS hardware SD cores on PPC and ARM platforms, ++ * a generic (private) SD command layer, sdcmd(), and SD flash card ++ * (public) routines for initialization + read/write + some SD security ++ * features. ++ * ++ */ ++ ++/* Register offset definitions. TS-SDCORE is 4 regs total. */ ++#define SDCMD 0 ++#define SDGPIO 0 /* version 2 register */ ++#define SDDAT 1 ++#define SDSTAT2 1 ++#define SDSTATE 2 ++#define SDCTRL 3 ++#define SDDAT2 4 ++#define SDCMD2 8 ++#define SDCTRL2 12 ++#define SDLUN2 2 ++ ++struct sdcore { ++ /* virtual address of SD block register start, to be filled in ++ * by client code before calling any sdcore functions. ++ */ ++ unsigned int sd_regstart; ++ ++ /* public bits for sd_state bitfield, can be read from client code. ++ * Do not write! Other bits are used internally. ++ */ ++ #define SDDAT_RX (1<<0) ++ #define SDDAT_TX (1<<1) ++ #define SDCMD_RX (1<<2) ++ #define SDCMD_TX (1<<3) ++ unsigned int sd_state; ++ ++ /* Erase hint for subsequent sdwrite() call, used to optimize ++ * write throughput on multi-sector writes by pre-erasing this ++ * many sectors. XXX: this doesn't have much benefit on most SDs ++ */ ++ unsigned int sd_erasehint; ++ ++ /* Following this comment are 3 function pointer declarations to ++ * OS helper functions. The 'os_arg' member is passed as the ++ * first argument to the helpers and should be set by ++ * client code before issueing sdreset() ++ * ++ * os_dmastream(os_arg, buf, buflen) ++ * This function should look at sd_state and set up and run an ++ * appropriate DMA transfer. If buf is NULL, callee doesn't care ++ * about the actual data sent/received and helper function ++ * can do whatever it wants. Should return 0 when DMA transfer was ++ * run and completed successfully. If this function pointer is ++ * NULL, PIO methods of transfer will be used instead of DMA. ++ * ++ * os_dmaprep(os_arg, buf, buflen) ++ * This function is used to prepare an area of memory for a possible ++ * DMA transfer. This function is called once per distinct buffer ++ * passed in. After this function is called, os_dmastream() may be ++ * called one or more times (for sequential addresses) on subregions ++ * of the address range passed here. Should write-back or invalidate ++ * L1 cache lines and possibly look up physical addresses for buf ++ * passed in if I/O buffers. If 'os_dmaprep' is set to NULL, function ++ * call will not happen. (though os_dmastream() calls may still) ++ * ++ * os_delay(os_arg, microseconds) ++ * This function is supposed to delay or stall the processor for ++ * the passed in value number of microseconds. ++ */ ++ void *os_arg; ++ int (*os_dmastream)(void *, unsigned char *, unsigned int); ++ void (*os_dmaprep)(void *, unsigned char *, unsigned int); ++ void (*os_delay)(void *, unsigned int); ++ void (*os_irqwait)(void *, unsigned int); ++ int (*os_powerok)(void *); ++ int (*os_timeout)(void *); ++ int (*os_reset_timeout)(void *); ++ ++ /* If the SD card last successfully reset is write protected, this ++ * member will be non-zero. ++ */ ++ unsigned int sd_wprot; ++ ++ /* If this card may have been already initialized by TS-SDBOOT, place ++ * the magic token it placed in the EP93xx SYSCON ScratchReg1 here ++ * to avoid re-initialization. ++ */ ++ unsigned int sdboot_token; ++ ++ /* CRC hint for subsequent sdwrite() call, used to optimize ++ * write throughput while using DMA by pre-calculating CRC's for ++ * next write ++ */ ++ unsigned char *sd_crchint; ++ ++ /* The block size of the memory device. Normally 512, but can be 1024 ++ * for larger cards ++ */ ++ unsigned int sd_blocksize; ++ ++ /* Password for auto-unlocking in sdreset() ++ */ ++ unsigned char *sd_pwd; ++ ++ /* If the SD card was password locked, this will be non-zero. ++ */ ++ unsigned int sd_locked; ++ ++ /* Whether or not writes can be parked. ++ */ ++ unsigned int sd_writeparking; ++ ++ /* Logical unit number. Some SD cores will have multiple card slots. ++ */ ++ unsigned int sd_lun; ++ ++ /* The rest of these members are for private internal use and should ++ * not be of interest to client code. ++ */ ++ unsigned int sd_rcaarg; ++ unsigned int sd_csd[17]; ++ unsigned int sd_crcseq; ++ unsigned short sd_crcs[4]; ++ unsigned int sd_crctmp[4]; ++ unsigned int sd_timeout; ++ unsigned int parked_sector; ++ unsigned int hw_version; ++ unsigned char sd_scr[8]; ++ unsigned int sd_sz; ++}; ++ ++/* For sdreadv() / sdwritev() */ ++struct sdiov { ++ unsigned char *sdiov_base; ++ unsigned int sdiov_nsect; ++}; ++ ++int sdreset(struct sdcore *); ++int sdread(struct sdcore *, unsigned int, unsigned char *, int); ++int sdwrite(struct sdcore *, unsigned int, unsigned char *, int); ++int sdreadv(struct sdcore *, unsigned int, struct sdiov *, int); ++int sdwritev(struct sdcore *, unsigned int, struct sdiov *, int); ++int sdsetwprot(struct sdcore *, unsigned int); ++#define SDLOCK_UNLOCK 0 ++#define SDLOCK_SETPWD 1 ++#define SDLOCK_CLRPWD 2 ++#define SDLOCK_ERASE 8 ++#ifndef SD_NOLOCKSUPPORT ++int sdlockctl(struct sdcore *, unsigned int, unsigned char *, unsigned char *); ++#endif ++ ++/* ++ * Everything below here is secret! This code shouldn't have to change ++ * even for different OS. ++ */ ++ ++const static unsigned short crc16tbl[256] = { ++ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, ++ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, ++ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, ++ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, ++ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, ++ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, ++ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, ++ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, ++ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, ++ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, ++ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, ++ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, ++ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, ++ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, ++ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, ++ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, ++ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, ++ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, ++ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, ++ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, ++ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, ++ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, ++ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, ++ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, ++ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, ++ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, ++ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, ++ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, ++ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, ++ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, ++ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, ++ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, ++}; ++ ++const static unsigned char destagger[256] = { ++ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ++ 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, ++ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ++ 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, ++ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ++ 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, ++ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ++ 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, ++ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ++ 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, ++ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ++ 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, ++ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ++ 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, ++ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ++ 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, ++}; ++ ++#ifndef MAX_SDCORES ++#define MAX_SDCORES 64 ++#endif ++static struct sdcore *sdcores[MAX_SDCORES]; ++ ++static unsigned int crc7(unsigned int, const unsigned int *, unsigned int); ++static int sdreset2(struct sdcore *); ++static int version(struct sdcore *); ++static int sdfastinit(struct sdcore *sd); ++static int sdcmd2(struct sdcore *, unsigned short, unsigned int, ++ unsigned int *, unsigned char **); ++static int sdcmd(struct sdcore *, unsigned short, unsigned int, ++ unsigned int *, unsigned char **); ++static void mkcommand(unsigned int, unsigned int, unsigned int *); ++static int stop(struct sdcore *); ++static int stop2(struct sdcore *); ++static int sdread2(struct sdcore *, unsigned int, unsigned char *, int) ++ __attribute__ ((unused)); ++static int do_read2(struct sdcore *, unsigned int, struct sdiov *, ++ unsigned int); ++static int do_read(struct sdcore *, unsigned int, struct sdiov *, ++ unsigned int); ++static int do_write(struct sdcore *, unsigned int, struct sdiov *, ++ unsigned int); ++static int do_write2(struct sdcore *, unsigned int, struct sdiov *, ++ unsigned int); ++static int sdsetwprot2(struct sdcore *, unsigned int); ++#ifndef SD_NOLOCKSUPPORT ++static int sdlockctl2(struct sdcore *, unsigned int, unsigned char *, ++ unsigned char *); ++#endif ++ ++#ifndef SDPOKE8 ++# define SDPOKE8(sd, x, y) \ ++ *(volatile unsigned char *)((sd)->sd_regstart + (x)) = (y) ++#endif ++#ifndef SDPOKE32 ++# define SDPOKE32(sd, x, y) \ ++ *(volatile unsigned int *)((sd)->sd_regstart + (x)) = (y) ++#endif ++#ifndef SDPOKE16 ++# define SDPOKE16(sd, x, y) \ ++ *(volatile unsigned short *)((sd)->sd_regstart + (x)) = (y) ++#endif ++#ifndef SDPEEK8 ++# define SDPEEK8(sd, x) *(volatile unsigned char *)((sd)->sd_regstart + (x)) ++#endif ++#ifndef SDPEEK32 ++# define SDPEEK32(sd, x) *(volatile unsigned int *)((sd)->sd_regstart + (x)) ++#endif ++#ifndef SDPEEK16 ++# define SDPEEK16(sd, x) *(volatile unsigned short *)((sd)->sd_regstart + (x)) ++#endif ++ ++#define S_DUMMY_CLK 0 ++#define S_SEND_CMD 1 ++#define S_WAIT_RESP 2 ++#define S_RX_RESP 3 ++#define S_WAIT_BUSY 4 ++#define S_TX_WRITE 5 ++#define S_CRC_CHECK 6 ++#define S_OFF 7 ++ ++#define TYPE_SHORTRESP 2 ++#define TYPE_LONGRESP 3 ++#define TYPE_BSYRESP 4 ++#define TYPE_NORESP 1 ++#define TYPE_RXDAT 0 ++#define TYPE_TXDAT 5 ++#define TYPE_ABORT 6 ++#define TYPE_RXDAT_IGNRESP 7 ++ ++#define CMD(idx, type) (0x40 | (idx) | ((type)<<8)) ++ ++#define CMD_GO_IDLE_STATE CMD(0, TYPE_NORESP) ++#define CMD_ALL_SEND_CID CMD(2, TYPE_LONGRESP) ++#define CMD_SEND_RELATIVE_ADDR CMD(3, TYPE_SHORTRESP) ++#define CMD_SWITCH_FUNC CMD(6, TYPE_RXDAT) ++#define CMD_SWITCH_FUNC2 CMD(6, TYPE_RXDAT_IGNRESP) ++#define CMD_SELECT_CARD CMD(7, TYPE_BSYRESP) ++#define CMD_DESELECT_CARD CMD(7, TYPE_NORESP) ++#define CMD_SEND_IF_COND CMD(8, TYPE_SHORTRESP) ++#define CMD_SEND_CSD CMD(9, TYPE_LONGRESP) ++#define CMD_PROGRAM_CSD CMD(27, TYPE_TXDAT) ++#define CMD_SET_BLOCKLEN CMD(16, TYPE_SHORTRESP) ++#define CMD_LOCK_UNLOCK CMD(42, TYPE_TXDAT) ++#define CMD_APP_CMD CMD(55, TYPE_SHORTRESP) ++#define CMD_READ_SINGLE_BLOCK CMD(17, TYPE_RXDAT) ++#define CMD_READ_MULTIPLE_BLOCK CMD(18, TYPE_RXDAT) ++#define CMD_READ_MULTIPLE_BLOCK2 CMD(18, TYPE_RXDAT_IGNRESP) ++#define CMD_STOP_TRANSMISSION CMD(12, TYPE_ABORT) ++#define CMD_SEND_STATUS CMD(13, TYPE_SHORTRESP) ++#define CMD_WRITE_BLOCK CMD(24, TYPE_TXDAT) ++#define CMD_WRITE_MULTIPLE_BLOCK CMD(25, TYPE_TXDAT) ++ ++#define ACMD_SD_SEND_OP_COND CMD(41, TYPE_SHORTRESP) ++#define ACMD_SET_CLR_CARD_DETECT CMD(42, TYPE_SHORTRESP) ++#define ACMD_SET_BUS_WIDTH CMD(6, TYPE_SHORTRESP) ++#define ACMD_SET_WR_BLK_ERASE_COUNT CMD(23, TYPE_SHORTRESP) ++#define ACMD_SEND_NUM_WR_BLOCKS CMD(22, TYPE_RXDAT) ++#define ACMD_SEND_SCR CMD(51, TYPE_RXDAT) ++#define ACMD_SEND_SCR2 CMD(51, TYPE_RXDAT_IGNRESP) ++ ++/* Private bits for struct sdcore, sd_state member */ ++#define DATSSP_NOCRC (1<<4) ++#define DATSSP_4BIT (1<<5) ++#define SD_HC (1<<6) ++#define SD_HISPEED (1<<7) ++#define SD_LOSPEED (1<<8) ++#define SD_SELECTED (1<<9) ++#define SD_RESET (1<<10) ++ ++#define NULL ((void *)0) ++ ++static void remember_sdcore(struct sdcore *sd) { ++ int i, newlun = 0; ++ ++ for (i = 0; i < sizeof(sdcores); i++) { ++ if (sdcores[i] == NULL) { ++ /* new core, first reset */ ++ sdcores[i] = sd; ++ /* core was almost definitely power-cycled on prev lun ++ * sdreset2(), so we don't need to have the sdreset2() ++ * do it again. ++ */ ++ if (newlun) sd->sd_state = SD_RESET; ++ break; ++ } else if (sdcores[i]->sd_regstart == sd->sd_regstart) { ++ newlun = 1; ++ if (sdcores[i]->sd_lun == sd->sd_lun) { ++ sdcores[i] = sd; ++ break; ++ } ++ } ++ } ++} ++ ++static int activate(struct sdcore *sd) { ++ int i; ++ ++ /* Are we already selected? */ ++ if ((sd->sd_state & (SD_SELECTED|SD_RESET)) == SD_SELECTED) ++ return 0; ++ ++ /* Find currently activated SD slot for this HW core */ ++ for (i = 0; i < sizeof(sdcores); i++) { ++ if (sdcores[i] == NULL) break; ++ if (sdcores[i]->sd_regstart == sd->sd_regstart && ++ sdcores[i]->sd_state & SD_SELECTED) break; ++ } ++ ++ /* Stop whatever parked transfer it has going on. */ ++ if (sdcores[i]) { ++ stop2(sdcores[i]); ++ sdcores[i]->sd_state &= ~SD_SELECTED; ++ } ++ ++ /* Change clock routing, mark us as selected */ ++#ifdef BIGENDIAN ++ SDPOKE16(sd, SDLUN2, sd->sd_lun << 8); ++#else ++ SDPOKE16(sd, SDLUN2, sd->sd_lun); ++#endif ++ ++ /* Change clock frequency */ ++ if (sd->sd_state & SD_HISPEED) SDPOKE8(sd, SDSTAT2, 0x38); ++ else SDPOKE8(sd, SDSTAT2, 0x18); ++ ++ sd->sd_state |= SD_SELECTED; ++ if (sd->sd_state & SD_RESET) return 1; ++ else return 0; ++ ++} ++ ++inline static unsigned short ++crc16_acc(unsigned short crc, unsigned int b) ++{ ++ return (crc << 8) ^ crc16tbl[(crc >> 8) ^ b]; ++} ++ ++static void sd_initcrc(struct sdcore *sd) ++{ ++ int i; ++ ++ for (i = 0; i < 4; i++) { ++ sd->sd_crctmp[i] = 0; ++ sd->sd_crcs[i] = 0; ++ } ++ sd->sd_crcseq = 6; ++} ++ ++static void sd_1bit_feedcrc(struct sdcore *sd, unsigned int dat) ++{ ++ sd->sd_crcs[0] = crc16_acc(sd->sd_crcs[0], dat); ++} ++ ++static void sd_4bit_feedcrc(struct sdcore *sd, unsigned int dat) ++{ ++ unsigned int a = 0, b = 0, c = 0, d = 0; ++ unsigned int shift = (sd->sd_crcseq & 0x7); ++ ++ a = sd->sd_crctmp[0]; ++ b = sd->sd_crctmp[1]; ++ c = sd->sd_crctmp[2]; ++ d = sd->sd_crctmp[3]; ++ ++ a |= destagger[dat] << shift; ++ dat >>= 1; ++ b |= destagger[dat] << shift; ++ dat >>= 1; ++ c |= destagger[dat] << shift; ++ dat >>= 1; ++ d |= destagger[dat] << shift; ++ ++ if (shift == 0) { ++ sd->sd_crcs[0] = crc16_acc(sd->sd_crcs[0], a); ++ sd->sd_crcs[1] = crc16_acc(sd->sd_crcs[1], b); ++ sd->sd_crcs[2] = crc16_acc(sd->sd_crcs[2], c); ++ sd->sd_crcs[3] = crc16_acc(sd->sd_crcs[3], d); ++ a = b = c = d = 0; ++ } ++ ++ sd->sd_crcseq -= 2; ++ sd->sd_crctmp[0] = a; ++ sd->sd_crctmp[1] = b; ++ sd->sd_crctmp[2] = c; ++ sd->sd_crctmp[3] = d; ++} ++ ++/* This should be called 8 times to get the full 8 bytes of CRC generated */ ++static unsigned int sd_4bit_getcrc(struct sdcore *sd) ++{ ++ static const unsigned char restaggertbl[4] = { 0x0, 0x1, 0x10, 0x11 }; ++ static const unsigned char restaggertbl_lsl1[4] = ++ { 0x0, 0x2, 0x20, 0x22 }; ++ static const unsigned char restaggertbl_lsl2[4] = ++ { 0x0, 0x4, 0x40, 0x44 }; ++ static const unsigned char restaggertbl_lsl3[4] = ++ { 0x0, 0x8, 0x80, 0x88 }; ++ unsigned int ret; ++ ++ ret = restaggertbl[sd->sd_crcs[0] >> 14]; ++ sd->sd_crcs[0] <<= 2; ++ ret |= restaggertbl_lsl1[sd->sd_crcs[1] >> 14]; ++ sd->sd_crcs[1] <<= 2; ++ ret |= restaggertbl_lsl2[sd->sd_crcs[2] >> 14]; ++ sd->sd_crcs[2] <<= 2; ++ ret |= restaggertbl_lsl3[sd->sd_crcs[3] >> 14]; ++ sd->sd_crcs[3] <<= 2; ++ ++ return ret; ++} ++ ++/* This should be called 2 times to get the full 2 bytes of CRC generated */ ++static unsigned int sd_1bit_getcrc(struct sdcore *sd) ++{ ++ unsigned int ret; ++ ++ ret = sd->sd_crcs[0] >> 8; ++ sd->sd_crcs[0] = (sd->sd_crcs[0] & 0xff) << 8; ++ return ret; ++} ++ ++static inline void datssp_feedcrc(struct sdcore *sd, unsigned int dat) ++{ ++ if (!(sd->sd_state & DATSSP_NOCRC)) { ++ if (sd->sd_state & DATSSP_4BIT) sd_4bit_feedcrc(sd, dat); ++ else sd_1bit_feedcrc(sd, dat); ++ } ++} ++ ++static inline unsigned int datssp_getcrc(struct sdcore *sd) ++{ ++ unsigned int ret = 0; ++ ++ if (!(sd->sd_state & DATSSP_NOCRC)) { ++ if (sd->sd_state & DATSSP_4BIT) ret = sd_4bit_getcrc(sd); ++ else ret = sd_1bit_getcrc(sd); ++ } ++ return ret; ++} ++ ++static inline unsigned int ++crc7(unsigned int crc, const unsigned int *pc, unsigned int len) ++{ ++ unsigned int i; ++ unsigned char ibit; ++ unsigned char c; ++ ++ for (i = 0; i < len; i++, pc++) { ++ c = *pc; ++ for (ibit = 0; ibit < 8; ibit++) { ++ crc <<= 1; ++ if ((c ^ crc) & 0x80) crc ^= 0x09; ++ ++ c <<= 1; ++ } ++ ++ crc &= 0x7F; ++ } ++ ++ return crc; ++} ++ ++static inline void ++mkcommand(unsigned int cmdidx, unsigned int arg, unsigned int *retcmd) ++{ ++ retcmd[0] = cmdidx; ++ retcmd[1] = arg >> 24; ++ retcmd[2] = arg >> 16; ++ retcmd[3] = arg >> 8; ++ retcmd[4] = arg; ++ retcmd[5] = (0x1 | (crc7(0, retcmd, 5) << 1)); ++} ++ ++static inline void reset_timeout(struct sdcore *sd) { ++ sd->sd_timeout = 0; ++ if (sd->os_reset_timeout) sd->os_reset_timeout(sd); ++} ++ ++static inline int timeout(struct sdcore *sd) { ++ if (sd->sd_timeout > 1000000) return 1; ++ else if (sd->os_timeout) return sd->os_timeout(sd); ++ else sd->sd_timeout++; ++ return 0; ++} ++ ++static ++unsigned int sdsize(struct sdcore *sd) ++{ ++ unsigned int csize, csize_mult, rd_bl_len; ++ ++ if (sd->sd_sz != 0) return sd->sd_sz; ++ ++ if (sd->sd_csd[1] & 0xc0) { ++ csize = (sd->sd_csd[10] | (sd->sd_csd[9] << 8)); ++ sd->sd_sz = (csize + 1) * 1024; ++ } else { ++ rd_bl_len = 1 << ((sd->sd_csd[6] & 0xf) - 9); ++ csize = ((sd->sd_csd[7] & 0x03) << 10) | ++ ((sd->sd_csd[8] << 2) | ((sd->sd_csd[9] & 0xc0) >> 6)); ++ csize_mult = ((sd->sd_csd[10] & 0x03) << 1) | ++ ((sd->sd_csd[11] & 0x80) >> 7); ++ sd->sd_sz = (csize + 1) * (1 << (csize_mult + 2)) * rd_bl_len; ++ } ++ return sd->sd_sz; ++} ++ ++static unsigned int tend_ssp(struct sdcore *sd, unsigned int **cmdresp, ++ unsigned char **dat) { ++ unsigned int d; ++ unsigned int s = SDPEEK8(sd, SDSTATE); ++ ++ if (s & 0x8) { ++ if (sd->sd_state & SDCMD_RX) { ++ d = SDPEEK8(sd, SDCMD); ++ if (cmdresp) { ++ **cmdresp = d; ++ *cmdresp = *cmdresp + 1; ++ reset_timeout(sd); ++ } ++ } else if (sd->sd_state & SDCMD_TX) { ++ SDPOKE8(sd, SDCMD, **cmdresp); ++ *cmdresp = *cmdresp + 1; ++ reset_timeout(sd); ++ } ++ } ++ ++ if (s & 0x10) { ++ if (sd->sd_state & SDDAT_RX) { ++ d = SDPEEK8(sd, SDDAT); ++ if (dat) { ++ **dat = d; ++ *dat = *dat + 1; ++ reset_timeout(sd); ++ } ++ } else if (sd->sd_state & SDDAT_TX) { ++ reset_timeout(sd); ++ if (dat) { ++ d = **dat; ++ *dat = *dat + 1; ++ SDPOKE8(sd, SDDAT, d); ++ datssp_feedcrc(sd, d); ++ } else { ++ d = datssp_getcrc(sd); ++ SDPOKE8(sd, SDDAT, d); ++ } ++ } ++ } ++ ++ return s; ++} ++ ++static int ++error(unsigned int *resp, unsigned short req) ++{ ++ unsigned int crc, status; ++ ++ if ((req & 0x3f) != resp[0]) return 1; ++ ++ crc = (0x1 | (crc7(0, resp, 5) << 1)); ++ if (crc != resp[5]) return 1; ++ ++ status = resp[1] << 24; ++ status |= resp[2] << 16; ++ status |= resp[3] << 8; ++ status |= resp[4]; ++ ++ return status & 0xfdf90008; ++} ++ ++static int ++sdcmd2(struct sdcore *sd, unsigned short req, unsigned int arg, ++ unsigned int *resp, unsigned char **dat) ++{ ++ unsigned int i, j, s, cmdresp[17]; ++ unsigned int resplen; ++ unsigned int type = (req >> 8); ++ unsigned int cmdidx = req; ++ unsigned int *cmdptr = cmdresp; ++ unsigned int *respptr; ++ unsigned int dly; ++ int ok32 = (sd->hw_version == 2); ++ int ok16 = (ok32 || (sd->hw_version == 3)); ++ int sddat2_8; ++ ++ // If no space for response provided by caller, use local buffer ++ if (resp == NULL) resp = cmdresp; ++ respptr = resp; ++ ++ if (activate(sd)) return 1; ++ ++ dly = sd->sd_state & SD_LOSPEED; ++ ++ if (!dly) { ++ unsigned int x; ++ SDPOKE8(sd, SDGPIO, 0xbf); ++#ifdef BIGENDIAN ++ x = (cmdidx & 0xff); ++ x |= ((arg >> 24) & 0xff) << 8; ++ x |= ((arg >> 16) & 0xff) << 16; ++ x |= ((arg >> 8) & 0xff) << 24; ++ if (ok32) SDPOKE32(sd, SDCMD2, x); ++ else if (ok16) { ++ SDPOKE16(sd, SDCMD2, x); ++ SDPOKE16(sd, SDCMD2, x >> 16); ++ } else { ++ SDPOKE8(sd, SDCMD2, x); ++ SDPOKE8(sd, SDCMD2, x >> 8); ++ SDPOKE8(sd, SDCMD2, x >> 16); ++ SDPOKE8(sd, SDCMD2, x >> 24); ++ } ++#else ++ x = (cmdidx & 0xff) << 24; ++ x |= ((arg >> 24) & 0xff) << 16; ++ x |= ((arg >> 16) & 0xff) << 8; ++ x |= ((arg >> 8) & 0xff); ++ if (ok32) SDPOKE32(sd, SDCMD2, x); ++ else if (ok16) { ++ SDPOKE16(sd, SDCMD2, x >> 16); ++ SDPOKE16(sd, SDCMD2, x); ++ } else { ++ SDPOKE8(sd, SDCMD2, x >> 24); ++ SDPOKE8(sd, SDCMD2, x >> 16); ++ SDPOKE8(sd, SDCMD2, x >> 8); ++ SDPOKE8(sd, SDCMD2, x); ++ } ++#endif ++ SDPOKE8(sd, SDCMD2, arg); ++ } else { ++ // Build command packet ++ mkcommand(cmdidx, arg, cmdptr); ++ ++ // Send command ++ for (i = 0; i < 6; i++) { ++ unsigned int b = *cmdptr++; ++ unsigned int x; ++ ++ if (timeout(sd)) break; ++ for (j = 0; j < 8; j++) { ++ x = 0x8f | ((b & 0x80) >> 3); ++ b = b << 1; ++ SDPOKE8(sd, SDGPIO, x); // clk negedge ++ SDPEEK8(sd, SDGPIO); // delay ++ SDPEEK8(sd, SDGPIO); // delay ++ x |= 0x20; ++ SDPOKE8(sd, SDGPIO, x); // clk posedge ++ SDPEEK8(sd, SDGPIO); // delay ++ SDPEEK8(sd, SDGPIO); // delay ++ } ++ } ++ } ++ ++ if (type == TYPE_NORESP) goto done; ++ else if (type == TYPE_RXDAT_IGNRESP) goto ignresp; ++ else if (type == TYPE_LONGRESP) resplen = 17; ++ else resplen = 6; ++ ++ // clock until start bit on CMD pin ++ while(1) { ++ if (timeout(sd)) { ++ goto done; ++ } ++ if (req == CMD_SEND_IF_COND) sd->sd_timeout += 100000; ++ SDPOKE8(sd, SDGPIO, 0xdf); // clk negedge ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ s = SDPEEK8(sd, SDGPIO); // sample ++ if ((s & 0x10) == 0x0) break; ++ SDPOKE8(sd, SDGPIO, 0xff); // clk posedge ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ } ++ reset_timeout(sd); ++ ++ // Next we receive the response. ++ if (ok16 && !ok32) sddat2_8 = SDDAT2 + 1; ++ else sddat2_8 = SDDAT2; ++ if (dly) for (i = 0; i < resplen; i++) { ++ unsigned int r = 0; ++ ++ for (j = 0; j < 8; j++) { ++ SDPOKE8(sd, SDGPIO, 0xdf); // clk negedge ++ SDPEEK8(sd, SDGPIO); // delay ++ s = SDPEEK8(sd, SDGPIO); // sample ++ SDPOKE8(sd, SDGPIO, 0xff); // clk posedge ++ SDPEEK8(sd, SDGPIO); // delay ++ SDPEEK8(sd, SDGPIO); // delay ++ r = r << 1; ++ r |= ((s & 0x10) >> 4); ++ } ++ ++ *respptr++ = r; ++ } else while (resplen > 0) { ++ unsigned int r; ++ ++#ifdef BIGENDIAN ++ if (ok32 && resplen >= 4) { ++ r = SDPEEK32(sd, SDCMD2); ++ *respptr++ = r & 0xff; ++ *respptr++ = (r >> 8) & 0xff; ++ *respptr++ = (r >> 16) & 0xff; ++ *respptr++ = (r >> 24); ++ resplen -= 4; ++ } else if (ok16 && resplen >= 2) { ++ r = SDPEEK16(sd, SDCMD2); ++ *respptr++ = r & 0xff; ++ *respptr++ = (r >> 8) & 0xff; ++ ++ resplen -= 2; ++ } else { ++ *respptr++ = SDPEEK8(sd, sddat2_8); ++ resplen--; ++ } ++#else ++ if (ok32 && resplen >= 4) { ++ r = SDPEEK32(sd, SDCMD2); ++ *respptr++ = (r >> 24); ++ *respptr++ = (r >> 16) & 0xff; ++ *respptr++ = (r >> 8) & 0xff; ++ *respptr++ = r & 0xff; ++ resplen -= 4; ++ } else if (ok16 && resplen >= 2) { ++ r = SDPEEK16(sd, SDCMD2); ++ *respptr++ = (r >> 8) & 0xff; ++ *respptr++ = r & 0xff; ++ resplen -= 2; ++ } else { ++ *respptr++ = SDPEEK8(sd, sddat2_8); ++ resplen--; ++ } ++#endif ++ } ++ if (type == TYPE_BSYRESP) { ++ s = 0; ++ while ((s & 0x7) != 0x7) { ++ if (timeout(sd)) break; ++ SDPOKE8(sd, SDGPIO, 0x9f); // clk negedge ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ s = s << 1; ++ s |= SDPEEK8(sd, SDGPIO) & 0x1; ++ SDPOKE8(sd, SDGPIO, 0xbf); ++ if (dly) SDPEEK8(sd, SDGPIO); ++ } ++ } ++ ++ignresp: ++ ++ if (type == TYPE_ABORT) ++ sd->sd_state &= ~(SDDAT_RX|SDDAT_TX); ++ ++#ifndef SD_READONLYDMA ++ if (type == TYPE_TXDAT) { ++ sd->sd_state |= SDDAT_TX; ++ /* 2 clocks for nWR */ ++ SDPOKE8(sd, SDGPIO, 0xdf); // clk negedge ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ SDPOKE8(sd, SDGPIO, 0xff); // clk posedge ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ SDPOKE8(sd, SDGPIO, 0xdf); // clk negedge ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ SDPOKE8(sd, SDGPIO, 0xff); // clk posedge ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ if (sd->sd_state & DATSSP_4BIT) ++ SDPOKE8(sd, SDGPIO, 0x10); // assert start, clk negedge ++ else ++ SDPOKE8(sd, SDGPIO, 0x1e); ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ if (sd->sd_state & DATSSP_4BIT) ++ SDPOKE8(sd, SDGPIO, 0x30); // clk posedge ++ else ++ SDPOKE8(sd, SDGPIO, 0x3e); ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ if (dly) SDPEEK8(sd, SDGPIO); // delay ++ } ++#endif ++ ++ if (type == TYPE_RXDAT || type == TYPE_RXDAT_IGNRESP) ++ sd->sd_state |= SDDAT_RX; ++ ++done: ++ // 8 clocks before stopping ++ if (!(sd->sd_state & (SDDAT_TX|SDDAT_RX))) { ++ if (dly) for (i = 0; i < 8; i++) { ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); // delay ++ SDPEEK8(sd, SDGPIO); // delay ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); // delay ++ SDPEEK8(sd, SDGPIO); // delay ++ } else { ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPOKE8(sd, SDCMD2, 0xff); ++ } ++ } ++ if (timeout(sd)) return 1; ++ else return 0; ++ ++} ++ ++ ++static int ++sdcmd(struct sdcore *sd, unsigned short req, unsigned int arg, ++ unsigned int *resp, unsigned char **dat) ++{ ++ unsigned int s, cmdresp[17]; ++ unsigned int resplen; ++ unsigned int type = (req >> 8); ++ unsigned int cmdidx = req; ++ unsigned int *cmdptr = cmdresp; ++ unsigned int *cmd = cmdresp; ++ unsigned int *respptr; ++ unsigned int ndat; ++ ++ if (sd->hw_version != 0) return sdcmd2(sd, req, arg, resp, dat); ++ ++ // If no space for response provided by caller, use local buffer ++ if (resp == NULL) resp = cmdresp; ++ respptr = resp; ++ ++ // Before continuing, we must wait for the FSM to get to the ++ // S_SEND_CMD state. After a previous command, we may still be ++ // in S_DUMMY_CLK or in case of an ABORT, we may be in the middle of ++ // clocking a byte for TX or RX. ++ s = SDPEEK8(sd, SDSTATE); ++ while ((s & 0x7) != S_SEND_CMD) { ++ if (timeout(sd)) break; ++ s = SDPEEK8(sd, SDSTATE); ++ } ++ ++ // We know we're in S_SEND_CMD, but we may need to change the ++ // command type. This won't cause a state change. ++ if ((s & 0xe7) != (S_SEND_CMD | (type << 5))) ++ SDPOKE8(sd, SDSTATE, S_SEND_CMD | (type << 5)); ++ ++ // Build command packet ++ mkcommand(cmdidx, arg, cmdptr); ++ ++ // Next, we loop while tending the SSPs until we get our last ++ // byte of command data out. We may get a few bytes from the DAT ++ // SSP if we are aborting a previous data transfer command. If we do ++ // those get placed in a buffer or thrown away based on the callers ++ // "dat" parameter. ++ sd->sd_state |= SDCMD_TX; ++ while ((cmdptr - cmd) != 6) { ++ if (timeout(sd)) break; ++ s = tend_ssp(sd, &cmdptr, dat); ++ } ++ sd->sd_state &= ~SDCMD_TX; ++ ++ // If we got out of sync with the hardware, that would be bad. ++ // The hardware should still be in S_SEND_CMD for the last CMDSSP ++ // byte. ++ if ((s & 0x7) != S_SEND_CMD) { ++ SDPOKE8(sd, SDSTATE, S_OFF); ++ return 1; ++ } ++ ++ if (type == TYPE_NORESP) goto done; ++ else if (type == TYPE_LONGRESP) resplen = 17; ++ else resplen = 6; ++ ++ // Next state should be S_WAIT_RESP or S_RX_RESP. We may get ++ // more bytes from the DATSSP while shifting out our last bits of cmd ++ while (((s & 0x7) != S_WAIT_RESP) && ((s & 0x7) != S_RX_RESP)) { ++ if (timeout(sd)) break; ++ if (req == CMD_SEND_IF_COND) sd->sd_timeout += 1000; ++ s = tend_ssp(sd, NULL, dat); ++ } ++ ++ // Once we're in S_WAIT_RESP or S_RX_RESP though, the DATSSP is only ++ // active for 2 more clocks at the beginning of the S_WAIT_RESP state. ++ // This is enough for one more byte in 4-bit mode, though we may have ++ // 2 bytes already in our DATSSP. ++ if (sd->sd_state & (SDDAT_RX|SDDAT_TX)) { ++ do { ++ if (timeout(sd)) break; ++ s = tend_ssp(sd, NULL, dat); ++ } while (!(s & 0x18)); ++ ++ // We've now read/wrote one more byte to the DATSSP ++ // which should allow our FSM to advance to the RX_RESP state. ++ // If we pick up more than 2 more DATSSP bytes, something is ++ // wrong. ++ ndat = 0; ++ while ((s & 0x7) != S_RX_RESP) { ++ if (timeout(sd) || ndat > 2) break; ++ s = tend_ssp(sd, NULL, dat); ++ if (s & 0x10) ndat++; ++ } ++ ++ if (ndat > 2) { ++ SDPOKE8(sd, SDSTATE, S_OFF); ++ return 1; ++ } ++ } ++ ++ // We're now done with whatever business we had remaining with the ++ // previous command's DATSSP transfer since we've either just got our ++ // first byte of response or our last byte of data ++ sd->sd_state &= ~(SDDAT_RX|SDDAT_TX); ++ if (type == TYPE_RXDAT) sd->sd_state |= SDDAT_RX; ++ ++ // Next we receive the response. If this is TYPE_RXDAT command, ++ // or an abortion of a previous TYPE_RXDAT command, we may get a ++ // few bytes from the DAT SSP also. ++ sd->sd_state |= SDCMD_RX; ++ while ((respptr - resp) != resplen) { ++ if (timeout(sd)) break; ++ s = tend_ssp(sd, &respptr, dat); ++ if ((s & 0x10) && (resp == respptr)) { ++ SDPOKE8(sd, SDSTATE, S_OFF); ++ sd->sd_state &= ~(SDCMD_RX|SDDAT_RX); ++ return 1; ++ } ++ } ++ sd->sd_state &= ~SDCMD_RX; ++ ++ if (type == TYPE_ABORT) ++ sd->sd_state &= ~(SDDAT_RX|SDDAT_TX); ++ ++ if (type == TYPE_TXDAT) sd->sd_state |= SDDAT_TX; ++ ++done: ++ if (timeout(sd)) return 1; ++ else return 0; ++ ++} ++ ++static int datssp_stream2(struct sdcore *sd, unsigned char **dat, ++ unsigned int buflen) ++{ ++ unsigned char *d; ++ int ret; ++#ifndef SD_READONLYDMA ++ int ok32; ++ int ok16; ++ int sddat2_8; ++ unsigned int x; ++#endif ++ ++ if (sd->os_dmastream /* && (sd->sd_state & SDDAT_RX) */) { ++ d = dat ? *dat : NULL; ++ ret = sd->os_dmastream(sd->os_arg, d, buflen); ++ if (!ret && d) *dat += buflen; ++ return ret; ++ } ++ ++#ifndef SD_READONLYDMA ++ d = *dat; ++ ++ while (buflen > 512) { ++ datssp_stream2(sd, dat, 512); ++ if (sd->os_irqwait) sd->os_irqwait(sd->os_arg, 1); ++ buflen -= 512; ++ d = *dat; ++ } ++ ++ ok32 = (sd->hw_version == 2); ++ ok16 = (ok32 || (sd->hw_version == 3)); ++ if (ok16 && !ok32) sddat2_8 = SDDAT2 + 1; ++ else sddat2_8 = SDDAT2; ++ ++ if (sd->sd_state & SDDAT_RX) { ++ ++ while (((int)d & 0x1) || buflen == 1) { ++ *d++ = SDPEEK8(sd, sddat2_8); ++ buflen--; ++ } ++ ++ if (((int)d & 0x2) && buflen >= 2) { ++ if (ok16) *(unsigned short *)(d) = SDPEEK16(sd, SDDAT2); ++ else { ++#ifdef BIGENDIAN ++ x = SDPEEK8(sd, sddat2_8) << 8; ++ x |= SDPEEK8(sd, sddat2_8); ++#else ++ x = SDPEEK8(sd, sddat2_8); ++ x |= SDPEEK8(sd, sddat2_8) << 8; ++#endif ++ *(unsigned short *)(d) = x; ++ } ++ buflen -= 2; ++ d += 2; ++ } ++ ++ if (ok32) while (buflen >= 4) { ++ *(unsigned int *)(d) = SDPEEK32(sd, SDDAT2); ++ buflen -= 4; ++ d += 4; ++ } else if (ok16) while (buflen >= 4) { ++#ifdef BIGENDIAN ++ x = SDPEEK16(sd, SDDAT2) << 16; ++ x |= SDPEEK16(sd, SDDAT2); ++#else ++ x = SDPEEK16(sd, SDDAT2); ++ x |= SDPEEK16(sd, SDDAT2) << 16; ++#endif ++ buflen -= 4; ++ *(unsigned int *)(d) = x; ++ d += 4; ++ } else while (buflen >= 4) { ++#ifdef BIGENDIAN ++ x = SDPEEK8(sd, sddat2_8) << 24; ++ x |= SDPEEK8(sd, sddat2_8) << 16; ++ x |= SDPEEK8(sd, sddat2_8) << 8; ++ x |= SDPEEK8(sd, sddat2_8); ++#else ++ x = SDPEEK8(sd, sddat2_8); ++ x |= SDPEEK8(sd, sddat2_8) << 8; ++ x |= SDPEEK8(sd, sddat2_8) << 16; ++ x |= SDPEEK8(sd, sddat2_8) << 24; ++#endif ++ buflen -= 4; ++ *(unsigned int *)(d) = x; ++ d += 4; ++ } ++ } else { ++ while (((int)d & 0x1) || buflen == 1) { ++ SDPOKE8(sd, SDDAT2, *d++); ++ buflen--; ++ } ++ ++ if (((int)d & 0x2) && buflen >= 2) { ++ if (ok16) SDPOKE16(sd, SDDAT2, *(unsigned short *)(d)); ++ else { ++ x = *(unsigned short *)(d); ++#ifdef BIGENDIAN ++ SDPOKE8(sd, SDDAT2, x >> 8); ++ SDPOKE8(sd, SDDAT2, x); ++#else ++ SDPOKE8(sd, SDDAT2, x); ++ SDPOKE8(sd, SDDAT2, x >> 8); ++#endif ++ } ++ buflen -= 2; ++ d += 2; ++ } ++ ++ if (ok32) while (buflen >= 4) { ++ SDPOKE32(sd, SDDAT2, *(unsigned int *)(d)); ++ buflen -= 4; ++ d += 4; ++ } else if (ok16) while (buflen >= 4) { ++ x = *(unsigned int *)(d); ++ buflen -= 4; ++ d += 4; ++#ifdef BIGENDIAN ++ SDPOKE16(sd, SDDAT2, x >> 16); ++ SDPOKE16(sd, SDDAT2, x); ++#else ++ SDPOKE16(sd, SDDAT2, x); ++ SDPOKE16(sd, SDDAT2, x >> 16); ++#endif ++ } else while (buflen >= 4) { ++ x = *(unsigned int *)(d); ++ buflen -= 4; ++ d += 4; ++#ifdef BIGENDIAN ++ SDPOKE8(sd, SDDAT2, x >> 24); ++ SDPOKE8(sd, SDDAT2, x >> 16); ++ SDPOKE8(sd, SDDAT2, x >> 8); ++ SDPOKE8(sd, SDDAT2, x); ++#else ++ SDPOKE8(sd, SDDAT2, x); ++ SDPOKE8(sd, SDDAT2, x >> 8); ++ SDPOKE8(sd, SDDAT2, x >> 16); ++ SDPOKE8(sd, SDDAT2, x >> 24); ++#endif ++ } ++ } ++ ++ *dat = d; ++ ++ if (buflen > 0) return datssp_stream2(sd, dat, buflen); ++ else return 0; ++#else ++ return 0; ++#endif ++} ++ ++static int datssp_stream(struct sdcore *sd, unsigned char **dat, ++ unsigned int buflen) ++{ ++ unsigned int s, t, byte = 0; ++ unsigned char *d; ++ ++ if (((sd->sd_state & SDDAT_RX) && sd->os_dmastream) /* || ++ ((sd->sd_state & SDDAT_TX) && sd->os_dmastream && dat) */ ) { ++ unsigned char *d = dat ? *dat : NULL; ++ int ret = sd->os_dmastream(sd->os_arg, d, buflen); ++ if (!ret && d) *dat += buflen; ++ return ret; ++ } ++ ++ if (sd->hw_version > 0) return datssp_stream2(sd, dat, buflen); ++ ++ while (buflen) { ++ if (timeout(sd)) return 1; ++ s = tend_ssp(sd, NULL, dat); ++ if (s & 0x10) { ++ buflen--; ++ if (byte++ > 7) { ++ if (sd->sd_state & SDDAT_RX) ++ goto fastrx; ++ else goto fasttx; ++ } ++ } ++ } ++ ++ // Now we can go faster (PIO) ++fastrx: ++ if (dat) { ++ d = *dat; ++ while (buflen) { ++ s = SDPEEK8(sd, SDDAT); ++ *d = s; ++ buflen--; ++ d++; ++ } ++ *dat = d; ++ } else { ++ while (buflen--) SDPEEK8(sd, SDDAT); ++ } ++ return 0; ++ ++fasttx: ++ if (dat) { ++ d = *dat; ++ while (buflen) { ++ t = *d; ++ SDPOKE8(sd, SDDAT, t); ++ buflen--; ++ d++; ++ datssp_feedcrc(sd, t); ++ } ++ *dat = d; ++ } else { ++ while (buflen--) SDPOKE8(sd, SDDAT, datssp_getcrc(sd)); ++ } ++ return 0; ++} ++ ++static int stop(struct sdcore *sd) ++{ ++ int ret; ++ unsigned int resp[6]; ++ ++ if (sd->hw_version) return stop2(sd); ++ ++ if (sd->parked_sector) { ++ if (sd->sd_state & SDDAT_TX) { ++ /* wait to get out of S_WAIT_BUSY */ ++ while ((SDPEEK8(sd, SDSTATE) & 0x7) != S_TX_WRITE) ++ if (timeout(sd)) break; ++ ++ /* abort parked write */ ++ SDPOKE8(sd, SDSTATE, S_SEND_CMD | (TYPE_ABORT << 5)); ++ sd->sd_state &= ~SDDAT_TX; ++ sd->sd_state |= SDDAT_RX; ++ ret = sdcmd(sd, CMD_STOP_TRANSMISSION, 0, resp, NULL); ++ sd->sd_state &= ~SDDAT_RX; ++ SDPOKE8(sd, SDSTATE, S_WAIT_BUSY | (TYPE_BSYRESP << 5)); ++ } else { ++ /* abort parked read */ ++ SDPOKE8(sd, SDSTATE, S_SEND_CMD | (TYPE_ABORT << 5)); ++ ret = sdcmd(sd, CMD_STOP_TRANSMISSION, 0, resp, NULL); ++ } ++ sd->parked_sector = 0; ++ if (ret || error(resp, CMD_STOP_TRANSMISSION) || timeout(sd)) ++ return 1; ++ } ++ return 0; ++} ++ ++static int stop2(struct sdcore *sd) ++{ ++ int ret; ++ unsigned int resp[6]; ++ ++ if (sd->parked_sector) { ++ if (sd->os_irqwait) sd->os_irqwait(sd->os_arg, 0); ++ if (sd->sd_state & SDDAT_TX) { ++ /* abort parked write */ ++ ret = sdcmd2(sd, CMD_STOP_TRANSMISSION, 0, resp, NULL); ++ SDPOKE8(sd, SDCTRL2, 0x0); ++ if (sd->os_irqwait) sd->os_irqwait(sd->os_arg, 5); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ ++ /* ++ while ((SDPEEK8(sd, SDGPIO) & 0xf) != 0xf) { ++ sd->os_delay(sd->os_arg, 1); ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ if (timeout(sd)) return 1; ++ } ++ */ ++ reset_timeout(sd); ++ } else { ++ /* abort parked read */ ++ ret = sdcmd2(sd, CMD_STOP_TRANSMISSION, 0, resp, NULL); ++ } ++ sd->parked_sector = 0; ++ if (ret || error(resp, CMD_STOP_TRANSMISSION) || timeout(sd)) { ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int do_read2(struct sdcore *sd, unsigned int sector, struct sdiov *iov, ++ unsigned int iovcnt) ++{ ++ unsigned int ret, n, s, sz; ++ unsigned char *datptr, *dat; ++ ++ if (iovcnt == 0) return 0; ++ ++ if (activate(sd)) return 1; ++ ++ n = iov->sdiov_nsect; ++ datptr = dat = iov->sdiov_base; ++ sz = sdsize(sd); ++ if (sector >= sz) return 0; ++ ++ if (sd->parked_sector) { ++ if (!(sd->sd_state & SDDAT_TX) && sd->parked_sector == sector) { ++ if (sd->os_irqwait && !sd->os_dmastream) ++ sd->os_irqwait(sd->os_arg, 3); ++ goto receive; ++ } ++ ++ stop2(sd); ++ } ++ ++ if (sd->sd_state & SD_HC) ++ ret = sdcmd2(sd, CMD_READ_MULTIPLE_BLOCK2, sector, NULL, NULL); ++ else ++ ret = sdcmd2(sd, CMD_READ_MULTIPLE_BLOCK2, sector * 512, NULL, NULL); ++ ++ do { ++ if (timeout(sd)) return 1;; ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ s = SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ } while ((s & 0xf) != 0x0); ++ reset_timeout(sd); ++ ++receive: ++ if (sd->os_dmaprep && sd->os_dmastream) ++ sd->os_dmaprep(sd->os_arg, datptr, n * 512); ++ ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ sd->parked_sector = sector + n; ++ ++nextiov: ++ if (sd->parked_sector > sz) { ++ n -= sd->parked_sector - sz; ++ sd->parked_sector = sz; ++ } ++ datssp_stream2(sd, &datptr, n * 512); ++ ++ if (--iovcnt) { ++ ++iov; ++ n = iov->sdiov_nsect; ++ datptr = iov->sdiov_base; ++ sd->parked_sector += n; ++ if (sd->os_dmaprep && sd->os_dmastream) ++ sd->os_dmaprep(sd->os_arg, datptr, n * 512); ++ goto nextiov; ++ } ++ ++ /* s = SDPEEK8(sd, SDSTAT2); ++ if (s & 0x44) { ++ sd->sd_timeout = 1000001; ++ return 1; ++ } ++ else */ return 0; ++} ++ ++static int do_read(struct sdcore *sd, unsigned int sector, struct sdiov *iov, ++ unsigned int iovcnt) ++{ ++ unsigned int resp[6], ret, n, sz; ++ unsigned char *datptr, *dat; ++ ++ if (iovcnt == 0) return 0; ++ ++ n = iov->sdiov_nsect; ++ datptr = dat = iov->sdiov_base; ++ sz = sdsize(sd); ++ if (sector >= sz) return 0; ++ ++ if (sd->parked_sector) { ++ if (!(sd->sd_state & SDDAT_TX) && sd->parked_sector == sector) ++ goto receive; ++ ++ stop(sd); ++ } ++ ++ if (sd->sd_state & SD_HC) ++ ret = sdcmd(sd, CMD_READ_MULTIPLE_BLOCK, sector, resp, &datptr); ++ else ++ ret = sdcmd(sd, CMD_READ_MULTIPLE_BLOCK, sector * 512, resp, &datptr); ++ if (ret || error(resp, CMD_READ_MULTIPLE_BLOCK)) return 1; ++ ++receive: ++ if (sd->os_dmaprep && sd->os_dmastream) ++ sd->os_dmaprep(sd->os_arg, datptr, n * 512 - (datptr - dat)); ++ ++ datssp_stream(sd, &datptr, 512 - (datptr - dat)); ++ datssp_stream(sd, NULL, 6); ++ ++ sd->parked_sector = sector + n; ++ if (sd->parked_sector > sz) { ++ n -= sd->parked_sector - sz; ++ sd->parked_sector = sz; ++ } ++ n--; ++ ++nextiov: ++ while (n--) { ++ SDPOKE8(sd, SDSTATE, S_WAIT_RESP | (TYPE_RXDAT << 5)); ++ datssp_stream(sd, NULL, 2); // last part of prev CRC ++ datssp_stream(sd, &datptr, 512); ++ datssp_stream(sd, NULL, 6); // first part of CRC ++ } ++ ++ if (--iovcnt) { ++ ++iov; ++ n = iov->sdiov_nsect; ++ datptr = iov->sdiov_base; ++ sd->parked_sector += n; ++ if (sd->parked_sector > sz) { ++ n -= sd->parked_sector - sz; ++ sd->parked_sector = sz; ++ } ++ if (sd->os_dmaprep && sd->os_dmastream && n > 0) ++ sd->os_dmaprep(sd->os_arg, datptr, n * 512); ++ goto nextiov; ++ } ++ ++ SDPOKE8(sd, SDSTATE, S_WAIT_RESP | (TYPE_RXDAT << 5)); ++ datssp_stream(sd, NULL, 2); // last part of prev CRC ++ return 0; ++} ++ ++static int do_write2(struct sdcore *sd, unsigned int sector, struct sdiov *iov, ++ unsigned int iovcnt) ++{ ++ unsigned char *datptr; ++ unsigned int resp[6], ret, n, s, sz, ss; ++ ++ if (sd->sd_wprot) return 1; ++ ++ if (iovcnt == 0) return 0; ++ ++ if (activate(sd)) return 1; ++ ++ sz = sdsize(sd); ++ if (sector >= sz) return 0; ++ ++ if (sd->os_powerok) { ++ int ok = sd->os_powerok(sd); ++ if (!ok && sd->parked_sector) { ++ stop2(sd); ++ return 1; ++ } else if (!ok) return 1; ++ } ++ ++ if (sd->parked_sector) { ++ if ((sd->sd_state & SDDAT_TX) && sd->parked_sector == sector) ++ goto transmit; ++ ++ stop2(sd); ++ } ++ ++ if (sd->sd_erasehint) { ++ sdcmd2(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd2(sd, ACMD_SET_WR_BLK_ERASE_COUNT, sd->sd_erasehint, ++ NULL, NULL); ++ sd->sd_erasehint = 0; ++ } ++ ++ if (sd->sd_state & SD_HC) ++ ret = sdcmd2(sd, CMD_WRITE_MULTIPLE_BLOCK, sector, resp, NULL); ++ else ++ ret = sdcmd2(sd, CMD_WRITE_MULTIPLE_BLOCK, sector * 512, resp, NULL); ++ if (ret || error(resp, CMD_WRITE_MULTIPLE_BLOCK)) { ++ return 1; ++ } ++ sd->parked_sector = sector; ++ ss = SDPEEK8(sd, SDSTAT2); ++ ++transmit: ++ while (iovcnt--) { ++ datptr = iov->sdiov_base; ++ n = iov->sdiov_nsect; ++ sd->parked_sector += n; ++ if (sd->parked_sector > sz) { ++ n -= sd->parked_sector - sz; ++ sd->parked_sector = sz; ++ } ++ datssp_stream2(sd, &datptr, n * 512); ++ iov++; ++ } ++ ++ if (!sd->sd_writeparking) { ++ ret = stop2(sd); ++ if (ret) return ret; ++ } ++ ++ if (sd->os_irqwait) sd->os_irqwait(sd->os_arg, 2); ++ ++ s = SDPEEK8(sd, SDSTAT2); ++ if (s & 0x44) { ++ sd->sd_timeout = 1000001; ++ return 1; ++ } else { ++ reset_timeout(sd); ++ return 0; ++ } ++} ++ ++static int do_write(struct sdcore *sd, unsigned int sector, struct sdiov *iov, ++ unsigned int iovcnt) ++{ ++ unsigned char *datptr, *crcptr, **crcptrptr; ++ unsigned int resp[6], ret, n, sz; ++ ++ if (sd->sd_wprot) return 1; ++ ++ if (iovcnt == 0) return 0; ++ ++ sz = sdsize(sd); ++ if (sector >= sz) return 0; ++ ++ if (0 /* sd->sd_crchint */) { ++ // CRC is pre-calculated so don't recalculate ++ crcptr = sd->sd_crchint; ++ crcptrptr = &crcptr; ++ sd->sd_state |= DATSSP_NOCRC; ++ sd->sd_crchint = NULL; ++ } else { ++ crcptrptr = NULL; ++ sd->sd_state &= ~DATSSP_NOCRC; ++ } ++ ++ if (sd->parked_sector) { ++ if ((sd->sd_state & SDDAT_TX) && sd->parked_sector == sector) ++ goto transmit; ++ ++ stop(sd); ++ } ++ ++ if (sd->sd_erasehint) { ++ sdcmd(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd(sd, ACMD_SET_WR_BLK_ERASE_COUNT, sd->sd_erasehint, ++ NULL, NULL); ++ sd->sd_erasehint = 0; ++ } ++ ++ if (sd->sd_state & SD_HC) ++ ret = sdcmd(sd, CMD_WRITE_MULTIPLE_BLOCK, sector, resp, NULL); ++ else ++ ret = sdcmd(sd, CMD_WRITE_MULTIPLE_BLOCK, sector * 512, resp, NULL); ++ if (ret || error(resp, CMD_WRITE_MULTIPLE_BLOCK)) { ++ return 1; ++ } ++ sd->parked_sector = sector; ++ ++transmit: ++ while (iovcnt--) { ++ datptr = iov->sdiov_base; ++ n = iov->sdiov_nsect; ++ sd->parked_sector += n; ++ if (sd->parked_sector > sz) { ++ n -= sd->parked_sector - sz; ++ sd->parked_sector = sz; ++ } ++ while (n--) { ++ datssp_stream(sd, &datptr, 512); ++ datssp_stream(sd, crcptrptr, 8); // CRC bytes ++ SDPOKE8(sd, SDSTATE, S_CRC_CHECK | (TYPE_TXDAT << 5)); ++ } ++ iov++; ++ } ++ ++ if (!sd->sd_writeparking) { ++ stop(sd); ++ } ++ ++ return 0; ++} ++ ++static ++int sdfastinit(struct sdcore *sd) ++{ ++ SDPOKE8(sd, SDCTRL, 0x40); ++ sd->sd_state = DATSSP_4BIT; ++ ++ sd->sd_rcaarg = ~sd->sdboot_token; ++ sdcmd(sd, CMD_DESELECT_CARD, ~sd->sd_rcaarg, NULL, NULL); ++ sdcmd(sd, CMD_SEND_CSD, sd->sd_rcaarg, sd->sd_csd, NULL); ++ sdcmd(sd, CMD_SELECT_CARD, sd->sd_rcaarg, NULL, NULL); ++ ++ if (sd->os_dmastream) SDPOKE8(sd, SDCTRL, 0x42); ++ if ((SDPEEK8(sd, SDCTRL) & 0x80) || (sd->sd_csd[15] & 0x30)) ++ sd->sd_wprot = 1; ++ sd->sd_blocksize = 1 << ((sd->sd_csd[6] & 0xf)); ++ if (timeout(sd)) return 0; ++ else return sdsize(sd); ++} ++ ++static ++int sdreset2(struct sdcore *sd) ++{ ++ unsigned int rca, s, i, x; ++ unsigned int resp[17]; ++ ++ reset_timeout(sd); ++ sd_initcrc(sd); ++ sd->parked_sector = 0; ++ sd->sd_wprot = 0; ++ sd->sd_blocksize = 0; ++ sd->sd_sz = 0; ++ if (sd->hw_version == 0) sd->hw_version = version(sd); ++ if (sd->hw_version == 0) return 0; ++ sd->sd_state &= SD_RESET; ++ remember_sdcore(sd); ++ activate(sd); ++ sd->sd_state |= SD_LOSPEED; ++ ++ if (!(sd->sd_state & SD_RESET) && (SDPEEK8(sd, SDGPIO) != 0x0)) { ++ SDPOKE8(sd, SDGPIO, 0x0); ++#ifdef BIGENDIAN ++ for (i = 0; i < 8; i++) SDPOKE16(sd, SDLUN2, i << 8); ++#else ++ for (i = 0; i < 8; i++) SDPOKE16(sd, SDLUN2, i); ++#endif ++ sd->os_delay(sd->os_arg, 100000); ++ ++ /* this was a global reset, so let the other luns know */ ++ for (i = 0; i < sizeof(sdcores); i++) { ++ if (sdcores[i] == NULL) break; ++ if (sdcores[i]->sd_regstart == sd->sd_regstart) ++ sdcores[i]->sd_state |= SD_RESET; ++ } ++#ifdef BIGENDIAN ++ SDPOKE16(sd, SDLUN2, sd->sd_lun << 8); ++#else ++ SDPOKE16(sd, SDLUN2, sd->sd_lun); ++#endif ++ } ++ sd->sd_state &= ~SD_RESET; ++ ++ // gratuitous clocks ++ SDPOKE8(sd, SDGPIO, 0xff); ++ sd->os_delay(sd->os_arg, 5000); ++ for (i = 0; i < 750; i++) { ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); /* delay */ ++ SDPEEK8(sd, SDGPIO); /* delay */ ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); /* delay */ ++ SDPEEK8(sd, SDGPIO); /* delay */ ++ } ++ ++ SDPEEK8(sd, SDSTAT2); /* reset any timeout/crc conditions */ ++ SDPOKE8(sd, SDSTAT2, 0x18); ++ s = sdcmd2(sd, CMD_SEND_IF_COND, 0x1aa, resp, NULL); ++ if (s) { ++ reset_timeout(sd); ++ x = 0x00ff0000; ++ } else { ++ x = 0x40ff0000; ++ } ++ ++ do { ++ sdcmd2(sd, CMD_APP_CMD, 0, NULL, NULL); ++ sdcmd2(sd, ACMD_SD_SEND_OP_COND, x, resp, NULL); ++ if (timeout(sd)) break; ++ // TODO: check for valid result or limit # of loops, ++ // otherwise we may loop forever on malfunctioning cards. ++ } while (((resp[1] & 0x80) == 0x0)); ++ ++ if ((x & 0x40000000) && (resp[1] & 0x40)) { ++ sd->sd_state |= SD_HC; ++ } ++ ++ sdcmd2(sd, CMD_ALL_SEND_CID, 0, resp, NULL); ++ sdcmd2(sd, CMD_SEND_RELATIVE_ADDR, 0, resp, NULL); ++ rca = resp[1] << 8 | resp[2]; ++ sd->sd_rcaarg = (rca & 0xff00) << 16 | (rca & 0xff) << 16; ++ sd->sdboot_token = ~sd->sd_rcaarg; ++ ++ sdcmd2(sd, CMD_SEND_CSD, sd->sd_rcaarg, sd->sd_csd, NULL); ++ sdcmd2(sd, CMD_SELECT_CARD, sd->sd_rcaarg, resp, NULL); ++ ++ if ((resp[1] & 0x2)) { ++ unsigned int ret = 1; ++ sd->sd_locked = 1; ++#ifndef SD_NOLOCKSUPPORT ++ if (sd->sd_pwd) ++ ret = sdlockctl2(sd, SDLOCK_UNLOCK, sd->sd_pwd, NULL); ++#endif ++ if (ret != 0) return 0; ++ } else sd->sd_locked = 0; ++ ++ sdcmd2(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd2(sd, ACMD_SET_CLR_CARD_DETECT, 0, NULL, NULL); ++ sdcmd2(sd, CMD_SET_BLOCKLEN, 512, NULL, NULL); ++ sdcmd2(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd2(sd, ACMD_SET_BUS_WIDTH, 2, resp, NULL); ++ sd->sd_state |= DATSSP_4BIT; ++ sd->sd_state &= ~SD_LOSPEED; ++ ++ sdcmd2(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd2(sd, ACMD_SEND_SCR2, 0, NULL, NULL); ++ do { ++ if (timeout(sd)) break; ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); ++ s = SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); ++ } while ((s & 0xf) != 0x0); ++ for (i = 0; i < 16; i++) { ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); ++ s = (SDPEEK8(sd, SDGPIO) & 0xf) << 4; ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); ++ s |= (SDPEEK8(sd, SDGPIO) & 0xf); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); ++ if (i < 8) sd->sd_scr[i] = s; ++ } ++ for (i = 0; i < 8; i++) { ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); ++ } ++ sd->sd_state &= ~SDDAT_RX; ++ ++#ifndef SD_NOHIGHSPEED ++ if ((sd->sd_scr[0] & 0xf) >= 1) { // SD version >= 1.10 ++ unsigned char dat[64]; ++ sdcmd2(sd, CMD_SWITCH_FUNC2, 0x80fffff1, NULL, NULL); ++ do { ++ if (timeout(sd)) break; ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); ++ s = SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); ++ } while ((s & 0xf) != 0x0); ++ for (i = 0; i < 72; i++) { ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); ++ s = (SDPEEK8(sd, SDGPIO) & 0xf) << 4; ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); ++ s |= (SDPEEK8(sd, SDGPIO) & 0xf); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); ++ if (i < 64) dat[i] = s; ++ } ++ for (i = 0; i < 8; i++) { ++ SDPOKE8(sd, SDGPIO, 0xdf); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xff); ++ SDPEEK8(sd, SDGPIO); ++ } ++ sd->sd_state &= ~SDDAT_RX; ++ if (dat[0] | dat[1]) { ++ SDPOKE8(sd, SDSTAT2, 0x38); ++ sd->sd_state |= SD_HISPEED; ++ } ++ } ++#endif ++ ++#ifdef BIGENDIAN ++ if ((sd->sd_csd[15] & 0x30) || (SDPEEK16(sd, SDGPIO) & 0x2)) ++#else ++ if ((sd->sd_csd[15] & 0x30) || (SDPEEK16(sd, SDGPIO) & 0x200)) ++#endif ++ sd->sd_wprot = 1; ++ sd->sd_blocksize = 1 << ((sd->sd_csd[6] & 0xf)); ++ if (timeout(sd)) return 0; ++ else { ++ reset_timeout(sd); ++ return sdsize(sd); ++ } ++} ++ ++/* ++ * return 0 : 8 bit TS-SDCORE v1 ++ * return 1 : 8 bit 4x8 TS-SDCORE v2 ++ * return 2 : 32 bit 4x32 TS-SDCORE v2 ++ * return 3 : 16 bit 4x32 TS-SDCORE v2 ++ * return 4 : 8 bit 4x32 TS-SDCORE v2 ++ */ ++static int version(struct sdcore *sd) ++{ ++ int a, b, i; ++ ++ ++#ifdef SD_FORCEVERSION ++ return SD_FORCEVERSION; ++#endif ++ for (i = 0; i < sizeof(sdcores); i++) { ++ if (sdcores[i] == NULL) break; ++ if (sdcores[i]->sd_regstart == sd->sd_regstart) ++ return sdcores[i]->hw_version; ++ } ++ ++ a = SDPEEK8(sd, 3); ++ SDPOKE8(sd, 3, (a ^ 0x40)); ++ b = SDPEEK8(sd, 3); ++ SDPOKE8(sd, 3, a); ++ if ((a & 0x40) ^ (b & 0x40)) return 0; ++ else if (a & 0x40) return 1; ++ /* either 2, 3, or 4 */ ++ a = SDPEEK32(sd, 12); ++ b = SDPEEK16(sd, 12); ++#ifdef BIGENDIAN ++ if ((a & 0x40000000) && (b & 0x4000)) return 2; ++#else ++ if ((a & 0x40) && (b & 0x40)) return 2; ++#endif ++ a = SDPEEK8(sd, 12); ++ if (a & 0x40) return 3; ++ else return 4; ++} ++ ++int sdreset(struct sdcore *sd) ++{ ++ unsigned int rca, s, x; ++ unsigned int resp[17]; ++ ++ reset_timeout(sd); ++ sd_initcrc(sd); ++ sd->parked_sector = 0; ++ sd->sd_wprot = 0; ++ sd->sd_blocksize = 0; ++ sd->sd_sz = 0; ++ ++ sd->hw_version = version(sd); ++ if (sd->hw_version >= 2) return sdreset2(sd); ++ ++ // check for no SD card present ++ if (SDPEEK8(sd, SDCTRL) & 0x8) return 0; ++ ++ if (sd->sdboot_token) { ++ int ret = sdfastinit(sd); ++ sd->sdboot_token = 0; ++ if (ret) return ret; ++ } ++ ++ // set controller for 1-bit mode, slow clock ++ SDPOKE8(sd, SDCTRL, 0x20); ++ ++ SDPOKE8(sd, SDSTATE, S_DUMMY_CLK); ++ sd->sd_state = SDCMD_RX|SDDAT_RX; ++ s = SDPEEK8(sd, SDSTATE); ++ while ((s & 0x7) != S_SEND_CMD) { ++ // If we timeout here, it would be VERY BAD as we have no ++ // further recourse to set things right if we can't turn ++ // the SD off. ++ if (timeout(sd)) return 0; ++ sd->os_delay(sd->os_arg, 10000); ++ sd->sd_timeout += 10000; ++ ++ // We won't be able to change state until both SSPs are empty ++ s = tend_ssp(sd, NULL, NULL); ++ } ++ SDPOKE8(sd, SDSTATE, S_OFF); ++ sd->sd_state = 0; ++ ++ sd->os_delay(sd->os_arg, 50000); ++ ++ SDPOKE8(sd, SDSTATE, S_DUMMY_CLK); ++ sd->os_delay(sd->os_arg, 100000); ++ if ((SDPEEK8(sd, SDSTATE) & 0x7) == S_OFF) { ++ // No card present ++ return 0; ++ } ++ ++ SDPOKE8(sd, SDSTATE, S_WAIT_RESP); ++ // clock will freerun waiting for a response that will never come ++ sd->os_delay(sd->os_arg, 50000); ++ ++ SDPOKE8(sd, SDSTATE, S_DUMMY_CLK); ++ ++ s = sdcmd(sd, CMD_SEND_IF_COND, 0x1aa, resp, NULL); ++ if (s) { ++ reset_timeout(sd); ++ SDPOKE8(sd, SDSTATE, S_DUMMY_CLK); ++ x = 0x00ff0000; ++ } else { ++ x = 0x40ff0000; ++ } ++ ++ do { ++ sdcmd(sd, CMD_APP_CMD, 0, NULL, NULL); ++ sdcmd(sd, ACMD_SD_SEND_OP_COND, x, resp, NULL); ++ if (timeout(sd)) break; ++ } while (((resp[1] & 0x80) == 0x0)); ++ ++ if ((x & 0x40000000) && (resp[1] & 0x40)) sd->sd_state |= SD_HC; ++ ++ sdcmd(sd, CMD_ALL_SEND_CID, 0, resp, NULL); ++ sdcmd(sd, CMD_SEND_RELATIVE_ADDR, 0, resp, NULL); ++ rca = resp[1] << 8 | resp[2]; ++ sd->sd_rcaarg = (rca & 0xff00) << 16 | (rca & 0xff) << 16; ++ sd->sdboot_token = ~sd->sd_rcaarg; ++ ++ sdcmd(sd, CMD_SEND_CSD, sd->sd_rcaarg, sd->sd_csd, NULL); ++ sdcmd(sd, CMD_SELECT_CARD, sd->sd_rcaarg, resp, NULL); ++ ++ if ((resp[1] & 0x2)) { ++ unsigned int ret = 1; ++ sd->sd_locked = 1; ++#ifndef SD_NOLOCKSUPPORT ++ if (sd->sd_pwd) ++ ret = sdlockctl(sd, SDLOCK_UNLOCK, sd->sd_pwd, NULL); ++#endif ++ if (ret != 0) return 0; ++ } else sd->sd_locked = 0; ++ ++ sdcmd(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd(sd, ACMD_SET_CLR_CARD_DETECT, 0, NULL, NULL); ++ /* ++ sdcmd(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd(sd, ACMD_SEND_SCR, 0, NULL, &datptr); ++ while ((datptr - sd->sd_scr) != 8) { ++ if (timeout(sd)) return 1; ++ tend_ssp(sd, NULL, &datptr); ++ } ++ datssp_stream(sd, NULL, 3); ++ SDPOKE8(sd, SDSTATE, (TYPE_ABORT << 5) | S_SEND_CMD); ++ sd->sd_state |= SDCMD_RX|SDDAT_RX; ++ while ((SDPEEK8(sd, SDSTATE) & 0x17) != S_SEND_CMD) { ++ if (timeout(sd)) break; ++ tend_ssp(sd, NULL, NULL); ++ } ++ sd->sd_state &= ~(SDCMD_RX|SDDAT_RX); ++ if ((sd->sd_scr[0] & 0xf) >= 1) { // SD version >= 1.10 ++ unsigned char dat[64]; ++ datptr = dat; ++ sdcmd(sd, CMD_SWITCH_FUNC, 0x80fffff1, NULL, &datptr); ++ while ((datptr - dat) != 64) { ++ if (timeout(sd)) break; ++ tend_ssp(sd, NULL, &datptr); ++ } ++ datssp_stream(sd, NULL, 3); ++ SDPOKE8(sd, SDSTATE, (TYPE_ABORT << 5) | S_SEND_CMD); ++ sd->sd_state |= SDCMD_RX|SDDAT_RX; ++ while ((SDPEEK8(sd, SDSTATE) & 0x7) != S_SEND_CMD) { ++ if (timeout(sd)) break; ++ tend_ssp(sd, NULL, NULL); ++ } ++ sd->sd_state &= ~(SDCMD_RX|SDDAT_RX); ++ } ++ */ ++ ++ sdcmd(sd, CMD_SET_BLOCKLEN, 512, NULL, NULL); ++ sdcmd(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd(sd, ACMD_SET_BUS_WIDTH, 2, resp, NULL); ++ ++ // set controller for 4-bit mode, fast clock ++ SDPOKE8(sd, SDCTRL, (0x40 | (sd->os_dmastream ? 0x2 : 0x0))); ++ sd->sd_state |= DATSSP_4BIT; ++ ++ /* ++ sdcmd(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd(sd, ACMD_SEND_SCR, 0, NULL, &datptr); ++ while ((datptr - sd->sd_scr) != 8) { ++ if (timeout(sd)) break; ++ tend_ssp(sd, NULL, &datptr); ++ } ++ datssp_stream(sd, NULL, 6); ++ SDPOKE8(sd, SDSTATE, S_DUMMY_CLK | (TYPE_SHORTRESP << 5)); ++ bzero(resp, 6 * 4); ++ sdcmd(sd, CMD_SEND_STATUS, 0, resp, NULL); ++ */ ++ ++ if ((SDPEEK8(sd, SDCTRL) & 0x80) || (sd->sd_csd[15] & 0x30)) ++ sd->sd_wprot = 1; ++ sd->sd_blocksize = 1 << ((sd->sd_csd[6] & 0xf)); ++ if (timeout(sd) || error(resp, ACMD_SET_BUS_WIDTH)) return 0; ++ else return sdsize(sd); ++} ++ ++static ++int sdread2(struct sdcore *sd, unsigned int sector, unsigned char *dat, ++ int nsectors) ++{ ++ struct sdiov iov; ++ int ret; ++ ++ iov.sdiov_base = dat; ++ iov.sdiov_nsect = nsectors; ++ ret = do_read2(sd, sector, &iov, 1); ++ return ret; ++} ++ ++int sdread(struct sdcore *sd, unsigned int sector, unsigned char *dat, ++ int nsectors) ++{ ++ struct sdiov iov; ++ int ret; ++ ++ iov.sdiov_base = dat; ++ iov.sdiov_nsect = nsectors; ++ if (sd->hw_version == 0) ret = do_read(sd, sector, &iov, 1); ++ else ret = do_read2(sd, sector, &iov, 1); ++ return ret; ++} ++ ++int sdwrite(struct sdcore *sd, unsigned int sector, unsigned char *dat, ++ int nsectors) ++{ ++ struct sdiov iov; ++ unsigned int ret; ++ ++ iov.sdiov_base = dat; ++ iov.sdiov_nsect = nsectors; ++ if (sd->hw_version == 0) ret = do_write(sd, sector, &iov, 1); ++ else ret = do_write2(sd, sector, &iov, 1); ++ return ret; ++ ++} ++ ++int sdreadv(struct sdcore *sd, unsigned int sector, struct sdiov *iov, ++ int niov) ++{ ++ if (sd->hw_version == 0) return do_read(sd, sector, iov, niov); ++ else return do_read2(sd, sector, iov, niov); ++} ++ ++int sdwritev(struct sdcore *sd, unsigned int sector, struct sdiov *iov, ++ int niov) ++{ ++ if (sd->hw_version == 0) return do_write(sd, sector, iov, niov); ++ else return do_write2(sd, sector, iov, niov); ++} ++ ++static ++int sdsetwprot2(struct sdcore *sd, unsigned int perm) ++{ ++ int i, ret, s; ++ unsigned int csd[16], resp[6]; ++ unsigned char csdchars[16]; ++ unsigned char *csdptr = csdchars; ++ ++ stop2(sd); ++ ++ perm = perm ? 0x3 : 0x1; ++ for (i = 0; i < 16; i++) csd[i] = sd->sd_csd[i + 1]; ++ csd[14] &= ~(0x3 << 4); ++ csd[14] |= (perm << 4); ++ csd[15] = 0x1 | crc7(0, csd, 15) << 1; ++ for (i = 0; i < 16; i++) csdchars[i] = csd[i]; ++ ++ ret = sdcmd2(sd, CMD_PROGRAM_CSD, 0, resp, NULL); ++ if (ret || error(resp, CMD_PROGRAM_CSD)) return 1; ++ for (i = 0; i < 16; i++) { ++ s = *csdptr++; ++ sd_4bit_feedcrc(sd, s); ++ SDPOKE8(sd, SDGPIO, (0x10|((s & 0xf0) >> 4))); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, (0x30|((s & 0xf0) >> 4))); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, (0x10|(s & 0xf))); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, (0x30|(s & 0xf))); ++ SDPEEK8(sd, SDGPIO); ++ } ++ for (i = 0; i < 8; i++) { ++ s = sd_4bit_getcrc(sd); ++ SDPOKE8(sd, SDGPIO, (0x10|((s & 0xf0) >> 4))); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, (0x30|((s & 0xf0) >> 4))); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, (0x10|(s & 0xf))); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, (0x30|(s & 0xf))); ++ SDPEEK8(sd, SDGPIO); ++ } ++ // End bit ++ SDPOKE8(sd, SDGPIO, 0x1f); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0x3f); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xbf); // tristate dat ++ // CRC ack ++ s = 0; ++ for (i = 0; i < 7; i++) { ++ SDPOKE8(sd, SDGPIO, 0x9f); // clk negedge ++ SDPEEK8(sd, SDGPIO); // delay ++ s = s << 1; ++ s |= (SDPEEK8(sd, SDGPIO) & 0x1); ++ SDPOKE8(sd, SDGPIO, 0xbf); // clk posedge ++ } ++ if ((s & 0xf) != 0x5) return 1; ++ // wait for unbusy ++ s = 0; ++ while ((s & 0x7) != 0x7) { ++ if (timeout(sd)) break; ++ SDPOKE8(sd, SDGPIO, 0x9f); // clk negedge ++ SDPEEK8(sd, SDGPIO); // delay ++ s = s << 1; ++ s |= SDPEEK8(sd, SDGPIO) & 0x1; ++ SDPOKE8(sd, SDGPIO, 0xbf); ++ } ++ for (i = 0; i < 8; i++) { ++ SDPOKE8(sd, SDGPIO, 0x9f); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xbf); ++ SDPEEK8(sd, SDGPIO); ++ } ++ sd->sd_state &= ~SDDAT_TX; ++ ++ sdcmd2(sd, CMD_DESELECT_CARD, ~sd->sd_rcaarg, NULL, NULL); ++ ret = sdcmd2(sd, CMD_SEND_CSD, sd->sd_rcaarg, sd->sd_csd, NULL); ++ if (ret || sd->sd_csd[15] != csd[14]) { ++ return 1; ++ } ++ sdcmd2(sd, CMD_SELECT_CARD, sd->sd_rcaarg, resp, NULL); ++ ++ sd->sd_wprot = 1; ++ return 0; ++} ++ ++int sdsetwprot(struct sdcore *sd, unsigned int perm) ++{ ++ int i, ret; ++ unsigned int csd[16], resp[6]; ++ unsigned char csdchars[16]; ++ unsigned char *csdptr = csdchars; ++ ++ if (sd->hw_version) return sdsetwprot2(sd, perm); ++ ++ if (stop(sd)) return 1; ++ ++ perm = perm ? 0x3 : 0x1; ++ for (i = 0; i < 16; i++) csd[i] = sd->sd_csd[i + 1]; ++ csd[14] &= ~(0x3 << 4); ++ csd[14] |= (perm << 4); ++ csd[15] = 0x1 | crc7(0, csd, 15) << 1; ++ for (i = 0; i < 16; i++) csdchars[i] = csd[i]; ++ ++ ret = sdcmd(sd, CMD_PROGRAM_CSD, 0, resp, NULL); ++ if (ret || error(resp, CMD_PROGRAM_CSD)) return 1; ++ datssp_stream(sd, &csdptr, 16); ++ datssp_stream(sd, NULL, 8); ++ SDPOKE8(sd, SDSTATE, S_CRC_CHECK | (TYPE_BSYRESP << 5)); ++ sd->sd_state &= ~SDDAT_TX; ++ ++ sdcmd(sd, CMD_DESELECT_CARD, ~sd->sd_rcaarg, NULL, NULL); ++ ret = sdcmd(sd, CMD_SEND_CSD, sd->sd_rcaarg, sd->sd_csd, NULL); ++ if (ret || sd->sd_csd[15] != csd[14]) { ++ return 1; ++ } ++ sdcmd(sd, CMD_SELECT_CARD, sd->sd_rcaarg, resp, NULL); ++ ++ sd->sd_wprot = 1; ++ return 0; ++} ++ ++#ifndef SD_NOLOCKSUPPORT ++int sdlockctl(struct sdcore *sd, unsigned int cmd, unsigned char *pwd, ++ unsigned char *sdbootdat) ++{ ++ unsigned char pwddat[18]; ++ unsigned char *pwdptr = pwddat; ++ unsigned int resp[6]; ++ int ret, i, len; ++ int ccc = (sd->sd_csd[5] << 4) | (sd->sd_csd[6] >> 4); ++ ++ if (sd->hw_version) return sdlockctl2(sd, cmd, pwd, sdbootdat); ++ ++ if (!(ccc & 0x80)) return 1; // Class 7 is lock-unlock commands ++ ++ if (pwd == NULL && cmd != SDLOCK_ERASE) return 1; ++ ++ if (stop(sd)) return 1; ++ ++ if (sd->sd_state & DATSSP_4BIT) { ++ int oldctrl = SDPEEK8(sd, SDCTRL); ++ int ret; ++ ++ sdcmd(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd(sd, ACMD_SET_BUS_WIDTH, 0, NULL, NULL); ++ SDPOKE8(sd, SDCTRL, 0x20); ++ sd->sd_state &= ~DATSSP_4BIT; ++ ret = sdlockctl(sd, cmd, pwd, sdbootdat); ++ sdcmd(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd(sd, ACMD_SET_BUS_WIDTH, 2, NULL, NULL); ++ sd->sd_state |= DATSSP_4BIT; ++ SDPOKE8(sd, SDCTRL, oldctrl); ++ return ret; ++ } ++ ++ pwddat[0] = cmd; ++ if (cmd != SDLOCK_ERASE) { ++ pwddat[1] = 16; // length ++ for (i = 0; i < 16; i++) { ++ pwddat[2 + i] = pwd[i]; ++ } ++ } ++ ++ if (cmd == SDLOCK_ERASE) len = 1; else len = 18; ++ ret = sdcmd(sd, CMD_SET_BLOCKLEN, len, resp, NULL); ++ if (ret || error(resp, CMD_SET_BLOCKLEN)) return 1; ++ ret = sdcmd(sd, CMD_LOCK_UNLOCK, 0, resp, NULL); ++ if (ret || error(resp, CMD_LOCK_UNLOCK)) return 1; ++ ++ while ((pwdptr - pwddat) != len) { ++ if (timeout(sd)) return 1; ++ tend_ssp(sd, NULL, &pwdptr); ++ } ++ ++ if (sd->sd_state & DATSSP_4BIT) datssp_stream(sd, NULL, 8); ++ else datssp_stream(sd, NULL, 2); ++ ++ SDPOKE8(sd, SDSTATE, S_CRC_CHECK | (TYPE_BSYRESP << 5)); ++ sd->sd_state &= ~SDDAT_TX; ++ ret = sdcmd(sd, CMD_SET_BLOCKLEN, 512, resp, NULL); ++ if (ret || error(resp, CMD_SET_BLOCKLEN)) return 1; ++ ret = sdcmd(sd, CMD_SEND_STATUS, sd->sd_rcaarg, resp, NULL); ++ if (ret || error(resp, CMD_SEND_STATUS)) return 1; ++ ++ if ((cmd == SDLOCK_ERASE || cmd == SDLOCK_UNLOCK || ++ cmd == SDLOCK_CLRPWD) && (resp[1] & 0x2)) { ++ return 1; ++ } ++ ++ if (sdbootdat) { ++ sdbootdat[0] = SDLOCK_UNLOCK; ++ for (i = 1; i < 18; i++) { ++ sdbootdat[i] = pwddat[i]; ++ sd_1bit_feedcrc(sd, pwddat[i]); ++ } ++ sdbootdat[18] = sd_1bit_getcrc(sd); ++ sdbootdat[19] = sd_1bit_getcrc(sd); ++ } ++ ++ return 0; ++} ++ ++static ++int sdlockctl2(struct sdcore *sd, unsigned int cmd, unsigned char *pwd, ++ unsigned char *sdbootdat) ++{ ++ unsigned char pwddat[18]; ++ unsigned char *pwdptr = pwddat; ++ unsigned int resp[6]; ++ int ret, i, j, len, s; ++ int ccc = (sd->sd_csd[5] << 4) | (sd->sd_csd[6] >> 4); ++ ++ if (!(ccc & 0x80)) return 1; // Class 7 is lock-unlock commands ++ ++ if (pwd == NULL && cmd != SDLOCK_ERASE) return 1; ++ ++ stop2(sd); ++ ++ if (sd->sd_state & DATSSP_4BIT) { ++ int ret; ++ ++ sdcmd2(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd2(sd, ACMD_SET_BUS_WIDTH, 0, NULL, NULL); ++ sd->sd_state &= ~DATSSP_4BIT; ++ ret = sdlockctl2(sd, cmd, pwd, sdbootdat); ++ sdcmd2(sd, CMD_APP_CMD, sd->sd_rcaarg, NULL, NULL); ++ sdcmd2(sd, ACMD_SET_BUS_WIDTH, 2, NULL, NULL); ++ sd->sd_state |= DATSSP_4BIT; ++ return ret; ++ } ++ ++ pwddat[0] = cmd; ++ if (cmd != SDLOCK_ERASE) { ++ pwddat[1] = 16; // length ++ for (i = 0; i < 16; i++) { ++ pwddat[2 + i] = pwd[i]; ++ } ++ } ++ ++ if (cmd == SDLOCK_ERASE) len = 1; else len = 18; ++ ret = sdcmd2(sd, CMD_SET_BLOCKLEN, len, resp, NULL); ++ if (ret || error(resp, CMD_SET_BLOCKLEN)) return 1; ++ ret = sdcmd2(sd, CMD_LOCK_UNLOCK, 0, resp, NULL); ++ if (ret || error(resp, CMD_LOCK_UNLOCK)) return 1; ++ ++ for (i = 0; i < len; i++) { ++ unsigned int b = *pwdptr++; ++ unsigned int x; ++ ++ sd_1bit_feedcrc(sd, b); ++ for (j = 0; j < 8; j++) { ++ x = 0x1e | ((b >> 7) & 0x1); ++ b = b << 1; ++ SDPOKE8(sd, SDGPIO, x); // clk negedge ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ x |= 0x20; ++ SDPOKE8(sd, SDGPIO, x); // clk posedge ++ SDPEEK8(sd, SDGPIO); ++ } ++ } ++ for (i = 0; i < 2; i++) { ++ unsigned int b = sd_1bit_getcrc(sd); ++ unsigned int x; ++ ++ for (j = 0; j < 8; j++) { ++ x = 0x1e | ((b >> 7) & 0x1); ++ b = b << 1; ++ SDPOKE8(sd, SDGPIO, x); // clk negedge ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ x |= 0x20; ++ SDPOKE8(sd, SDGPIO, x); // clk posedge ++ SDPEEK8(sd, SDGPIO); ++ } ++ } ++ // End bit ++ SDPOKE8(sd, SDGPIO, 0x1f); // clk negedge ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xbf); // clk posedge, tristate dat ++ // CRC ack ++ s = 0; ++ for (i = 0; i < 7; i++) { ++ SDPOKE8(sd, SDGPIO, 0x9f); // clk negedge ++ SDPEEK8(sd, SDGPIO); // delay ++ s = s << 1; ++ s |= SDPEEK8(sd, SDGPIO) & 0x1; ++ SDPOKE8(sd, SDGPIO, 0xbf); // clk posedge ++ SDPEEK8(sd, SDGPIO); ++ } ++ if ((s & 0xf) != 0x5) return 1; ++ ++ // wait for unbusy ++ s = 0; ++ while ((s & 0x7) != 0x7) { ++ if (timeout(sd)) break; ++ SDPOKE8(sd, SDGPIO, 0x9f); // clk negedge ++ SDPEEK8(sd, SDGPIO); // delay ++ s = s << 1; ++ s |= SDPEEK8(sd, SDGPIO) & 0x1; ++ SDPOKE8(sd, SDGPIO, 0xbf); ++ SDPEEK8(sd, SDGPIO); ++ } ++ for (i = 0; i < 8; i++) { ++ SDPOKE8(sd, SDGPIO, 0x9f); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xbf); ++ SDPEEK8(sd, SDGPIO); ++ } ++ ++ sd->sd_state &= ~SDDAT_TX; ++ ret = sdcmd2(sd, CMD_SET_BLOCKLEN, 512, resp, NULL); ++ if (ret || error(resp, CMD_SET_BLOCKLEN)) { ++ return 1; ++ } ++ ret = sdcmd2(sd, CMD_SEND_STATUS, sd->sd_rcaarg, resp, NULL); ++ if (ret || error(resp, CMD_SEND_STATUS)) { ++ return 1; ++ } ++ ++ if ((cmd == SDLOCK_ERASE || cmd == SDLOCK_UNLOCK || ++ cmd == SDLOCK_CLRPWD) && (resp[1] & 0x2)) { ++ return 1; ++ } ++ ++ if (sdbootdat) { ++ sdbootdat[0] = SDLOCK_UNLOCK; ++ for (i = 1; i < 18; i++) { ++ sdbootdat[i] = pwddat[i]; ++ sd_1bit_feedcrc(sd, pwddat[i]); ++ } ++ sdbootdat[18] = sd_1bit_getcrc(sd); ++ sdbootdat[19] = sd_1bit_getcrc(sd); ++ } ++ ++ for (i = 0; i < 8; i++) { ++ SDPOKE8(sd, SDGPIO, 0x9f); ++ SDPEEK8(sd, SDGPIO); ++ SDPEEK8(sd, SDGPIO); ++ SDPOKE8(sd, SDGPIO, 0xbf); ++ SDPEEK8(sd, SDGPIO); ++ } ++ return 0; ++} ++#endif +diff --git a/drivers/block/sdcore2.h b/drivers/block/sdcore2.h +new file mode 100644 +index 0000000..38d5b96 +--- /dev/null ++++ b/drivers/block/sdcore2.h +@@ -0,0 +1,372 @@ ++/* ++ * Copyright (c) 2006-2008, Technologic Systems ++ * All rights reserved. ++ */ ++ ++#ifndef _SDCORE_H_ ++#define _SDCORE_H_ ++ ++// Additional missing defs ++#define SDCMD 0 // cmd register ++#define SDDAT 1 // data register ++#define SDSTATE 2 // state register ++#define SDCTRL 3 // ctrl register ++ ++ ++// this bit is set when no card inserted ++#define SDCTRL_CARD_ABSENT 0x08 ++ ++ ++ ++/* public bits for sd_state bitfield, can be read from client code. ++ * Do not write! Other bits are used internally. ++ */ ++#define SDDAT_RX (1<<0) ++#define SDDAT_TX (1<<1) ++#define SDCMD_RX (1<<2) ++#define SDCMD_TX (1<<3) ++ ++// used to disable CRC calculations in write mode ++#define SDCRC_DISABLE (1 << 4) ++ ++ ++ ++// used to choose between 4 bit crc mode and 1 bit crc mode ++ ++// note - likely set in sdreset when configuring interface bit width ++#define SDSSP_4BIT_MODE (1 << 5) ++ ++// SD_ADDRESSING_DIRECT means that sd card addresses ++// will be communicated in read/write mode using ++// full offsets, not 512 byte block offsets. ++// the core will mulitply the address by 512 if this ++// bit is cleared ++#define SD_ADDRESSING_DIRECT (1 << 6) ++ ++ ++/* These structs should start intialized to all 0's (bzero()'ed). Proper ++ * operation can be assured by setting sd_regstart, and the os_delay ++ * callback. sdreset() should be called to initialize the core, then ++ * sdread() and sdwrite() can be used. ++ */ ++struct sdcore { ++ /* virtual address of SD block register start, to be filled in ++ * by client code before calling any sdcore functions. ++ */ ++ // 0-3 ++ unsigned char* sd_regstart; ++ ++ ++ ++ // 4-7 ++ unsigned int sd_state; ++ ++ /* Erase hint for subsequent sdwrite() call, used to optimize ++ * write throughput on multi-sector writes by pre-erasing this ++ * many sectors. ++ */ ++ // 8-11 ++ unsigned int sd_erasehint; ++ ++ /* Following this comment are 5 function pointer declarations to ++ * OS helper functions. The 'os_arg' member is passed as the ++ * first argument to the helpers and should be set by ++ * client code before issueing sdreset() ++ * ++ * os_dmastream(os_arg, buf, buflen) ++ * This function should look at sd_state and set up and run an ++ * appropriate DMA transfer. If buf is NULL, callee doesn't care ++ * about the actual data sent/received and helper function ++ * can do whatever it wants. Should return 0 when DMA transfer was ++ * run and completed successfully. If this function pointer is ++ * NULL, PIO methods of transfer will be used instead of DMA. ++ * ++ * os_dmaprep(os_arg, buf, buflen) ++ * This function is used to prepare an area of memory for a possible ++ * DMA transfer. This function is called once per distinct buffer ++ * passed in. After this function is called, os_dmastream() may be ++ * called one or more times (for sequential addresses) on subregions ++ * of the address range passed here. Should write-back or invalidate ++ * L1 cache lines and possibly look up physical addresses for buf ++ * passed in if I/O buffers. If 'os_dmaprep' is set to NULL, function ++ * call will not happen. (though os_dmastream() calls may still) ++ * ++ * os_delay(os_arg, microseconds) ++ * This function is supposed to delay or stall the processor for ++ * the passed in value number of microseconds. ++ * ++ * os_irqwait(os_arg, type) ++ * Called at certain times to request to be put to sleep/block until ++ * an SD interrupt occurs. It is not critical to set this function. ++ * When NULL, the sdcore routines simply busy-wait. ++ * ++ * os_powerok(os_arg) ++ * Experimental callback function -- set to NULL for now. ++ */ ++ // 12-15 ++ void *os_arg; ++ // 16-19 ++ int (*os_dmastream)(void *, unsigned char *, unsigned int); ++ // 20-23 ++ void (*os_dmaprep)(void *, unsigned char *, unsigned int); ++ // 24-27 ++ void (*os_delay)(void *, unsigned int); ++ // 28-31 ++ void (*os_irqwait)(void *, unsigned int); ++ // 32-35 ++ int (*os_powerok)(void *); ++ ++ int (*os_timeout)(void *); ++ int (*os_reset_timeout)(void *); ++ ++ /* If the SD card last successfully reset is write protected, this ++ * member will be non-zero. ++ */ ++ // 36-39 ++ unsigned int sd_wprot; ++ ++ /* If this card may have been already initialized by TS-SDBOOT, place ++ * the magic token it placed in the EP93xx SYSCON ScratchReg1 here ++ * before calling sdreset() to avoid re-initialization. ++ */ ++ // 40-43 ++ unsigned int sdboot_token; ++ ++ /* CRC hint for subsequent sdwrite() call, used to optimize ++ * write throughput while using DMA by pre-calculating CRC's for ++ * next write. NULL means no hint supplied. ++ */ ++ // 44-47 ++ unsigned char *sd_crchint; ++ ++ /* The block size of the memory device. Normally 512, but can be 1024 ++ * for larger cards. Read-only member and actually not very useful. ++ */ ++ // 48-51 ++ unsigned int sd_blocksize; ++ ++ /* Password for auto-unlocking in sdreset() ++ */ ++ // 52-55 ++ unsigned char *sd_pwd; ++ ++ /* If the SD card was password locked, this will be non-zero after ++ * unsuccessful sdreset(). ++ */ ++ // 56-59 ++ unsigned int sd_locked; ++ ++ /* Whether or not writes can be parked. Definitely should be set to 1 ++ * as writes are very slow without it. ++ */ ++ // 60-63 ++ unsigned int sd_writeparking; ++ ++ /* Logical unit number. Some SD cores will have multiple card slots. ++ * LUN #0 is the first. ++ */ ++ // 64-67 ++ unsigned int sd_lun; ++ ++ /* The rest of these members are for private internal use and should ++ * not be of interest to client code. ++ */ ++ ++ ++ // 68-71 ++ unsigned int rca; // relative card address ++ ++ ++ unsigned int sd_csd[17]; ++ /* ++ ++ ++ // 72 -75 0 ++ unsigned int unknown72; // one of the csds? ++ ++ // 76 -79 1 ++ unsigned int unknown76; // csd 0x00 ++ // 80 -83 2 ++ unsigned int unknown04; // csd 0x01 ++ // 84 - 87 3 ++ unsigned int unknown05; // csd 0x02 ++ // 88 - 91 4 ++ unsigned int unknown06; // csd 0x03 ++ // 92 - 95 5 ++ unsigned int unknown92; // csd 0x04 ++ // 96 - 100 6 ++ unsigned int unknown96; // csd 0x05 ++ // 100 - 103 7 ++ unsigned int unknown100; // csd 0x06 ++ // 104 - 107 8 ++ unsigned int unknown104; // csd 0x07 ++ // 108 - 111 9 ++ unsigned int unknown108; // csd 0x08 ++ // 112 - 115 10 ++ unsigned int unknown112; // csd 0x09 ++ // 116 - 119 11 ++ unsigned int unknown116; // csd 0x0a ++ // 120 12 ++ unsigned int unknown24; // csd 0x0b ++ // 124 13 ++ unsigned int unknown25; // csd 0x0c ++ // 128 14 ++ unsigned int unknown26; // csd 0x0d ++ // 132 15 ++ unsigned int unknown132; // csd 0x0e ++ // 136 16 ++ unsigned int unknown28; // csd 0x0f ++ */ ++ ++ ++ // 140 ++ unsigned int sd_crc_shift; ++ ++ // 144 + 4 + 4 ++ unsigned short s_crc_table[4]; // 4 shorts ++ // 152 + 4 + 4 + 4 + 4 ++ unsigned int l_crc_table[4]; // 4 longs ++ ++ // 168 ++ unsigned int sd_timeout; // used to busy wait ++ ++ // 172 ++ unsigned int sd_cur_sector; // stop indicator - if zero , then stop procedure will be skipped ++ ++ // 176 ++ unsigned int sdcore_version; // hardware version ++ // 180 ++ unsigned int unknown39; ++ // 184 ++ unsigned int unknown40; ++ // 188 ++ unsigned int sdcore_sdsize; ++ ++ ++ ++ unsigned int unknown42; ++ unsigned int unknown43; ++ unsigned int unknown44; ++ unsigned int unknown45; ++ unsigned int unknown46; ++ unsigned int unknown47; ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++}; ++ ++/* I believe sdcores is a table mapping ++ id -> sdcore struct. The table is ++ 64 long, meaning that one could build a ts device with ++ 64 sdcores on it. ++*/ ++//extern unsigned char sdcores[256]; ++ ++ ++ ++ ++/* For sdreadv() / sdwritev() */ ++struct sdiov { ++ unsigned char *sdiov_base; ++ unsigned int sdiov_nsect; ++}; ++ ++ ++ ++ ++int sdreset(struct sdcore *); ++ ++int sdsize(struct sdcore* sdcore); ++ ++ ++int sdread(struct sdcore* sdcore, ++ unsigned int sector, ++ unsigned char* buffer, ++ int nsect); ++ ++int sdwrite(struct sdcore *, unsigned int, unsigned char *, int); ++ ++ ++// same signature as do_read ++int do_read(struct sdcore*, unsigned int, struct sdiov*, int); ++int sdreadv(struct sdcore * sdcore, ++ unsigned int sector, ++ struct sdiov * sdiov, ++ int nsdiov); ++ ++// same signature as do_write ++int do_write(struct sdcore* sdcore, ++ unsigned int sector, ++ struct sdiov* sdiov, ++ int nsdiov); ++ ++int sdwritev(struct sdcore *, unsigned int, struct sdiov *, int); ++ ++ ++void sd_1bit_feedcrc(struct sdcore*, unsigned int); ++void sd_4bit_feedcrc(struct sdcore*, unsigned int); ++ ++unsigned char sd_1bit_getcrc(struct sdcore*); ++unsigned char sd_4bit_getcrc(struct sdcore*); ++ ++/** stop takes only sdcore parameters */ ++int stop(struct sdcore*); ++ ++ ++int tend_ssp(struct sdcore* sdcore, ++ unsigned int** unknown_r1, // r1 ++ unsigned char** unknown_r2); ++ ++ ++int datssp_stream(struct sdcore* sdcore, ++ unsigned char** data, ++ int count); ++ ++/* ++ * @param cmd is the command - I believe that the lower byte is the command, and ++ * the upper one is the crc ++ * ++ * @param data is a character buffer for data received in the ssp dat register, as ++ * a result of a command execution. ++ */ ++ ++int sdcmd(struct sdcore* sdcore, ++ unsigned short cmd, ++ unsigned int sdargs, ++ unsigned int* response, ++ unsigned char** data); // command response buffer? ++ ++ ++/** ++ * Error tests if a sdcommand error has been received. ++ * It does this by checking that the command was ++ * correctly returned by the card (the first byte in buffer), ++ * and that a CRC error has not occurred. IF one has occurred ++ * it will attempt a 1bit fix. (suspected) ++ */ ++int error(unsigned int* buffer, unsigned int cmd); ++ ++ ++ ++int sdsetwprot(struct sdcore *, unsigned int); ++#define SDLOCK_UNLOCK 0 ++#define SDLOCK_SETPWD 1 ++#define SDLOCK_CLRPWD 2 ++#define SDLOCK_ERASE 8 ++ ++int sdlockctl(struct sdcore *, ++ unsigned int, // op code ++ unsigned char *, ++ unsigned char *); ++ ++#endif +diff --git a/drivers/block/tssdcard.c b/drivers/block/tssdcard.c +new file mode 100644 +index 0000000..c76d9a7 +--- /dev/null ++++ b/drivers/block/tssdcard.c +@@ -0,0 +1,415 @@ ++/* ++ * TS SD Card device driver ++ * ++ * (c) Copyright 2010 Matthieu Crapet <mcrapet@gmail.com> ++ * Based on Technologic Systems & Breton M. Saunders work ++ * ++ * 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. ++ * ++ * Notes: ++ * - request processing method is: no request queue ++ * - no M2M DMA is used ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/major.h> ++#include <linux/blkdev.h> ++#include <linux/bio.h> ++#include <linux/delay.h> ++#include <linux/hdreg.h> ++#include <linux/slab.h> ++#include <linux/platform_device.h> ++ ++#include "sdcore2.h" ++ ++#define SDCARD_DEV_NAME "tssd" /* will appear in /proc/partitions & /sys/class/block */ ++#define SD_SHIFT 4 /* max 16 partitions = 1 << 4 */ ++ ++#define KERN_SECTOR_SIZE 512 /* in bytes */ ++#define HARD_SECTOR_SIZE 512 /* in bytes */ ++#define HARD_2_KERN_SECTOR_RATIO 1 /* 1 kernel sector = 1 hardware sector */ ++ ++ ++struct ts72xx_sdcard_device { ++ struct sdcore tssdcore; /* Physical core layer */ ++ void __iomem *mmio_base; ++ long size; /* Device size in (hardware) sectors */ ++ int id; ++ int media_change; ++ int users; ++ ++ spinlock_t lock; ++ struct device *dev; ++ struct request_queue *queue; ++ struct gendisk *disk; ++}; ++ ++ ++/* ++ * Low level function to handle an I/O request ++ */ ++static inline int sdcard_ll_transfer(struct ts72xx_sdcard_device *dev, ++ unsigned long sector, unsigned long nsect, char *buffer, int rw) ++{ ++ int ret; ++ ++ //spin_unlock(&dev->lock); // ??? ++ ++ if ((sector + nsect) > (dev->size * HARD_2_KERN_SECTOR_RATIO)) { ++ dev_err(dev->dev, "tranfer: beyond-end write (%ld %ld)\n", sector, nsect); ++ //spin_lock(&dev->lock); // ??? ++ return -1; ++ } ++ ++ switch (rw) { ++ case WRITE: ++ ret = sdwrite(&dev->tssdcore, sector, buffer, nsect); ++ if (ret && !dev->tssdcore.sd_wprot) { ++ sdreset(&dev->tssdcore); ++ ret = sdwrite(&dev->tssdcore, sector, buffer, nsect); ++ } ++ break; ++ ++ case READ: ++ case READA: ++ ret = sdread(&dev->tssdcore, sector, buffer, nsect); ++ if (ret) { ++ // SDCARD RESET may be printed when the core determines that the SD card has ++ // f*ed up.this is not handled correctly yet; and should likely be inside a while loop ++ dev_err(dev->dev, "transfer: SDCARD RESET\n"); ++ sdreset(&dev->tssdcore); ++ ret = sdread(&dev->tssdcore, sector, buffer, nsect); ++ } ++ break; ++ } ++ ++ //spin_lock(&dev->lock); // ??? ++ return 0; ++} ++ ++/* ++ * The direct make request version. ++ */ ++static int sdcard_make_request(struct request_queue *q, struct bio *bio) ++{ ++ struct ts72xx_sdcard_device *dev = q->queuedata; ++ ++ struct bio_vec *bvec; ++ sector_t sector; ++ int i, rw; ++ int err = -EIO; ++ ++ /* handle bio */ ++ sector = bio->bi_sector; ++ rw = bio_rw(bio); ++ ++ bio_for_each_segment(bvec, bio, i) { ++ char *buffer = __bio_kmap_atomic(bio, i, KM_USER0); ++ unsigned int len = bvec->bv_len / HARD_SECTOR_SIZE; ++ ++ //printk("bvec: len=%d offt=%d page=%p\n", bvec->bv_len, bvec->bv_offset, bvec->bv_page); ++ ++ err = sdcard_ll_transfer(dev, sector, len, buffer, rw); ++ if (err) ++ break; ++ ++ sector += len; ++ __bio_kunmap_atomic(bio, KM_USER0); ++ } ++ bio_endio(bio, err); ++ ++ return 0; ++} ++ ++static void sdcard_delay(void *arg, unsigned int us) ++{ ++ udelay(us); ++} ++ ++static int sdcard_open(struct block_device *bdev, fmode_t mode) ++{ ++ struct ts72xx_sdcard_device *dev = bdev->bd_disk->private_data; ++ unsigned long flags; ++ ++ dev_dbg(dev->dev, "open() users=%i\n", dev->users + 1); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->users++; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ check_disk_change(bdev); ++ return 0; ++}; ++ ++static int sdcard_release(struct gendisk *disk, fmode_t mode) ++{ ++ struct ts72xx_sdcard_device *dev = disk->private_data; ++ unsigned long flags; ++ ++ dev_dbg(dev->dev, "release() users=%i\n", dev->users - 1); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->users--; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++static int sdcard_media_changed(struct gendisk *disk) ++{ ++ struct ts72xx_sdcard_device *dev = disk->private_data; ++ ++ char buf[HARD_SECTOR_SIZE]; ++ dev->media_change = sdread(&dev->tssdcore, 1, buf, 1); ++ ++ dev_dbg(dev->dev, "media_changed() %i\n", dev->media_change); ++ return dev->media_change; ++} ++ ++static int sdcard_revalidate(struct gendisk *disk) ++{ ++ struct ts72xx_sdcard_device *dev = disk->private_data; ++ int ret = 0; ++ ++ dev_dbg(dev->dev, "revalidate() %i\n", dev->media_change); ++ if (dev->media_change) { ++ dev->size = sdreset(&dev->tssdcore); ++ set_disk_ro(dev->disk, !!(dev->tssdcore.sd_wprot)); ++ if (dev->size > 0) { ++ set_capacity(dev->disk, dev->size * HARD_2_KERN_SECTOR_RATIO); ++ dev->media_change = 0; ++ } else { ++ dev_err(dev->dev, "revalidate() no card found\n"); ++ ret = -1; ++ } ++ } ++ return ret; ++} ++ ++static int sdcard_getgeo(struct block_device *bdev, struct hd_geometry *geo) ++{ ++ struct gendisk *disk = bdev->bd_disk; ++ struct ts72xx_sdcard_device *dev = disk->private_data; ++ ++ /* We don't have real geometry info, but let's at least return ++ * values consistent with the size of the device */ ++ geo->heads = 16; ++ geo->sectors = 32; ++ geo->cylinders = get_capacity(disk) / (16 * 32); ++ ++ dev_dbg(dev->dev, "getgeo() %d heads, %d sectors, %d cylinders\n", ++ geo->heads, geo->sectors, geo->cylinders); ++ return 0; ++} ++ ++/* ++ * The device operations structure. ++ */ ++static struct block_device_operations ts72xx_sdcard_ops = { ++ .owner = THIS_MODULE, ++ .open = sdcard_open, ++ .release = sdcard_release, ++ .media_changed = sdcard_media_changed, ++ .revalidate_disk = sdcard_revalidate, ++ .getgeo = sdcard_getgeo ++}; ++ ++static int sdcard_major; ++ ++/* --------------------------------------------------------------------- ++ * Device setup ++ */ ++ ++static int ts72xx_sdcard_setup(const char *name, struct ts72xx_sdcard_device *dev) ++{ ++ int rc; ++ ++ spin_lock_init(&dev->lock); ++ ++ /* ++ * Initialize the request queue ++ */ ++ dev->queue = blk_alloc_queue(GFP_KERNEL); ++ if (!dev->queue) ++ goto err_alloc_queue; ++ ++ dev->queue->queuedata = dev; ++ blk_queue_make_request(dev->queue, sdcard_make_request); ++ blk_queue_logical_block_size(dev->queue, HARD_SECTOR_SIZE); ++ ++ dev->tssdcore.sd_regstart = (unsigned char *)dev->mmio_base; ++ dev->tssdcore.os_arg = dev; ++ dev->tssdcore.os_delay = sdcard_delay; ++ dev->tssdcore.os_dmastream = NULL; ++ dev->tssdcore.os_dmaprep = NULL; ++ ++ // don't want to write park ++ dev->tssdcore.sd_writeparking = 1; ++ // I do want to pre-erase blocks - 8 blocks pre-erase ++ dev->tssdcore.sd_erasehint = 8; ++ dev->tssdcore.sdboot_token = 0; ++ ++ dev->disk = alloc_disk(1 << SD_SHIFT); ++ if (!dev->disk) { ++ goto err_alloc_disk; ++ } ++ ++ dev->disk->major = sdcard_major; ++ dev->disk->first_minor = dev->id << SD_SHIFT; ++ dev->disk->flags = GENHD_FL_REMOVABLE; ++ dev->disk->fops = &ts72xx_sdcard_ops; ++ dev->disk->queue = dev->queue; ++ dev->disk->private_data = dev; ++ snprintf(dev->disk->disk_name, 32, SDCARD_DEV_NAME "%c", dev->id + 'a'); ++ ++ /* SD Card size and Reset ++ * (set_disk_ro, set_capacity will be called) */ ++ dev->media_change = 1; ++ rc = sdcard_revalidate(dev->disk); ++ if (rc) { ++ dev_info(dev->dev, "No SD card detected!\n"); ++ goto err_alloc_disk; ++ } ++ ++ dev_info(dev->dev, "SD card hardware revision: %08x\n", ++ dev->tssdcore.sdcore_version); ++ dev_info(dev->dev, "block device major number = %d\n", ++ sdcard_major); ++ dev_info(dev->dev, "New SD card detected, name=%s size=%ld (sectors)\n", ++ dev->disk->disk_name, dev->size); ++ ++ /* Make the sysace device 'live' */ ++ add_disk(dev->disk); ++ ++ return 0; ++ ++err_alloc_disk: ++ blk_cleanup_queue(dev->queue); ++err_alloc_queue: ++ return -ENOMEM; ++} ++ ++ ++/* --------------------------------------------------------------------- ++ * Platform drivers functons ++ */ ++ ++static int __init ts72xx_sdcard_probe(struct platform_device *pdev) ++{ ++ struct ts72xx_sdcard_device *dev; ++ struct resource *res; ++ int rc; ++ ++ dev = kzalloc(sizeof(struct ts72xx_sdcard_device), GFP_KERNEL); ++ if (!dev) { ++ rc = -ENOMEM; ++ goto fail_no_mem; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ rc = -ENXIO; ++ goto fail_no_mem_resource; ++ } ++ ++ res = request_mem_region(res->start, resource_size(res), pdev->name); ++ if (res == NULL) { ++ rc = -EBUSY; ++ goto fail_no_mem_resource; ++ } ++ ++ dev->mmio_base = ioremap(res->start, resource_size(res)); ++ if (dev->mmio_base == NULL) { ++ rc = -ENXIO; ++ goto fail_no_ioremap; ++ } ++ ++ dev->dev = &pdev->dev; ++ dev->id = pdev->id; ++ platform_set_drvdata(pdev, dev); ++ ++ rc = ts72xx_sdcard_setup(SDCARD_DEV_NAME, dev); ++ if (rc) { ++ dev_err(dev->dev, "ts72xx_sdcard_setup failed\n"); ++ goto fail_sdcard_setup; ++ } ++ ++ return 0; ++ ++fail_sdcard_setup: ++ iounmap(dev->mmio_base); ++fail_no_ioremap: ++ release_mem_region(res->start, resource_size(res)); ++fail_no_mem_resource: ++ kfree(dev); ++fail_no_mem: ++ return rc; ++} ++ ++static int __exit ts72xx_sdcard_remove(struct platform_device *pdev) ++{ ++ struct ts72xx_sdcard_device *dev = platform_get_drvdata(pdev); ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ platform_set_drvdata(pdev, NULL); ++ iounmap(dev->mmio_base); ++ release_mem_region(res->start, resource_size(res)); ++ blk_cleanup_queue(dev->queue); ++ del_gendisk(dev->disk); ++ put_disk(dev->disk); ++ kfree(dev); ++ ++ return 0; ++} ++ ++static struct platform_driver ts72xx_sdcard_driver = { ++ .driver = { ++ .name = "ts72xx-sdcard", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __exit_p(ts72xx_sdcard_remove), ++}; ++ ++ ++/* --------------------------------------------------------------------- ++ * Module init/exit routines ++ */ ++ ++static int __init ts72xx_sdcard_init(void) ++{ ++ int rc; ++ ++ sdcard_major = rc = register_blkdev(sdcard_major, SDCARD_DEV_NAME); ++ if (rc <= 0) { ++ printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__, ++ __LINE__, rc); ++ return rc; ++ } ++ ++ rc = platform_driver_probe(&ts72xx_sdcard_driver, ts72xx_sdcard_probe); ++ if (rc) ++ unregister_blkdev(sdcard_major, SDCARD_DEV_NAME); ++ ++ return rc; ++} ++ ++static void __exit ts72xx_sdcard_exit(void) ++{ ++ unregister_blkdev(sdcard_major, SDCARD_DEV_NAME); ++ platform_driver_unregister(&ts72xx_sdcard_driver); ++} ++ ++module_init(ts72xx_sdcard_init); ++module_exit(ts72xx_sdcard_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("TS72xx SD Card block driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK0_MAJOR); ++MODULE_ALIAS("tssd"); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0016-ts72xx_nand_flash.patch b/recipes/linux/linux-2.6.34/ts72xx/0016-ts72xx_nand_flash.patch new file mode 100644 index 0000000000..ce73a860bc --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0016-ts72xx_nand_flash.patch @@ -0,0 +1,270 @@ +From 45a115b858cf82a35e51a7541a3f83c4dd4e08ea Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sun, 20 Jun 2010 11:31:22 +0200 +Subject: [PATCH 16/18] ts72xx_nand_flash + +This is Hartley Sweeten's patch: +Update the ts72xx platform's nand driver support. + +It's available since 2.6.35-rc1 + +Only one concern here, it's the size of TS72XX_BOOTROM_PART_SIZE, +defined here to SZ_16K but can be SZ_128K for boards with 128mb flash. +--- + arch/arm/mach-ep93xx/ts72xx.c | 186 ++++++++++++++++++++++++++++------------- + drivers/mtd/nand/Kconfig | 2 +- + 2 files changed, 129 insertions(+), 59 deletions(-) + +diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c +index 7fd8f80..475559e 100644 +--- a/arch/arm/mach-ep93xx/ts72xx.c ++++ b/arch/arm/mach-ep93xx/ts72xx.c +@@ -10,6 +10,8 @@ + * your option) any later version. + */ + ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ + #include <linux/kernel.h> + #include <linux/init.h> + #include <linux/platform_device.h> +@@ -19,6 +21,8 @@ + #include <linux/gpio.h> + #include <linux/i2c.h> + #include <linux/i2c-gpio.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> + + #include <mach/hardware.h> + #include <mach/ts72xx.h> +@@ -91,90 +95,156 @@ static struct map_desc ts72xx_io_desc[] __initdata = { + //} + }; + +-static struct map_desc ts72xx_nand_io_desc[] __initdata = { +- { +- .virtual = TS72XX_NAND_DATA_VIRT_BASE, +- .pfn = __phys_to_pfn(TS72XX_NAND1_DATA_PHYS_BASE), +- .length = TS72XX_NAND_DATA_SIZE, +- .type = MT_DEVICE, +- }, { +- .virtual = TS72XX_NAND_CONTROL_VIRT_BASE, +- .pfn = __phys_to_pfn(TS72XX_NAND1_CONTROL_PHYS_BASE), +- .length = TS72XX_NAND_CONTROL_SIZE, +- .type = MT_DEVICE, +- }, { +- .virtual = TS72XX_NAND_BUSY_VIRT_BASE, +- .pfn = __phys_to_pfn(TS72XX_NAND1_BUSY_PHYS_BASE), +- .length = TS72XX_NAND_BUSY_SIZE, +- .type = MT_DEVICE, ++static void __init ts72xx_map_io(void) ++{ ++ ep93xx_map_io(); ++ iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc)); ++} ++ ++ ++/************************************************************************* ++ * NAND flash ++ *************************************************************************/ ++#define TS72XX_NAND_CONTROL_ADDR_LINE 22 /* 0xN0400000 */ ++#define TS72XX_NAND_BUSY_ADDR_LINE 23 /* 0xN0800000 */ ++ ++static void ts72xx_nand_hwcontrol(struct mtd_info *mtd, ++ int cmd, unsigned int ctrl) ++{ ++ struct nand_chip *chip = mtd->priv; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ void __iomem *addr = chip->IO_ADDR_R; ++ unsigned char bits; ++ ++ addr += (1 << TS72XX_NAND_CONTROL_ADDR_LINE); ++ ++ bits = __raw_readb(addr) & ~0x07; ++ bits |= (ctrl & NAND_NCE) << 2; /* bit 0 -> bit 2 */ ++ bits |= (ctrl & NAND_CLE); /* bit 1 -> bit 1 */ ++ bits |= (ctrl & NAND_ALE) >> 2; /* bit 2 -> bit 0 */ ++ ++ __raw_writeb(bits, addr); + } +-}; ++ if (cmd != NAND_CMD_NONE) ++ __raw_writeb(cmd, chip->IO_ADDR_W); ++} ++ ++static int ts72xx_nand_device_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ void __iomem *addr = chip->IO_ADDR_R; ++ ++ addr += (1 << TS72XX_NAND_BUSY_ADDR_LINE); ++ ++ return !!(__raw_readb(addr) & 0x20); ++} + +-static struct map_desc ts72xx_alternate_nand_io_desc[] __initdata = { ++static const char *ts72xx_nand_part_probes[] = { "cmdlinepart", NULL }; ++ ++#define TS72XX_BOOTROM_PART_SIZE (SZ_16K) ++#define TS72XX_REDBOOT_PART_SIZE (SZ_2M + SZ_1M) ++ ++static struct mtd_partition ts72xx_nand_parts[] = { + { +- .virtual = TS72XX_NAND_DATA_VIRT_BASE, +- .pfn = __phys_to_pfn(TS72XX_NAND2_DATA_PHYS_BASE), +- .length = TS72XX_NAND_DATA_SIZE, +- .type = MT_DEVICE, ++ .name = "TS-BOOTROM", ++ .offset = 0, ++ .size = TS72XX_BOOTROM_PART_SIZE, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- .virtual = TS72XX_NAND_CONTROL_VIRT_BASE, +- .pfn = __phys_to_pfn(TS72XX_NAND2_CONTROL_PHYS_BASE), +- .length = TS72XX_NAND_CONTROL_SIZE, +- .type = MT_DEVICE, ++ .name = "Linux", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 0, /* filled in later */ + }, { +- .virtual = TS72XX_NAND_BUSY_VIRT_BASE, +- .pfn = __phys_to_pfn(TS72XX_NAND2_BUSY_PHYS_BASE), +- .length = TS72XX_NAND_BUSY_SIZE, +- .type = MT_DEVICE, +- } ++ .name = "RedBoot", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, + }; + +-static void __init ts72xx_map_io(void) ++static void ts72xx_nand_set_parts(uint64_t size, ++ struct platform_nand_chip *chip) + { +- ep93xx_map_io(); +- iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc)); +- +- /* +- * The TS-7200 has NOR flash, the other models have NAND flash. +- */ +- if (!board_is_ts7200()) { +- if (is_ts9420_installed()) { +- iotable_init(ts72xx_alternate_nand_io_desc, +- ARRAY_SIZE(ts72xx_alternate_nand_io_desc)); +- } else { +- iotable_init(ts72xx_nand_io_desc, +- ARRAY_SIZE(ts72xx_nand_io_desc)); +- } ++ /* Factory TS-72xx boards only come with 32MiB or 128MiB NAND options */ ++ if (size == SZ_32M || size == SZ_128M) { ++ /* Set the "Linux" partition size */ ++ ts72xx_nand_parts[1].size = size - TS72XX_REDBOOT_PART_SIZE; ++ chip->partitions = ts72xx_nand_parts; ++ chip->nr_partitions = ARRAY_SIZE(ts72xx_nand_parts); ++ } else { ++ pr_warning("Unknown nand disk size:%lluMiB\n", size >> 20); + } + } + ++static struct platform_nand_data ts72xx_nand_data = { ++ .chip = { ++ .nr_chips = 1, ++ .chip_offset = 0, ++ .chip_delay = 15, ++ .part_probe_types = ts72xx_nand_part_probes, ++ .set_parts = ts72xx_nand_set_parts, ++ }, ++ .ctrl = { ++ .cmd_ctrl = ts72xx_nand_hwcontrol, ++ .dev_ready = ts72xx_nand_device_ready, ++ }, ++}; ++ ++static struct resource ts72xx_nand_resource[] = { ++ { ++ .start = 0, /* filled in later */ ++ .end = 0, /* filled in later */ ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device ts72xx_nand_flash = { ++ .name = "gen_nand", ++ .id = -1, ++ .dev.platform_data = &ts72xx_nand_data, ++ .resource = ts72xx_nand_resource, ++ .num_resources = ARRAY_SIZE(ts72xx_nand_resource), ++}; ++ + /************************************************************************* + * NOR flash (TS-7200 only) + *************************************************************************/ +-static struct physmap_flash_data ts72xx_flash_data = { ++static struct physmap_flash_data ts72xx_nor_data = { + .width = 2, + }; + +-static struct resource ts72xx_flash_resource = { ++static struct resource ts72xx_nor_resource = { + .start = EP93XX_CS6_PHYS_BASE, + .end = EP93XX_CS6_PHYS_BASE + SZ_16M - 1, + .flags = IORESOURCE_MEM, + }; + +-static struct platform_device ts72xx_flash = { +- .name = "physmap-flash", +- .id = 0, +- .dev = { +- .platform_data = &ts72xx_flash_data, +- }, +- .num_resources = 1, +- .resource = &ts72xx_flash_resource, ++static struct platform_device ts72xx_nor_flash = { ++ .name = "physmap-flash", ++ .id = 0, ++ .dev.platform_data = &ts72xx_nor_data, ++ .resource = &ts72xx_nor_resource, ++ .num_resources = 1, + }; + + static void __init ts72xx_register_flash(void) + { +- if (board_is_ts7200()) +- platform_device_register(&ts72xx_flash); ++ if (board_is_ts7200()) { ++ platform_device_register(&ts72xx_nor_flash); ++ } else { ++ resource_size_t start; ++ ++ if (is_ts9420_installed()) ++ start = EP93XX_CS7_PHYS_BASE; ++ else ++ start = EP93XX_CS6_PHYS_BASE; ++ ++ ts72xx_nand_resource[0].start = start; ++ ts72xx_nand_resource[0].end = start + SZ_16M - 1; ++ ++ platform_device_register(&ts72xx_nand_flash); ++ } + } + + /************************************************************************* +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 42e5ea4..952ae85 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -96,7 +96,7 @@ config MTD_NAND_OMAP_PREFETCH_DMA + Say y for DMA mode or MPU mode will be used + + config MTD_NAND_TS7250 +- tristate "NAND Flash device on TS-7250 board" ++ tristate "NAND Flash device on TS-7250 board (DEPRECATED)" + depends on MACH_TS72XX + help + Support for NAND flash on Technologic Systems TS-7250 platform. +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0017-ep93xx_spi.patch b/recipes/linux/linux-2.6.34/ts72xx/0017-ep93xx_spi.patch new file mode 100644 index 0000000000..bba5516434 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0017-ep93xx_spi.patch @@ -0,0 +1,1312 @@ +From be8b82a083b6df32a1016b327730ce16be7c2b24 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Tue, 22 Jun 2010 11:21:05 +0200 +Subject: [PATCH 17/18] ep93xx_spi + +Include Mika Westerberg's driver: + +ARM: 5998/1: ep93xx: added chip revision reading function: +http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=99e6a23adfadc2da2006f3715c4332c3bf502c07 + +ARM: 6124/1: ep93xx: SPI driver platform support code: +http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=4fec9978822a66b25f5645eb20c115034a18cfd1 + +spi/ep93xx: implemented driver for Cirrus EP93xx SPI controller: +http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=011f23a3c2f20ae15b7664d3942493af107fe39b +--- + Documentation/spi/ep93xx_spi | 95 +++ + arch/arm/mach-ep93xx/clock.c | 13 + + arch/arm/mach-ep93xx/core.c | 66 ++ + arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h | 27 + + arch/arm/mach-ep93xx/include/mach/platform.h | 12 + + drivers/spi/Kconfig | 10 + + drivers/spi/Makefile | 1 + + drivers/spi/ep93xx_spi.c | 938 ++++++++++++++++++++++++ + 8 files changed, 1162 insertions(+), 0 deletions(-) + create mode 100644 Documentation/spi/ep93xx_spi + create mode 100644 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h + create mode 100644 drivers/spi/ep93xx_spi.c + +diff --git a/Documentation/spi/ep93xx_spi b/Documentation/spi/ep93xx_spi +new file mode 100644 +index 0000000..6325f5b +--- /dev/null ++++ b/Documentation/spi/ep93xx_spi +@@ -0,0 +1,95 @@ ++Cirrus EP93xx SPI controller driver HOWTO ++========================================= ++ ++ep93xx_spi driver brings SPI master support for EP93xx SPI controller. Chip ++selects are implemented with GPIO lines. ++ ++NOTE: If possible, don't use SFRMOUT (SFRM1) signal as a chip select. It will ++not work correctly (it cannot be controlled by software). Use GPIO lines ++instead. ++ ++Sample configuration ++==================== ++ ++Typically driver configuration is done in platform board files (the files under ++arch/arm/mach-ep93xx/*.c). In this example we configure MMC over SPI through ++this driver on TS-7260 board. You can adapt the code to suit your needs. ++ ++This example uses EGPIO9 as SD/MMC card chip select (this is wired in DIO1 ++header on the board). ++ ++You need to select CONFIG_MMC_SPI to use mmc_spi driver. ++ ++arch/arm/mach-ep93xx/ts72xx.c: ++ ++... ++#include <linux/gpio.h> ++#include <linux/spi/spi.h> ++ ++#include <mach/ep93xx_spi.h> ++ ++/* this is our GPIO line used for chip select */ ++#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9 ++ ++static int ts72xx_mmc_spi_setup(struct spi_device *spi) ++{ ++ int err; ++ ++ err = gpio_request(MMC_CHIP_SELECT_GPIO, spi->modalias); ++ if (err) ++ return err; ++ ++ gpio_direction_output(MMC_CHIP_SELECT_GPIO, 1); ++ ++ return 0; ++} ++ ++static void ts72xx_mmc_spi_cleanup(struct spi_device *spi) ++{ ++ gpio_set_value(MMC_CHIP_SELECT_GPIO, 1); ++ gpio_direction_input(MMC_CHIP_SELECT_GPIO); ++ gpio_free(MMC_CHIP_SELECT_GPIO); ++} ++ ++static void ts72xx_mmc_spi_cs_control(struct spi_device *spi, int value) ++{ ++ gpio_set_value(MMC_CHIP_SELECT_GPIO, value); ++} ++ ++static struct ep93xx_spi_chip_ops ts72xx_mmc_spi_ops = { ++ .setup = ts72xx_mmc_spi_setup, ++ .cleanup = ts72xx_mmc_spi_cleanup, ++ .cs_control = ts72xx_mmc_spi_cs_control, ++}; ++ ++static struct spi_board_info ts72xx_spi_devices[] __initdata = { ++ { ++ .modalias = "mmc_spi", ++ .controller_data = &ts72xx_mmc_spi_ops, ++ /* ++ * We use 10 MHz even though the maximum is 7.4 MHz. The driver ++ * will limit it automatically to max. frequency. ++ */ ++ .max_speed_hz = 10 * 1000 * 1000, ++ .bus_num = 0, ++ .chip_select = 0, ++ .mode = SPI_MODE_0, ++ }, ++}; ++ ++static struct ep93xx_spi_info ts72xx_spi_info = { ++ .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices), ++}; ++ ++static void __init ts72xx_init_machine(void) ++{ ++ ... ++ ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices, ++ ARRAY_SIZE(ts72xx_spi_devices)); ++} ++ ++Thanks to ++========= ++Martin Guy, H. Hartley Sweeten and others who helped me during development of ++the driver. Simplemachines.it donated me a Sim.One board which I used testing ++the driver on EP9307. +diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c +index 5f80092..e29bdef 100644 +--- a/arch/arm/mach-ep93xx/clock.c ++++ b/arch/arm/mach-ep93xx/clock.c +@@ -96,6 +96,10 @@ static struct clk clk_keypad = { + .enable_mask = EP93XX_SYSCON_KEYTCHCLKDIV_KEN, + .set_rate = set_keytchclk_rate, + }; ++static struct clk clk_spi = { ++ .parent = &clk_xtali, ++ .rate = EP93XX_EXT_CLK_RATE, ++}; + static struct clk clk_pwm = { + .parent = &clk_xtali, + .rate = EP93XX_EXT_CLK_RATE, +@@ -186,6 +190,7 @@ static struct clk_lookup clocks[] = { + INIT_CK("ep93xx-ohci", NULL, &clk_usb_host), + INIT_CK("ep93xx-keypad", NULL, &clk_keypad), + INIT_CK("ep93xx-fb", NULL, &clk_video), ++ INIT_CK("ep93xx-spi.0", NULL, &clk_spi), + INIT_CK(NULL, "pwm_clk", &clk_pwm), + INIT_CK(NULL, "m2p0", &clk_m2p0), + INIT_CK(NULL, "m2p1", &clk_m2p1), +@@ -473,6 +478,14 @@ static int __init ep93xx_clock_init(void) + /* Initialize the pll2 derived clocks */ + clk_usb_host.rate = clk_pll2.rate / (((value >> 28) & 0xf) + 1); + ++ /* ++ * EP93xx SSP clock rate was doubled in version E2. For more information ++ * see: ++ * http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf ++ */ ++ if (ep93xx_chip_revision() < EP93XX_CHIP_REV_E2) ++ clk_spi.rate /= 2; ++ + pr_info("PLL1 running at %ld MHz, PLL2 at %ld MHz\n", + clk_pll1.rate / 1000000, clk_pll2.rate / 1000000); + pr_info("FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n", +diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c +index 90fb591..4e55b7d 100644 +--- a/arch/arm/mach-ep93xx/core.c ++++ b/arch/arm/mach-ep93xx/core.c +@@ -31,10 +31,12 @@ + #include <linux/amba/serial.h> + #include <linux/i2c.h> + #include <linux/i2c-gpio.h> ++#include <linux/spi/spi.h> + + #include <mach/hardware.h> + #include <mach/fb.h> + #include <mach/ep93xx_keypad.h> ++#include <mach/ep93xx_spi.h> + + #include <asm/mach/map.h> + #include <asm/mach/time.h> +@@ -222,6 +224,20 @@ void ep93xx_devcfg_set_clear(unsigned int set_bits, unsigned int clear_bits) + } + EXPORT_SYMBOL(ep93xx_devcfg_set_clear); + ++/** ++ * ep93xx_chip_revision() - returns the EP93xx chip revision ++ * ++ * See <mach/platform.h> for more information. ++ */ ++unsigned int ep93xx_chip_revision(void) ++{ ++ unsigned int v; ++ ++ v = __raw_readl(EP93XX_SYSCON_SYSCFG); ++ v &= EP93XX_SYSCON_SYSCFG_REV_MASK; ++ v >>= EP93XX_SYSCON_SYSCFG_REV_SHIFT; ++ return v; ++} + + /************************************************************************* + * EP93xx peripheral handling +@@ -398,6 +414,56 @@ void __init ep93xx_register_i2c(struct i2c_gpio_platform_data *data, + platform_device_register(&ep93xx_i2c_device); + } + ++/************************************************************************* ++ * EP93xx SPI peripheral handling ++ *************************************************************************/ ++static struct ep93xx_spi_info ep93xx_spi_master_data; ++ ++static struct resource ep93xx_spi_resources[] = { ++ { ++ .start = EP93XX_SPI_PHYS_BASE, ++ .end = EP93XX_SPI_PHYS_BASE + 0x18 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = IRQ_EP93XX_SSP, ++ .end = IRQ_EP93XX_SSP, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device ep93xx_spi_device = { ++ .name = "ep93xx-spi", ++ .id = 0, ++ .dev = { ++ .platform_data = &ep93xx_spi_master_data, ++ }, ++ .num_resources = ARRAY_SIZE(ep93xx_spi_resources), ++ .resource = ep93xx_spi_resources, ++}; ++ ++/** ++ * ep93xx_register_spi() - registers spi platform device ++ * @info: ep93xx board specific spi master info (__initdata) ++ * @devices: SPI devices to register (__initdata) ++ * @num: number of SPI devices to register ++ * ++ * This function registers platform device for the EP93xx SPI controller and ++ * also makes sure that SPI pins are muxed so that I2S is not using those pins. ++ */ ++void __init ep93xx_register_spi(struct ep93xx_spi_info *info, ++ struct spi_board_info *devices, int num) ++{ ++ /* ++ * When SPI is used, we need to make sure that I2S is muxed off from ++ * SPI pins. ++ */ ++ ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2SONSSP); ++ ++ ep93xx_spi_master_data = *info; ++ spi_register_board_info(devices, num); ++ platform_device_register(&ep93xx_spi_device); ++} + + /************************************************************************* + * EP93xx LEDs +diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h +new file mode 100644 +index 0000000..0a37961 +--- /dev/null ++++ b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h +@@ -0,0 +1,27 @@ ++#ifndef __ASM_MACH_EP93XX_SPI_H ++#define __ASM_MACH_EP93XX_SPI_H ++ ++struct spi_device; ++ ++/** ++ * struct ep93xx_spi_info - EP93xx specific SPI descriptor ++ * @num_chipselect: number of chip selects on this board, must be ++ * at least one ++ */ ++struct ep93xx_spi_info { ++ int num_chipselect; ++}; ++ ++/** ++ * struct ep93xx_spi_chip_ops - operation callbacks for SPI slave device ++ * @setup: setup the chip select mechanism ++ * @cleanup: cleanup the chip select mechanism ++ * @cs_control: control the device chip select ++ */ ++struct ep93xx_spi_chip_ops { ++ int (*setup)(struct spi_device *spi); ++ void (*cleanup)(struct spi_device *spi); ++ void (*cs_control)(struct spi_device *spi, int value); ++}; ++ ++#endif /* __ASM_MACH_EP93XX_SPI_H */ +diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h +index c6dc14d..9a4413d 100644 +--- a/arch/arm/mach-ep93xx/include/mach/platform.h ++++ b/arch/arm/mach-ep93xx/include/mach/platform.h +@@ -6,9 +6,11 @@ + + struct i2c_gpio_platform_data; + struct i2c_board_info; ++struct spi_board_info; + struct platform_device; + struct ep93xxfb_mach_info; + struct ep93xx_keypad_platform_data; ++struct ep93xx_spi_info; + + struct ep93xx_eth_data + { +@@ -33,9 +35,19 @@ static inline void ep93xx_devcfg_clear_bits(unsigned int bits) + ep93xx_devcfg_set_clear(0x00, bits); + } + ++#define EP93XX_CHIP_REV_D0 3 ++#define EP93XX_CHIP_REV_D1 4 ++#define EP93XX_CHIP_REV_E0 5 ++#define EP93XX_CHIP_REV_E1 6 ++#define EP93XX_CHIP_REV_E2 7 ++ ++unsigned int ep93xx_chip_revision(void); ++ + void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr); + void ep93xx_register_i2c(struct i2c_gpio_platform_data *data, + struct i2c_board_info *devices, int num); ++void ep93xx_register_spi(struct ep93xx_spi_info *info, ++ struct spi_board_info *devices, int num); + void ep93xx_register_fb(struct ep93xxfb_mach_info *data); + void ep93xx_register_pwm(int pwm0, int pwm1); + int ep93xx_pwm_acquire_gpio(struct platform_device *pdev); +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index a191fa2..2b2f4c3 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -117,6 +117,16 @@ config SPI_DAVINCI + help + SPI master controller for DaVinci and DA8xx SPI modules. + ++config SPI_EP93XX ++ tristate "Cirrus Logic EP93xx SPI controller" ++ depends on ARCH_EP93XX ++ help ++ This enables using the Cirrus EP93xx SPI controller in master ++ mode. ++ ++ To compile this driver as a module, choose M here. The module will be ++ called ep93xx_spi. ++ + config SPI_GPIO + tristate "GPIO-based bitbanging SPI Master" + depends on GENERIC_GPIO +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index d7d0f89..377f845 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o + obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o + obj-$(CONFIG_SPI_DW_PCI) += dw_spi_pci.o + obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o ++obj-$(CONFIG_SPI_EP93XX) += ep93xx_spi.o + obj-$(CONFIG_SPI_GPIO) += spi_gpio.o + obj-$(CONFIG_SPI_IMX) += spi_imx.o + obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o +diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c +new file mode 100644 +index 0000000..0ba35df +--- /dev/null ++++ b/drivers/spi/ep93xx_spi.c +@@ -0,0 +1,938 @@ ++/* ++ * Driver for Cirrus Logic EP93xx SPI controller. ++ * ++ * Copyright (c) 2010 Mika Westerberg ++ * ++ * Explicit FIFO handling code was inspired by amba-pl022 driver. ++ * ++ * Chip select support using other than built-in GPIOs by H. Hartley Sweeten. ++ * ++ * For more information about the SPI controller see documentation on Cirrus ++ * Logic web site: ++ * http://www.cirrus.com/en/pubs/manual/EP93xx_Users_Guide_UM1.pdf ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/io.h> ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/bitops.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/workqueue.h> ++#include <linux/sched.h> ++#include <linux/spi/spi.h> ++ ++#include <mach/ep93xx_spi.h> ++ ++#define SSPCR0 0x0000 ++#define SSPCR0_MODE_SHIFT 6 ++#define SSPCR0_SCR_SHIFT 8 ++ ++#define SSPCR1 0x0004 ++#define SSPCR1_RIE BIT(0) ++#define SSPCR1_TIE BIT(1) ++#define SSPCR1_RORIE BIT(2) ++#define SSPCR1_LBM BIT(3) ++#define SSPCR1_SSE BIT(4) ++#define SSPCR1_MS BIT(5) ++#define SSPCR1_SOD BIT(6) ++ ++#define SSPDR 0x0008 ++ ++#define SSPSR 0x000c ++#define SSPSR_TFE BIT(0) ++#define SSPSR_TNF BIT(1) ++#define SSPSR_RNE BIT(2) ++#define SSPSR_RFF BIT(3) ++#define SSPSR_BSY BIT(4) ++#define SSPCPSR 0x0010 ++ ++#define SSPIIR 0x0014 ++#define SSPIIR_RIS BIT(0) ++#define SSPIIR_TIS BIT(1) ++#define SSPIIR_RORIS BIT(2) ++#define SSPICR SSPIIR ++ ++/* timeout in milliseconds */ ++#define SPI_TIMEOUT 5 ++/* maximum depth of RX/TX FIFO */ ++#define SPI_FIFO_SIZE 8 ++ ++/** ++ * struct ep93xx_spi - EP93xx SPI controller structure ++ * @lock: spinlock that protects concurrent accesses to fields @running, ++ * @current_msg and @msg_queue ++ * @pdev: pointer to platform device ++ * @clk: clock for the controller ++ * @regs_base: pointer to ioremap()'d registers ++ * @irq: IRQ number used by the driver ++ * @min_rate: minimum clock rate (in Hz) supported by the controller ++ * @max_rate: maximum clock rate (in Hz) supported by the controller ++ * @running: is the queue running ++ * @wq: workqueue used by the driver ++ * @msg_work: work that is queued for the driver ++ * @wait: wait here until given transfer is completed ++ * @msg_queue: queue for the messages ++ * @current_msg: message that is currently processed (or %NULL if none) ++ * @tx: current byte in transfer to transmit ++ * @rx: current byte in transfer to receive ++ * @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one ++ * frame decreases this level and sending one frame increases it. ++ * ++ * This structure holds EP93xx SPI controller specific information. When ++ * @running is %true, driver accepts transfer requests from protocol drivers. ++ * @current_msg is used to hold pointer to the message that is currently ++ * processed. If @current_msg is %NULL, it means that no processing is going ++ * on. ++ * ++ * Most of the fields are only written once and they can be accessed without ++ * taking the @lock. Fields that are accessed concurrently are: @current_msg, ++ * @running, and @msg_queue. ++ */ ++struct ep93xx_spi { ++ spinlock_t lock; ++ const struct platform_device *pdev; ++ struct clk *clk; ++ void __iomem *regs_base; ++ int irq; ++ unsigned long min_rate; ++ unsigned long max_rate; ++ bool running; ++ struct workqueue_struct *wq; ++ struct work_struct msg_work; ++ struct completion wait; ++ struct list_head msg_queue; ++ struct spi_message *current_msg; ++ size_t tx; ++ size_t rx; ++ size_t fifo_level; ++}; ++ ++/** ++ * struct ep93xx_spi_chip - SPI device hardware settings ++ * @spi: back pointer to the SPI device ++ * @rate: max rate in hz this chip supports ++ * @div_cpsr: cpsr (pre-scaler) divider ++ * @div_scr: scr divider ++ * @dss: bits per word (4 - 16 bits) ++ * @ops: private chip operations ++ * ++ * This structure is used to store hardware register specific settings for each ++ * SPI device. Settings are written to hardware by function ++ * ep93xx_spi_chip_setup(). ++ */ ++struct ep93xx_spi_chip { ++ const struct spi_device *spi; ++ unsigned long rate; ++ u8 div_cpsr; ++ u8 div_scr; ++ u8 dss; ++ struct ep93xx_spi_chip_ops *ops; ++}; ++ ++/* converts bits per word to CR0.DSS value */ ++#define bits_per_word_to_dss(bpw) ((bpw) - 1) ++ ++static inline void ++ep93xx_spi_write_u8(const struct ep93xx_spi *espi, u16 reg, u8 value) ++{ ++ __raw_writeb(value, espi->regs_base + reg); ++} ++ ++static inline u8 ++ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg) ++{ ++ return __raw_readb(spi->regs_base + reg); ++} ++ ++static inline void ++ep93xx_spi_write_u16(const struct ep93xx_spi *espi, u16 reg, u16 value) ++{ ++ __raw_writew(value, espi->regs_base + reg); ++} ++ ++static inline u16 ++ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg) ++{ ++ return __raw_readw(spi->regs_base + reg); ++} ++ ++static int ep93xx_spi_enable(const struct ep93xx_spi *espi) ++{ ++ u8 regval; ++ int err; ++ ++ err = clk_enable(espi->clk); ++ if (err) ++ return err; ++ ++ regval = ep93xx_spi_read_u8(espi, SSPCR1); ++ regval |= SSPCR1_SSE; ++ ep93xx_spi_write_u8(espi, SSPCR1, regval); ++ ++ return 0; ++} ++ ++static void ep93xx_spi_disable(const struct ep93xx_spi *espi) ++{ ++ u8 regval; ++ ++ regval = ep93xx_spi_read_u8(espi, SSPCR1); ++ regval &= ~SSPCR1_SSE; ++ ep93xx_spi_write_u8(espi, SSPCR1, regval); ++ ++ clk_disable(espi->clk); ++} ++ ++static void ep93xx_spi_enable_interrupts(const struct ep93xx_spi *espi) ++{ ++ u8 regval; ++ ++ regval = ep93xx_spi_read_u8(espi, SSPCR1); ++ regval |= (SSPCR1_RORIE | SSPCR1_TIE | SSPCR1_RIE); ++ ep93xx_spi_write_u8(espi, SSPCR1, regval); ++} ++ ++static void ep93xx_spi_disable_interrupts(const struct ep93xx_spi *espi) ++{ ++ u8 regval; ++ ++ regval = ep93xx_spi_read_u8(espi, SSPCR1); ++ regval &= ~(SSPCR1_RORIE | SSPCR1_TIE | SSPCR1_RIE); ++ ep93xx_spi_write_u8(espi, SSPCR1, regval); ++} ++ ++/** ++ * ep93xx_spi_calc_divisors() - calculates SPI clock divisors ++ * @espi: ep93xx SPI controller struct ++ * @chip: divisors are calculated for this chip ++ * @rate: desired SPI output clock rate ++ * ++ * Function calculates cpsr (clock pre-scaler) and scr divisors based on ++ * given @rate and places them to @chip->div_cpsr and @chip->div_scr. If, ++ * for some reason, divisors cannot be calculated nothing is stored and ++ * %-EINVAL is returned. ++ */ ++static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi, ++ struct ep93xx_spi_chip *chip, ++ unsigned long rate) ++{ ++ unsigned long spi_clk_rate = clk_get_rate(espi->clk); ++ int cpsr, scr; ++ ++ /* ++ * Make sure that max value is between values supported by the ++ * controller. Note that minimum value is already checked in ++ * ep93xx_spi_transfer(). ++ */ ++ rate = clamp(rate, espi->min_rate, espi->max_rate); ++ ++ /* ++ * Calculate divisors so that we can get speed according the ++ * following formula: ++ * rate = spi_clock_rate / (cpsr * (1 + scr)) ++ * ++ * cpsr must be even number and starts from 2, scr can be any number ++ * between 0 and 255. ++ */ ++ for (cpsr = 2; cpsr <= 254; cpsr += 2) { ++ for (scr = 0; scr <= 255; scr++) { ++ if ((spi_clk_rate / (cpsr * (scr + 1))) <= rate) { ++ chip->div_scr = (u8)scr; ++ chip->div_cpsr = (u8)cpsr; ++ return 0; ++ } ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static void ep93xx_spi_cs_control(struct spi_device *spi, bool control) ++{ ++ struct ep93xx_spi_chip *chip = spi_get_ctldata(spi); ++ int value = (spi->mode & SPI_CS_HIGH) ? control : !control; ++ ++ if (chip->ops && chip->ops->cs_control) ++ chip->ops->cs_control(spi, value); ++} ++ ++/** ++ * ep93xx_spi_setup() - setup an SPI device ++ * @spi: SPI device to setup ++ * ++ * This function sets up SPI device mode, speed etc. Can be called multiple ++ * times for a single device. Returns %0 in case of success, negative error in ++ * case of failure. When this function returns success, the device is ++ * deselected. ++ */ ++static int ep93xx_spi_setup(struct spi_device *spi) ++{ ++ struct ep93xx_spi *espi = spi_master_get_devdata(spi->master); ++ struct ep93xx_spi_chip *chip; ++ ++ if (spi->bits_per_word < 4 || spi->bits_per_word > 16) { ++ dev_err(&espi->pdev->dev, "invalid bits per word %d\n", ++ spi->bits_per_word); ++ return -EINVAL; ++ } ++ ++ chip = spi_get_ctldata(spi); ++ if (!chip) { ++ dev_dbg(&espi->pdev->dev, "initial setup for %s\n", ++ spi->modalias); ++ ++ chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ chip->spi = spi; ++ chip->ops = spi->controller_data; ++ ++ if (chip->ops && chip->ops->setup) { ++ int ret = chip->ops->setup(spi); ++ if (ret) { ++ kfree(chip); ++ return ret; ++ } ++ } ++ ++ spi_set_ctldata(spi, chip); ++ } ++ ++ if (spi->max_speed_hz != chip->rate) { ++ int err; ++ ++ err = ep93xx_spi_calc_divisors(espi, chip, spi->max_speed_hz); ++ if (err != 0) { ++ spi_set_ctldata(spi, NULL); ++ kfree(chip); ++ return err; ++ } ++ chip->rate = spi->max_speed_hz; ++ } ++ ++ chip->dss = bits_per_word_to_dss(spi->bits_per_word); ++ ++ ep93xx_spi_cs_control(spi, false); ++ return 0; ++} ++ ++/** ++ * ep93xx_spi_transfer() - queue message to be transferred ++ * @spi: target SPI device ++ * @msg: message to be transferred ++ * ++ * This function is called by SPI device drivers when they are going to transfer ++ * a new message. It simply puts the message in the queue and schedules ++ * workqueue to perform the actual transfer later on. ++ * ++ * Returns %0 on success and negative error in case of failure. ++ */ ++static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) ++{ ++ struct ep93xx_spi *espi = spi_master_get_devdata(spi->master); ++ struct spi_transfer *t; ++ unsigned long flags; ++ ++ if (!msg || !msg->complete) ++ return -EINVAL; ++ ++ /* first validate each transfer */ ++ list_for_each_entry(t, &msg->transfers, transfer_list) { ++ if (t->bits_per_word) { ++ if (t->bits_per_word < 4 || t->bits_per_word > 16) ++ return -EINVAL; ++ } ++ if (t->speed_hz && t->speed_hz < espi->min_rate) ++ return -EINVAL; ++ } ++ ++ /* ++ * Now that we own the message, let's initialize it so that it is ++ * suitable for us. We use @msg->status to signal whether there was ++ * error in transfer and @msg->state is used to hold pointer to the ++ * current transfer (or %NULL if no active current transfer). ++ */ ++ msg->state = NULL; ++ msg->status = 0; ++ msg->actual_length = 0; ++ ++ spin_lock_irqsave(&espi->lock, flags); ++ if (!espi->running) { ++ spin_unlock_irqrestore(&espi->lock, flags); ++ return -ESHUTDOWN; ++ } ++ list_add_tail(&msg->queue, &espi->msg_queue); ++ queue_work(espi->wq, &espi->msg_work); ++ spin_unlock_irqrestore(&espi->lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * ep93xx_spi_cleanup() - cleans up master controller specific state ++ * @spi: SPI device to cleanup ++ * ++ * This function releases master controller specific state for given @spi ++ * device. ++ */ ++static void ep93xx_spi_cleanup(struct spi_device *spi) ++{ ++ struct ep93xx_spi_chip *chip; ++ ++ chip = spi_get_ctldata(spi); ++ if (chip) { ++ if (chip->ops && chip->ops->cleanup) ++ chip->ops->cleanup(spi); ++ spi_set_ctldata(spi, NULL); ++ kfree(chip); ++ } ++} ++ ++/** ++ * ep93xx_spi_chip_setup() - configures hardware according to given @chip ++ * @espi: ep93xx SPI controller struct ++ * @chip: chip specific settings ++ * ++ * This function sets up the actual hardware registers with settings given in ++ * @chip. Note that no validation is done so make sure that callers validate ++ * settings before calling this. ++ */ ++static void ep93xx_spi_chip_setup(const struct ep93xx_spi *espi, ++ const struct ep93xx_spi_chip *chip) ++{ ++ u16 cr0; ++ ++ cr0 = chip->div_scr << SSPCR0_SCR_SHIFT; ++ cr0 |= (chip->spi->mode & (SPI_CPHA|SPI_CPOL)) << SSPCR0_MODE_SHIFT; ++ cr0 |= chip->dss; ++ ++ dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n", ++ chip->spi->mode, chip->div_cpsr, chip->div_scr, chip->dss); ++ dev_dbg(&espi->pdev->dev, "setup: cr0 %#x", cr0); ++ ++ ep93xx_spi_write_u8(espi, SSPCPSR, chip->div_cpsr); ++ ep93xx_spi_write_u16(espi, SSPCR0, cr0); ++} ++ ++static inline int bits_per_word(const struct ep93xx_spi *espi) ++{ ++ struct spi_message *msg = espi->current_msg; ++ struct spi_transfer *t = msg->state; ++ ++ return t->bits_per_word ? t->bits_per_word : msg->spi->bits_per_word; ++} ++ ++static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t) ++{ ++ if (bits_per_word(espi) > 8) { ++ u16 tx_val = 0; ++ ++ if (t->tx_buf) ++ tx_val = ((u16 *)t->tx_buf)[espi->tx]; ++ ep93xx_spi_write_u16(espi, SSPDR, tx_val); ++ espi->tx += sizeof(tx_val); ++ } else { ++ u8 tx_val = 0; ++ ++ if (t->tx_buf) ++ tx_val = ((u8 *)t->tx_buf)[espi->tx]; ++ ep93xx_spi_write_u8(espi, SSPDR, tx_val); ++ espi->tx += sizeof(tx_val); ++ } ++} ++ ++static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t) ++{ ++ if (bits_per_word(espi) > 8) { ++ u16 rx_val; ++ ++ rx_val = ep93xx_spi_read_u16(espi, SSPDR); ++ if (t->rx_buf) ++ ((u16 *)t->rx_buf)[espi->rx] = rx_val; ++ espi->rx += sizeof(rx_val); ++ } else { ++ u8 rx_val; ++ ++ rx_val = ep93xx_spi_read_u8(espi, SSPDR); ++ if (t->rx_buf) ++ ((u8 *)t->rx_buf)[espi->rx] = rx_val; ++ espi->rx += sizeof(rx_val); ++ } ++} ++ ++/** ++ * ep93xx_spi_read_write() - perform next RX/TX transfer ++ * @espi: ep93xx SPI controller struct ++ * ++ * This function transfers next bytes (or half-words) to/from RX/TX FIFOs. If ++ * called several times, the whole transfer will be completed. Returns ++ * %-EINPROGRESS when current transfer was not yet completed otherwise %0. ++ * ++ * When this function is finished, RX FIFO should be empty and TX FIFO should be ++ * full. ++ */ ++static int ep93xx_spi_read_write(struct ep93xx_spi *espi) ++{ ++ struct spi_message *msg = espi->current_msg; ++ struct spi_transfer *t = msg->state; ++ ++ /* read as long as RX FIFO has frames in it */ ++ while ((ep93xx_spi_read_u8(espi, SSPSR) & SSPSR_RNE)) { ++ ep93xx_do_read(espi, t); ++ espi->fifo_level--; ++ } ++ ++ /* write as long as TX FIFO has room */ ++ while (espi->fifo_level < SPI_FIFO_SIZE && espi->tx < t->len) { ++ ep93xx_do_write(espi, t); ++ espi->fifo_level++; ++ } ++ ++ if (espi->rx == t->len) { ++ msg->actual_length += t->len; ++ return 0; ++ } ++ ++ return -EINPROGRESS; ++} ++ ++/** ++ * ep93xx_spi_process_transfer() - processes one SPI transfer ++ * @espi: ep93xx SPI controller struct ++ * @msg: current message ++ * @t: transfer to process ++ * ++ * This function processes one SPI transfer given in @t. Function waits until ++ * transfer is complete (may sleep) and updates @msg->status based on whether ++ * transfer was succesfully processed or not. ++ */ ++static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi, ++ struct spi_message *msg, ++ struct spi_transfer *t) ++{ ++ struct ep93xx_spi_chip *chip = spi_get_ctldata(msg->spi); ++ ++ msg->state = t; ++ ++ /* ++ * Handle any transfer specific settings if needed. We use ++ * temporary chip settings here and restore original later when ++ * the transfer is finished. ++ */ ++ if (t->speed_hz || t->bits_per_word) { ++ struct ep93xx_spi_chip tmp_chip = *chip; ++ ++ if (t->speed_hz) { ++ int err; ++ ++ err = ep93xx_spi_calc_divisors(espi, &tmp_chip, ++ t->speed_hz); ++ if (err) { ++ dev_err(&espi->pdev->dev, ++ "failed to adjust speed\n"); ++ msg->status = err; ++ return; ++ } ++ } ++ ++ if (t->bits_per_word) ++ tmp_chip.dss = bits_per_word_to_dss(t->bits_per_word); ++ ++ /* ++ * Set up temporary new hw settings for this transfer. ++ */ ++ ep93xx_spi_chip_setup(espi, &tmp_chip); ++ } ++ ++ espi->rx = 0; ++ espi->tx = 0; ++ ++ /* ++ * Now everything is set up for the current transfer. We prime the TX ++ * FIFO, enable interrupts, and wait for the transfer to complete. ++ */ ++ if (ep93xx_spi_read_write(espi)) { ++ ep93xx_spi_enable_interrupts(espi); ++ wait_for_completion(&espi->wait); ++ } ++ ++ /* ++ * In case of error during transmit, we bail out from processing ++ * the message. ++ */ ++ if (msg->status) ++ return; ++ ++ /* ++ * After this transfer is finished, perform any possible ++ * post-transfer actions requested by the protocol driver. ++ */ ++ if (t->delay_usecs) { ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(usecs_to_jiffies(t->delay_usecs)); ++ } ++ if (t->cs_change) { ++ if (!list_is_last(&t->transfer_list, &msg->transfers)) { ++ /* ++ * In case protocol driver is asking us to drop the ++ * chipselect briefly, we let the scheduler to handle ++ * any "delay" here. ++ */ ++ ep93xx_spi_cs_control(msg->spi, false); ++ cond_resched(); ++ ep93xx_spi_cs_control(msg->spi, true); ++ } ++ } ++ ++ if (t->speed_hz || t->bits_per_word) ++ ep93xx_spi_chip_setup(espi, chip); ++} ++ ++/* ++ * ep93xx_spi_process_message() - process one SPI message ++ * @espi: ep93xx SPI controller struct ++ * @msg: message to process ++ * ++ * This function processes a single SPI message. We go through all transfers in ++ * the message and pass them to ep93xx_spi_process_transfer(). Chipselect is ++ * asserted during the whole message (unless per transfer cs_change is set). ++ * ++ * @msg->status contains %0 in case of success or negative error code in case of ++ * failure. ++ */ ++static void ep93xx_spi_process_message(struct ep93xx_spi *espi, ++ struct spi_message *msg) ++{ ++ unsigned long timeout; ++ struct spi_transfer *t; ++ int err; ++ ++ /* ++ * Enable the SPI controller and its clock. ++ */ ++ err = ep93xx_spi_enable(espi); ++ if (err) { ++ dev_err(&espi->pdev->dev, "failed to enable SPI controller\n"); ++ msg->status = err; ++ return; ++ } ++ ++ /* ++ * Just to be sure: flush any data from RX FIFO. ++ */ ++ timeout = jiffies + msecs_to_jiffies(SPI_TIMEOUT); ++ while (ep93xx_spi_read_u16(espi, SSPSR) & SSPSR_RNE) { ++ if (time_after(jiffies, timeout)) { ++ dev_warn(&espi->pdev->dev, ++ "timeout while flushing RX FIFO\n"); ++ msg->status = -ETIMEDOUT; ++ return; ++ } ++ ep93xx_spi_read_u16(espi, SSPDR); ++ } ++ ++ /* ++ * We explicitly handle FIFO level. This way we don't have to check TX ++ * FIFO status using %SSPSR_TNF bit which may cause RX FIFO overruns. ++ */ ++ espi->fifo_level = 0; ++ ++ /* ++ * Update SPI controller registers according to spi device and assert ++ * the chipselect. ++ */ ++ ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi)); ++ ep93xx_spi_cs_control(msg->spi, true); ++ ++ list_for_each_entry(t, &msg->transfers, transfer_list) { ++ ep93xx_spi_process_transfer(espi, msg, t); ++ if (msg->status) ++ break; ++ } ++ ++ /* ++ * Now the whole message is transferred (or failed for some reason). We ++ * deselect the device and disable the SPI controller. ++ */ ++ ep93xx_spi_cs_control(msg->spi, false); ++ ep93xx_spi_disable(espi); ++} ++ ++#define work_to_espi(work) (container_of((work), struct ep93xx_spi, msg_work)) ++ ++/** ++ * ep93xx_spi_work() - EP93xx SPI workqueue worker function ++ * @work: work struct ++ * ++ * Workqueue worker function. This function is called when there are new ++ * SPI messages to be processed. Message is taken out from the queue and then ++ * passed to ep93xx_spi_process_message(). ++ * ++ * After message is transferred, protocol driver is notified by calling ++ * @msg->complete(). In case of error, @msg->status is set to negative error ++ * number, otherwise it contains zero (and @msg->actual_length is updated). ++ */ ++static void ep93xx_spi_work(struct work_struct *work) ++{ ++ struct ep93xx_spi *espi = work_to_espi(work); ++ struct spi_message *msg; ++ ++ spin_lock_irq(&espi->lock); ++ if (!espi->running || espi->current_msg || ++ list_empty(&espi->msg_queue)) { ++ spin_unlock_irq(&espi->lock); ++ return; ++ } ++ msg = list_first_entry(&espi->msg_queue, struct spi_message, queue); ++ list_del_init(&msg->queue); ++ espi->current_msg = msg; ++ spin_unlock_irq(&espi->lock); ++ ++ ep93xx_spi_process_message(espi, msg); ++ ++ /* ++ * Update the current message and re-schedule ourselves if there are ++ * more messages in the queue. ++ */ ++ spin_lock_irq(&espi->lock); ++ espi->current_msg = NULL; ++ if (espi->running && !list_empty(&espi->msg_queue)) ++ queue_work(espi->wq, &espi->msg_work); ++ spin_unlock_irq(&espi->lock); ++ ++ /* notify the protocol driver that we are done with this message */ ++ msg->complete(msg->context); ++} ++ ++static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id) ++{ ++ struct ep93xx_spi *espi = dev_id; ++ u8 irq_status = ep93xx_spi_read_u8(espi, SSPIIR); ++ ++ /* ++ * If we got ROR (receive overrun) interrupt we know that something is ++ * wrong. Just abort the message. ++ */ ++ if (unlikely(irq_status & SSPIIR_RORIS)) { ++ /* clear the overrun interrupt */ ++ ep93xx_spi_write_u8(espi, SSPICR, 0); ++ dev_warn(&espi->pdev->dev, ++ "receive overrun, aborting the message\n"); ++ espi->current_msg->status = -EIO; ++ } else { ++ /* ++ * Interrupt is either RX (RIS) or TX (TIS). For both cases we ++ * simply execute next data transfer. ++ */ ++ if (ep93xx_spi_read_write(espi)) { ++ /* ++ * In normal case, there still is some processing left ++ * for current transfer. Let's wait for the next ++ * interrupt then. ++ */ ++ return IRQ_HANDLED; ++ } ++ } ++ ++ /* ++ * Current transfer is finished, either with error or with success. In ++ * any case we disable interrupts and notify the worker to handle ++ * any post-processing of the message. ++ */ ++ ep93xx_spi_disable_interrupts(espi); ++ complete(&espi->wait); ++ return IRQ_HANDLED; ++} ++ ++static int __init ep93xx_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct ep93xx_spi_info *info; ++ struct ep93xx_spi *espi; ++ struct resource *res; ++ int error; ++ ++ info = pdev->dev.platform_data; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*espi)); ++ if (!master) { ++ dev_err(&pdev->dev, "failed to allocate spi master\n"); ++ return -ENOMEM; ++ } ++ ++ master->setup = ep93xx_spi_setup; ++ master->transfer = ep93xx_spi_transfer; ++ master->cleanup = ep93xx_spi_cleanup; ++ master->bus_num = pdev->id; ++ master->num_chipselect = info->num_chipselect; ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; ++ ++ platform_set_drvdata(pdev, master); ++ ++ espi = spi_master_get_devdata(master); ++ ++ espi->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(espi->clk)) { ++ dev_err(&pdev->dev, "unable to get spi clock\n"); ++ error = PTR_ERR(espi->clk); ++ goto fail_release_master; ++ } ++ ++ spin_lock_init(&espi->lock); ++ init_completion(&espi->wait); ++ ++ /* ++ * Calculate maximum and minimum supported clock rates ++ * for the controller. ++ */ ++ espi->max_rate = clk_get_rate(espi->clk) / 2; ++ espi->min_rate = clk_get_rate(espi->clk) / (254 * 256); ++ espi->pdev = pdev; ++ ++ espi->irq = platform_get_irq(pdev, 0); ++ if (espi->irq < 0) { ++ error = -EBUSY; ++ dev_err(&pdev->dev, "failed to get irq resources\n"); ++ goto fail_put_clock; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "unable to get iomem resource\n"); ++ error = -ENODEV; ++ goto fail_put_clock; ++ } ++ ++ res = request_mem_region(res->start, resource_size(res), pdev->name); ++ if (!res) { ++ dev_err(&pdev->dev, "unable to request iomem resources\n"); ++ error = -EBUSY; ++ goto fail_put_clock; ++ } ++ ++ espi->regs_base = ioremap(res->start, resource_size(res)); ++ if (!espi->regs_base) { ++ dev_err(&pdev->dev, "failed to map resources\n"); ++ error = -ENODEV; ++ goto fail_free_mem; ++ } ++ ++ error = request_irq(espi->irq, ep93xx_spi_interrupt, 0, ++ "ep93xx-spi", espi); ++ if (error) { ++ dev_err(&pdev->dev, "failed to request irq\n"); ++ goto fail_unmap_regs; ++ } ++ ++ espi->wq = create_singlethread_workqueue("ep93xx_spid"); ++ if (!espi->wq) { ++ dev_err(&pdev->dev, "unable to create workqueue\n"); ++ goto fail_free_irq; ++ } ++ INIT_WORK(&espi->msg_work, ep93xx_spi_work); ++ INIT_LIST_HEAD(&espi->msg_queue); ++ espi->running = true; ++ ++ /* make sure that the hardware is disabled */ ++ ep93xx_spi_write_u8(espi, SSPCR1, 0); ++ ++ error = spi_register_master(master); ++ if (error) { ++ dev_err(&pdev->dev, "failed to register SPI master\n"); ++ goto fail_free_queue; ++ } ++ ++ dev_info(&pdev->dev, "EP93xx SPI Controller at 0x%08lx irq %d\n", ++ (unsigned long)res->start, espi->irq); ++ ++ return 0; ++ ++fail_free_queue: ++ destroy_workqueue(espi->wq); ++fail_free_irq: ++ free_irq(espi->irq, espi); ++fail_unmap_regs: ++ iounmap(espi->regs_base); ++fail_free_mem: ++ release_mem_region(res->start, resource_size(res)); ++fail_put_clock: ++ clk_put(espi->clk); ++fail_release_master: ++ spi_master_put(master); ++ platform_set_drvdata(pdev, NULL); ++ ++ return error; ++} ++ ++static int __exit ep93xx_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct ep93xx_spi *espi = spi_master_get_devdata(master); ++ struct resource *res; ++ ++ spin_lock_irq(&espi->lock); ++ espi->running = false; ++ spin_unlock_irq(&espi->lock); ++ ++ destroy_workqueue(espi->wq); ++ ++ /* ++ * Complete remaining messages with %-ESHUTDOWN status. ++ */ ++ spin_lock_irq(&espi->lock); ++ while (!list_empty(&espi->msg_queue)) { ++ struct spi_message *msg; ++ ++ msg = list_first_entry(&espi->msg_queue, ++ struct spi_message, queue); ++ list_del_init(&msg->queue); ++ msg->status = -ESHUTDOWN; ++ spin_unlock_irq(&espi->lock); ++ msg->complete(msg->context); ++ spin_lock_irq(&espi->lock); ++ } ++ spin_unlock_irq(&espi->lock); ++ ++ free_irq(espi->irq, espi); ++ iounmap(espi->regs_base); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(res->start, resource_size(res)); ++ clk_put(espi->clk); ++ platform_set_drvdata(pdev, NULL); ++ ++ spi_unregister_master(master); ++ return 0; ++} ++ ++static struct platform_driver ep93xx_spi_driver = { ++ .driver = { ++ .name = "ep93xx-spi", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __exit_p(ep93xx_spi_remove), ++}; ++ ++static int __init ep93xx_spi_init(void) ++{ ++ return platform_driver_probe(&ep93xx_spi_driver, ep93xx_spi_probe); ++} ++module_init(ep93xx_spi_init); ++ ++static void __exit ep93xx_spi_exit(void) ++{ ++ platform_driver_unregister(&ep93xx_spi_driver); ++} ++module_exit(ep93xx_spi_exit); ++ ++MODULE_DESCRIPTION("EP93xx SPI Controller driver"); ++MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ep93xx-spi"); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/0018-ts72xx_spi_tmp124.patch b/recipes/linux/linux-2.6.34/ts72xx/0018-ts72xx_spi_tmp124.patch new file mode 100644 index 0000000000..cbc5a65965 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/0018-ts72xx_spi_tmp124.patch @@ -0,0 +1,298 @@ +From 314f5d55f44d63c4ff418260580b93727eee00dc Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Tue, 22 Jun 2010 15:48:27 +0200 +Subject: [PATCH 18/18] ts72xx_spi_tmp124 + +It's an option. A 3-wire spi temperature sensor can be populated on TS-72XX sbc. +--- + arch/arm/mach-ep93xx/ts72xx.c | 61 ++++++++++++++++ + drivers/spi/Kconfig | 7 ++ + drivers/spi/Makefile | 1 + + drivers/spi/tmp124.c | 158 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 227 insertions(+), 0 deletions(-) + create mode 100644 drivers/spi/tmp124.c + +diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c +index 475559e..bf3a666 100644 +--- a/arch/arm/mach-ep93xx/ts72xx.c ++++ b/arch/arm/mach-ep93xx/ts72xx.c +@@ -23,7 +23,9 @@ + #include <linux/i2c-gpio.h> + #include <linux/mtd/nand.h> + #include <linux/mtd/partitions.h> ++#include <linux/spi/spi.h> + ++#include <mach/ep93xx_spi.h> + #include <mach/hardware.h> + #include <mach/ts72xx.h> + +@@ -367,6 +369,60 @@ static struct i2c_gpio_platform_data ts72xx_i2c_gpio_data = { + static struct i2c_board_info __initdata ts72xx_i2c_board_info[] = { + }; + ++/************************************************************************* ++ * SPI ++ *************************************************************************/ ++#if defined(CONFIG_SPI_TMP124) || defined(CONFIG_SPI_TMP124_MODULE) ++ ++/* this is our GPIO line used for chip select */ ++#define TMP124_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_MCCD2 ++ ++static int ts72xx_tmp124_setup(struct spi_device *spi) ++{ ++ int err; ++ ++ err = gpio_request(TMP124_CHIP_SELECT_GPIO, spi->modalias); ++ if (err) ++ return err; ++ ++ gpio_direction_output(TMP124_CHIP_SELECT_GPIO, 1); ++ ++ return 0; ++} ++ ++static void ts72xx_tmp124_cleanup(struct spi_device *spi) ++{ ++ gpio_set_value(TMP124_CHIP_SELECT_GPIO, 1); ++ gpio_direction_input(TMP124_CHIP_SELECT_GPIO); ++ gpio_free(TMP124_CHIP_SELECT_GPIO); ++} ++ ++static void ts72xx_tmp124_cs_control(struct spi_device *spi, int value) ++{ ++ gpio_set_value(TMP124_CHIP_SELECT_GPIO, value); ++} ++ ++static struct ep93xx_spi_chip_ops ts72xx_tmp124_ops = { ++ .setup = ts72xx_tmp124_setup, ++ .cleanup = ts72xx_tmp124_cleanup, ++ .cs_control = ts72xx_tmp124_cs_control, ++}; ++ ++static struct spi_board_info ts72xx_spi_devices[] = { ++ { ++ .modalias = "tmp124", ++ .max_speed_hz = 2 * 1000 * 1000, ++ .bus_num = 0, ++ .chip_select = 0, ++ .controller_data = &ts72xx_tmp124_ops, ++ }, ++}; ++ ++static struct ep93xx_spi_info ts72xx_spi_info = { ++ .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices), ++}; ++#endif ++ + static void __init ts72xx_init_machine(void) + { + ep93xx_init_devices(); +@@ -380,6 +436,11 @@ static void __init ts72xx_init_machine(void) + ts72xx_i2c_board_info, + ARRAY_SIZE(ts72xx_i2c_board_info)); + ++ #if defined(CONFIG_SPI_TMP124) || defined(CONFIG_SPI_TMP124_MODULE) ++ ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices, ++ ARRAY_SIZE(ts72xx_spi_devices)); ++ #endif ++ + if (is_max197_installed()) { + platform_device_register(&ts72xx_max197_device); + } +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 2b2f4c3..9f40a43 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -372,6 +372,13 @@ config SPI_TLE62X0 + sysfs interface, with each line presented as a kind of GPIO + exposing both switch control and diagnostic feedback. + ++config SPI_TMP124 ++ tristate "Texas Instruments TMP1224, TMP124" ++ depends on SPI_MASTER && SYSFS ++ help ++ SPI driver for TMP12X temperature sensor chips. ++ This provides a sysfs entry for temperature reading (2�C accurate). ++ + # + # Add new SPI protocol masters in alphabetical order above this line + # +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 377f845..2798b38 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -56,6 +56,7 @@ spi_s3c24xx_hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi_s3c24xx_fiq.o + # SPI protocol drivers (device/link on bus) + obj-$(CONFIG_SPI_SPIDEV) += spidev.o + obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o ++obj-$(CONFIG_SPI_TMP124) += tmp124.o + # ... add above this line ... + + # SPI slave controller drivers (upstream link) +diff --git a/drivers/spi/tmp124.c b/drivers/spi/tmp124.c +new file mode 100644 +index 0000000..e41ec8c +--- /dev/null ++++ b/drivers/spi/tmp124.c +@@ -0,0 +1,158 @@ ++/* ++ * TMP124 SPI protocol driver ++ * ++ * (c) Copyright 2008-2010 Matthieu Crapet <mcrapet@gmail.com> ++ * Based on tle62x0.c by Ben Dooks, <ben@simtec.co.uk> ++ * ++ * 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. ++ * ++ * Note: The chip uses a '3-wire SPI' (miso and mosi are the same pin). ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/spi/spi.h> ++#include <linux/slab.h> ++#include <linux/sched.h> ++ ++struct tmp124_state { ++ struct spi_device *bus; ++ u8 tx_buff[2]; ++ u8 rx_buff[2]; ++}; ++ ++static inline int tmp124_write_then_read(struct tmp124_state *st) ++{ ++ struct spi_message msg; ++ struct spi_transfer xfer[2] = { ++ { ++ .tx_buf = st->tx_buff, ++ .rx_buf = NULL, ++ .len = 2, ++ }, { ++ .tx_buf = NULL, ++ .rx_buf = st->rx_buff, ++ .len = 2, ++ } ++ }; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer[0], &msg); ++ spi_sync(st->bus, &msg); ++ ++ /* SPI_3WIRE is not handled by ep93xx_spi, the 2 messages must be ++ * splitted. We must wait to not confuse driver with read/write. */ ++ schedule_timeout(usecs_to_jiffies(1000)); ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer[1], &msg); ++ return spi_sync(st->bus, &msg); ++} ++ ++static ssize_t tmp124_temperature_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct tmp124_state *st = dev_get_drvdata(dev); ++ int ret; ++ ++ ((short *)st->tx_buff)[0] = 0x8000; ++ ++ ret = tmp124_write_then_read(st); ++ if (ret < 0) { ++ dev_err(&st->bus->dev, "tmp124_write_then_read\n"); ++ ret = 0; ++ } else { ++ signed long val = ((short *)st->rx_buff)[0]; ++ ++ val = val >> 3; ++ ++ /* 2 digit precision (0.0625*100) */ ++ val = (val * 50) / 8; ++ ret = snprintf(buf, PAGE_SIZE, "%ld.%02ld\n", val/100, abs(val%100)); ++ } ++ return ret; ++} ++ ++static DEVICE_ATTR(temperature, S_IRUGO, tmp124_temperature_show, NULL); ++ ++static int __devinit tmp124_probe(struct spi_device *spi) ++{ ++ struct tmp124_state *st; ++ int ret; ++ ++ st = kzalloc(sizeof(struct tmp124_state), GFP_KERNEL); ++ if (st == NULL) { ++ dev_err(&spi->dev, "no memory for device state\n"); ++ return -ENOMEM; ++ } ++ ++ /* required config */ ++ spi->bits_per_word = 16; ++ spi->mode = SPI_MODE_0; ++ ++ ret = spi_setup(spi); ++ if (ret) { ++ dev_err(&spi->dev, "setup device\n"); ++ goto err; ++ } ++ ++ ret = device_create_file(&spi->dev, &dev_attr_temperature); ++ if (ret) { ++ dev_err(&spi->dev, "cannot create temperature attribute\n"); ++ goto err; ++ } ++ ++ st->bus = spi; ++ spi_set_drvdata(spi, st); ++ return 0; ++ ++err: ++ kfree(st); ++ return ret; ++} ++ ++static int __devexit tmp124_remove(struct spi_device *spi) ++{ ++ struct tmp124_state *st = spi_get_drvdata(spi); ++ ++ device_remove_file(&spi->dev, &dev_attr_temperature); ++ kfree(st); ++ return 0; ++} ++ ++static struct spi_driver tmp124_driver = { ++ .driver = { ++ .name = "tmp124", ++ .owner = THIS_MODULE, ++ }, ++ .probe = tmp124_probe, ++ .remove = __devexit_p(tmp124_remove), ++}; ++ ++static __init int tmp124_init(void) ++{ ++ return spi_register_driver(&tmp124_driver); ++} ++ ++static __exit void tmp124_exit(void) ++{ ++ spi_unregister_driver(&tmp124_driver); ++} ++ ++module_init(tmp124_init); ++module_exit(tmp124_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("TMP124 SPI Protocol Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("spi:tmp124"); ++MODULE_VERSION("0.2"); +-- +1.7.1 + diff --git a/recipes/linux/linux-2.6.34/ts72xx/defconfig b/recipes/linux/linux-2.6.34/ts72xx/defconfig new file mode 100644 index 0000000000..e125912f44 --- /dev/null +++ b/recipes/linux/linux-2.6.34/ts72xx/defconfig @@ -0,0 +1,1679 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.34.7 +# Sat Oct 2 10:02:23 2010 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +CONFIG_RELAY=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +CONFIG_IPC_NS=y +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_NET_NS is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_FREEZER is not set + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +CONFIG_ARCH_EP93XX=y +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P6440 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set + +# +# Cirrus EP93xx Implementation Options +# +# CONFIG_CRUNCH is not set +CONFIG_CR1_NFBIT=y + +# +# EP93xx Platforms +# +CONFIG_EP93XX_SDCE3_SYNC_PHYS_OFFSET=y +# CONFIG_EP93XX_SDCE0_PHYS_OFFSET is not set +# CONFIG_EP93XX_SDCE1_PHYS_OFFSET is not set +# CONFIG_EP93XX_SDCE2_PHYS_OFFSET is not set +# CONFIG_EP93XX_SDCE3_ASYNC_PHYS_OFFSET is not set +# CONFIG_MACH_ADSSPHERE is not set +# CONFIG_MACH_EDB9301 is not set +# CONFIG_MACH_EDB9302 is not set +# CONFIG_MACH_EDB9307 is not set +# CONFIG_MACH_EDB9312 is not set +# CONFIG_MACH_EDB9315 is not set +# CONFIG_MACH_GESBC9312 is not set +# CONFIG_MACH_MICRO9H is not set +# CONFIG_MACH_MICRO9L is not set +CONFIG_MACH_TS72XX=y +CONFIG_EP93XX_EARLY_UART1=y +# CONFIG_EP93XX_EARLY_UART2 is not set +# CONFIG_EP93XX_EARLY_UART3 is not set +CONFIG_MACH_TS72XX_FORCE_MACHINEID=y +CONFIG_MACH_TS72XX_SBCINFO=y + +# +# Processor Type +# +CONFIG_CPU_ARM920T=y +CONFIG_CPU_32v4T=y +CONFIG_CPU_ABRT_EV4T=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_V4WT=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_VIC=y +CONFIG_ARM_VIC_NR=2 +CONFIG_COMMON_CLKDEV=y + +# +# Bus support +# +CONFIG_ARM_AMBA=y +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +# CONFIG_FLATMEM_MANUAL is not set +# CONFIG_DISCONTIGMEM_MANUAL is not set +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM=y +CONFIG_HAVE_MEMORY_PRESENT=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="console=ttyAM0,115200 ip=192.168.1.3:192.168.1.2:192.168.1.2:255.255.255.0 root=/dev/nfs nfsroot=192.168.1.2:/media/data/devel/oe/ts72xx-stable/tmp/deploy/glibc/images/ts72xx/nfsroot debug " +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=m + +# +# Power management options +# +# CONFIG_PM is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_NET_KEY=m +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_MULTIPLE_TABLES is not set +# CONFIG_IP_ROUTE_MULTIPATH is not set +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=m +CONFIG_INET_TCP_DIAG=m +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_TCP_MD5SIG=y +# CONFIG_IPV6 is not set +# CONFIG_NETLABEL is not set +CONFIG_NETWORK_SECMARK=y +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=m +CONFIG_NETFILTER_NETLINK_QUEUE=m +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CT_ACCT=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_ZONES=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=m +CONFIG_NF_CT_PROTO_GRE=m +CONFIG_NF_CT_PROTO_SCTP=m +CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +# CONFIG_NETFILTER_TPROXY is not set +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HL=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_HL=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_IP_VS=m +CONFIG_IP_VS_DEBUG=y +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_AH_ESP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +# CONFIG_IP_VS_PROTO_SCTP is not set + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_PROTO_DCCP=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_PROTO_UDPLITE=m +CONFIG_NF_NAT_PROTO_SCTP=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_H323=m +CONFIG_NF_NAT_SIP=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +# CONFIG_IP_NF_SECURITY is not set +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +# CONFIG_IP_DCCP is not set +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +CONFIG_NET_CLS_ROUTE=y +# CONFIG_DCB is not set + +# +# Network testing +# +CONFIG_NET_PKTGEN=m +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=m +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=m +# CONFIG_LIB80211_DEBUG is not set +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +CONFIG_MAC80211_RC_PID=y +# CONFIG_MAC80211_RC_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT_PID=y +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT="pid" +# CONFIG_MAC80211_MESH is not set +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=m +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_CONNECTOR=m +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1 +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +# CONFIG_MTD_BLOCK is not set +# CONFIG_MTD_BLOCK_RO is not set +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +# CONFIG_MTD_NAND_TS7250 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_PLATFORM=y +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_CRYPTOLOOP=m +# CONFIG_BLK_DEV_DRBD is not set +CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=65536 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +CONFIG_BLK_DEV_TS72XX_SDCARD=m +CONFIG_MISC_DEVICES=y +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_EP93XX_PWM=m +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +CONFIG_EEPROM_93CX6=y +CONFIG_TS72XX_MAX197=m +CONFIG_TS72XX_MAX197_AVERAGE=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +CONFIG_SCSI_TGT=m +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_EP93XX_ETH=y +CONFIG_AX88796=m +CONFIG_AX88796_93CX6=y +CONFIG_AX88796_TS_ETH100=m +# CONFIG_SMC91X is not set +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +CONFIG_USB_ZD1201=m +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_P54_COMMON is not set +# CONFIG_RT2X00 is not set +# CONFIG_WL12XX is not set +CONFIG_ZD1211RW=m +# CONFIG_ZD1211RW_DEBUG is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_FF_MEMLESS=m +CONFIG_INPUT_POLLDEV=m +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=m +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=m +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_EP93XX=m +CONFIG_KEYBOARD_GPIO=m +CONFIG_KEYBOARD_MATRIX=m +CONFIG_TS72XX_DIO_3X4_KEYPAD=m +CONFIG_TS72XX_DIO_4X4_KEYPAD=m +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +CONFIG_MOUSE_SERIAL=m +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=m +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_AMBAKMI is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=m +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set +CONFIG_SERIAL_8250_TS_SER1=m +CONFIG_SERIAL_8250_TS_SER1_IRQ=5 + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_AMBA_PL010=y +CONFIG_SERIAL_AMBA_PL010_CONSOLE=y +CONFIG_SERIAL_AMBA_PL010_TS72XX=y +# CONFIG_SERIAL_AMBA_PL011 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=m +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_I2C is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_IT8761E is not set +# CONFIG_GPIO_PL061 is not set + +# +# I2C GPIO expanders: +# + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_EP93XX_WATCHDOG=m +CONFIG_TS72XX_WATCHDOG=m +# CONFIG_MAX63XX_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=m +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=m +# CONFIG_USB_EZUSB is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP210X is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_EMPEG is not set +CONFIG_USB_SERIAL_FTDI_SIO=m +# CONFIG_USB_SERIAL_FUNSOFT is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_GARMIN is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MOTOROLA is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +CONFIG_USB_SERIAL_PL2303=m +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_QCAUX is not set +# CONFIG_USB_SERIAL_QUALCOMM is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIEMENS_MPI is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_SYMBOL is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OPTION is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_VIVOPAY_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_GADGET is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=m + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc1" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +CONFIG_RTC_DRV_M48T86=y +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_EP93XX=y +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +# CONFIG_EXT4_FS_XATTR is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=m +CONFIG_FUSE_FS=m +# CONFIG_CUSE is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=m +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=m +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +CONFIG_NLS_CODEPAGE_1250=m +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +CONFIG_NLS_ISO8859_15=m +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=m +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=2048 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_LATENCYTOP is not set +CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=m +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_GF128MUL=m +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=m +CONFIG_CRYPTO_TEST=m + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_LRW=m +CONFIG_CRYPTO_PCBC=m +CONFIG_CRYPTO_XTS=m + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_XCBC=m +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=m +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_WP512=m + +# +# Ciphers +# +CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_FCRYPT=m +CONFIG_CRYPTO_KHAZAD=m +# CONFIG_CRYPTO_SALSA20 is not set +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=m +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +CONFIG_CRC_ITU_T=m +CONFIG_CRC32=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=m +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y |