diff options
Diffstat (limited to 'recipes/linux/linux/simpad')
33 files changed, 23619 insertions, 0 deletions
diff --git a/recipes/linux/linux/simpad/collie-kexec.patch b/recipes/linux/linux/simpad/collie-kexec.patch new file mode 100644 index 0000000000..2c8fa9352b --- /dev/null +++ b/recipes/linux/linux/simpad/collie-kexec.patch @@ -0,0 +1,13 @@ +--- linux-2.6.20.4.orig/include/asm-arm/kexec.h 2007-04-10 19:16:39.000000000 +0200 ++++ linux-2.6.20.4/include/asm-arm/kexec.h 2007-04-03 05:01:09.000000000 +0200 +@@ -8,8 +8,8 @@ + /* Maximum address we can reach in physical address mode */ + #define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL) + /* Maximum address we can use for the control code buffer */ +-#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE +- ++//#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE ++#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL) + #define KEXEC_CONTROL_CODE_SIZE 4096 + + #define KEXEC_ARCH KEXEC_ARCH_ARM diff --git a/recipes/linux/linux/simpad/connectplus-remove-ide-HACK.patch b/recipes/linux/linux/simpad/connectplus-remove-ide-HACK.patch new file mode 100644 index 0000000000..4414b21191 --- /dev/null +++ b/recipes/linux/linux/simpad/connectplus-remove-ide-HACK.patch @@ -0,0 +1,12 @@ +Index: linux-2.6.13/drivers/ide/legacy/ide-cs.c +=================================================================== +--- linux-2.6.13.orig/drivers/ide/legacy/ide-cs.c 2005-09-01 22:43:46.000000000 +0100 ++++ linux-2.6.13/drivers/ide/legacy/ide-cs.c 2005-09-01 22:45:46.000000000 +0100 +@@ -488,7 +488,6 @@ + PCMCIA_DEVICE_PROD_ID123("KODAK Picture Card ", "KODAK ", "V100K", 0x94a0d8f3, 0xe4fc3ea0, 0xe5e7eed4), + PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209), + PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e), +- PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6), + PCMCIA_DEVICE_NULL, + }; + MODULE_DEVICE_TABLE(pcmcia, ide_ids); diff --git a/recipes/linux/linux/simpad/export_atags-r2.patch b/recipes/linux/linux/simpad/export_atags-r2.patch new file mode 100644 index 0000000000..e6fd85cf20 --- /dev/null +++ b/recipes/linux/linux/simpad/export_atags-r2.patch @@ -0,0 +1,320 @@ +[PATCH] Export atags to userspace and allow kexec to use customised atags + +Currently, the atags used by kexec are fixed to the ones originally used +to boot the kernel. This is less than ideal as changing the commandline, +initrd and other options would be a useful feature. + +This patch exports the atags used for the current kernel to userspace +through an "atags" file in procfs. The presence of the file is +controlled by its own Kconfig option and cleans up several ifdef blocks +into a separate file. The tags for the new kernel are assumed to be at +a fixed location before the kernel image itself. The location of the +tags used to boot the original kernel is unimportant and no longer +saved. + +Based on a patch from Uli Luckas <u.luckas@road.de> + +Signed-off-by: Richard Purdie <rpurdie@rpsys.net> + +--- + arch/arm/Kconfig | 7 +++ + arch/arm/kernel/Makefile | 1 + arch/arm/kernel/atags.c | 86 ++++++++++++++++++++++++++++++++++++++ + arch/arm/kernel/atags.h | 5 ++ + arch/arm/kernel/machine_kexec.c | 2 + arch/arm/kernel/relocate_kernel.S | 30 +------------ + arch/arm/kernel/setup.c | 32 -------------- + include/asm-arm/kexec.h | 3 + + 8 files changed, 110 insertions(+), 56 deletions(-) + +Index: linux-2.6.23/arch/arm/kernel/atags.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/arch/arm/kernel/atags.c 2008-01-20 15:56:02.000000000 +0000 +@@ -0,0 +1,86 @@ ++#include <linux/slab.h> ++#include <linux/kexec.h> ++#include <linux/proc_fs.h> ++#include <asm/setup.h> ++#include <asm/types.h> ++#include <asm/page.h> ++ ++struct buffer { ++ size_t size; ++ char *data; ++}; ++static struct buffer tags_buffer; ++ ++static int ++read_buffer(char* page, char** start, off_t off, int count, ++ int* eof, void* data) ++{ ++ struct buffer *buffer = (struct buffer *)data; ++ ++ if (off >= buffer->size) { ++ *eof = 1; ++ return 0; ++ } ++ ++ count = min((int) (buffer->size - off), count); ++ ++ memcpy(page, &buffer->data[off], count); ++ ++ return count; ++} ++ ++ ++static int ++create_proc_entries(void) ++{ ++ struct proc_dir_entry* tags_entry; ++ ++ tags_entry = create_proc_read_entry("atags", 0400, &proc_root, read_buffer, &tags_buffer); ++ if (!tags_entry) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++ ++static char __initdata atags_copy_buf[KEXEC_BOOT_PARAMS_SIZE]; ++static char __initdata *atags_copy; ++ ++void __init save_atags(const struct tag *tags) ++{ ++ atags_copy = atags_copy_buf; ++ memcpy(atags_copy, tags, KEXEC_BOOT_PARAMS_SIZE); ++} ++ ++ ++static int __init init_atags_procfs(void) ++{ ++ struct tag *tag; ++ int error; ++ ++ if (!atags_copy) { ++ printk(KERN_WARNING "Exporting ATAGs: No saved tags found\n"); ++ return -EIO; ++ } ++ ++ for (tag = (struct tag *) atags_copy; tag->hdr.size; tag = tag_next(tag)) ++ ; ++ ++ tags_buffer.size = ((char *) tag - atags_copy) + sizeof(tag->hdr); ++ tags_buffer.data = kmalloc(tags_buffer.size, GFP_KERNEL); ++ if (tags_buffer.data == NULL) ++ return -ENOMEM; ++ memcpy(tags_buffer.data, atags_copy, tags_buffer.size); ++ ++ error = create_proc_entries(); ++ if (error) { ++ printk(KERN_ERR "Exporting ATAGs: not enough memory\n"); ++ kfree(tags_buffer.data); ++ tags_buffer.size = 0; ++ tags_buffer.data = NULL; ++ } ++ ++ return error; ++} ++ ++arch_initcall(init_atags_procfs); +Index: linux-2.6.23/arch/arm/kernel/atags.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/arch/arm/kernel/atags.h 2008-01-20 15:56:02.000000000 +0000 +@@ -0,0 +1,5 @@ ++#ifdef CONFIG_ATAGS_PROC ++extern void save_atags(struct tag *tags); ++#else ++static inline void save_atags(struct tag *tags) { } ++#endif +Index: linux-2.6.23/arch/arm/kernel/Makefile +=================================================================== +--- linux-2.6.23.orig/arch/arm/kernel/Makefile 2007-10-09 21:31:38.000000000 +0100 ++++ linux-2.6.23/arch/arm/kernel/Makefile 2008-01-20 15:56:02.000000000 +0000 +@@ -19,6 +19,7 @@ obj-$(CONFIG_ISA_DMA) += dma-isa.o + obj-$(CONFIG_PCI) += bios32.o isa.o + obj-$(CONFIG_SMP) += smp.o + obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o ++obj-$(CONFIG_ATAGS_PROC) += atags.o + obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o + + obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o +Index: linux-2.6.23/arch/arm/kernel/setup.c +=================================================================== +--- linux-2.6.23.orig/arch/arm/kernel/setup.c 2008-01-20 15:55:43.000000000 +0000 ++++ linux-2.6.23/arch/arm/kernel/setup.c 2008-01-20 15:56:02.000000000 +0000 +@@ -24,7 +24,6 @@ + #include <linux/interrupt.h> + #include <linux/smp.h> + #include <linux/fs.h> +-#include <linux/kexec.h> + + #include <asm/cpu.h> + #include <asm/elf.h> +@@ -39,6 +38,7 @@ + #include <asm/mach/time.h> + + #include "compat.h" ++#include "atags.h" + + #ifndef MEM_SIZE + #define MEM_SIZE (16*1024*1024) +@@ -784,23 +784,6 @@ static int __init customize_machine(void + } + arch_initcall(customize_machine); + +-#ifdef CONFIG_KEXEC +- +-/* Physical addr of where the boot params should be for this machine */ +-extern unsigned long kexec_boot_params_address; +- +-/* Physical addr of the buffer into which the boot params are copied */ +-extern unsigned long kexec_boot_params_copy; +- +-/* Pointer to the boot params buffer, for manipulation and display */ +-unsigned long kexec_boot_params; +-EXPORT_SYMBOL(kexec_boot_params); +- +-/* The buffer itself - make sure it is sized correctly */ +-static unsigned long kexec_boot_params_buf[(KEXEC_BOOT_PARAMS_SIZE + 3) / 4]; +- +-#endif +- + void __init setup_arch(char **cmdline_p) + { + struct tag *tags = (struct tag *)&init_tags; +@@ -819,18 +802,6 @@ void __init setup_arch(char **cmdline_p) + else if (mdesc->boot_params) + tags = phys_to_virt(mdesc->boot_params); + +-#ifdef CONFIG_KEXEC +- kexec_boot_params_copy = virt_to_phys(kexec_boot_params_buf); +- kexec_boot_params = (unsigned long)kexec_boot_params_buf; +- if (__atags_pointer) { +- kexec_boot_params_address = __atags_pointer; +- memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE); +- } else if (mdesc->boot_params) { +- kexec_boot_params_address = mdesc->boot_params; +- memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE); +- } +-#endif +- + /* + * If we have the old style parameters, convert them to + * a tag list. +@@ -846,6 +817,7 @@ void __init setup_arch(char **cmdline_p) + if (tags->hdr.tag == ATAG_CORE) { + if (meminfo.nr_banks != 0) + squash_mem_tags(tags); ++ save_atags(tags); + parse_tags(tags); + } + +Index: linux-2.6.23/arch/arm/Kconfig +=================================================================== +--- linux-2.6.23.orig/arch/arm/Kconfig 2008-01-20 15:55:43.000000000 +0000 ++++ linux-2.6.23/arch/arm/Kconfig 2008-01-20 15:58:52.000000000 +0000 +@@ -865,6 +865,13 @@ config KEXEC + initially work for you. It may help to enable device hotplugging + support. + ++config ATAGS_PROC ++ bool "Export atags in procfs" ++ default n ++ help ++ Should the atags used to boot the kernel be exported in an "atags" ++ file in procfs. Useful with kexec. ++ + endmenu + + if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX ) +Index: linux-2.6.23/arch/arm/kernel/machine_kexec.c +=================================================================== +--- linux-2.6.23.orig/arch/arm/kernel/machine_kexec.c 2007-10-09 21:31:38.000000000 +0100 ++++ linux-2.6.23/arch/arm/kernel/machine_kexec.c 2008-01-20 15:56:03.000000000 +0000 +@@ -21,6 +21,7 @@ extern void setup_mm_for_reboot(char mod + extern unsigned long kexec_start_address; + extern unsigned long kexec_indirection_page; + extern unsigned long kexec_mach_type; ++extern unsigned long kexec_boot_atags; + + /* + * Provide a dummy crash_notes definition while crash dump arrives to arm. +@@ -62,6 +63,7 @@ void machine_kexec(struct kimage *image) + kexec_start_address = image->start; + kexec_indirection_page = page_list; + kexec_mach_type = machine_arch_type; ++ kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET; + + /* copy our kernel relocation code to the control code page */ + memcpy(reboot_code_buffer, +Index: linux-2.6.23/arch/arm/kernel/relocate_kernel.S +=================================================================== +--- linux-2.6.23.orig/arch/arm/kernel/relocate_kernel.S 2008-01-20 15:55:43.000000000 +0000 ++++ linux-2.6.23/arch/arm/kernel/relocate_kernel.S 2008-01-20 15:56:03.000000000 +0000 +@@ -7,23 +7,6 @@ + .globl relocate_new_kernel + relocate_new_kernel: + +- /* Move boot params back to where the kernel expects them */ +- +- ldr r0,kexec_boot_params_address +- teq r0,#0 +- beq 8f +- +- ldr r1,kexec_boot_params_copy +- mov r6,#KEXEC_BOOT_PARAMS_SIZE/4 +-7: +- ldr r5,[r1],#4 +- str r5,[r0],#4 +- subs r6,r6,#1 +- bne 7b +- +-8: +- /* Boot params moved, now go on with the kernel */ +- + ldr r0,kexec_indirection_page + ldr r1,kexec_start_address + +@@ -67,7 +50,7 @@ relocate_new_kernel: + mov lr,r1 + mov r0,#0 + ldr r1,kexec_mach_type +- ldr r2,kexec_boot_params_address ++ ldr r2,kexec_boot_atags + mov pc,lr + + .globl kexec_start_address +@@ -82,14 +65,9 @@ kexec_indirection_page: + kexec_mach_type: + .long 0x0 + +- /* phy addr where new kernel will expect to find boot params */ +- .globl kexec_boot_params_address +-kexec_boot_params_address: +- .long 0x0 +- +- /* phy addr where old kernel put a copy of orig boot params */ +- .globl kexec_boot_params_copy +-kexec_boot_params_copy: ++ /* phy addr of the atags for the new kernel */ ++ .globl kexec_boot_atags ++kexec_boot_atags: + .long 0x0 + + relocate_new_kernel_end: +Index: linux-2.6.23/include/asm-arm/kexec.h +=================================================================== +--- linux-2.6.23.orig/include/asm-arm/kexec.h 2008-01-20 15:55:57.000000000 +0000 ++++ linux-2.6.23/include/asm-arm/kexec.h 2008-01-20 15:56:03.000000000 +0000 +@@ -16,6 +16,9 @@ + + #define KEXEC_BOOT_PARAMS_SIZE 1536 + ++#define KEXEC_ARM_ATAGS_OFFSET 0x1000 ++#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000 ++ + #ifndef __ASSEMBLY__ + + struct kimage; diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-GPIO-MMC-mod.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-GPIO-MMC-mod.patch new file mode 100644 index 0000000000..22f220de8a --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-GPIO-MMC-mod.patch @@ -0,0 +1,1650 @@ +diff -uNr linux-2.6.21.vanilla/drivers/mmc/Kconfig linux-2.6.21/drivers/mmc/Kconfig +--- linux-2.6.21.vanilla/drivers/mmc/Kconfig 2007-05-30 18:00:30.000000000 +0200 ++++ linux-2.6.21/drivers/mmc/Kconfig 2007-05-30 18:26:18.000000000 +0200 +@@ -4,6 +4,12 @@ + + menu "MMC/SD Card support" + ++config MMC_SPI_BLOCK ++ tristate "MMC support for SIMpad over GPIO" ++ help ++ Say Y here to enable MMC block device over GPIO ++ if you have done the MMC-Mod. For Module say M. ++ + config MMC + tristate "MMC support" + help +diff -uNr linux-2.6.21.vanilla/drivers/mmc/Makefile linux-2.6.21/drivers/mmc/Makefile +--- linux-2.6.21.vanilla/drivers/mmc/Makefile 2007-05-30 18:00:30.000000000 +0200 ++++ linux-2.6.21/drivers/mmc/Makefile 2007-05-30 18:26:18.000000000 +0200 +@@ -2,6 +2,8 @@ + # Makefile for the kernel mmc device drivers. + # + ++obj-$(CONFIG_MMC_SPI_BLOCK) += mmc_spi_block.o ++ + # + # Core + # +diff -uNr linux-2.6.21.vanilla/drivers/mmc/mmc_spi_block.c linux-2.6.21/drivers/mmc/mmc_spi_block.c +--- linux-2.6.21.vanilla/drivers/mmc/mmc_spi_block.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/mmc/mmc_spi_block.c 2007-05-30 18:30:58.000000000 +0200 +@@ -0,0 +1,1618 @@ ++/* ++ * Copyright (c) Cl�ent Ballabriga, 2005 - GPL ++ * Copyright (c) Guylhem Aznar, 2005 - GPL ++ * ++ * Please check http://externe.net/zaurus/simpad-bluetooth reference design first. ++ * ++ * Based on Madsuk/Rohde work on a MMC driver for the WRT54G. ++ * ++ * This is an ugly hack of a driver. I am surprised if it ever works! ++ * So please use a real driver or contribute one to the 2.4/2.6 mmc framework ++ * ++ * mrdata: ported to 2.6 ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#include <linux/sched.h> ++#include <linux/kernel.h> ++#include <linux/fs.h> ++#include <linux/errno.h> ++#include <linux/hdreg.h> ++#include <linux/kdev_t.h> ++#include <linux/blkdev.h> ++#include <linux/spinlock.h> ++#include <linux/time.h> ++#include <linux/delay.h> ++#include <linux/timer.h> ++ ++#include <linux/platform_device.h> ++ ++#include <asm/hardware.h> ++#include <asm/arch/simpad.h> ++#include <asm/arch/gpio.h> ++ ++static int major = 121; ++ ++#define DEVICE_NAME "mmc_spi" ++ ++static int hd_sizes[1<<6]; ++static int hd_blocksizes[1<<6]; ++static int hd_hardsectsizes[1<<6]; ++static int hd_maxsect[1<<6]; ++static struct hd_struct hd[1<<6]; ++ ++static struct gendisk *mmc_disk; ++ ++static struct platform_device *mmc_dev; /* the one and only instance */ ++ ++static spinlock_t mmc_spi_lock; ++ ++/* ++ * ******************************************************************* ++ * ++ * This is the only configurable part. ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++// #define DEBUG 1 ++// #define DEBUG_HD 1 ++// #define CHECK_MEDIA_CHANGE // for developement ONLY, not working yet ++ ++/* Let that include where it is or compilation fails on INIT_REQUEST/CURRENT */ ++ ++ ++/* ++ * If you are using different GPIOs in your hardware hack, you must ++ * first make sure they are unused for other functions and then ++ * configure them here. ++ * ++ * On the simpad I use spare pins from the UART1 (internal serial port -> DECT 20-polig): ++ * ++ * Funktion PIN ## Original direction GPIO ## SPI function New direction SD/MMC ++ * - DCD PIN 08 (in) GPIO 23 DO - new name: DI -> MISO (in) PIN 7 Data Out ++ * - DTR PIN 11 (out) GPIO 07 CS (out) PIN 1 Chip Select ++ * - RI PIN 14 (in) GPIO 19 CLK (out) PIN 5 Clock ++ * - DSR PIN 16 (in) GPIO 06 DI - new name: DO -> MOSI (out) PIN 2 Data In ++ * ++ * ++ * SPI: MISO = Master In / Slave OUT MOSI = Master Out / Slave In ++ * ++ * Don't worry about in/out original function - the GPIOs will be ++ * reprogrammed. ++ */ ++ ++#define GPIO_SD_DI 23 ++#define GPIO_SD_CS 7 ++#define GPIO_SD_CLK 19 ++#define GPIO_SD_DO 6 ++ ++// #define FAST_GPIO_SD_DI GPIO_GPIO23 ++// #define FAST_GPIO_SD_CS GPIO_GPIO7 ++// #define FAST_GPIO_SD_CLK GPIO_GPIO19 ++// #define FAST_GPIO_SD_DO GPIO_GPIO6 ++ ++#define FAST_GPIO_SD_DI GPIO_UART1_DCD ++#define FAST_GPIO_SD_CS GPIO_UART1_DTR ++#define FAST_GPIO_SD_CLK GPIO_UART1_RI ++#define FAST_GPIO_SD_DO GPIO_UART1_DSR ++ ++/* ++ * ******************************************************************* ++ * ++ * Do not change anything below ! ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++/* GPIO states */ ++#define LOW 0 ++#define HIGH 1 ++ ++#define INPUT 0 ++#define OUTPUT 1 ++ ++#define PRESENT 1 ++#define ABSENT 0 ++ ++typedef unsigned int uint32; ++typedef unsigned long u32_t; ++typedef unsigned short u16_t; ++typedef unsigned char u8_t; ++ ++static struct timer_list mmc_timer; ++ ++// static struct timeval s_zeit, e_zeit; ++ ++/* start with no card */ ++static int mmc_media_detect = 0; ++static int mmc_media_changed = 1; ++ ++ ++///////////////////// ++// prototypes ++static int mmc_open(struct inode *inode, struct file *filp); ++static int mmc_release(struct inode *inode, struct file *filp); ++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); ++static void mmc_request(request_queue_t *q); ++ ++ ++/* ++ * ******************************************************************* ++ * ++ * Begin GPIO hardware access functions. ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++#define gpio_read(a) ((GPLR & a) ? 1 : 0) ++#define gpio_write_high(a) GPSR = a ++#define gpio_write_low(a) GPCR = a ++ ++/* set MMC_Chip_Select to HIGH (MMC/SD-Card inaktiv) */ ++#define MMC_Disable() gpio_write_high( FAST_GPIO_SD_CS) ++ ++/* set MMC_Chip_Select to LOW (MMC/SD-Card aktiv) */ ++#define MMC_Enable() gpio_write_low( FAST_GPIO_SD_CS) ++ ++/* ++ * ******************************************************************* ++ * ++ * Begin SPI hardware access functions. ++ * ++ * ******************************************************************* ++ * ++ */ ++static int mmc_spi_media_detect(void) ++{ ++// FIXME: add card detection/test by SPI ++ ++ return 1; ++} ++ ++static int mmc_spi_hardware_init(void) ++{ ++ printk("\nmmc: GPIO init\n"); ++ ++ /* cut existing functions */ ++ gpio_set_alternative_function(GPIO_SD_CLK, 0); ++ gpio_set_alternative_function(GPIO_SD_DI, 0); ++ gpio_set_alternative_function(GPIO_SD_DO, 0); ++ gpio_set_alternative_function(GPIO_SD_CS, 0); ++ ++ /* remap directions and set state of spi pins */ ++ gpio_direction_output(GPIO_SD_CLK, 0); ++ gpio_direction_input(GPIO_SD_DI); ++ gpio_direction_output(GPIO_SD_DO, 0); ++ gpio_direction_output(GPIO_SD_CS, 0); ++ ++ printk("mmc: initialising MMC\n"); ++ ++ /* Start */ ++ MMC_Disable(); ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ gpio_write_high( FAST_GPIO_SD_DO); ++ return 0; ++} ++ ++/* return what has been read, write the parameter */ ++/* Clockrate round about 1,2 MHz */ ++ ++static unsigned char mmc_spi_readwrite(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ return (result); ++} ++ ++/* return what has been read, write the parameter */ ++/* Clockrate round 200 kHz */ ++ ++static unsigned char mmc_spi_readwrite_slow(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ udelay(10); ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ udelay(10); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ udelay(10); ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ udelay(10); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ udelay(10); ++ ++ // printk("Send Byte = 0x%2X Receive Byte = 0x%2X \n", data_out, result); ++ ++ return (result); ++} ++ ++/* return what has been read */ ++ ++static unsigned char mmc_spi_read_only(void) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ return (result); ++} ++ ++/* write the parameter */ ++/* Clockrate round about 3,6 MHz */ ++ ++static unsigned char mmc_spi_write_only(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ return (result); ++} ++ ++ ++/** ++ * this function was contributed by: rcichielo from openwrt forums ++ * ++ * Comments added by Marc DENTY on 2007-03-20 ++ * ++ * Sequence to read a card's "CID" bytes (name, serial number etc) ++ * ++ * Send: 4ah,00h,00h,00h,00h,00h - CMD10, no args, null CRC ++ * Read: xx - NCR Time ++ * Read: xx - Command Response (Should be 00h) ++ * Read: until FEh is received - Wait for Data token ++ * Read: yy * 16 - Get 16 bytes from CID ++ * Read: zz - Read CRC lo byte ++ * Read: zz - Read CRC hi byte ++ * ++ * Useful locations in the returned data packet: ++ * ++ * 03h-08h Manufacturers's name in ascii ++ * 0ah-0dh Card's 32 bit serial number ++ */ ++/** ++ * Comments added by Cyril CATTIAUX on 2007-03-21 ++ * ++ * CID format specification (from Sandisk SD Product Manual v1.9) ++ * ++ * cid[00 ] Manufacturer ID (unsigned byte) ++ * cid[01-02] OEM/Application ID (ASCII) ++ * cid[03-07] Product Name (ASCII) ++ * cid[08 ] Product Revistion (BCD coded number) ++ * cid[09-12] Serial Number (32-bit unsigned int) ++ * cid[13-14] Reserved(bit 12->15) - Manufacture Date(bit 0->11) ++ * cid[15 ] CRC7(bit 1->7) - Not used, allways 1 (bit 0) ++*/ ++static int mmc_read_cid(unsigned char *cid) ++{ ++ unsigned char result = 0; ++ int i; ++ ++ MMC_Enable(); ++ ++ /* wait */ ++ for (i = 0; i < 4; i++) ++ { ++ result=mmc_spi_readwrite(0xff); ++ } ++ ++ /* issue CID (card identification data) read request */ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0x40 | 10); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x95); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result=mmc_spi_readwrite(0xff); ++ ++ if(result == 0x00) ++ break; ++ } ++ ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return(1); ++ } ++ ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xfe) break; ++ } ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return(2); ++ } ++ ++ for (i = 0; i < 16; i++) { ++ result = mmc_spi_readwrite(0xff); ++ cid[i] = result; ++ } ++ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0xff); ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ ++ return 0; ++} ++ ++ ++/** ++ * Comments added by Cyril CATTIAUX on 2007-03-21 ++ * ++ * CID format specification (from Sandisk SD Product Manual v1.9) ++ * ++ * cid[00 ] Manufacturer ID (unsigned byte) ++ * cid[01-02] OEM/Application ID (ASCII) ++ * cid[03-07] Product Name (ASCII) ++ * cid[08 ] Product Revision (BCD coded 2 digit number) ++ * cid[09-12] Serial Number (32-bit unsigned int) ++ * cid[13-14] Manufacture Date(bit 0->11) (BCD coded 3 digit number YYM offset from 2000) - Reserved(bit 12->15) ++ * cid[15 ] Not used, allways 1 (bit 0) - CRC7(bit 1->7) ++*/ ++static void mmc_show_cid_info(void) ++{ ++ int i, result; ++ unsigned short tmps; ++ unsigned char cid[16]; ++ ++ char manufacturer_id; ++ char oem_id[3]; ++ char product_name[6]; ++ unsigned char product_revision_h, product_revision_l; ++ unsigned int product_sn; ++ unsigned short product_date_y; ++ unsigned char product_date_m; ++ ++ result = mmc_read_cid(cid); ++ ++ if (result == 0) ++ { ++ printk("mmc_init: MMC/SD Card ID: "); ++ for (i=0; i<16; i++) { ++ printk("%02X ", cid[i]); ++ } ++ manufacturer_id=cid[0]; ++ strncpy(oem_id, &cid[1], 2); ++ oem_id[2]='\0'; ++ strncpy(product_name, &cid[3], 5); ++ product_name[5]='\0'; ++ product_revision_h=(cid[8] >> 4) & 0xf; ++ product_revision_l=cid[8] & 0xf; ++ product_sn=(cid[9]<<24) + (cid[10]<<16) + (cid[11]<<8) + cid[12]; ++ tmps=((cid[13]<<8) + cid[14]) & 0x0fff; ++ product_date_y=2000 + (((tmps >> 8) & 0xf) * 10) + ((tmps >> 4) & 0xf); ++ product_date_m=tmps & 0xf; ++ ++ printk("\nManufacturer ID : %02X\n", manufacturer_id); ++ printk("OEM/Application ID: %s\n", oem_id); ++ printk("Product name : %s\n", product_name); ++ printk("Product revision : %d.%d\n", product_revision_h, product_revision_l); ++ printk("Product SN : %08X\n", product_sn); ++ printk("Product Date : %d-%d\n", product_date_y, product_date_m); ++ ++ } else { ++ printk("mmc_init: impossible to get card indentification info for reason code: %02x", result); ++ } ++} ++ ++ ++/* ++static int mmc_spi_hw_test(void) ++{ ++ unsigned char result, k; ++ ++ unsigned int i, j, t; ++ ++ printk("mmc_spi_hw_test -> \n\n"); ++ k = 0x55; ++ for ( i = 0 ; i < 5; i++) { ++ ++ printk("\n0x%2X - ", k); ++ for ( j = 0 ; j < 8; j++ ) { ++ do_gettimeofday( &s_zeit ); ++ result = mmc_spi_readwrite_slow(k); ++ do_gettimeofday( &e_zeit ); ++ ++ if ( result != k ) { ++ printk("!>ERROR<! Transfer = 0x%2X Receive = 0x%2X Trail = %d \n", k, result, j); ++ // i = 255; j = 1000; ++ } ++ ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("Durchlauf: %i Versuch: %i von 8 -> Laufzeit: 0x%X s\n", i , j, t); ++ udelay(200); ++ } ++ printk("ready "); ++ ++ // k++; ++ } ++ printk("ready "); ++ printk("\n\n"); ++ return (0); ++} ++*/ ++ ++/* ++static int mmc_spi_speed_test(void) ++{ ++ unsigned int i, j, k, l, t; ++ ++ MMC_Disable(); ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_readwrite(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_readwrite: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 1; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 1; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_readwrite_slow(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_readwrite_slow: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_read_only(); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_read_only: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_write_only(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_write_only: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ return (1); ++ ++} ++*/ ++ ++ ++static int mmc_spi_card_init(void) ++{ ++ unsigned char result = 0; ++ short i, j; ++ ++// unsigned long flags; ++ ++ // save_flags(flags); ++ // cli(); ++ ++/* ++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CS), gpio_getalt(&gp, GPIO_SD_CS)); ++ printk("GPIO_SD_DI dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DI), gpio_getalt(&gp, GPIO_SD_DI)); ++ printk("GPIO_SD_DO dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DO), gpio_getalt(&gp, GPIO_SD_DO)); ++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CLK), gpio_getalt(&gp, GPIO_SD_CLK)); ++*/ ++ ++ // printk("\nmmc: mmc_spi_hw_test() *START*\n"); ++ ++ // mmc_spi_hw_test(); ++ ++ printk("\nmmc: card init 1/2 (CMD0)\n"); ++ ++ for (j = 0; j < 10; j++) ++ { ++ MMC_Disable(); ++ ++ for (i = 0; i < 10; i++) ++ mmc_spi_readwrite_slow(0xff); ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite_slow(0xff); ++ ++ mmc_spi_readwrite_slow(0x40); ++ ++ for (i = 0; i < 4; i++) { ++ ++ mmc_spi_readwrite_slow(0x00); ++ ++ } ++ ++ mmc_spi_readwrite_slow(0x95); ++ ++ for (i = 0; i < 8; i++) { ++ ++ result = mmc_spi_readwrite_slow(0xff); ++ ++#ifdef DEBUG_HD ++ if (result != 0xff) { ++ if (result > 0x1F && result < 0x80) ++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ else ++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X\n", j, i, result); ++ } ++#endif ++ if (result == 0x01) ++ break; ++ } ++ ++ if (result == 0x01) ++ break; ++ ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ ++ mdelay(60); ++ } ++ ++ if (result != 0x01) { ++ ++ printk("mmc: card init 1/2 error: %d (CMD0) failed\n", result); ++ printk(" -> Hint: MMC/SD-Card realy (fully) inserted ?\n"); ++ ++ return (1); ++ } ++ ++ printk("mmc: card init 1/2 (CMD0) success\n\n"); ++ ++ mdelay(1); ++ ++ printk("mmc: card init 2/2 (CMD1)\n"); ++ for (j = 0; j < 10; j++) { ++ ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x41); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // if (result >= 32 && result <= 127) ++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ // else ++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ ++ mmc_spi_readwrite_slow(0xff); ++ ++ if (result == 0x00) { ++ printk("mmc: card init 2/2 (CMD1) success\n\n"); ++ ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x4d); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 6; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // if (result > 31 && result < 128) ++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ // else ++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result); ++#endif ++ // if (result == 0x00) ++ // break; ++ } ++ // mdelay(60); ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ // mdelay(10); ++ ++ // restore_flags(flags); ++ ++ return (0); ++ } ++ mdelay(60); ++ } ++ return (2); ++} ++ ++ ++static int mmc_spi_card_config(void) ++{ ++ unsigned char result = 0; ++ short i, j; ++ unsigned char csd[32]; ++ unsigned int c_size; ++ unsigned int c_size_mult; ++ unsigned int mult; ++ unsigned int read_bl_len; ++ unsigned int blocknr = 0; ++ unsigned int block_len = 0; ++ unsigned int size = 0; ++ unsigned char rd_buffer[528]; ++// unsigned long flags; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite_slow(0xff); ++ result = mmc_spi_readwrite_slow(0x51); ++ // mmc_spi_readwrite_slow(0x4A); ++ // mmc_spi_readwrite_slow(0x40+0x0D); ++ // mmc_spi_readwrite_slow(0x42); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ ++ // printk("mmc: (CMD17) response von 0x51 result = 0x%X\n", result); ++ ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // printk("mmc: (CMD17) response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // restore_flags(flags); ++ // mmc_spi_readwrite_slow(0xff); ++ return (1); ++ } ++ // restore_flags(flags); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ rd_buffer[i] = result; ++#ifdef DEBUG_HD ++ /* ++ if (result >= 32 && result <= 127) ++ printk("mmc: CMD17 response (start token) result = 0x%X Zeichen = %c\n", result, result); ++ else ++ printk("mmc: CMD17 response (start token) result = 0x%X\n", result); ++ */ ++#endif ++ // if (result == 0xfe) ++ // break; ++ } ++ /* ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0xff); ++ return(1); ++ } ++ */ ++ ++ for (i = 8; i < 520; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ rd_buffer[i] = result; ++ } ++ for (i = 0; i < 2; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ } ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ ++ printk("Buffer - Start\n"); ++ ++ for ( i = 0 ; i < 33 ; i++) { ++ printk("\r\n%4X - ", i*16); ++ for ( j = 0 ; j < 16 ; j++) { ++ if ( rd_buffer[i*16+j] < 16) ++ printk("0%X ", rd_buffer[i*16+j]); ++ else ++ printk("%2X ", rd_buffer[i*16+j]); ++ } ++ for ( j = 0 ; j < 16 ; j++) { ++ if ( rd_buffer[i*16+j] < ' ') ++ printk("."); ++ else ++ printk("%c", rd_buffer[i*16+j]); ++ } ++ } ++ ++ printk("\nBuffer - Ende\n"); ++ ++ mmc_show_cid_info(); ++ ++ for(j = 0 ; j < 1; j++) { ++ MMC_Enable(); ++ ++ // mdelay(1); ++ ++ // save_flags(flags); ++ // cli(); ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x49); ++ // mmc_spi_readwrite_slow(0x4A); ++ // mmc_spi_readwrite_slow(0x40+0x0D); ++ // mmc_spi_readwrite_slow(0x42); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // printk("mmc: (CMD9) response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ // restore_flags(flags); ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ return (1); ++ } ++ for (i = 0; i < 22; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ if (result >= 32 && result <= 127) ++ printk("mmc: response (start token) result = 0x%X Zeichen = %c\n", result, result); ++ else ++ printk("mmc: response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0xfe) ++ break; ++ } ++ if (result == 0xfe) ++ break; ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ } ++ mdelay(60); ++ } ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ printk("mmc: mmc card config (CMD9) failed result = 0x%X\n\n", result); ++ return (2); ++ } ++ for (i = 0; i < 16; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ csd[i] = result; ++ } ++ for (i = 0; i < 2; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ } ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ ++ if (result == 0x00) ++ return (3); ++ ++ c_size = (csd[8] & 0xC0) + (csd[7] << 8) + ((csd[6] & 0x03) << 16); ++ c_size >>= 6; ++ c_size_mult = (csd[10] & 0x80) + ((csd[9] & 0x03) << 8); ++ c_size_mult >>= 7; ++ read_bl_len = csd[5] & 0x0f; ++ mult = 1; ++ mult <<= c_size_mult + 2; ++ blocknr = (c_size + 1) * mult; ++ block_len = 1; ++ block_len <<= read_bl_len; ++ size = block_len * blocknr; ++ size >>= 10; ++ ++ for (i = 0; i < (1 << 6); i++) { ++ hd_blocksizes[i] = 1024; ++ hd_hardsectsizes[i] = block_len; ++ hd_maxsect[i] = 256; ++ } ++ hd_sizes[0] = size; ++ hd[0].nr_sects = blocknr; ++ ++ printk("Size = %d, hardsectsize = %d, sectors = %d\n", ++ size, block_len, blocknr); ++ ++ return 0; ++} ++ ++ ++/* ++ * ******************************************************************* ++ * ++ * End of SPI hardware access functions. ++ * ++ * ******************************************************************* ++ */ ++ ++ ++static int mmc_spi_write_block(unsigned int dest_addr, unsigned char *data) ++{ ++ unsigned int address; ++ unsigned char result = 0; ++ unsigned char ab0, ab1, ab2, ab3; ++ int i; ++ ++ address = dest_addr; ++ ++ ab3 = 0xff & (address >> 24); ++ ab2 = 0xff & (address >> 16); ++ ab1 = 0xff & (address >> 8); ++ ab0 = 0xff & address; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite(0xff); ++ ++ mmc_spi_readwrite(0x58); ++ mmc_spi_readwrite(ab3); /* msb */ ++ mmc_spi_readwrite(ab2); ++ mmc_spi_readwrite(ab1); ++ mmc_spi_readwrite(ab0); /* lsb */ ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0x00) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0x00) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (1); ++ } ++ ++ mmc_spi_readwrite(0xfe); ++ ++ for (i = 0; i < 512; i += 32) ++ { ++ mmc_spi_write_only(data[i]); ++ mmc_spi_write_only(data[i+1]); ++ mmc_spi_write_only(data[i+2]); ++ mmc_spi_write_only(data[i+3]); ++ mmc_spi_write_only(data[i+4]); ++ mmc_spi_write_only(data[i+5]); ++ mmc_spi_write_only(data[i+6]); ++ mmc_spi_write_only(data[i+7]); ++ mmc_spi_write_only(data[i+8]); ++ mmc_spi_write_only(data[i+9]); ++ mmc_spi_write_only(data[i+10]); ++ mmc_spi_write_only(data[i+11]); ++ mmc_spi_write_only(data[i+12]); ++ mmc_spi_write_only(data[i+13]); ++ mmc_spi_write_only(data[i+14]); ++ mmc_spi_write_only(data[i+15]); ++ mmc_spi_write_only(data[i+16]); ++ mmc_spi_write_only(data[i+17]); ++ mmc_spi_write_only(data[i+18]); ++ mmc_spi_write_only(data[i+19]); ++ mmc_spi_write_only(data[i+20]); ++ mmc_spi_write_only(data[i+21]); ++ mmc_spi_write_only(data[i+22]); ++ mmc_spi_write_only(data[i+23]); ++ mmc_spi_write_only(data[i+24]); ++ mmc_spi_write_only(data[i+25]); ++ mmc_spi_write_only(data[i+26]); ++ mmc_spi_write_only(data[i+27]); ++ mmc_spi_write_only(data[i+28]); ++ mmc_spi_write_only(data[i+29]); ++ mmc_spi_write_only(data[i+30]); ++ mmc_spi_write_only(data[i+31]); ++ } ++ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 1000000; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xff) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0xff) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (3); ++ } ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (0); ++} ++ ++static int mmc_spi_read_block(unsigned char *data, unsigned int src_addr) ++{ ++ unsigned int address; ++ unsigned char result = 0; ++ unsigned char ab0, ab1, ab2, ab3; ++ unsigned long flags; ++ int i, j; ++ ++ address = src_addr; ++ ++ ab3 = 0xff & (address >> 24); ++ ab2 = 0xff & (address >> 16); ++ ab1 = 0xff & (address >> 8); ++ ab0 = 0xff & address; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite(0xff); ++ ++ mmc_spi_readwrite(0x51); ++ mmc_spi_readwrite(ab3); /* msb */ ++ mmc_spi_readwrite(ab2); ++ mmc_spi_readwrite(ab1); ++ mmc_spi_readwrite(ab0); /* lsb */ ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0x00) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0x00) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (1); ++ } ++ ++ for (i = 0; i < 100000; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xfe) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0xfe) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (2); ++ } ++ ++ for (i = 0; i < 512; i += 32 ) ++ { ++ data[i] = mmc_spi_read_only(); ++ data[i+1] = mmc_spi_read_only(); ++ data[i+2] = mmc_spi_read_only(); ++ data[i+3] = mmc_spi_read_only(); ++ data[i+4] = mmc_spi_read_only(); ++ data[i+5] = mmc_spi_read_only(); ++ data[i+6] = mmc_spi_read_only(); ++ data[i+7] = mmc_spi_read_only(); ++ data[i+8] = mmc_spi_read_only(); ++ data[i+9] = mmc_spi_read_only(); ++ data[i+10] = mmc_spi_read_only(); ++ data[i+11] = mmc_spi_read_only(); ++ data[i+12] = mmc_spi_read_only(); ++ data[i+13] = mmc_spi_read_only(); ++ data[i+14] = mmc_spi_read_only(); ++ data[i+15] = mmc_spi_read_only(); ++ data[i+16] = mmc_spi_read_only(); ++ data[i+17] = mmc_spi_read_only(); ++ data[i+18] = mmc_spi_read_only(); ++ data[i+19] = mmc_spi_read_only(); ++ data[i+20] = mmc_spi_read_only(); ++ data[i+21] = mmc_spi_read_only(); ++ data[i+22] = mmc_spi_read_only(); ++ data[i+23] = mmc_spi_read_only(); ++ data[i+24] = mmc_spi_read_only(); ++ data[i+25] = mmc_spi_read_only(); ++ data[i+26] = mmc_spi_read_only(); ++ data[i+27] = mmc_spi_read_only(); ++ data[i+28] = mmc_spi_read_only(); ++ data[i+29] = mmc_spi_read_only(); ++ data[i+30] = mmc_spi_read_only(); ++ data[i+31] = mmc_spi_read_only(); ++ } ++ ++ result = mmc_spi_readwrite(0xff); ++ result = mmc_spi_readwrite(0xff); ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ ++ return (0); ++} ++ ++/* ++static int mmc_revalidate(kdev_t dev) ++{ ++ int target, max_p, start, i; ++ ++ mmc_media_detect = mmc_spi_media_detect(); ++ ++ if (mmc_media_detect == 0) ++ { ++ return -ENODEV; ++ } ++ ++ target = DEVICE_NR(dev); ++ ++ max_p = hd_gendisk.max_p; ++ start = target << 6; ++ for (i = max_p - 1; i >= 0; i--) ++ { ++ int minor = start + i; ++ invalidate_device(MKDEV(MAJOR_NR, minor), 1); ++ hd_gendisk.part[minor].start_sect = 0; ++ hd_gendisk.part[minor].nr_sects = 0; ++ } ++ ++ grok_partitions(&hd_gendisk, target, 1 << 6, hd_sizes[0] * 2); ++ ++ return 0; ++} ++*/ ++ ++ ++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ if (!inode || !inode->i_rdev) ++ return -EINVAL; ++ ++ switch (cmd) { ++#if 0 ++ case BLKGETSIZE: ++ return put_user(hd[MINOR(inode->i_rdev)].nr_sects, ++ (unsigned long *)arg); ++ case BLKGETSIZE64: ++ return put_user((u64) hd[MINOR(inode->i_rdev)]. ++ nr_sects, (u64 *) arg); ++ case BLKRRPART: ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ return mmc_revalidate(inode->i_rdev); ++#endif ++ case HDIO_GETGEO: ++ { ++ struct block_device *bdev = inode->i_bdev; ++ struct hd_geometry *loc, g; ++ loc = (struct hd_geometry *)arg; ++ if (!loc) ++ return -EINVAL; ++ memset(loc, 0, sizeof(struct hd_geometry)); ++ g.heads = 4; ++ g.sectors = 16; ++ g.cylinders = get_capacity(bdev->bd_disk) / (4*16); ++ g.start = get_start_sect(bdev); ++ return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0; ++ } ++ default: ++ return -ENOTTY; ++ } ++} ++ ++ ++/* ++static int mmc_check_media_change(kdev_t dev) ++{ ++ (void) dev; ++ if (mmc_media_changed == 1) ++ { ++ mmc_media_changed = 0; ++ return 1; ++ } ++ else ++ { ++ return 0; ++ } ++} ++*/ ++ ++ ++/* ++static int mmc_init(void) ++{ ++ int result; ++ ++ result = mmc_spi_hardware_init(); ++ ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_spi_hardware_init\n", result); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_hw_test(); ++ ++ if (result != 0) ++ { ++ printk("\n mmc: mmc_spi_hw_test i.O. \n\n"); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_speed_test(); ++ ++ if (result != 0) ++ { ++ printk("\n mmc: mmc_spi_speed_test i.O. \n\n"); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_card_init(); ++ mdelay(50); ++ if (result != 0) ++ { ++ // Give it an extra shot ++ // result = mmc_spi_card_init(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_card_init\n", result); ++ return -1; ++ } ++ } ++ ++ result = mmc_spi_card_config(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_card_config\n", result); ++ return -1; ++ } ++ ++ ++ return 0; ++} ++*/ ++ ++/* ++static void mmc_exit(void) ++{ ++} ++*/ ++ ++ ++/* ++static void mmc_check_media(void) ++{ ++ int old_state, new_state; ++ int result; ++ ++ old_state = mmc_media_detect; ++ new_state = mmc_spi_media_detect(); ++ ++ if (old_state != new_state) ++ { ++ mmc_media_changed = 1; ++ if (new_state == PRESENT) ++ { ++ result = mmc_init(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_init\n", result); ++ } ++ else ++ { ++ mmc_exit(); ++ } ++ } ++ } ++ ++#ifdef CHECK_MEDIA_CHANGE ++ del_timer(&mmc_timer); ++ mmc_timer.expires = jiffies + 10*HZ; ++ add_timer(&mmc_timer); ++#endif ++ ++} ++*/ ++ ++ ++/* NB: There might be several requests in the queue, simply dequeuing only one ++ and not checking for more will cause a stall because the block subsystem ++ will not call this function again unless the queue is "plugged" which can ++ only happen if it runs empty... */ ++static void mmc_spi_request(struct request_queue *q) ++{ ++ struct request *req; ++ int ret; ++ ++ unsigned int mmc_address; ++ unsigned char *buffer_address; ++ int nr_sectors; ++ int i; ++ int result, success; ++ ++ if (blk_queue_plugged(q)) { ++ return; ++ } ++ ++ spin_lock(&mmc_spi_lock); ++ for(;;) { ++ req = elv_next_request(q); ++ if (!req) ++ break; ++ ++ if (!blk_fs_request(req)) { ++ printk("not a blk_fs_request\n"); ++ spin_unlock(&mmc_spi_lock); ++ continue; ++ } ++ ++ mmc_address = req->sector * hd_hardsectsizes[0]; ++ buffer_address = req->buffer; ++ nr_sectors = req->current_nr_sectors; ++ success = 1; ++ if (rq_data_dir(req) == READ) { ++ spin_unlock_irq(q->queue_lock); ++ for (i = 0; i < nr_sectors; i++) { ++ result = mmc_spi_read_block(buffer_address, mmc_address); ++ if (unlikely(result < 0)) { ++ printk(KERN_ERR "mmi_spi_block: error reading block (%d)\n", result); ++ success = 0; ++ break; ++ } ++ mmc_address += hd_hardsectsizes[0]; ++ buffer_address += hd_hardsectsizes[0]; ++ } ++ spin_lock_irq(q->queue_lock); ++ } else { ++ spin_unlock_irq(q->queue_lock); ++ for (i = 0; i < nr_sectors; i++) { ++ result = mmc_spi_write_block(mmc_address, buffer_address); ++ if (unlikely(result < 0)) { ++ printk(KERN_ERR "mmi_spi_block: error writing block (%d)\n", result); ++ success = 0; ++ break; ++ } ++ mmc_address += hd_hardsectsizes[0]; ++ buffer_address += hd_hardsectsizes[0]; ++ } ++ spin_lock_irq(q->queue_lock); ++ } ++ ret = end_that_request_chunk(req, success, nr_sectors * hd_hardsectsizes[0]); ++ if (!ret) { ++ blkdev_dequeue_request(req); ++ end_that_request_last(req, 0); ++ } ++ } ++ spin_unlock(&mmc_spi_lock); ++} ++ ++ ++static int mmc_open(struct inode *inode, struct file *filp) ++{ ++ // int device; ++ (void) filp; ++ mmc_media_detect = mmc_spi_media_detect(); ++ ++ if (mmc_media_detect == 0) ++ return -ENODEV; ++ ++ return 0; ++} ++ ++static int mmc_release(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++ ++static struct block_device_operations mmc_spi_bdops = { ++ .open = mmc_open, ++ .release = mmc_release, ++ .ioctl = mmc_ioctl, ++ .owner = THIS_MODULE, ++#if 0 ++ .check_media_change = mmc_check_media_change, ++ .revalidate = mmc_revalidate, ++#endif ++}; ++ ++static int detect_card(void) ++{ ++ int result; ++ ++ result = mmc_spi_card_init(); ++ if (result != 0) { ++ // Give it an extra shot ++ result = mmc_spi_card_init(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_init (%d)\n", result); ++ return -ENODEV; ++ } ++ } ++ ++ result = mmc_spi_card_config(); ++ // result = mmc_spi_speed_test(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_config (%d)\n", result); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/* Fills in the gendisk structure from the received card ++ data. */ ++static int gendisk_init(struct device *dev, struct gendisk *gd) ++{ ++ if (!gd) ++ return -EINVAL; ++ ++ gd->major = major; ++ gd->first_minor = 0; /* only one device supported */ ++ gd->fops = &mmc_spi_bdops; ++ gd->driverfs_dev = dev; ++ ++ gd->queue = blk_init_queue(mmc_spi_request,NULL); ++ ++ if (!gd->queue) ++ return -ENOMEM; ++ ++ sprintf(gd->disk_name, "mmcblk"); ++ ++ blk_queue_hardsect_size(gd->queue, hd_hardsectsizes[0]); ++ ++ set_capacity(gd, hd_sizes[0]<<1); ++ ++ return 0; ++} ++ ++static int gendisk_fini(struct gendisk *gd) ++{ ++ BUG_ON(!gd); ++ ++ if (gd->queue) ++ blk_cleanup_queue(gd->queue); ++ ++ del_gendisk(gd); ++} ++ ++/* platform driver device instance routines */ ++static int mmc_spi_probe(struct platform_device *pdev) ++{ ++ int result; ++ printk("$Id: mmc_spi_block.c,v 1.04 2007/05/25 23:27:16 mrdata Exp $\n"); ++ ++ result = mmc_spi_hardware_init(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_spi_hardware_init (%d)\n", result); ++ result = -ENODEV; ++ return result; ++ } ++ ++ result = detect_card(); ++ if (result < 0) ++ return result; ++ ++ mmc_media_detect = 1; ++ ++ result = register_blkdev(major, DEVICE_NAME); ++ if (result < 0) ++ return result; ++ ++ if (!major) ++ major = result; ++ ++ /* allow 8 partitions per device */ ++ BUG_ON(mmc_disk!=NULL); ++ mmc_disk = alloc_disk(1 << 3); ++ if (!mmc_disk) { ++ result = -ENOMEM; ++ goto out; ++ } ++ ++ result = gendisk_init(&pdev->dev, mmc_disk); ++ if (result < 0) ++ goto out; ++ ++ add_disk(mmc_disk); ++ ++ /*init_timer(&mmc_timer); ++ mmc_timer.expires = jiffies + HZ; ++ mmc_timer.function = (void *)mmc_check_media; ++ add_timer(&mmc_timer); */ ++ return 0; ++ ++out: ++ if (mmc_disk) ++ put_disk(mmc_disk); ++ ++ unregister_blkdev(major, DEVICE_NAME); ++ return result; ++} ++ ++static int mmc_spi_remove(struct platform_device *dev) ++{ ++ int ret; ++ ++ if (mmc_disk) { ++ gendisk_fini(mmc_disk); ++ put_disk(mmc_disk); ++ } ++ ++ ret = unregister_blkdev(major, DEVICE_NAME); ++ return ret; ++} ++ ++struct platform_driver mmc_spi_driver = { ++ .probe = mmc_spi_probe, ++ .remove = mmc_spi_remove, ++ .driver = { ++ .name = "mmc_spi", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++ ++/* module init/exit */ ++static int __init mmc_block_spi_init(void) ++{ ++ int ret; ++ spin_lock_init(&mmc_spi_lock); ++ ++ ret = platform_driver_register(&mmc_spi_driver); ++ if (ret < 0) ++ return ret; ++ ++ /* we just support one device */ ++ mmc_dev = platform_device_register_simple("mmc_spi", -1, NULL, 0); ++ if (IS_ERR(mmc_dev)) ++ return PTR_ERR(mmc_dev); ++ ++ return 0; ++} ++ ++ ++static void __exit mmc_block_spi_exit(void) ++{ ++ platform_driver_unregister(&mmc_spi_driver); ++ if (mmc_dev) ++ platform_device_unregister(mmc_dev); ++} ++ ++ ++module_init(mmc_block_spi_init); ++module_exit(mmc_block_spi_exit); ++ ++MODULE_AUTHOR("Madsuk,Rohde,TaGana,Carsten Juttner <carjay@gmx.net>,Guylhem Aznar <mmc-driver@externe.net>,mrdata"); ++MODULE_DESCRIPTION("Driver for MMC/SD-Cards in SPI mode by GPIO"); ++MODULE_SUPPORTED_DEVICE("SIMpad"); ++MODULE_LICENSE("GPL"); ++ ++module_param(major, int, 0444); ++MODULE_PARM_DESC(major, "specify the major device number for the MMC/SD SPI driver"); diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-battery-old-way-but-also-with-sysfs.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-battery-old-way-but-also-with-sysfs.patch new file mode 100644 index 0000000000..ee18ff2bb0 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-battery-old-way-but-also-with-sysfs.patch @@ -0,0 +1,571 @@ +diff -uNr linux-2.6.21.vanilla/drivers/mfd/Makefile linux-2.6.21/drivers/mfd/Makefile +--- linux-2.6.21.vanilla/drivers/mfd/Makefile 2007-05-30 18:00:30.000000000 +0200 ++++ linux-2.6.21/drivers/mfd/Makefile 2007-05-30 18:43:10.000000000 +0200 +@@ -12,3 +12,7 @@ + ifeq ($(CONFIG_SA1100_ASSABET),y) + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o + endif ++ ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++obj-$(CONFIG_MCP_UCB1200) += ucb1x00-simpad.o ++endif +diff -uNr linux-2.6.21.vanilla/drivers/mfd/ucb1x00-simpad.c linux-2.6.21/drivers/mfd/ucb1x00-simpad.c +--- linux-2.6.21.vanilla/drivers/mfd/ucb1x00-simpad.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/mfd/ucb1x00-simpad.c 2007-05-30 18:35:48.000000000 +0200 +@@ -0,0 +1,226 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-simpad.c ++ * ++ * Copyright (C) 2001-2003 Russell King, All Rights Reserved. ++ * 2007/03/18 mrdata: ++ * - adapted ucb1x00-assabet.c ++ * - transfer ucb1x00-simpad.c from kernel24 ++ * to new structur of kernel26 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ * ++ * We handle the machine-specific bits of the UCB1x00 driver here. ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/proc_fs.h> ++#include <linux/device.h> ++ ++#include <linux/apm-emulation.h> ++ ++#include <asm/dma.h> ++ ++#include <asm/arch/simpad.h> ++#include <asm/arch-sa1100/simpad_pm.h> ++ ++#include "ucb1x00.h" ++#include "ucb1x00-simpad.h" ++ ++#define UCB1X00_ATTR(name,input,designation) \ ++static ssize_t name##_show(struct class_device *dev, char *buf) \ ++{ \ ++ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \ ++ int val; \ ++ ucb1x00_adc_enable(ucb); \ ++ val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC); \ ++ ucb1x00_adc_disable(ucb); \ ++ return sprintf(buf, "%d\n", CALIBRATE_##designation(val)); \ ++} \ ++static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL) ++ ++UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1, BATTERY); ++UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD2, SUPPLY); ++UCB1X00_ATTR(icharger, UCB_ADC_INP_AD3, CHARGING); ++ ++static struct ucb1x00 *ucb_alt; ++ ++#define UCB1X00_WERT(name,input,designation) \ ++static int ucb1x00_simpad_read_##name(struct ucb1x00 *ucb_alt) \ ++{ \ ++ int val; \ ++ ucb1x00_adc_enable(ucb_alt); \ ++ val = ucb1x00_adc_read(ucb_alt, input, UCB_NOSYNC); \ ++ ucb1x00_adc_disable(ucb_alt); \ ++ return CALIBRATE_##designation(val); \ ++} ++ ++UCB1X00_WERT(vbatt, UCB_ADC_INP_AD1, BATTERY); ++UCB1X00_WERT(vcharger, UCB_ADC_INP_AD2, SUPPLY); ++UCB1X00_WERT(icharger, UCB_ADC_INP_AD3, CHARGING); ++ ++static int ucb1x00_simpad_add(struct ucb1x00_dev *dev) ++{ ++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_vbatt); ++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_vcharger); ++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_icharger); ++ ucb_alt = dev->ucb; ++ return 0; ++} ++ ++static void ucb1x00_simpad_remove(struct ucb1x00_dev *dev) ++{ ++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_icharger); ++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vcharger); ++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vbatt); ++} ++ ++static struct ucb1x00_driver ucb1x00_simpad_driver = { ++ .add = ucb1x00_simpad_add, ++ .remove = ucb1x00_simpad_remove, ++}; ++ ++static int __init ucb1x00_simpad_init(void) ++{ ++ apm_get_power_status = simpad_apm_get_power_status; ++ return ucb1x00_register_driver(&ucb1x00_simpad_driver); ++} ++ ++static void __exit ucb1x00_simpad_exit(void) ++{ ++ apm_get_power_status = NULL; ++ ucb1x00_unregister_driver(&ucb1x00_simpad_driver); ++} ++ ++/****************************************************************************/ ++/* Functions exported for use by the kernel and kernel modules */ ++/****************************************************************************/ ++ ++int simpad_get_battery(struct simpad_battery_apm *bstat) ++{ ++ int icharger, vcharger, vbatt; ++ ++ if ( ucb_alt ) { ++ icharger = ucb1x00_simpad_read_icharger( ucb_alt ); ++ vcharger = ucb1x00_simpad_read_vcharger( ucb_alt ); ++ vbatt = ucb1x00_simpad_read_vbatt( ucb_alt ); ++ } else { ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_UNKNOWN; ++ bstat->status = SIMPAD_BATT_STATUS_UNKNOWN; ++ bstat->percentage = 0x64; // lets say 100% ++ bstat->life = 330; // lets say a long time ++ return 0; ++ } ++ ++ /* AC status */ ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_OFFLINE; ++ if ( vcharger>MIN_SUPPLY ) { ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_ONLINE; ++ } ++ ++ /* charging */ ++ bstat->status = 0x0; ++ if ( icharger >= CHARGING_LED_LEVEL ) ++ bstat->status = SIMPAD_BATT_STATUS_CHARGING; ++ ++ if ( vbatt > BATT_LOW ) ++ bstat->status |= SIMPAD_BATT_STATUS_HIGH; ++ else if ( vbatt < BATT_CRITICAL ) ++ bstat->status |= SIMPAD_BATT_STATUS_CRITICAL; ++ else ++ bstat->status |= SIMPAD_BATT_STATUS_LOW; ++ ++ if (bstat->status & SIMPAD_BATT_STATUS_CHARGING) { ++ if (icharger > CHARGING_MAX_LEVEL) ++ icharger = CHARGING_MAX_LEVEL; ++ if (icharger < CHARGING_LED_LEVEL) ++ icharger = CHARGING_LED_LEVEL; ++ ++ bstat->percentage = 100 - 100 * (icharger - CHARGING_LED_LEVEL) / ++ (CHARGING_MAX_LEVEL - CHARGING_LED_LEVEL); ++ } else { ++ if (vbatt > BATT_FULL) ++ vbatt = BATT_FULL; ++ if (vbatt < BATT_EMPTY) ++ vbatt = BATT_EMPTY; ++ ++ bstat->percentage = 100 * (vbatt - BATT_EMPTY) / (BATT_FULL - BATT_EMPTY); ++ } ++ ++ /* let's assume: full load is 7h */ ++ /* bstat->life = 420*bstat->percentage/100; */ ++ /* mrdata: think, 4h is more realistic */ ++ bstat->life = 240*(bstat->percentage)/100; ++ ++#if 0 ++ printk("get_battery: ac: %02x / ch: %02x / perc: %02x / life: %d \n", ++ bstat->ac_status, bstat->status, ++ bstat->percentage, bstat->life ); ++#endif ++ ++ return 0; ++} ++ ++void simpad_apm_get_power_status(struct apm_power_info *info) ++{ ++ struct simpad_battery_apm bstat; ++ unsigned char ac = APM_AC_UNKNOWN; ++ unsigned char level = APM_BATTERY_STATUS_UNKNOWN; ++ int status, result; ++ ++ result = simpad_get_battery(&bstat); ++ if (result) { ++ printk("%s: unable to access battery information: result=%d\n", __FUNCTION__, result); ++ return; ++ } ++ ++ switch (bstat.ac_status) { ++ case SIMPAD_AC_STATUS_AC_OFFLINE: ++ ac = APM_AC_OFFLINE; ++ break; ++ case SIMPAD_AC_STATUS_AC_ONLINE: ++ ac = APM_AC_ONLINE; ++ break; ++ case SIMPAD_AC_STATUS_AC_BACKUP: ++ ac = APM_AC_BACKUP; ++ break; ++ } ++ ++ info->ac_line_status = ac; ++ ++ status = bstat.status; ++ if (status & (SIMPAD_BATT_STATUS_CHARGING | SIMPAD_BATT_STATUS_CHARGE_MAIN)) ++ level = APM_BATTERY_STATUS_CHARGING; ++ else if (status & (SIMPAD_BATT_STATUS_HIGH | SIMPAD_BATT_STATUS_FULL)) ++ level = APM_BATTERY_STATUS_HIGH; ++ else if (status & SIMPAD_BATT_STATUS_LOW) ++ level = APM_BATTERY_STATUS_LOW; ++ else if (status & SIMPAD_BATT_STATUS_CRITICAL) ++ level = APM_BATTERY_STATUS_CRITICAL; ++ ++ info->battery_status = level; ++ info->battery_flag = info->battery_status; ++ ++ info->battery_life = bstat.percentage; ++ ++ /* we have a dumb battery - so we know nothing */ ++ info->time = bstat.life; ++ info->units = APM_UNITS_MINS; ++ ++#if 0 ++ printk("apm_get_power: ac: %02x / bs: %02x / bf: %02x / perc: %02x / life: %d\n", ++ info->ac_line_status, info->battery_status, info->battery_flag, ++ info->battery_life, info->time ); ++#endif ++ return; ++} ++ ++module_init(ucb1x00_simpad_init); ++module_exit(ucb1x00_simpad_exit); ++ ++ ++MODULE_AUTHOR("Juergen Messerer <juergen.messerer@freesurf.ch>"); ++MODULE_DESCRIPTION("SIMpad noddy testing only example ADC driver"); ++MODULE_LICENSE("GPL"); +diff -uNr linux-2.6.21.vanilla/drivers/mfd/ucb1x00-simpad.h linux-2.6.21/drivers/mfd/ucb1x00-simpad.h +--- linux-2.6.21.vanilla/drivers/mfd/ucb1x00-simpad.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/mfd/ucb1x00-simpad.h 2007-05-30 18:35:48.000000000 +0200 +@@ -0,0 +1,86 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-simpad.h ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ */ ++#ifndef UCB1300_SIMPAD_H ++#define UCB1300_SIMPAD_H ++ ++/* ++ * Conversion from AD -> mV ++ * 7.5V = 1023 7.33137mV/Digit ++ * ++ * 400 Units == 9.7V ++ * a = ADC value ++ * 2007/03/24 mrdata: ++ * according UCB1300 datasheet ADC error max. 3 LSB ++ * 5-95% of full scale -> 3*7.33137mV = 21.99mV ++ * // old ++ * 21 = ADC error ++ * 12600 = Divident to get 2*7.3242 ++ * 860 = Divider to get 2*7.3242 ++ * 170 = Voltagedrop over ++ * ++ * // new ++ * 3 = ADC error ++ * 12610 = Divident to get 2*7.33139 ++ * 860 = Divider to get 2*7.33139 ++ * 170 = Voltagedrop over ++ */ ++// #define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170) ++#define CALIBRATE_BATTERY(a) ((((a + 3)*12610)/860) + 170) ++ ++/* ++ * We have two types of batteries a small and a large one ++ * To get the right value we to distinguish between those two ++ * 450 Units == 15 V ++ */ ++#ifdef SMALL_BATTERY ++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 51) ++#define MIN_SUPPLY 8500 /* Less then 8.5V means no powersupply */ ++#else ++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45) ++//#define MIN_SUPPLY 14000 /* Less then 14V means no powersupply */ ++#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */ ++#endif ++ ++/* ++ * Charging Current ++ * if value is >= 50 then charging is on ++ */ ++// #define CALIBRATE_CHARGING(a) (((a) * 1000) / (152/4)) ++#define CALIBRATE_CHARGING(a) (a) ++//#define CHARGING_LED_LEVEL 50 ++ ++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// type small battery ++#define CHARGING_LED_LEVEL 12 ++#define CHARGING_MAX_LEVEL 120 ++#define BATT_FULL 8100 ++#define BATT_LOW 7300 ++#define BATT_CRITICAL 6700 ++#define BATT_EMPTY 6400 ++ ++#else // CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// type large battery ++// because of ADC error CHARGING_LED_LEVEL can changed ++// from 27 to 28 ++#define CHARGING_LED_LEVEL 27 ++#define CHARGING_MAX_LEVEL 265 ++// BATT_FULL with SIMPAD_AC_STATUS_AC_OFFLINE ++#define BATT_FULL 8100 ++#define BATT_LOW 7400 ++#define BATT_CRITICAL 6800 ++#define BATT_EMPTY 6500 ++ ++#endif // CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// int simpad_get_battery(struct simpad_battery_apm *bstat); ++ ++#endif +diff -uNr linux-2.6.21.vanilla/include/asm-arm/arch-sa1100/simpad_pm.h linux-2.6.21/include/asm-arm/arch-sa1100/simpad_pm.h +--- linux-2.6.21.vanilla/include/asm-arm/arch-sa1100/simpad_pm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/include/asm-arm/arch-sa1100/simpad_pm.h 2007-05-30 18:36:07.000000000 +0200 +@@ -0,0 +1,236 @@ ++/* ++* Abstraction interface for microcontroller connection to rest of system ++* ++* Copyright 2003 Peter Pregler ++* Copyright 2000,1 Compaq Computer Corporation. ++* ++* Use consistent with the GNU GPL is permitted, ++* provided that this copyright notice is ++* preserved in its entirety in all copies and derived works. ++* ++* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, ++* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS ++* FITNESS FOR ANY PARTICULAR PURPOSE. ++* ++* Author: Peter Pregler (based on work for ipaq by Andrew Christian) ++* ++*/ ++ ++#ifndef __SIMPAD_HAL_H ++#define __SIMPAD_HAL_H ++ ++#include <linux/apm-emulation.h> ++ ++struct simpad_battery_apm { ++ unsigned char ac_status; /* line connected yes/no */ ++ unsigned char status; /* battery loading yes/no */ ++ unsigned char percentage; /* percentage loaded */ ++ unsigned short life; /* life till empty */ ++}; ++ ++extern void simpad_apm_get_power_status(struct apm_power_info *); ++ ++// extern int simpad_get_battery(struct simpad_battery_apm *bstat); ++ ++/* These should match the apm_bios.h definitions */ ++#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00 ++#define SIMPAD_AC_STATUS_AC_ONLINE 0x01 ++#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */ ++#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff ++ ++ ++/* These bitfields are rarely "or'd" together */ ++#define SIMPAD_BATT_STATUS_HIGH 0x01 ++#define SIMPAD_BATT_STATUS_LOW 0x02 ++#define SIMPAD_BATT_STATUS_CRITICAL 0x04 ++#define SIMPAD_BATT_STATUS_CHARGING 0x08 ++#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10 ++#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */ ++#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */ ++#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */ ++#define SIMPAD_BATT_STATUS_NOBATT 0x80 ++#define SIMPAD_BATT_STATUS_UNKNOWN 0xff ++ ++#if 0 // FIXME ++#include <linux/simpad_ts.h> ++ ++enum simpad_asset_type { ++ ASSET_TCHAR = 0, ++ ASSET_SHORT, ++ ASSET_LONG ++}; ++ ++#define TTYPE(_type) (((unsigned int)_type) << 8) ++#define TCHAR(_len) (TTYPE(ASSET_TCHAR) | (_len)) ++#define TSHORT TTYPE(ASSET_SHORT) ++#define TLONG TTYPE(ASSET_LONG) ++#define ASSET(_type,_num) ((((unsigned int)_type)<<16) | (_num)) ++ ++#define ASSET_HM_VERSION ASSET( TCHAR(10), 0 ) /* 1.1, 1.2 */ ++#define ASSET_SERIAL_NUMBER ASSET( TCHAR(40), 1 ) /* Unique iPAQ serial number */ ++#define ASSET_MODULE_ID ASSET( TCHAR(20), 2 ) /* E.g., "iPAQ 3700" */ ++#define ASSET_PRODUCT_REVISION ASSET( TCHAR(10), 3 ) /* 1.0, 2.0 */ ++#define ASSET_PRODUCT_ID ASSET( TSHORT, 4 ) /* 2 = Palm-sized computer */ ++#define ASSET_FRAME_RATE ASSET( TSHORT, 5 ) ++#define ASSET_PAGE_MODE ASSET( TSHORT, 6 ) /* 0 = Flash memory */ ++#define ASSET_COUNTRY_ID ASSET( TSHORT, 7 ) /* 0 = USA */ ++#define ASSET_IS_COLOR_DISPLAY ASSET( TSHORT, 8 ) /* Boolean, 1 = yes */ ++#define ASSET_ROM_SIZE ASSET( TSHORT, 9 ) /* 16, 32 */ ++#define ASSET_RAM_SIZE ASSET( TSHORT, 10 ) /* 32768 */ ++#define ASSET_HORIZONTAL_PIXELS ASSET( TSHORT, 11 ) /* 240 */ ++#define ASSET_VERTICAL_PIXELS ASSET( TSHORT, 12 ) /* 320 */ ++ ++#define ASSET_TYPE(_asset) (((_asset)&0xff000000)>>24) ++#define ASSET_TCHAR_LEN(_asset) (((_asset)&0x00ff0000)>>16) ++#define ASSET_NUMBER(_asset) ((_asset)&0x0000ffff) ++ ++#define MAX_TCHAR_LEN 40 ++ ++struct simpad_asset { ++ unsigned int type; ++ union { ++ unsigned char tchar[ MAX_TCHAR_LEN ]; ++ unsigned short vshort; ++ unsigned long vlong; ++ } a; ++}; ++ ++/******************************************************************** ++ * Interface to the hardware-type specific functions ++ * ++ * get_version Read the version number of the microcontroller on the option pack SPI bus ++ * spi_read Reads from the serial EEPROM memory on the option pack SPI bus ++ * spi_write Write to the serial EEPROM memory on the option pack SPI bus ++ * get_option_detect Returns whether or not an option pack is present ++ * ++ * get_thermal_sensor Return measured temperature of the unit, in units of 0.125 deg C ++ * set_notify_led Turns on, off, or blinks the Green LED ++ * read_light_sensor Returns the value of the front light sensor ++ * get_battery Returns the current voltage and charging state of all batteries ++ * audio_clock Sets the audio CODEC to run at a particular rate ++ * audio_power Turns on/off audio CODEC (internally calls audio_clock) ++ * audio_mute Mutes the audio CODEC ++ * asset_read Extracts PocketPC-style asset information from persistent memory ++ * backlight_control Adjusts the backlight level (only on/off for 3100) ++ * ++ * ++ * iPAQ 3100 only ++ * ============== ++ * codec_control Reset/mute/control level of 3100 audio codec ++ * contrast_control Adjusts the contrast level (only for 3100) ++ * ++ * iPAQ 3600, 3700 only ++ * ==================== ++ * eeprom_read Reads from the asset information on the eeprom of a 3600 (deprecated) ++ * eeprom_write Writes to the asset information on the eeprom (deprecated) ++ * ++ * The interfaces to the EEPROM functions are maintained only because the simpad_ts driver has ++ * a deprecated ioctl call for them. They are used internally by the "asset_read" function. ++ * ++ * iPAQ 3800, 3900 only ++ * ==================== ++ * set_ebat Tells enhanced PCMCIA sleeves that this iPAQ can handle ++ * a wider voltage range (applies to 3800, 3900) ++ * ++ *********************************************************************/ ++ ++struct simpad_hal_ops { ++ /* Functions provided by the underlying hardware */ ++ int (*get_version)( struct simpad_ts_version * ); ++ int (*eeprom_read)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*eeprom_write)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*get_thermal_sensor)( unsigned short * ); ++ int (*set_notify_led)( unsigned char mode, unsigned char duration, ++ unsigned char ontime, unsigned char offtime ); ++ int (*read_light_sensor)( unsigned char *result ); ++ int (*get_battery)( struct simpad_battery * ); ++ int (*spi_read)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*spi_write)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*codec_control)( unsigned char, unsigned char ); ++ int (*get_option_detect)( int *result ); ++ int (*audio_clock)( long samplerate ); ++ int (*audio_power)( long samplerate ); ++ int (*audio_mute)( int mute ); ++ int (*asset_read)( struct simpad_asset *asset ); ++ int (*set_ebat)( void ); ++ ++ /* Functions indirectly provided by the underlying hardware */ ++ int (*backlight_control)( enum flite_pwr power, unsigned char level ); ++ int (*contrast_control)( unsigned char level ); ++ ++ /* for module use counting */ ++ struct module *owner; ++}; ++ ++/* Used by the device-specific hardware module to register itself */ ++extern int simpad_hal_register_interface( struct simpad_hal_ops *ops ); ++extern void simpad_hal_unregister_interface( struct simpad_hal_ops *ops ); ++ ++/* ++ * Calls into HAL from the device-specific hardware module ++ * These run at interrupt time ++ */ ++extern void simpad_hal_keypress( unsigned char key ); ++extern void simpad_hal_touchpanel( unsigned short x, unsigned short y, int down ); ++extern void simpad_hal_option_detect( int present ); ++ ++/* Callbacks registered by device drivers */ ++struct simpad_driver_ops { ++ void (*keypress)( unsigned char key ); ++ void (*touchpanel)( unsigned short x, unsigned short y, int down ); ++ void (*option_detect)( int present ); ++}; ++ ++extern int simpad_hal_register_driver( struct simpad_driver_ops * ); ++extern void simpad_hal_unregister_driver( struct simpad_driver_ops * ); ++ ++ ++/* Calls into HAL from device drivers and other kernel modules */ ++extern void simpad_get_flite( struct simpad_ts_backlight *bl ); ++extern void simpad_get_contrast( unsigned char *contrast ); ++extern int simpad_set_flite( enum flite_pwr pwr, unsigned char brightness ); ++extern int simpad_set_contrast( unsigned char contrast ); ++extern int simpad_toggle_frontlight( void ); ++ ++extern int simpad_apm_get_power_status(unsigned char *ac_line_status, unsigned char *battery_status, ++ unsigned char *battery_flag, unsigned char *battery_percentage, ++ unsigned short *battery_life); ++ ++extern struct simpad_hal_ops *simpad_hal_ops; ++ ++/* Do not use this macro in driver files - instead, use the inline functions defined below */ ++#define CALL_HAL( f, args... ) \ ++ { int __result = -EIO; \ ++ if ( simpad_hal_ops && simpad_hal_ops->f ) { \ ++ __MOD_INC_USE_COUNT(simpad_hal_ops->owner); \ ++ __result = simpad_hal_ops->f(args); \ ++ __MOD_DEC_USE_COUNT(simpad_hal_ops->owner); \ ++ } \ ++ return __result; } ++ ++#define HFUNC static __inline__ int ++ ++/* The eeprom_read/write address + len has a maximum value of 512. Both must be even numbers */ ++HFUNC simpad_eeprom_read( u16 addr, u8 *data, u16 len ) CALL_HAL(eeprom_read,addr,data,len) ++HFUNC simpad_eeprom_write( u16 addr, u8 *data, u16 len) CALL_HAL(eeprom_write,addr,data,len) ++HFUNC simpad_spi_read( u8 addr, u8 *data, u16 len) CALL_HAL(spi_read,addr,data,len) ++HFUNC simpad_spi_write( u8 addr, u8 *data, u16 len) CALL_HAL(spi_write,addr,data,len) ++HFUNC simpad_get_version( struct simpad_ts_version *v ) CALL_HAL(get_version,v) ++HFUNC simpad_get_thermal_sensor( u16 *thermal ) CALL_HAL(get_thermal_sensor,thermal) ++HFUNC simpad_set_led( u8 mode, u8 dur, u8 ont, u8 offt ) CALL_HAL(set_notify_led, mode, dur, ont, offt) ++HFUNC simpad_get_light_sensor( u8 *result ) CALL_HAL(read_light_sensor,result) ++HFUNC simpad_get_battery( struct simpad_battery *bat ) CALL_HAL(get_battery,bat) ++HFUNC simpad_get_option_detect( int *result) CALL_HAL(get_option_detect,result) ++HFUNC simpad_audio_clock( long samplerate ) CALL_HAL(audio_clock,samplerate) ++HFUNC simpad_audio_power( long samplerate ) CALL_HAL(audio_power,samplerate) ++HFUNC simpad_audio_mute( int mute ) CALL_HAL(audio_mute,mute) ++HFUNC simpad_asset_read( struct simpad_asset *asset ) CALL_HAL(asset_read,asset) ++HFUNC simpad_set_ebat( void ) CALL_HAL(set_ebat) ++ ++/* Don't use these functions directly - rather, call {get,set}_{flite,contrast} */ ++ /* Functions indirectly provided by the underlying hardware */ ++HFUNC simpad_backlight_control( enum flite_pwr p, u8 v ) CALL_HAL(backlight_control,p,v) ++HFUNC simpad_contrast_control( u8 level ) CALL_HAL(contrast_control,level) ++ ++#endif ++#endif diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-cs3-simpad.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-cs3-simpad.patch new file mode 100644 index 0000000000..cfb9aad906 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-cs3-simpad.patch @@ -0,0 +1,184 @@ +diff -uNr linux-2.6.21.vanilla/arch/arm/mach-sa1100/Makefile linux-2.6.21/arch/arm/mach-sa1100/Makefile +--- linux-2.6.21.vanilla/arch/arm/mach-sa1100/Makefile 2007-05-29 21:34:59.000000000 +0200 ++++ linux-2.6.21/arch/arm/mach-sa1100/Makefile 2007-05-30 17:44:04.000000000 +0200 +@@ -40,6 +40,7 @@ + obj-$(CONFIG_SA1100_SHANNON) += shannon.o + + obj-$(CONFIG_SA1100_SIMPAD) += simpad.o ++obj-$(CONFIG_SA1100_SIMPAD) += cs3-simpad.o + led-$(CONFIG_SA1100_SIMPAD) += leds-simpad.o + + # LEDs support +diff -uNr linux-2.6.21.vanilla/arch/arm/mach-sa1100/cs3-simpad.c linux-2.6.21/arch/arm/mach-sa1100/cs3-simpad.c +--- linux-2.6.21.vanilla/arch/arm/mach-sa1100/cs3-simpad.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/arch/arm/mach-sa1100/cs3-simpad.c 2007-05-30 17:45:51.000000000 +0200 +@@ -0,0 +1,169 @@ ++/* ++ * simpad-cs3.c ++ * ++ * This driver shows the GPIO states of the cs3 latch. You can also ++ * switch some GPIOS. ++ * ++ * (c) 2007 Bernhard Guillon <Bernhard.Guillon@OpenSIMpad.org> ++ * ++ * You may use this code as per GPL version 2 ++ * ++ * Some parts are based on battery.c ++ * ++ * mrdata: -added cs3_ro support ++ * -added preprocessor stuff ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/device.h> ++ ++#include <asm/arch/simpad.h> ++ ++extern long get_cs3_ro(void); ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int value); ++extern void clear_cs3_bit(int value); ++ ++struct cs3 { ++ struct class_device class_dev; ++ const char *name; ++ char *id; ++ int type; ++}; ++ ++struct cs3 cs3 ={ ++ .name = "latch_cs3", ++}; ++ ++ ++#define CS3_STORE_ATTR(namek,nameg) \ ++static ssize_t namek##_store (struct class_device *cdev, const char * buf, size_t count) \ ++{ \ ++ char val; \ ++ if (sscanf(buf, "%c",&val) != 1) \ ++ return -EINVAL; \ ++ if (val == '1') \ ++ set_cs3_bit(nameg); \ ++ else if (val == '0') \ ++ clear_cs3_bit(nameg); \ ++ return strlen(buf); \ ++} ++ ++CS3_STORE_ATTR(display_on, DISPLAY_ON); ++CS3_STORE_ATTR(dect_power_on, DECT_POWER_ON); ++CS3_STORE_ATTR(irda_sd, IRDA_SD); ++CS3_STORE_ATTR(sd_mediaq, SD_MEDIAQ); ++CS3_STORE_ATTR(led2_on, LED2_ON); ++CS3_STORE_ATTR(irda_mode, IRDA_MODE); ++CS3_STORE_ATTR(reset_simcard, RESET_SIMCARD); ++ ++ ++#define CS3_ATTR(shadro,namek,nameg,mode,store) \ ++static ssize_t namek##_show(struct class_device *class_dev, char *buf) \ ++{ \ ++ if (get_cs3_##shadro() & nameg ) \ ++ return sprintf(buf, "1\n"); \ ++ else \ ++ return sprintf(buf, "0\n"); \ ++} \ ++static CLASS_DEVICE_ATTR(namek, mode, namek##_show, store) ++ ++CS3_ATTR(shadow, vcc_5v_en, VCC_5V_EN, 0444, NULL); ++CS3_ATTR(shadow, vcc_3v_en, VCC_3V_EN, 0444, NULL); ++CS3_ATTR(shadow, en1, EN1, 0444, NULL); ++CS3_ATTR(shadow, en0, EN0, 0444, NULL); ++CS3_ATTR(shadow, display_on, DISPLAY_ON, 0664, display_on_store); ++CS3_ATTR(shadow, pcmcia_buff_dis, PCMCIA_BUFF_DIS, 0444, NULL); ++CS3_ATTR(shadow, mq_reset, MQ_RESET, 0444, NULL); ++CS3_ATTR(shadow, pcmcia_reset, PCMCIA_RESET, 0444, NULL); ++CS3_ATTR(shadow, dect_power_on, DECT_POWER_ON, 0664, dect_power_on_store); ++CS3_ATTR(shadow, irda_sd, IRDA_SD, 0664, irda_sd_store); ++CS3_ATTR(shadow, rs232_on, RS232_ON, 0444, NULL); ++CS3_ATTR(shadow, sd_mediaq, SD_MEDIAQ, 0664, sd_mediaq_store); ++CS3_ATTR(shadow, led2_on, LED2_ON, 0664, led2_on_store); ++CS3_ATTR(shadow, irda_mode, IRDA_MODE, 0664, irda_mode_store); ++CS3_ATTR(shadow, enable_5v, ENABLE_5V, 0444, NULL); ++CS3_ATTR(shadow, reset_simcard, RESET_SIMCARD, 0664, reset_simcard_store); ++CS3_ATTR(ro, pcmcia_bvd1, PCMCIA_BVD1, 0444, NULL); ++CS3_ATTR(ro, pcmcia_bvd2, PCMCIA_BVD2, 0444, NULL); ++CS3_ATTR(ro, pcmcia_vs1, PCMCIA_VS1, 0444, NULL); ++CS3_ATTR(ro, pcmcia_vs2, PCMCIA_VS2, 0444, NULL); ++CS3_ATTR(ro, lock_ind, LOCK_IND, 0444, NULL); ++CS3_ATTR(ro, charging_state, CHARGING_STATE, 0444, NULL); ++CS3_ATTR(ro, pcmcia_short, PCMCIA_SHORT, 0444, NULL); ++ ++static struct class simpad_gpios_class = { ++ .name = "simpad", ++}; ++ ++#define create_entry_conditional(namek) \ ++ rc = class_device_create_file(&cs3->class_dev, &class_device_attr_##namek); \ ++ if (rc) goto out; \ ++ ++static int register_cs3_latch(struct cs3 *cs3) ++{ ++ int rc = 0; ++ cs3->class_dev.class = &simpad_gpios_class; ++ strcpy(cs3->class_dev.class_id, cs3->name); ++ rc = class_device_register(&cs3->class_dev); ++ if(rc) ++ goto out; ++ ++ create_entry_conditional(vcc_5v_en); ++ create_entry_conditional(vcc_3v_en); ++ create_entry_conditional(en1); ++ create_entry_conditional(en0); ++ create_entry_conditional(display_on); ++ create_entry_conditional(pcmcia_buff_dis); ++ create_entry_conditional(mq_reset); ++ create_entry_conditional(pcmcia_reset); ++ create_entry_conditional(dect_power_on); ++ create_entry_conditional(irda_sd); ++ create_entry_conditional(rs232_on); ++ create_entry_conditional(sd_mediaq); ++ create_entry_conditional(led2_on); ++ create_entry_conditional(irda_mode); ++ create_entry_conditional(enable_5v); ++ create_entry_conditional(reset_simcard); ++ create_entry_conditional(pcmcia_bvd1); ++ create_entry_conditional(pcmcia_bvd2); ++ create_entry_conditional(pcmcia_vs1); ++ create_entry_conditional(pcmcia_vs2); ++ create_entry_conditional(lock_ind); ++ create_entry_conditional(charging_state); ++ create_entry_conditional(pcmcia_short); ++ ++out: ++ return rc; ++} ++ ++static int __init simpad_gpios_class_init(void) ++{ ++ int rc = 0; ++ ++ rc = class_register(&simpad_gpios_class); ++ ++ if(rc != 0) ++ { ++ printk(KERN_ERR "cs3 latch class failed to register properly\n"); ++ return rc; ++ } ++ ++ rc = register_cs3_latch(&cs3); ++ return rc; ++} ++ ++static void __exit simpad_gpios_class_exit(void) ++{ ++ class_unregister(&simpad_gpios_class); ++} ++ ++module_init(simpad_gpios_class_init); ++module_exit(simpad_gpios_class_exit); ++ ++MODULE_AUTHOR("Bernhard Guillon"); ++MODULE_DESCRIPTION("CS3_latch driver"); ++MODULE_LICENSE("GPL"); diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-mq200.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-mq200.patch new file mode 100644 index 0000000000..5726779ad6 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-mq200.patch @@ -0,0 +1,2511 @@ +diff -uNr linux-2.6.21.vanilla/drivers/video/Kconfig linux-2.6.21/drivers/video/Kconfig +--- linux-2.6.21.vanilla/drivers/video/Kconfig 2007-05-01 16:40:48.000000000 +0200 ++++ linux-2.6.21/drivers/video/Kconfig 2007-05-01 17:02:17.000000000 +0200 +@@ -139,7 +139,7 @@ + This is particularly important to one driver, matroxfb. If + unsure, say N. + +-comment "Frame buffer hardware drivers" ++comment "Frambuffer hardware drivers" + depends on FB + + config FB_CIRRUS +@@ -1120,6 +1120,15 @@ + ---help--- + Driver for graphics boards with S3 Trio / S3 Virge chip. + ++config FB_MQ200 ++ bool "MQ200 Driver" ++ depends on (FB = y) && ARM && ARCH_SA1100 ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ help ++ This is a MQ200 driver tested only on Siemens SIMpads. ++ + config FB_SAVAGE + tristate "S3 Savage support" + depends on FB && PCI && EXPERIMENTAL +diff -uNr linux-2.6.21.vanilla/drivers/video/Makefile linux-2.6.21/drivers/video/Makefile +--- linux-2.6.21.vanilla/drivers/video/Makefile 2007-05-01 16:40:48.000000000 +0200 ++++ linux-2.6.21/drivers/video/Makefile 2007-05-01 17:02:17.000000000 +0200 +@@ -29,6 +29,7 @@ + obj-$(CONFIG_FB_PM2) += pm2fb.o + obj-$(CONFIG_FB_PM3) += pm3fb.o + ++obj-$(CONFIG_FB_MQ200) += mq200/ + obj-$(CONFIG_FB_MATROX) += matrox/ + obj-$(CONFIG_FB_RIVA) += riva/ vgastate.o + obj-$(CONFIG_FB_NVIDIA) += nvidia/ +diff -uNr linux-2.6.21.vanilla/drivers/video/backlight/Kconfig linux-2.6.21/drivers/video/backlight/Kconfig +--- linux-2.6.21.vanilla/drivers/video/backlight/Kconfig 2007-05-01 16:40:48.000000000 +0200 ++++ linux-2.6.21/drivers/video/backlight/Kconfig 2007-05-01 17:02:17.000000000 +0200 +@@ -63,3 +63,20 @@ + help + If you have a Frontpath ProGear say Y to enable the + backlight driver. ++ ++config BACKLIGHT_SIMPAD ++ tristate "SIMpad MQ200 Backlight driver" ++ depends on SA1100_SIMPAD && BACKLIGHT_CLASS_DEVICE ++ default y ++ help ++ If you have a Siemens SIMpad say Y to enable the ++ backlight driver. ++ ++config LCD_SIMPAD ++ tristate "SIMpad MQ200 LCD driver" ++ depends on SA1100_SIMPAD && LCD_CLASS_DEVICE ++ default y ++ help ++ If you have a Siemens SIMpad say Y to enable the ++ LCD driver. ++ +diff -uNr linux-2.6.21.vanilla/drivers/video/backlight/Makefile linux-2.6.21/drivers/video/backlight/Makefile +--- linux-2.6.21.vanilla/drivers/video/backlight/Makefile 2007-05-01 16:40:48.000000000 +0200 ++++ linux-2.6.21/drivers/video/backlight/Makefile 2007-05-01 17:02:17.000000000 +0200 +@@ -6,3 +6,5 @@ + obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o + obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o + obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o ++obj-$(CONFIG_BACKLIGHT_SIMPAD) += simpad_bl.o ++obj-$(CONFIG_LCD_SIMPAD) += simpad_lcd.o +diff -uNr linux-2.6.21.vanilla/drivers/video/backlight/simpad_bl.c linux-2.6.21/drivers/video/backlight/simpad_bl.c +--- linux-2.6.21.vanilla/drivers/video/backlight/simpad_bl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/video/backlight/simpad_bl.c 2007-05-01 17:02:17.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* ++ * GPLv2 <zecke@handhelds.org ++ * ++ * Implementation of the backlight_driver for ++ * the mq200 framebuffer ++ * ++ * 2007/03/17 mrdata: ++ * - small changes simpad_bl_get_brightness() ++ * simpad_bl_set_brightness() ++ * - new function simpad_bl_update_status() ++ * - changed struct backlight_properties simpad_bl_props() ++ * to new one ++ * - changed __init simpad_bl_init() -> backlight_device_register ++ * ++ * 2007/03/24 mrdata ++ * - added .brightness=127 in ++ * struct backlight_properties simpad_bl_props() ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/fb.h> ++#include <linux/backlight.h> ++ ++#include <asm/types.h> ++#include <asm/hardware.h> ++#include <asm/io.h> ++ ++#include "../mq200/mq200_data.h" ++ ++#define SIMPAD_BACKLIGHT_MASK 0x00a10044 ++#define SIMPAD_DEFAULT_INTENSITY 127 ++#define SIMPAD_MAX_INTENSITY 254 ++#define REGISTER_BASE 0xf2e00000 ++ ++static int simpad_bl_suspended; ++static int current_intensity = 0; ++ ++static void simpad_bl_send_intensity(struct backlight_device *bd) ++{ ++ int intensity = bd->props.brightness; ++ ++ union fp0fr fp0fr; ++ unsigned long dutyCycle, pwmcontrol; ++ ++ if (intensity > SIMPAD_MAX_INTENSITY) ++ intensity = SIMPAD_MAX_INTENSITY; ++ ++ if (bd->props.power != FB_BLANK_UNBLANK) ++ intensity = 0; ++ ++ if (bd->props.fb_blank != FB_BLANK_UNBLANK) ++ intensity = 0; ++ ++ if (simpad_bl_suspended) ++ intensity = 0; ++ ++ if (intensity != current_intensity) ++ { ++ /* ++ * Determine dutyCycle. ++ * Note: the lower the value, the brighter the display! ++ */ ++ ++ dutyCycle = SIMPAD_MAX_INTENSITY - intensity; ++ ++ /* ++ * Configure PWM0 (source clock = oscillator clock, pwm always enabled, ++ * zero, clock pre-divider = 4) pwm frequency = 12.0kHz ++ */ ++ ++ fp0fr.whole = readl(FP0FR(REGISTER_BASE)); ++ pwmcontrol = fp0fr.whole & 0xffff00ff; ++ fp0fr.whole &= 0xffffff00; ++ fp0fr.whole |= 0x00000044; ++ writel(fp0fr.whole, FP0FR(REGISTER_BASE)); ++ ++ /* Write to pwm duty cycle register. */ ++ fp0fr.whole = dutyCycle << 8; ++ fp0fr.whole &= 0x0000ff00; ++ fp0fr.whole |= pwmcontrol; ++ writel(fp0fr.whole, FP0FR(REGISTER_BASE)); ++ ++ current_intensity = intensity; ++ } ++} ++ ++ ++#ifdef CONFIG_PM ++static int simpad_bl_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ simpad_bl_suspended = 1; ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++ ++static int simpad_bl_resume(struct platform_device *pdev) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ simpad_bl_suspended = 0; ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++#else ++#define simpad_bl_suspend NULL ++#define simpad_bl_resume NULL ++#endif ++ ++ ++static int simpad_bl_set_intensity(struct backlight_device *bd) ++{ ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++ ++ ++static int simpad_bl_get_intensity(struct backlight_device *bd) ++{ ++ return current_intensity; ++} ++ ++ ++static struct backlight_ops simpad_bl_ops = { ++ .get_brightness = simpad_bl_get_intensity, ++ .update_status = simpad_bl_set_intensity, ++}; ++ ++ ++static int __init simpad_bl_probe(struct platform_device *pdev) ++{ ++ struct backlight_device *bd; ++ ++ bd = backlight_device_register("simpad-mq200-bl", &pdev->dev, NULL, &simpad_bl_ops); ++ ++ if (IS_ERR (bd)) ++ return PTR_ERR (bd); ++ ++ platform_set_drvdata(pdev, bd); ++ ++ bd->props.max_brightness = SIMPAD_MAX_INTENSITY; ++ bd->props.brightness = SIMPAD_DEFAULT_INTENSITY; ++ simpad_bl_send_intensity(bd); ++ ++ return 0; ++} ++ ++ ++static int simpad_bl_remove(struct platform_device *pdev) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ bd->props.brightness = 0; ++ bd->props.power = 0; ++ simpad_bl_send_intensity(bd); ++ ++ backlight_device_unregister(bd); ++ ++ return 0; ++} ++ ++static struct platform_driver simpad_bl_driver = { ++ .probe = simpad_bl_probe, ++ .remove = simpad_bl_remove, ++ .suspend = simpad_bl_suspend, ++ .resume = simpad_bl_resume, ++ .driver = { ++ .name = "simpad-mq200-bl", ++ }, ++}; ++ ++static struct platform_device *simpad_bl_device = NULL; ++ ++static int __init simpad_bl_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&simpad_bl_driver); ++ if (!ret) { ++ simpad_bl_device = platform_device_alloc("simpad-mq200-bl", -1); ++ if (!simpad_bl_device) ++ return -ENOMEM; ++ ++ ret = platform_device_add(simpad_bl_device); ++ ++ if (ret) { ++ platform_device_put(simpad_bl_device); ++ platform_driver_unregister(&simpad_bl_driver); ++ } ++ } ++ return ret; ++} ++ ++static void __exit simpad_bl_exit(void) ++{ ++ platform_device_unregister(simpad_bl_device); ++ platform_driver_unregister(&simpad_bl_driver); ++} ++ ++ ++module_init(simpad_bl_init); ++module_exit(simpad_bl_exit); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); ++MODULE_LICENSE("GPL"); +diff -uNr linux-2.6.21.vanilla/drivers/video/backlight/simpad_lcd.c linux-2.6.21/drivers/video/backlight/simpad_lcd.c +--- linux-2.6.21.vanilla/drivers/video/backlight/simpad_lcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/video/backlight/simpad_lcd.c 2007-05-01 17:02:17.000000000 +0200 +@@ -0,0 +1,170 @@ ++/* ++ * GPLv2 <zecke@handhelds.org ++ * ++ * Implementation of the lcd_driver for the mq200 framebuffer ++ * ++ * 2007/03/24 mrdata: ++ * - added simpad_lcd_get_contrast() ++ * - added simpad_lcd_set_contrast() ++ * - modify struct lcd_properties simpad_lcd_props ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/fb.h> ++#include <linux/lcd.h> ++ ++#include <asm/arch/simpad.h> ++#include <asm/hardware.h> ++ ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int); ++extern void clear_cs3_bit(int); ++ ++#define UNUSED(x) x=x ++ ++static int simpad_lcd_get_power(struct lcd_device* dev) ++{ ++ UNUSED(dev); ++ ++ return (get_cs3_shadow() & DISPLAY_ON) ? 0 : 4; ++} ++ ++static int simpad_lcd_set_power(struct lcd_device* dev, int power) ++{ ++ UNUSED(dev); ++ ++ if( power == 4 ) ++ clear_cs3_bit(DISPLAY_ON); ++ else ++ set_cs3_bit(DISPLAY_ON); ++ ++ return 0; ++} ++ ++static int simpad_lcd_get_contrast(struct lcd_device* dev) ++{ ++ UNUSED(dev); ++ ++ return 0; ++} ++ ++static int simpad_lcd_set_contrast(struct lcd_device* dev, int contrast) ++{ ++ UNUSED(dev); ++ ++ UNUSED(contrast); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int simpad_lcd_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ UNUSED(state); ++ static int ret; ++ ++ struct lcd_device* ld; ++ ++ ld = platform_get_drvdata(pdev); ++ ++ ret = simpad_lcd_set_power(ld, 4); ++ ++ return ret; ++} ++ ++static int simpad_lcd_resume(struct platform_device *pdev) ++{ ++ struct lcd_device *ld; ++ static int ret; ++ ++ ld = platform_get_drvdata(pdev); ++ ++ ret = simpad_lcd_set_power(ld, 0); ++ ++ return ret; ++} ++#else ++#define simpad_lcd_suspend NULL ++#define simpad_lcd_resume NULL ++#endif ++ ++static struct lcd_properties simpad_lcd_props = { ++ .max_contrast = 0, ++}; ++ ++ ++static struct lcd_ops simpad_lcd_ops = { ++ .get_power = simpad_lcd_get_power, ++ .set_power = simpad_lcd_set_power, ++ .get_contrast = simpad_lcd_get_contrast, ++ .set_contrast = simpad_lcd_set_contrast, ++}; ++ ++static int __init simpad_lcd_probe(struct platform_device *pdev) ++{ ++ struct lcd_device *ld; ++ ++ ld = lcd_device_register ("simpad-mq200-lcd", &pdev->dev, &simpad_lcd_ops); ++ ++ if (IS_ERR(ld)) ++ return PTR_ERR(ld); ++ ++ platform_set_drvdata(pdev, ld); ++ ++ ld->props.max_contrast = 0; ++ ++ return 0; ++} ++ ++static int simpad_lcd_remove(struct platform_device *pdev) ++{ ++ struct lcd_device *ld = platform_get_drvdata(pdev); ++ ++ lcd_device_unregister(ld); ++ ++ return 0; ++} ++ ++static struct platform_driver simpad_lcd_driver = { ++ .probe = simpad_lcd_probe, ++ .remove = simpad_lcd_remove, ++ .suspend = simpad_lcd_suspend, ++ .resume = simpad_lcd_resume, ++ .driver = { ++ .name = "simpad-mq200-lcd", ++ }, ++}; ++ ++static struct platform_device *simpad_lcd_device = NULL; ++ ++static int __init simpad_lcd_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&simpad_lcd_driver); ++ if (!ret) { ++ simpad_lcd_device = platform_device_alloc("simpad-mq200-lcd", -1); ++ if (!simpad_lcd_device) ++ return -ENOMEM; ++ ++ ret = platform_device_add(simpad_lcd_device); ++ ++ if (ret) { ++ platform_device_put(simpad_lcd_device); ++ platform_driver_unregister(&simpad_lcd_driver); ++ } ++ } ++ return ret; ++} ++ ++static void __exit simpad_lcd_exit(void) { ++ platform_driver_unregister(&simpad_lcd_driver); ++ platform_device_unregister(simpad_lcd_device); ++} ++ ++module_init(simpad_lcd_init); ++module_exit(simpad_lcd_exit); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); ++MODULE_LICENSE("GPL"); +diff -uNr linux-2.6.21.vanilla/drivers/video/mq200/Makefile linux-2.6.21/drivers/video/mq200/Makefile +--- linux-2.6.21.vanilla/drivers/video/mq200/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/video/mq200/Makefile 2007-05-01 17:02:17.000000000 +0200 +@@ -0,0 +1,6 @@ ++# Makefile for mq200 video driver ++# 4 Aug 2003, Holger Hans Peter Freyther ++# ++ ++obj-$(CONFIG_FB_MQ200) += mq_skeleton.o mq_external.o ++ +diff -uNr linux-2.6.21.vanilla/drivers/video/mq200/mq200_data.h linux-2.6.21/drivers/video/mq200/mq200_data.h +--- linux-2.6.21.vanilla/drivers/video/mq200/mq200_data.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/video/mq200/mq200_data.h 2007-05-01 17:02:17.000000000 +0200 +@@ -0,0 +1,1120 @@ ++/* ++ * From ucLinux mq200fb.c and mq200fb.h ++ * ++ * 2007/03/11 mrdata: ++ * insert registers for graphics controller 2 module ++ */ ++ ++#ifndef __MQ200_FB_H__ ++#define __MQ200_FB_H__ ++ ++struct mq200_io_regions { ++ u32 fb_size; /* framebuffer size */ ++ unsigned long phys_mmio_base; /* physical register memory base */ ++ unsigned long virt_mmio_base; /* virtual start of registers */ ++ unsigned long phys_fb_base; /* physical address of frame buffer */ ++ unsigned long virt_fb_base; /* virtual start of the framebuffer */ ++}; ++ ++#define MQ200_MONITOR_HORI_RES(info) info->monitor_info.horizontal_res ++#define MQ200_MONITOR_VERT_RES(info) info->monitor_info.vertical_res ++#define MQ200_MONITOR_DEPTH(info) info->monitor_info.depth ++#define MQ200_MONITOR_LINE_LENGTH(info) info->monitor_info.line_length ++ ++struct mq200_monitor_info { ++ unsigned int horizontal_res; ++ unsigned int vertical_res; ++ unsigned int depth; ++ unsigned int refresh; ++ unsigned int line_length; ++ unsigned long flags; ++}; ++ ++ ++/** ++ * Addresses of Module ++ */ ++#define MQ200_FB_BASE (x) (x + 0x1800000) /* framebuffer */ ++#define MQ200_FB_SIZE 0x200000 /* framebuffer size in bytes */ ++#define MQ200_REGS_BASE(x) (x + 0x1e00000) /* start of registers area */ ++#define MQ200_REGS_SIZE 0x200000 /* registers area size */ ++ ++#define PMU_OFFSET 0x00000 /* power management */ ++#define CPU_OFFSET 0x02000 /* CPU interface */ ++#define MIU_OFFSET 0x04000 /* memory controller */ ++#define IN_OFFSET 0x08000 /* interrupt controller */ ++#define GC_OFFSET 0x0a000 /* graphics controller 1&2 */ ++#define GE_OFFSET 0x0c000 /* graphics engine */ ++#define FPI_OFFSET 0x0e000 /* flat panel controller */ ++#define CP1_OFFSET 0x10000 /* color palette 1 */ ++#define DC_OFFSET 0x14000 /* device configuration */ ++#define PCI_OFFSET 0x16000 /* PCI configuration */ ++#define PSF_OFFSET 0x18000 /* ??? */ ++ ++ ++/**** ++ * Registers ++ */ ++ ++/* power management unit */ ++#define PMR(addr) (addr + PCI_OFFSET + 0x40)/* power management ++ register */ ++#define PMR_VALUE 0x06210001 /* expected read value of PMR register */ ++#define PM00R(addr) (addr + PMU_OFFSET + 0x00) /* power management unit ++ configuration ++ register */ ++#define PM01R(addr) (addr + PMU_OFFSET + 0x04) /* D1 state control */ ++#define PM02R(addr) (addr + PMU_OFFSET + 0x08) /* d2 state control */ ++#define PM06R(addr) (addr + PMU_OFFSET + 0x18) /* PLL 2 programming */ ++#define PM07R(addr) (addr + PMU_OFFSET + 0x1c) /* PLL 3 programming */ ++ ++#define PMCSR(addr) (addr + PCI_OFFSET + 0x44) /* power management ++ control/status ++ register */ ++ ++/* memory interface unit */ ++#define MM00R(addr) (addr + MIU_OFFSET + 0x00)/* MIU interface control ++ 0 */ ++#define MM01R(addr) (addr + MIU_OFFSET + 0x04) /* MIU interface control ++ 1 */ ++#define MM02R(addr) (addr + MIU_OFFSET + 0x08) /* memory interface ++ control 2 */ ++#define MM03R(addr) (addr + MIU_OFFSET + 0x0c) /* memory interface ++ control 3 */ ++#define MM04R(addr) (addr + MIU_OFFSET + 0x10) /* memory interface ++ control 4 */ ++/* graphics controller 1 module */ ++#define GC00R(addr) (addr + GC_OFFSET + 0x00) /* graphics controller 1 ++ control */ ++#define GC01R(addr) (addr + GC_OFFSET + 0x04) /* graphics controller ++ CRT control */ ++#define GC02R(addr) (addr + GC_OFFSET + 0x08) /* horizontal display 1 ++ control */ ++#define GC03R(addr) (addr + GC_OFFSET + 0x0c) /* vertical display 1 ++ control */ ++#define GC04R(addr) (addr + GC_OFFSET + 0x10) /* horizontal sync 1 ++ control */ ++#define GC05R(addr) (addr + GC_OFFSET + 0x14) /* vertical sync 1 ++ control */ ++#define GC07R(addr) (addr + GC_OFFSET + 0x1c) /* vertical display 1 ++ count */ ++#define GC08R(addr) (addr + GC_OFFSET + 0x20) /* horizontal window 1 ++ control */ ++#define GC09R(addr) (addr + GC_OFFSET + 0x24) /* vertical window 1 ++ control */ ++#define GC0AR(addr) (addr + GC_OFFSET + 0x28) /* alternate horizontal ++ window 1 control */ ++#define GC0BR(addr) (addr + GC_OFFSET + 0x2c) /* alternate vertical ++ window 1 control */ ++#define GC0CR(addr) (addr + GC_OFFSET + 0x30) /* window 1 ++ start address */ ++#define GC0DR(addr) (addr + GC_OFFSET + 0x34) /* alternate window 1 ++ start address */ ++#define GC0ER(addr) (addr + GC_OFFSET + 0x38) /* alternate window 1 ++ stride */ ++#define GC0FR(addr) (addr + GC_OFFSET + 0x3c) /* alternate window 1 ++ line size */ ++#define GC10R(addr) (addr + GC_OFFSET + 0x40) /* hardware cursor 1 ++ position */ ++#define GC11R(addr) (addr + GC_OFFSET + 0x44) /* hardware cursor 1 ++ start address and ++ offset */ ++#define GC12R(addr) (addr + GC_OFFSET + 0x48) /* hardware cursor 1 ++ foreground color */ ++#define GC13R(addr) (addr + GC_OFFSET + 0x4c) /* hardware cursor 1 ++ background color */ ++ ++/* graphics controller 2 module */ ++#define GC20R(addr) (addr + GC_OFFSET + 0x80) /* graphics controller 2 ++ control */ ++#define GC21R(addr) (addr + GC_OFFSET + 0x84) /* graphics controller ++ CRC control */ ++#define GC22R(addr) (addr + GC_OFFSET + 0x88) /* horizontal display 2 ++ control */ ++#define GC23R(addr) (addr + GC_OFFSET + 0x8c) /* vertical display 2 ++ control */ ++#define GC24R(addr) (addr + GC_OFFSET + 0x90) /* horizontal sync 2 ++ control */ ++#define GC25R(addr) (addr + GC_OFFSET + 0x94) /* vertical sync 2 ++ control */ ++#define GC27R(addr) (addr + GC_OFFSET + 0x9c) /* vertical display 2 ++ count */ ++#define GC28R(addr) (addr + GC_OFFSET + 0xa0) /* horizontal window 2 ++ control */ ++#define GC29R(addr) (addr + GC_OFFSET + 0xa4) /* vertical window 2 ++ control */ ++#define GC2AR(addr) (addr + GC_OFFSET + 0xa8) /* alternate horizontal ++ window 2 control */ ++#define GC2BR(addr) (addr + GC_OFFSET + 0xac) /* alternate vertical ++ window 2 control */ ++#define GC2CR(addr) (addr + GC_OFFSET + 0xb0) /* window 2 ++ start address */ ++#define GC2DR(addr) (addr + GC_OFFSET + 0xb4) /* alternate window 2 ++ start address */ ++#define GC2ER(addr) (addr + GC_OFFSET + 0xb8) /* alternate window 2 ++ stride */ ++#define GC2FR(addr) (addr + GC_OFFSET + 0xbc) /* alternate window 2 ++ line size */ ++#define GC30R(addr) (addr + GC_OFFSET + 0xc0) /* hardware cursor 2 ++ position */ ++#define GC31R(addr) (addr + GC_OFFSET + 0xc4) /* hardware cursor 2 ++ start address and ++ offset */ ++#define GC32R(addr) (addr + GC_OFFSET + 0xc8) /* hardware cursor 2 ++ foreground color */ ++#define GC33R(addr) (addr + GC_OFFSET + 0xcc) /* hardware cursor 2 ++ background color */ ++ ++/* graphics engine */ ++#define ROP_SRCCOPY 0xCC /* dest = source */ ++#define ROP_SRCPAINT 0xEE /* dest = source OR dest */ ++#define ROP_SRCAND 0x88 /* dest = source AND dest */ ++#define ROP_SRCINVERT 0x66 /* dest = source XOR dest */ ++#define ROP_SRCERASE 0x44 /* dest = source AND (NOT dest) */ ++#define ROP_NOTSRCCOPY 0x33 /* dest = NOT source */ ++#define ROP_NOTSRCERASE 0x11 /* dest = (NOT source) AND (NOT dest) */ ++#define ROP_MERGECOPY 0xC0 /* dest = source AND pattern */ ++#define ROP_MERGEPAINT 0xBB /* dest = (NOT source) OR dest */ ++#define ROP_PATCOPY 0xF0 /* dest = pattern */ ++#define ROP_PATPAINT 0xFB /* dest = DPSnoo */ ++#define ROP_PATINVERT 0x5A /* dest = pattern XOR dest */ ++#define ROP_DSTINVERT 0x55 /* dest = NOT dest */ ++#define ROP_BLACKNESS 0x00 /* dest = BLACK */ ++#define ROP_WHITENESS 0xFF /* dest = WHITE */ ++ ++#define GE00R(addr) (addr + GE_OFFSET + 0x00) /* primary drawing command ++ register */ ++#define GE01R(addr) (addr + GE_OFFSET + 0x04) /* primary width and ++ height register */ ++#define GE02R(addr) (addr + GE_OFFSET + 0x08) /* primary destination ++ address register */ ++#define GE03R(addr) (addr + GE_OFFSET + 0x0c) /* primary source XY ++ register */ ++#define GE04R(addr) (addr + GE_OFFSET + 0x10) /* primary color compare ++ register */ ++#define GE05R(addr) (addr + GE_OFFSET + 0x14) /* primary clip left/top ++ register */ ++#define GE06R(addr) (addr + GE_OFFSET + 0x18) /* primary clip ++ right/bottom register ++ */ ++#define GE07R(addr) (addr + GE_OFFSET + 0x1c) /* primary source and ++ pattern offset ++ register */ ++#define GE08R(addr) (addr + GE_OFFSET + 0x20) /* primary foreground ++ color ++ register/rectangle ++ fill register */ ++#define GE09R(addr) (addr + GE_OFFSET + 0x24) /* source stride/offset ++ register */ ++#define GE0AR(addr) (addr + GE_OFFSET + 0x28) /* destination stride ++ register and color ++ depth */ ++#define GE0BR(addr) (addr + GE_OFFSET + 0x2c) /* image base address ++ register */ ++#define GE40R(addr) (addr + GE_OFFSET + 0x100) /* mono pattern register ++ 0 */ ++#define GE41R(addr) (addr + GE_OFFSET + 0x104) /* mono pattern register ++ 1 */ ++#define GE42R(addr) (addr + GE_OFFSET + 0x108) /* foreground color ++ register */ ++#define GE43R(addr) (addr + GE_OFFSET + 0x10c) /* background color ++ register */ ++/* color palette */ ++#define C1xxR(addr, regno) \ ++ (addr + CP1_OFFSET + (regno) * 4) /* graphics controller color ++ palette 1 */ ++/* device configuration */ ++#define DC00R(addr) (addr + DC_OFFSET + 0x00) /* device configuration ++ register 0 */ ++#define DC_RESET 0x4000 ++/* PCI configuration space */ ++#define PC00R(addr) (addr + PCI_OFFSET + 0x00)/* device ID/vendor ID ++ register */ ++/* Flatpanel Control */ ++#define FP00R(addr) (addr + FPI_OFFSET + 0x00) /* Flat Panel Control 0 */ ++#define FP01R(addr) (addr + FPI_OFFSET + 0x04) /* Flat Panel Output Pin */ ++#define FP02R(addr) (addr + FPI_OFFSET + 0x08) /* Flat Panel Gener Purpose ++ Outout Control Register */ ++#define FP03R(addr) (addr + FPI_OFFSET + 0x0c) /* General Purpose I/O Port ++ Control Register */ ++#define FP04R(addr) (addr + FPI_OFFSET + 0x10) /* STN Panel Control Register */ ++#define FP05R(addr) (addr + FPI_OFFSET + 0x14) /* D-STN Half Frame Buffer ++ Control Register -By Guess */ ++#define FP0FR(addr) (addr + FPI_OFFSET + 0x3c) /* Pulse Width Modulation ++ Control Register */ ++#define FRCTL_PATTERN_COUNT 32 ++#define FP10R(addr) (addr + FPI_OFFSET + 0x40) /* Frame-Rate Control Pattern ++ Register */ ++#define FP11R(addr) (addr + FPI_OFFSET + 0x44) ++#define FP2FR(addr) (addr + FPI_OFFSET + 0xc0) /* Frame-Rate Control Weight ++ Registers */ ++ ++ ++ ++ ++/* power management miscellaneous control */ ++union pm00r { ++ struct { ++ u32 pll1_n_b5 :1; /* PLL 1 N parameter bit 5 is 0 */ ++ u32 reserved_1 :1; ++ u32 pll2_enbl :1; /* PLL 2 enable */ ++ u32 pll3_enbl :1; /* PLL 3 enable */ ++ u32 reserved_2 :1; ++ u32 pwr_st_ctrl :1; /* power state status control */ ++ u32 reserved_3 :2; ++ ++ u32 ge_enbl :1; /* graphics engine enable */ ++ u32 ge_bsy_gl :1; /* graphics engine force busy (global) */ ++ u32 ge_bsy_lcl :1; /* graphics engine force busy (local) */ ++ u32 ge_clock :2; /* graphics engine clock select */ ++ u32 ge_cmd_fifo :1; /* graphics engine command FIFO reset */ ++ u32 ge_src_fifo :1; /* graphics engine CPU source FIFO reset */ ++ u32 miu_pwr_seq :1; /* memory interface unit power sequencing ++ enable */ ++ ++ u32 d3_mem_rfsh :1; /* D3 memory refresh */ ++ u32 d4_mem_rfsh :1; /* D4 memory refresh */ ++ u32 gpwr_intrvl :2; /* general power sequencing interval */ ++ u32 fppwr_intrvl:2; /* flat panel power sequencing interval */ ++ u32 gpwr_seq_ctr:1; /* general power sequencing interval control */ ++ u32 pmu_tm :1; /* PMU test mode */ ++ ++ u32 pwr_state :2; /* power state (read only) */ ++ u32 pwr_seq_st :1; /* power sequencing active status (read ++ only) */ ++ u32 reserved_4 :5; ++ } part; ++ u32 whole; ++}; ++ ++/* D1 state control */ ++union pm01r { ++ struct { ++ u32 osc_enbl :1; /* D1 oscillator enable */ ++ u32 pll1_enbl :1; /* D1 PLL 1 enable */ ++ u32 pll2_enbl :1; /* D1 PLL 2 enable */ ++ u32 pll3_enbl :1; /* D1 PLL 3 enable */ ++ u32 miu_enbl :1; /* D1 Memory Interface Unit (MIU) enable */ ++ u32 mem_rfsh :1; /* D1 memory refresh enable */ ++ u32 ge_enbl :1; /* D1 Graphics Engine (GE) enable */ ++ u32 reserved_1 :1; ++ ++ u32 crt_enbl :1; /* D1 CRT enable */ ++ u32 fpd_enbl :1; /* D1 Flat Panel enable */ ++ u32 reserved_2 :6; ++ ++ u32 ctl1_enbl :1; /* D1 controller 1 enable */ ++ u32 win1_enbl :1; /* D1 window 1 enable */ ++ u32 awin1_enbl :1; /* D1 alternate window 1 enable */ ++ u32 cur1_enbl :1; /* D1 cursor 1 enable */ ++ u32 reserved_3 :4; ++ ++ u32 ctl2_enbl :1; /* D1 controller 2 enable */ ++ u32 win2_enbl :1; /* D1 window 2 enable */ ++ u32 awin2_enbl :1; /* D1 alternate window 2 enable */ ++ u32 cur2_enbl :1; /* D1 cursor 2 enable */ ++ u32 reserved_4 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* D2 state control */ ++union pm02r { ++ struct { ++ u32 osc_enbl :1; /* D2 oscillator enable */ ++ u32 pll1_enbl :1; /* D2 PLL 1 enable */ ++ u32 pll2_enbl :1; /* D2 PLL 2 enable */ ++ u32 pll3_enbl :1; /* D2 PLL 3 enable */ ++ u32 miu_enbl :1; /* D2 Memory Interface Unit (MIU) enable */ ++ u32 mem_rfsh :1; /* D2 memory refresh enable */ ++ u32 ge_enbl :1; /* D2 Graphics Engine (GE) enable */ ++ u32 reserved_1 :1; ++ ++ u32 crt_enbl :1; /* D2 CRT enable */ ++ u32 fpd_enbl :1; /* D2 Flat Panel enable */ ++ u32 reserved_2 :6; ++ ++ u32 ctl1_enbl :1; /* D2 controller 1 enable */ ++ u32 win1_enbl :1; /* D2 window 1 enable */ ++ u32 awin1_enbl :1; /* D2 alternate window 1 enable */ ++ u32 cur1_enbl :1; /* D2 cursor 1 enable */ ++ u32 reserved_3 :4; ++ ++ u32 ctl2_enbl :1; /* D2 controller 2 enable */ ++ u32 win2_enbl :1; /* D2 window 2 enable */ ++ u32 awin2_enbl :1; /* D2 alternate window 2 enable */ ++ u32 cur2_enbl :1; /* D2 cursor 2 enable */ ++ u32 reserved_4 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* PLL 2 programming */ ++union pm06r { ++ struct { ++ u32 clk_src :1; /* PLL 2 reference clock source */ ++ u32 bypass :1; /* PLL 2 bypass */ ++ u32 reserved_1 :2; ++ u32 p_par :3; /* PLL 2 P parameter */ ++ u32 reserved_2 :1; ++ ++ u32 n_par :5; /* PLL 2 N parameter */ ++ u32 reserved_3 :3; ++ ++ u32 m_par :8; /* PLL 2 M parameter */ ++ ++ u32 reserved_4 :4; ++ u32 trim :4; /* PLL 2 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++/* PLL 3 programming */ ++union pm07r { ++ struct { ++ u32 clk_src :1; /* PLL 3 reference clock source */ ++ u32 bypass :1; /* PLL 3 bypass */ ++ u32 reserved_1 :2; ++ u32 p_par :3; /* PLL 3 P parameter */ ++ u32 reserved_2 :1; ++ ++ u32 n_par :5; /* PLL 3 N parameter */ ++ u32 reserved_3 :3; ++ ++ u32 m_par :8; /* PLL 3 M parameter */ ++ ++ u32 reserved_4 :4; ++ u32 trim :4; /* PLL 3 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++ ++ ++/* MIU interface control 1 */ ++union mm00r { ++ struct { ++ u32 miu_enbl :1; /* MIU enable bit */ ++ u32 mr_dsbl :1; /* MIU reset disable bit */ ++ u32 edr_dsbl :1; /* embedded DRAM reset disable bit */ ++ u32 reserved_1 :29; ++ } part; ++ u32 whole; ++}; ++ ++/* MIU interface control 2 */ ++union mm01r { ++ struct { ++ u32 mc_src :1; /* memory clock source */ ++ u32 msr_enbl :1; /* memory slow refresh enable bit */ ++ u32 pb_cpu :1; /* page break enable for CPU */ ++ u32 pb_gc1 :1; /* page break enable for GC1 */ ++ u32 pb_gc2 :1; /* page break enable for GC2 */ ++ u32 pb_stn_r :1; /* page break enable for STN read */ ++ u32 pb_stn_w :1; /* page break enable for STN write */ ++ u32 pb_ge :1; /* page break enable for GE */ ++ u32 reserved_1 :4; ++ u32 mr_interval :14; /* normal memory refresh time interval */ ++ u32 reserved_2 :4; ++ u32 edarm_enbl :1; /* embedded DRAM auto-refresh mode enable */ ++ u32 eds_enbl :1; /* EDRAM standby enable for EDRAM normal ++ mode operation */ ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 3 */ ++union mm02r { ++ struct { ++ u32 bs_ :2; ++ u32 bs_stnr :2; /* burst count for STN read memory cycles */ ++ u32 bs_stnw :2; /* burst count for STN write memroy cycles */ ++ u32 bs_ge :2; /* burst count for graphics engine ++ read/write memroy cycles */ ++ u32 bs_cpuw :2; /* burst count for CPU write memory cycles */ ++ u32 fifo_gc1 :4; /* GC1 display refresh FIFO threshold */ ++ u32 fifo_gc2 :4; /* GC2 display refresh FIFO threshold */ ++ u32 fifo_stnr :4; /* STN read FIFO threshold */ ++ u32 fifo_stnw :4; /* STN write FIFO threshold */ ++ u32 fifo_ge_src :3; /* GE source read FIFO threshold */ ++ u32 fifo_ge_dst :3; /* GE destination read FIFO threshold */ ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 4 */ ++union mm03r { ++ struct { ++ u32 rd_late_req :1; /* read latency request */ ++ u32 reserved_1 :31; ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 5 */ ++union mm04r { ++ struct { ++ u32 latency :3; /* EDRAM latency */ ++ u32 dmm_cyc :1; /* enable for the dummy cycle insertion ++ between read and write cycles */ ++ u32 pre_dmm_cyc :1; /* enable for the dummy cycle insertion ++ between read/write and precharge cycles ++ for the same bank */ ++ u32 reserved_1 :3; ++ u32 bnk_act_cls :2; /* bank activate command to bank close ++ command timing interval control */ ++ u32 bnk_act_rw :1; /* bank activate command to read/wirte ++ command timing interval control */ ++ u32 bnk_cls_act :1; /* bank close command to bank activate ++ command timing interval control */ ++ u32 trc :1; /* row cycle time */ ++ u32 reserved_2 :3; ++ u32 delay_r :2; /* programmable delay for read clock */ ++ u32 delay_m :2; /* programmable delay for internal memory ++ clock */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller 1 register */ ++union gc00r { ++ struct { ++ u32 ctl_enbl :1; /* Controller 1 Enable */ ++ u32 hc_reset :1; /* Horizontal Counter 1 Reset */ ++ u32 vc_reset :1; /* Vertical Counter 1 Reset */ ++ u32 iwin_enbl :1; /* Image Window 1 Enable */ ++ u32 gcd :4; /* Graphics Color Depth (GCD) */ ++ ++ u32 hc_enbl :1; /* Hardware Cursor 1 Enable */ ++ u32 reserved_1 :2; ++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */ ++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */ ++ ++ u32 g1rclk_src :2; /* G1RCLK Source */ ++ u32 tm0 :1; /* Test Mode 0 */ ++ u32 tm1 :1; /* Test Mode 1 */ ++ u32 fd :3; /* G1MCLK First Clock Divisor (FD1) */ ++ u32 reserved_2 :1; ++ ++ u32 sd :8; /* G1MCLK Second Clock Divisor (SD1) */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller CRT control */ ++union gc01r { ++ struct { ++ u32 dac_enbl :2; /* CRT DAC enable */ ++ u32 hsync_out :1; /* CRT HSYNC output during power down mode */ ++ u32 vsync_out :1; /* CRT VSYNC output during power down mode */ ++ u32 hsync_ctl :2; /* CRT HSYNC control */ ++ u32 vsync_ctl :2; /* CRT VSYNC control */ ++ /**/ ++ u32 hsync_pol :1; /* CRT HSYNC polarity */ ++ u32 vsync_pol :1; /* CRT VSYNC polarity */ ++ u32 sync_p_enbl :1; /* sync pedestal enable */ ++ u32 blnk_p_enbl :1; /* blank pedestal enable */ ++ u32 c_sync_enbl :1; /* composite sync enable */ ++ u32 vref_sel :1; /* VREF select */ ++ u32 mn_sns_enbl :1; /* monitor sense enable */ ++ u32 ct_out_enbl :1; /* constant output enable */ ++ /**/ ++ u32 dac_out_lvl :8; /* monitor sense DAC output level */ ++ /**/ ++ u32 blue_dac_r :1; /* blue DAC sense result */ ++ u32 green_dac_r :1; /* green DAC sense result */ ++ u32 red_dac_r :1; /* red DAC sense result */ ++ u32 reserved_1 :1; ++ u32 mon_col_sel :1; /* mono/color monitor select */ ++ u32 reserved_2 :3; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal display 1 control */ ++union gc02r { ++ struct { ++ u32 hd1t :12; /* horizontal display 1 total */ ++ u32 reserved_1 :4; ++ ++ u32 hd1e :12; /* horizontal display 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 1 control */ ++union gc03r { ++ struct { ++ u32 vd1t :12; /* vertical display 1 total */ ++ u32 reserved_1 :4; ++ ++ u32 vd1e :12; /* vertical display 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal sync 1 control */ ++union gc04r { ++ struct { ++ u32 hs1s :12; /* horizontal sync 1 start */ ++ u32 reserved_1 :4; ++ ++ u32 hs1e :12; /* horizontal sync 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical sync 1 control */ ++union gc05r { ++ struct { ++ u32 vs1s :12; /* vertical sync 1 start */ ++ u32 reserved_1 :4; ++ ++ u32 vs1e :12; /* vertical sync 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 1 count */ ++union gc07r { ++ struct { ++ u32 vd_cnt :12; /* vertical display 1 count */ ++ u32 reverved_1 :20; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal window 1 control */ ++union gc08r { ++ struct { ++ u32 hw1s :12; /* horizontal window 1 start (HW1S) */ ++ u32 reserved_1 :4; ++ ++ u32 hw1w :12; /* horizontal window 1 width (HW1W) */ ++ u32 w1ald :4; /* window 1 additional line data */ ++ } part; ++ u32 whole; ++}; ++ ++/* vertical window 1 control */ ++union gc09r { ++ struct { ++ u32 vw1s :12; /* vertical window 1 start */ ++ u32 reserved_1 :4; ++ u32 vw1h :12; /* vertical window 1 height */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* window 1 start address */ ++union gc0cr { ++ struct { ++ u32 w1sa :21; /* window 1 start address */ ++ u32 reserved_1 :11; ++ } part; ++ u32 whole; ++}; ++ ++/* window 1 stride */ ++union gc0er { ++ struct { ++ s16 w1st; /* window 1 stride */ ++ s16 aw1st; /* alternate window 1 stride */ ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 position */ ++union gc10r { ++ struct { ++ u32 hc1s :12; /* horizontal cursor 1 start */ ++ u32 reserved_1 :4; ++ u32 vc1s :12; /* vertical cursor 1 start */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 start address and offset */ ++union gc11r { ++ struct { ++ u32 hc1sa :11; /* hardware cursor 1 start address */ ++ u32 reserved_1 :5; ++ u32 hc1o :6; /* horizontal cursor 1 offset */ ++ u32 reserved_2 :2; ++ u32 vc1o :6; /* vertical cursor 1 offset */ ++ u32 reserved_3 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 foreground color */ ++union gc12r { ++ struct { ++ u32 hc1fc :24; /* hardware cursor 1 foreground color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 background color */ ++union gc13r { ++ struct { ++ u32 hc1bc :24; /* hardware cursor 1 background color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++ ++/* graphics controller 2 register */ ++union gc20r { ++ struct { ++ u32 ctl_enbl :1; /* Controller 2 Enable */ ++ u32 hc_reset :1; /* Horizontal Counter 2 Reset */ ++ u32 vc_reset :1; /* Vertical Counter 2 Reset */ ++ u32 iwin_enbl :1; /* Image Window 2 Enable */ ++ u32 gcd :4; /* Graphics Color Depth (GCD) */ ++ ++ u32 hc_enbl :1; /* Hardware Cursor 2 Enable */ ++ u32 reserved_1 :2; ++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */ ++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */ ++ ++ u32 g2rclk_src :2; /* G2RCLK Source */ ++ u32 tm0 :1; /* Test Mode 0 */ ++ u32 tm1 :1; /* Test Mode 1 */ ++ u32 fd :3; /* G2MCLK First Clock Divisor (FD1) */ ++ u32 reserved_2 :1; ++ ++ u32 sd :8; /* G2MCLK Second Clock Divisor (SD1) */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller CRC control */ ++union gc21r { ++ struct { ++ u32 crc_enbl :1; /* CRC enable */ ++ u32 vsync_wait :1; /* CRC input data control waitime of VSYNC */ ++ u32 crc_o_sel :2; /* CRC output select */ ++ u32 reserved_1 :4; ++ u32 crc_result :22; /* CRC result (read only) */ ++ u32 reserved_2 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal display 2 control */ ++union gc22r { ++ struct { ++ u32 hd2t :12; /* horizontal display 2 total */ ++ u32 reserved_1 :4; ++ ++ u32 hd2e :12; /* horizontal display 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 2 control */ ++union gc23r { ++ struct { ++ u32 vd2t :12; /* vertical display 2 total */ ++ u32 reserved_1 :4; ++ ++ u32 vd2e :12; /* vertical display 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal sync 2 control */ ++union gc24r { ++ struct { ++ u32 hs2s :12; /* horizontal sync 2 start */ ++ u32 reserved_1 :4; ++ ++ u32 hs2e :12; /* horizontal sync 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical sync 2 control */ ++union gc25r { ++ struct { ++ u32 vs2s :12; /* vertical sync 2 start */ ++ u32 reserved_1 :4; ++ ++ u32 vs2e :12; /* vertical sync 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 2 count */ ++union gc27r { ++ struct { ++ u32 vd_cnt :12; /* vertical display 2 count */ ++ u32 reverved_1 :20; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal window 2 control */ ++union gc28r { ++ struct { ++ u32 hw2s :12; /* horizontal window 2 start (HW2S) */ ++ u32 reserved_1 :4; ++ ++ u32 hw2w :12; /* horizontal window 2 width (HW2W) */ ++ u32 w2ald :4; /* window 2 additional line data */ ++ } part; ++ u32 whole; ++}; ++ ++/* vertical window 2 control */ ++union gc29r { ++ struct { ++ u32 vw2s :12; /* vertical window 2 start */ ++ u32 reserved_1 :4; ++ u32 vw2h :12; /* vertical window 2 height */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* window 2 start address */ ++union gc2cr { ++ struct { ++ u32 w2sa :21; /* window 2 start address */ ++ u32 reserved_1 :11; ++ } part; ++ u32 whole; ++}; ++ ++/* window 2 stride */ ++union gc2er { ++ struct { ++ s16 w2st; /* window 2 stride */ ++ s16 aw2st; /* alternate window 2 stride */ ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 position */ ++union gc30r { ++ struct { ++ u32 hc2s :12; /* horizontal cursor 2 start */ ++ u32 reserved_1 :4; ++ u32 vc2s :12; /* vertical cursor 2 start */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 start address and offset */ ++union gc31r { ++ struct { ++ u32 hc2sa :11; /* hardware cursor 2 start address */ ++ u32 reserved_1 :5; ++ u32 hc2o :6; /* horizontal cursor 2 offset */ ++ u32 reserved_2 :2; ++ u32 vc2o :6; /* vertical cursor 2 offset */ ++ u32 reserved_3 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 foreground color */ ++union gc32r { ++ struct { ++ u32 hc2fc :24; /* hardware cursor 2 foreground color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 background color */ ++union gc33r { ++ struct { ++ u32 hc2bc :24; /* hardware cursor 2 background color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++ ++/* primary drawing command register */ ++union ge00r { ++ struct { ++ u32 rop :8; /* raster operation */ ++ /**/ ++ u32 cmd_typ :3; /* command type */ ++ u32 x_dir :1; /* x direction */ ++ u32 y_dir :1; /* y direction */ ++ u32 src_mem :1; /* source memory */ ++ u32 mon_src :1; /* mono source */ ++ u32 mon_ptn :1; /* mono pattern */ ++ /**/ ++ u32 dst_trns_e :1; /* destination transparency enable */ ++ u32 dst_trns_p :1; /* destination transparency polarity */ ++ u32 mon_trns_e :1; /* mono source or mono pattern transparency ++ enable */ ++ u32 mon_trns_p :1; /* mono transparency polarity */ ++ u32 mod_sel :1; /* memory to screen or off screen to screen ++ mode select */ ++ u32 alpha_sel :2; /* Alpha byte mask selection */ ++ u32 sol_col :1; /* solid color */ ++ /**/ ++ u32 stride_eq :1; /* source stride is equal to destination ++ stride */ ++ u32 rop2_sel :1; /* ROP2 code selection */ ++ u32 clipping :1; /* enable clipping */ ++ u32 auto_exec :1; /* auto execute */ ++ u32 reserved_1 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* primary width and height register */ ++union ge01r { ++ struct { ++ u32 width :12; /* source/destination window width */ ++ u32 reserved_1 :4; ++ ++ u32 height :12; /* source/destination window height */ ++ u32 reserved_2 :1; ++ u32 reserved_3 :3; ++ } bitblt; ++ struct { ++ u32 dm :17; ++ u32 axis_major :12; ++ u32 x_y :1; /* x-major or y-major */ ++ u32 last_pix :1; /* decision to draw or not to draw the last ++ pixel of the line */ ++ u32 reserved_1 :1; ++ } bresenham; ++ u32 whole; ++}; ++ ++/* primary destination address register */ ++union ge02r { ++ struct { ++ u32 dst_x :12; /* destination x position */ ++ u32 reserved_1 :1; ++ u32 h_offset :3; /* mono/color pattern horizontal offset */ ++ ++ u32 dst_y :12; /* destination y position */ ++ u32 reserved_2 :1; ++ u32 v_offset :3; /* mono/color pattern vertical offset */ ++ } window; ++ struct { ++ u32 x :12; /* starting x coordinate */ ++ u32 dm :17; /* 17 bits major-axis delta */ ++ u32 reserved_1 :3; ++ } line; ++ u32 whole; ++}; ++ ++/* source XY register/line draw starting Y coordinate and mintor axis delta */ ++union ge03r { ++ struct { ++ u32 src_x :12; /* source X position */ ++ u32 reserved_1 :4; ++ ++ u32 src_y :12; /* source Y position */ ++ u32 reserved_2 :4; ++ } window; ++ struct { ++ u32 start_y :12; /* starting Y coordinate */ ++ u32 dn :17; /* 17 bits minor-axis delta */ ++ u32 reserved_1 :3; ++ } line; ++ u32 whole; ++}; ++ ++/* clip left/top register */ ++union ge05r { ++ struct { ++ u32 left :12; /* left edge of clipping rectangle */ ++ u32 reserved_1 :4; ++ ++ u32 top :12; /* top edge of clipping rectangle */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* source stride/offset register */ ++union ge09r { ++ struct { ++ u32 src_strid :12; /* source line stride */ ++ u32 reserved_1 :13; ++ u32 strt_bit :3; /* initial mono source bit offset */ ++ u32 strt_byte :3; /* initial mono/color source byte offset */ ++ u32 reserved_2 :1; ++ } line; ++ struct { ++ u32 strt_bit :5; /* initial mono source bit offset */ ++ u32 reserved_1 :1; ++ u32 amount :10; /* number of 16 bytes amount that MIU need ++ to fetch from frame buffer */ ++ ++ u32 reserved_2 :9; ++ u32 bit_spc :7; /* bit space between lines */ ++ } pack_mono; ++ struct { ++ u32 strt_bit :3; /* initial mono source bit offset */ ++ u32 strt_byte :3; /* initial mono/color source byte offset */ ++ u32 amount :10; /* number of 16 bytes amount that MIU need ++ to fetch from frame buffer */ ++ ++ u32 reserved_1 :9; ++ u32 bit_spc :3; /* bit space between lines */ ++ u32 byt_spc :4; /* byte space between lines */ ++ } pack_color; ++ u32 whole; ++}; ++ ++/* destination stride register and color depth */ ++union ge0ar { ++ struct { ++ u32 dst_strid :12; /* destination line stride and color depth */ ++ u32 reserved_1 :18; ++ u32 col_dpth :2; /* color depth */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller color pallete */ ++union c1xxr { ++ struct { ++ u8 red; /* red color pallete */ ++ u8 green; /* green/gray color pallete */ ++ u8 blue; /* blue color palette */ ++ u8 reserved_1; ++ } part; ++ u32 whole; ++}; ++ ++/* devicee configuration register 0 */ ++union dc00r { ++ struct { ++ u32 osc_bypass :1; /* oscillator bypass */ ++ u32 osc_enbl :1; /* oscillator enable */ ++ u32 pll1_bypass :1; /* PLL1 bypass */ ++ u32 pll1_enbl :1; /* PLL1 enable */ ++ u32 pll1_p_par :3; /* PLL1 P parameter */ ++ u32 cpu_div :1; /* CPU interface clock divisor */ ++ u32 pll1_n_par :5; /* PLL1 N parameter */ ++ u32 saisc :1; /* StrongARM interface synchronizer control */ ++ u32 s_chp_reset :1; /* software chip reset */ ++ u32 mem_enbl :1; /* memory standby enable */ ++ u32 pll1_m_par :8; /* PLL 1 M parameter */ ++ u32 osc_shaper :1; /* oscillator shaper disable */ ++ u32 fast_pwr :1; /* fast power sequencing */ ++ u32 osc_frq :2; /* oscillator frequency select */ ++ u32 pll1_trim :4; /* PLL 1 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++/* device ID/vendor ID register */ ++union pc00r { ++ struct { ++ u16 device; /* device ID */ ++ u16 vendor; /* vendor ID */ ++ } part; ++ u32 whole; ++}; ++ ++/* Flat Panel Control Register */ ++union fp00r { ++ struct { ++ u32 flatp_enbl : 2; /* Flat Panel Interface Enable */ ++ u32 flatp_type : 2; /* Flat Panel Type */ ++ u32 mono : 1; /* Mono/Color Panel Select */ ++ u32 flatp_intf : 3; /* Flat Panel Interface */ ++ u32 dither_pat : 2; /* Dither Pattern */ ++ u32 reserved : 2; /* Reserved Must Be 0*/ ++ u32 dither_col : 3; /* Dither Base Color */ ++ u32 alt_win_ctl: 1; /* Alternate Window Control */ ++ u32 frc_ctl : 2; /* FRC Control */ ++ u32 dither_adj1: 6; /* Dither Pattern Adjust 1 */ ++ u32 dither_adj2: 3; /* Dither Pattern Adjust 2 */ ++ u32 dither_adj3: 1; /* Dither Pattern Adjust 3 */ ++ u32 test_mode0 : 1; /* Test Mode 0 */ ++ u32 test_mode1 : 1; /* Test Mode 1 */ ++ u32 test_mode2 : 1; /* Test Mode 2 */ ++ u32 test_mode3 : 1; /* Test Mode 3 */ ++ } part; ++ u32 whole; ++}; ++ ++union fp01r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp02r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp03r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp04r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp05r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp0fr { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++ ++ ++ ++/**** ++ * Others ++ */ ++ ++#define CHIPNAME "MQ-200" ++ ++extern void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr); ++extern void mq200_external_setqmode(struct mq200_monitor_info*, unsigned long, spinlock_t *); ++extern void mq200_external_offdisplay(unsigned long); ++extern void mq200_external_ondisplay (unsigned long); ++extern int mq200_external_probe(unsigned long); ++ ++ ++ ++#endif +diff -uNr linux-2.6.21.vanilla/drivers/video/mq200/mq_external.c linux-2.6.21/drivers/video/mq200/mq_external.c +--- linux-2.6.21.vanilla/drivers/video/mq200/mq_external.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/video/mq200/mq_external.c 2007-05-01 17:02:17.000000000 +0200 +@@ -0,0 +1,513 @@ ++/* ++ * Copyright (C) 2005 Holger Hans Peter Freyther ++ * ++ * Based ON: ++ * ++ * linux/drivers/video/mq200fb.c -- MQ-200 for a frame buffer device ++ * based on linux/driver/video/pm2fb.c ++ * ++ * 2007/03/11 mrdata: ++ * bug found in gc1_reset(), renaming to gc1_gc2_reset() ++ * extend mq200_external_ondisplay() -> LCD for GC2 and CRT for GC1 ++ * ++ * Copyright (C) 2000 Lineo, Japan ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ */ ++ ++#include <asm/types.h> ++#include <asm/io.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++ ++#include <asm/hardware.h> ++ ++#include "mq200_data.h" ++ ++ ++#if 1 ++#define PRINTK(args...) printk(args) ++#else ++#define PRINTK(args...) ++#endif ++ ++ ++/**** ++ * power state transition to "state". ++ */ ++static void ++power_state_transition(unsigned long register_base, int state) ++{ ++ int i; ++ writel(state, PMCSR(register_base)); ++ mdelay(300); ++ for (i = 1; ; i++) { ++ udelay(100); ++ if ((readl(PMCSR(register_base)) & 0x3) == state) { ++ break; ++ } ++ } ++} ++ ++ ++/**** ++ * device configuration initialization. ++ */ ++static void ++dc_reset(unsigned long register_base) ++{ ++ union dc00r dc00r; ++ ++ /* Reset First */ ++ dc00r.whole = DC_RESET; ++ writel(dc00r.whole, DC00R(register_base)); ++ mdelay(100); ++ ++ dc00r.whole = 0xEF2082A; ++ writel(dc00r.whole, DC00R(register_base)); ++ mdelay(300); ++ PRINTK(CHIPNAME ": DC00R = 0x%08X\n", readl(DC00R(register_base))); ++} ++ ++ ++/**** ++ * initialize memory interface unit. ++ */ ++static void ++miu_reset(unsigned long register_base) ++{ ++ union mm00r mm00r; ++ union mm01r mm01r; ++ union mm02r mm02r; ++ union mm03r mm03r; ++ union mm04r mm04r; ++ ++ /* MIU interface control 1 */ ++ mm00r.whole = 0x4; ++ writel(mm00r.whole, MM00R(register_base)); ++ mdelay(50); ++ writel(0, MM00R(register_base)); ++ mdelay(50); ++ ++ /* MIU interface control 2 ++ * o PLL 1 output is used as memory clock source. ++ */ ++ mm01r.whole = 0x4143e086; ++ writel(mm01r.whole, MM01R(register_base)); ++ ++ /* memory interface control 3 */ ++ mm02r.whole = 0x6d6aabff; ++ writel(mm02r.whole, MM02R(register_base)); ++ ++ /* memory interface control 5 */ ++ mm04r.whole = 0x10d; ++ writel(mm04r.whole, MM04R(register_base)); ++ ++ /* memory interface control 4 */ ++ mm03r.whole = 0x1; ++ writel(mm03r.whole, MM03R(register_base)); ++ mdelay(50); ++ ++ /* MIU interface control 1 */ ++ mm00r.whole = 0x3; ++ writel(mm00r.whole, MM00R(register_base)); ++ mdelay(50); ++} ++ ++/**** ++ * ++ */ ++static ++void fpctrl_reset(unsigned long addr) ++{ ++ /* ++ * We're in D0 State, let us set the FPCTRL ++ */ ++ union fp00r fp00r; ++ union fp01r fp01r; ++ union fp02r fp02r; ++ union fp03r fp03r; ++ union fp04r fp04r; ++ union fp0fr fp0fr; ++ ++ fp00r.whole = 0x6320; ++ writel(fp00r.whole, FP00R(addr)); ++ ++ fp01r.whole = 0x20; ++ writel(fp01r.whole, FP01R(addr)); ++ ++ fp04r.whole = 0xBD0001; ++ writel(fp04r.whole, FP04R(addr)); ++ ++ /* Set Flat Panel General Purpose register first */ ++ fp02r.whole = 0x0; ++ writel(fp02r.whole, FP02R(addr)); ++ ++ fp03r.whole = 0x0; ++ writel(fp03r.whole, FP03R(addr)); ++ ++ fp0fr.whole = 0xA16c44; ++ writel(fp0fr.whole, FP0FR(addr)); ++ ++ /* Set them again */ ++ fp02r.whole = 0x0; ++ writel(fp02r.whole, FP02R(addr)); ++ ++ fp03r.whole = 0x0; ++ writel(fp03r.whole, FP03R(addr)); ++} ++ ++ ++/**** ++ * initialize power management unit. ++ */ ++static void ++pmu_reset(unsigned long register_base) ++{ ++ union pm00r pm00r; ++ union pm01r pm01r; ++ union pm02r pm02r; ++ ++ /* power management miscellaneous control ++ * o GE is driven by PLL 1 clock. ++ */ ++ pm00r.whole = 0xc0900; ++ writel(pm00r.whole, PM00R(register_base)); ++ ++ /* D1 state control */ ++ pm01r.whole = 0x5000271; ++ writel(pm01r.whole, PM01R(register_base)); ++ ++ /* D2 state control */ ++ pm02r.whole = 0x271; ++ writel(pm02r.whole, PM02R(register_base)); ++} ++ ++/**** ++ * initialize graphics controller 1 ++ * and graphics controller 2 ++ */ ++static void ++gc1_gc2_reset(unsigned long register_base, spinlock_t *lock ) ++{ ++ unsigned long flags; ++ union gc00r gc00r; ++ union gc01r gc01r; ++ union gc02r gc02r; ++ union gc03r gc03r; ++ union gc04r gc04r; ++ union gc05r gc05r; ++ union gc08r gc08r; ++ union gc09r gc09r; ++ union gc0cr gc0cr; ++ union gc0er gc0er; ++ union gc20r gc20r; ++ union gc22r gc22r; ++ union gc23r gc23r; ++ union gc24r gc24r; ++ union gc25r gc25r; ++ union gc28r gc28r; ++ union gc29r gc29r; ++ union gc2cr gc2cr; ++ union gc2er gc2er; ++ ++ union pm00r pm00r; ++ union pm06r pm06r; ++ union pm06r pm07r; ++ ++ spin_lock_irqsave(lock, flags); ++ ++ /* alternate window 1 stride */ ++ gc0er.whole = 0x640; ++ writel(gc0er.whole, GC0ER(register_base)); ++ ++ /* image window 1 start address */ ++ gc0cr.whole = 0x0; ++ writel(gc0cr.whole, GC0CR(register_base)); ++ ++ /* alternate window 2 stride */ ++ gc2er.whole = 0x640; ++ writel(gc0er.whole, GC2ER(register_base)); ++ ++ /* image window 2 start address */ ++ gc2cr.whole = 0x0; ++ writel(gc2cr.whole, GC2CR(register_base)); ++ ++ /* read PM Register */ ++ pm00r.whole = readl(PM00R(register_base)); ++ ++ /* horizontal window 1 control */ ++ gc08r.whole = 0x131f0000; ++ writel(gc08r.whole, GC08R(register_base)); ++ ++ /* vertical window 1 control */ ++ gc09r.whole = 0x12570000; ++ writel(gc09r.whole, GC09R(register_base)); ++ ++ /* horizontal display 1 control */ ++ gc02r.whole = 0x320041e; ++ writel(gc02r.whole, GC02R(register_base)); ++ ++ /* vertical display 1 control */ ++ gc03r.whole = 0x2570273; ++ writel(gc03r.whole, GC03R(register_base)); ++ ++ /* horizontal sync 1 control */ ++ gc04r.whole = 0x3c70347; ++ writel(gc04r.whole, GC04R(register_base)); ++ ++ /* vertical sync 1 control */ ++ gc05r.whole = 0x25d0259; ++ writel(gc05r.whole, GC05R(register_base)); ++ ++ /* graphics controller CRT control */ ++ gc01r.whole = 0x800; ++ writel(gc01r.whole, GC01R(register_base)); ++ ++ /* PLL 2 programming */ ++ pm06r.whole = 0xE90830; ++ writel(pm06r.whole, PM06R(register_base)); ++ ++ /* graphics controller 1 register ++ * o GC1 clock source is PLL 2. ++ * o hardware cursor is disabled. ++ */ ++ gc00r.whole = 0x10000C8 | 0x20000; ++ writel(gc00r.whole, GC00R(register_base)); ++ ++#if 0 ++ /* alternate horizontal window 1 control */ ++ writel(0, GC0AR(register_base)); ++ ++ /* alternate vertical window 1 control */ ++ writel(0, GC0BR(register_base)); ++ ++ /* window 1 start address */ ++ writel(0x2004100, GC0CR(register_base)); ++ ++ /* alternate window 1 start address */ ++ writel(0, GC0DR(register_base)); ++ ++ /* window 1 stride */ ++ gc0er.whole = 0x5100048; ++ writel(gc0er.whole, GC0ER(register_base)); ++ ++ /* reserved register - ??? - */ ++ writel(0x31f, GC0FR(register_base)); ++#endif ++ ++#if 0 ++ /* hardware cursor 1 position */ ++ writel(0, GC10R(register_base)); ++ ++ /* hardware cursor 1 start address and offset */ ++ gc11r.whole = 0x5100048; ++ writel(gc11r.whole, GC11R(register_base)); ++ ++ /* hardware cursor 1 foreground color */ ++ writel(0x00ffffff, GC12R(register_base)); ++ ++ /* hardware cursor 1 background color */ ++ writel(0x00000000, GC13R(register_base)); ++#endif ++ ++ /* horizontal window 2 control */ ++ gc28r.whole = 0x31f0000; ++ writel(gc28r.whole, GC28R(register_base)); ++ ++ /* vertical window 2 control */ ++ gc29r.whole = 0x2570000; ++ writel(gc29r.whole, GC29R(register_base)); ++ ++ /* horizontal display 2 control */ ++ gc22r.whole = 0x320041e; ++ writel(gc22r.whole, GC22R(register_base)); ++ ++ /* vertical display 2 control */ ++ gc23r.whole = 0x2570273; ++ writel(gc23r.whole, GC23R(register_base)); ++ ++ /* horizontal sync 2 control */ ++ gc24r.whole = 0x3c70347; ++ writel(gc24r.whole, GC24R(register_base)); ++ ++ /* vertical sync 2 control */ ++ gc25r.whole = 0x25d0259; ++ writel(gc25r.whole, GC25R(register_base)); ++ ++ /* graphics controller CRT control */ ++ gc01r.whole = 0x800; ++ writel(gc01r.whole, GC01R(register_base)); ++ ++ /* PLL 3 programming */ ++ pm07r.whole = 0xE90830; ++ writel(pm07r.whole, PM07R(register_base)); ++ ++ /* graphics controller 2 register ++ * o GC2 clock source is PLL 3. ++ * o hardware cursor is disabled. ++ */ ++ gc20r.whole = 0x10000C8 | 0x30000; ++ writel(gc20r.whole, GC20R(register_base)); ++ ++ /* ++ * Enable PLL2 and PLL3 in the PM Register ++ */ ++ pm00r.part.pll2_enbl = 0x1; ++ pm00r.part.pll3_enbl = 0x1; ++ writel(pm00r.whole, PM00R(register_base)); ++ ++ spin_unlock_irqrestore(lock, flags); ++} ++ ++ ++/**** ++ * initialize graphics engine. ++ */ ++static void ++ge_reset(unsigned long register_base) ++{ ++ /* drawing command register */ ++ writel(0, GE00R(register_base)); ++ ++ /* promary width and height register */ ++ writel(0, GE01R(register_base)); ++ ++ /* primary destination address register */ ++ writel(0, GE02R(register_base)); ++ ++ /* primary source XY register */ ++ writel(0, GE03R(register_base)); ++ ++ /* primary color compare register */ ++ writel(0, GE04R(register_base)); ++ ++ /* primary clip left/top register */ ++ writel(0, GE05R(register_base)); ++ ++ /* primary clip right/bottom register */ ++ writel(0, GE06R(register_base)); ++ ++ /* primary source and pattern offset register */ ++ writel(0, GE07R(register_base)); ++ ++ /* primary foreground color register/rectangle fill color depth */ ++ writel(0, GE08R(register_base)); ++ ++ /* source stride/offset register */ ++ writel(0, GE09R(register_base)); ++ ++ /* destination stride register and color depth */ ++ writel(0, GE0AR(register_base)); ++ ++ /* image base address register */ ++ writel(0, GE0BR(register_base)); ++} ++ ++/**** ++ * initialize Color Palette 1. ++ */ ++static void ++cp1_reset(unsigned long addr_info) ++{ ++ int i; ++ ++ for (i = 0; i < 256; i++) ++ writel(0, C1xxR(addr_info, i)); ++} ++ ++ ++/* ++ * Below functions are called from the skeleton ++ */ ++void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr) ++{ ++ writel(color,C1xxR(addr,regno)); ++} ++ ++void mq200_external_setqmode(struct mq200_monitor_info* info, ++ unsigned long addr, spinlock_t *lock) ++{ ++ dc_reset(addr); /* device configuration */ ++ ++ power_state_transition(addr, 0); /* transition to D0 state */ ++ ++ pmu_reset(addr); /* power management unit */ ++ ++ miu_reset(addr); /* memory interface unit */ ++ ++ ge_reset(addr); /* graphics engine */ ++ ++ fpctrl_reset(addr); /* reset the panel settings */ ++ ++ gc1_gc2_reset(addr, lock); /* graphics controller 1 and 2 */ ++ ++ cp1_reset(addr); /* color palette 1 */ ++ ++ mq200_external_ondisplay(addr); /* LCD and CRT */ ++} ++ ++void mq200_external_offdisplay(unsigned long addr) ++{ ++ /* ++ * Move the MQ200 to D3 mode ++ */ ++ power_state_transition(addr, 3); ++} ++ ++/** ++ * to be called after mq200_external_setqmode ++ */ ++void mq200_external_ondisplay (unsigned long addr) ++{ ++ /* ++ * Set the framebuffer details ++ */ ++ union gc00r gc00r; ++ union gc01r gc01r; ++ union gc20r gc20r; ++ union fp00r fp00r; ++ ++ /* enable LCD for GC2 */ ++ fp00r.whole = readl(FP00R(addr)); ++ fp00r.whole &= 0xfffffffc; ++ ++ gc20r.whole = readl(GC20R(addr)); ++ ++ if(!(gc20r.whole & 0x1)) { ++ gc20r.whole |= 0x1; ++ writel(gc20r.whole, GC20R(addr)); ++ } ++ ++ fp00r.whole |= 0x3; ++ writel(fp00r.whole, FP00R(addr)); ++ ++ /* enable CRT for GC1 */ ++ gc00r.whole = readl(GC00R(addr)); ++ ++ if(!(gc00r.whole & 0x1)) { ++ gc00r.whole |= 0x1; ++ writel(gc00r.whole, GC00R(addr)); ++ } ++ ++ gc01r.whole = readl(GC01R(addr)); ++ gc01r.whole &= 0xfffffffc; ++ ++ gc01r.whole |= 0x1; ++ writel(gc01r.whole, GC01R(addr)); ++ ++} ++ ++int mq200_external_probe(unsigned long addr) ++{ ++ union pc00r pc00r; ++ if(readl(PMR(addr)) != PMR_VALUE) ++ return 0; ++ ++ pc00r.whole = readl(PC00R(addr)); ++ printk(KERN_INFO "mq200 video driver found Vendor: 0x%X Device: 0x%X\n", ++ pc00r.part.device, pc00r.part.vendor); ++ return 1; ++} +diff -uNr linux-2.6.21.vanilla/drivers/video/mq200/mq_skeleton.c linux-2.6.21/drivers/video/mq200/mq_skeleton.c +--- linux-2.6.21.vanilla/drivers/video/mq200/mq_skeleton.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/video/mq200/mq_skeleton.c 2007-05-01 17:02:17.000000000 +0200 +@@ -0,0 +1,398 @@ ++/* ++ * Author: Holger Hans Peter Freyther ++ * ++ * ++ * This implements the frame buffer driver interface to communicate ++ * with the kernel. ++ * It uses the mq200 routines from the ucLinux driver from Lineo ++ * ++ * 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/autoconf.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/fb.h> ++#include <linux/types.h> ++#include <linux/spinlock.h> ++ ++#include "mq200_data.h" ++ ++#if CONFIG_SA1100_SIMPAD ++/* ++ * Siemens SIMpad specefic data ++ */ ++#include <asm/arch/simpad.h> ++#include <asm/arch/hardware.h> ++ ++#define MQ200_REGIONS simpad_mq200_regions ++#define MQ200_MONITOR simpad_mq200_panel ++ ++static struct mq200_io_regions simpad_mq200_regions = { ++ .fb_size = MQ200_FB_SIZE, ++ .phys_mmio_base = 0x4be00000, ++ .virt_mmio_base = 0xf2e00000, ++ .phys_fb_base = 0x4b800000, ++ .virt_fb_base = 0xf2800000, ++}; ++ ++static struct mq200_monitor_info simpad_mq200_panel = { ++ .horizontal_res = 800, ++ .vertical_res = 600, ++ .depth = 16, ++ .refresh = 60, ++ .line_length = 1600, ++ .flags = 0x00130004, ++}; ++ ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int value); ++extern void clear_cs3_bit(int value); ++#endif ++ ++ ++ ++struct mq200_info { ++ struct fb_info fb_info; ++ struct mq200_io_regions io_regions; ++ struct mq200_monitor_info monitor_info; ++ ++ /* palette */ ++ u32 pseudo_palette[17]; /* 16 colors + 1 in reserve not that well documented... */ ++ spinlock_t lock; ++}; ++ ++ ++ ++static int mq200_blank( int blank_mode, struct fb_info *info ) ++{ ++#ifdef CONFIG_SA1100_SIMPAD ++ if(blank_mode ){ ++ clear_cs3_bit(DISPLAY_ON); ++ }else { ++ set_cs3_bit(DISPLAY_ON); ++ } ++#endif ++ return 0; ++} ++ ++ ++static int mq200_check_var(struct fb_var_screeninfo *var, ++ struct fb_info *info ) ++{ /* TODO do we need sanity checks here */ ++ return 0; ++} ++ ++ ++static int mq200_set_par( struct fb_info *info ) ++{ ++ /* TODO set paraemeter */ ++ return 0; ++} ++ ++static int mq200_setcolreg(unsigned regno, unsigned red, unsigned green, ++ unsigned blue, unsigned transp, ++ struct fb_info *info ) ++{ ++ struct mq200_info *p; ++ unsigned long color; ++ u32* pal = info->pseudo_palette; ++ ++ p = info->par; ++ ++ if(regno > 255 ) ++ return 1; ++ ++ switch( info->var.bits_per_pixel ){ ++ case 16: ++ pal[regno] = ++ ((red & 0xf800) >> 0) | ++ ((green & 0xf800) >> 5) | ((blue & 0xf800) >> 11); ++ break; ++ case 24: ++ pal[regno] = ++ ((red & 0xff00) << 8) | ++ ((green & 0xff00)) | ((blue & 0xff00) >> 8); ++ break; ++ case 32: ++ pal[regno] = ++ ((red & 0xff00) >> 8) | ++ ((green & 0xff00)) | ((blue & 0xff00) << 8); ++ break; ++ default: ++ break; ++ } ++ ++ red &= 0xFF; ++ green &= 0xFF; ++ blue &= 0xFF; ++ ++ color = red | (green << 8) | (blue << 16); ++ mq200_external_setpal(regno, color, p->io_regions.virt_mmio_base); ++ ++ return 0; ++} ++ ++ ++static struct fb_ops mq200_ops = { ++ .owner = THIS_MODULE, ++ .fb_check_var = mq200_check_var, ++ .fb_set_par = mq200_set_par, ++ .fb_setcolreg = mq200_setcolreg, ++#ifdef FB_SOFT_CURSOR ++ .fb_cursor = soft_cursor, /* FIXME use hardware cursor */ ++#endif ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++ .fb_blank = mq200_blank, ++}; ++ ++ ++/********************************************************************* ++ * ++ * Device driver and module init code ++ * this will register to the fb layer later ++ * ++ *********************************************************************/ ++static void mq200_internal_init_color( struct fb_bitfield* red, ++ struct fb_bitfield* green, ++ struct fb_bitfield* blue, ++ int bpp ) ++{ ++ switch ( bpp ) ++ { ++ case 16: ++ red->offset = 11; ++ green->offset = 5; ++ blue->offset = 0; ++ ++ red->length = 5; ++ green->length = 6; ++ blue->length = 5; ++ break; ++ case 24: ++ red->offset = 16; ++ green->offset = 8; ++ blue->offset = 0; ++ ++ red->length = 8; ++ green->length = 8; ++ blue->length = 8; ++ break; ++ case 32: ++ red->offset = 0; ++ green->offset = 8; ++ blue->offset = 16; ++ ++ red->length = 8; ++ green->length = 8; ++ blue->length = 8; ++ case 8: /* fall through */ ++ default: ++ red->offset = green->offset = blue->offset = 0; ++ red->length = green->length = blue->length = bpp; ++ break; ++ } ++ ++} ++ ++ ++static struct mq200_info* __init mq200_internal_init_fbinfo(void) ++{ ++ struct mq200_info *info = NULL; ++ ++ info = (struct mq200_info*)kmalloc(sizeof(*info), GFP_KERNEL); ++ if(!info) ++ return NULL; ++ ++ /* ++ * Initialize memory ++ */ ++ memset(info, 0, sizeof(struct mq200_info) ); ++ spin_lock_init(&info->lock); ++ ++ /* set the base IO addresses */ ++ info->io_regions = MQ200_REGIONS; ++ info->monitor_info = MQ200_MONITOR; ++ ++ info->fb_info.screen_base = (char *)info->io_regions.virt_fb_base; ++ ++ /* fb_fix_screeninfo filling */ ++ strcpy(info->fb_info.fix.id, "MQ200_FB" ); ++ info->fb_info.fix.smem_start = info->io_regions.phys_fb_base; ++ info->fb_info.fix.smem_len = info->io_regions.fb_size; /* - CURSOR_IMAGE */ ++ info->fb_info.fix.mmio_start = info->io_regions.phys_mmio_base; ++ info->fb_info.fix.mmio_len = MQ200_REGS_SIZE; ++ info->fb_info.fix.type = FB_TYPE_PACKED_PIXELS; ++ info->fb_info.fix.accel = FB_ACCEL_NONE; ++ info->fb_info.fix.line_length = MQ200_MONITOR_LINE_LENGTH(info); ++ ++ if(MQ200_MONITOR_DEPTH(info) <= 8 ) ++ info->fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ else if( MQ200_MONITOR_DEPTH(info) >= 16 ) ++ info->fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR; ++ else ++ panic("Calling mq200 with wrong display data\n"); ++ ++ /* set the variable screen info */ ++ info->fb_info.var.xres = MQ200_MONITOR_HORI_RES(info); ++ info->fb_info.var.yres = MQ200_MONITOR_VERT_RES(info); ++ info->fb_info.var.xres_virtual = MQ200_MONITOR_HORI_RES(info); ++ info->fb_info.var.yres_virtual = MQ200_MONITOR_VERT_RES(info); ++ info->fb_info.var.bits_per_pixel = MQ200_MONITOR_DEPTH(info); ++ ++ mq200_internal_init_color(&info->fb_info.var.red, ++ &info->fb_info.var.green, ++ &info->fb_info.var.blue, ++ MQ200_MONITOR_DEPTH(info) ); ++ ++ info->fb_info.var.transp.length = info->fb_info.var.transp.offset = 0; ++ info->fb_info.var.height = info->fb_info.var.width = -1; ++ ++ info->fb_info.var.vmode = FB_VMODE_NONINTERLACED; ++ info->fb_info.var.pixclock = 10000; ++ info->fb_info.var.left_margin = info->fb_info.var.right_margin = 16; ++ info->fb_info.var.upper_margin = info->fb_info.var.lower_margin = 16; ++ info->fb_info.var.hsync_len = info->fb_info.var.vsync_len = 8; ++ ++ info->fb_info.var.nonstd = 0; ++ info->fb_info.var.activate = FB_ACTIVATE_NOW; ++ info->fb_info.var.accel_flags = 0; ++ ++ return info; ++} ++ ++ ++extern void mq200_register_attributes(struct device* ); ++/* ++ * gets called from the bus ++ * we will register our framebuffer from here ++ */ ++static int __init mq200_probe(struct device *dev) ++{ ++ struct mq200_info *info = NULL; ++ int retv= 0; ++ ++ info = mq200_internal_init_fbinfo(); ++ if(!mq200_external_probe(info->io_regions.virt_mmio_base)) ++ goto error_out; ++ ++ GPDR |= (1<<3); ++ GAFR &= ~(1<<3); ++ GPSR |= (1<<3); ++ ++ mq200_external_setqmode(&info->monitor_info, ++ info->io_regions.virt_mmio_base, ++ &info->lock); ++ ++ info->fb_info.fbops = &mq200_ops; ++ info->fb_info.flags = FBINFO_FLAG_DEFAULT; ++ ++ mq200_check_var(&info->fb_info.var, &info->fb_info ); ++ ++ fb_alloc_cmap(&info->fb_info.cmap, 1 << MQ200_MONITOR_DEPTH(info), 0 ); ++ ++ info->fb_info.pseudo_palette = (void*)info->pseudo_palette; ++ ++ /* save the pointer to the mq200 struct in var */ ++ info->fb_info.par = info; ++ ++ retv = register_framebuffer(&info->fb_info ); ++ if(retv < 0) ++ goto error_out; ++ ++ ++ /* will get unset if retv != 0 */ ++ dev_set_drvdata(dev, info ); ++ return retv; ++ ++/* ++ * Free the info and exit ++ */ ++error_out: ++ kfree(info); ++ return -EINVAL; ++} ++ ++#ifdef CONFIG_PM ++static struct mq200_info* get_mq200_info( struct device *dev) ++{ ++ return dev_get_drvdata(dev); ++} ++ ++static unsigned long get_mmio_base( struct device *dev ) ++{ ++ struct mq200_info *info = get_mq200_info(dev); ++ return info->io_regions.virt_mmio_base; ++} ++ ++static struct mq200_monitor_info* get_monitor_info( struct device *dev) ++{ ++ struct mq200_info *info = get_mq200_info(dev); ++ return &info->monitor_info; ++} ++ ++static spinlock_t* get_spinlock( struct device *dev) ++{ ++ return &get_mq200_info(dev)->lock; ++} ++ ++/* ++ * FIXME: make sure we only call mq200_external_offdisplay only once ++ * a 2nd time will hang the kernel -zecke ++ * ++ * FIXME: save the content of the framebuffer inside dev->saved_state ++ * so on resume we can memcpy it back into the buffer and userspace ++ * does not need to redraw ++ * ++ * functions for suspending and resuming ++ */ ++static int mq200_suspend(struct device *dev, pm_message_t state) ++{ ++ ++ mq200_external_offdisplay( get_mmio_base(dev) ); ++ clear_cs3_bit(DISPLAY_ON); ++ ++ ++ return 0; ++} ++ ++static int mq200_resume(struct device *dev) ++{ ++ unsigned long mem = get_mmio_base(dev); ++ struct mq200_monitor_info *monitor = get_monitor_info(dev); ++ mq200_external_setqmode(monitor, mem, get_spinlock(dev) ); ++ ++ ++ /* ++ * Set display on if it was on ++ */ ++ set_cs3_bit(DISPLAY_ON); ++ ++ return 0; ++} ++ ++ ++#endif ++ ++ ++static struct device_driver mq200fb_driver = { ++ .name = "simpad-mq200", ++ .bus = &platform_bus_type, ++ .probe = mq200_probe, /* will be called after we've registered the driver */ ++ .suspend = mq200_suspend, ++ .resume = mq200_resume ++}; ++ ++int __devinit mq200_init(void) ++{ ++ return driver_register(&mq200fb_driver); ++} ++ ++module_init(mq200_init); ++MODULE_DESCRIPTION("MQ200 framebuffer driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-net-shared-irq.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-net-shared-irq.patch new file mode 100644 index 0000000000..e925781ccd --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-net-shared-irq.patch @@ -0,0 +1,63 @@ +diff -uNr linux-2.6.21.vanilla/drivers/net/pcmcia/pcnet_cs.c linux-2.6.21/drivers/net/pcmcia/pcnet_cs.c +--- linux-2.6.21.vanilla/drivers/net/pcmcia/pcnet_cs.c 2007-05-29 21:35:00.000000000 +0200 ++++ linux-2.6.21/drivers/net/pcmcia/pcnet_cs.c 2007-05-30 17:51:21.000000000 +0200 +@@ -26,6 +26,8 @@ + CCAE support. Drivers merged back together, and shared-memory + Socket EA support added, by Ken Raeburn, September 1995. + ++ mrdata: -added shared irq ++ + ======================================================================*/ + + #include <linux/kernel.h> +@@ -254,7 +256,7 @@ + info->p_dev = link; + link->priv = dev; + +- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; ++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; +diff -uNr linux-2.6.21.vanilla/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.21/drivers/net/wireless/hostap/hostap_cs.c +--- linux-2.6.21.vanilla/drivers/net/wireless/hostap/hostap_cs.c 2007-05-29 21:35:00.000000000 +0200 ++++ linux-2.6.21/drivers/net/wireless/hostap/hostap_cs.c 2007-05-30 17:53:20.000000000 +0200 +@@ -1,3 +1,8 @@ ++/* ++ * ++ * mrdata: -added shared irq ++ */ ++ + #define PRISM2_PCCARD + + #include <linux/module.h> +@@ -695,7 +700,7 @@ + * irq structure is initialized. + */ + if (link->conf.Attributes & CONF_ENABLE_IRQ) { +- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; ++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->irq.Handler = prism2_interrupt; + link->irq.Instance = dev; +diff -uNr linux-2.6.21.vanilla/drivers/net/wireless/orinoco_cs.c linux-2.6.21/drivers/net/wireless/orinoco_cs.c +--- linux-2.6.21.vanilla/drivers/net/wireless/orinoco_cs.c 2007-05-29 21:35:00.000000000 +0200 ++++ linux-2.6.21/drivers/net/wireless/orinoco_cs.c 2007-05-30 17:52:19.000000000 +0200 +@@ -8,6 +8,8 @@ + * cards such as the 3Com AirConnect and Ericsson WLAN. + * + * Copyright notice & release notes in file orinoco.c ++ * ++ * mrdata: added shared irq + */ + + #define DRIVER_NAME "orinoco_cs" +@@ -120,7 +122,7 @@ + link->priv = dev; + + /* Interrupt setup */ +- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; ++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->irq.Handler = orinoco_interrupt; + link->irq.Instance = dev; diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-pcmcia.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-pcmcia.patch new file mode 100644 index 0000000000..6ff5e37dcf --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-pcmcia.patch @@ -0,0 +1,162 @@ +diff -uNr linux-2.6.21.vanilla/drivers/pcmcia/cs.c linux-2.6.21/drivers/pcmcia/cs.c +--- linux-2.6.21.vanilla/drivers/pcmcia/cs.c 2007-05-30 18:00:30.000000000 +0200 ++++ linux-2.6.21/drivers/pcmcia/cs.c 2007-05-30 18:10:20.000000000 +0200 +@@ -10,6 +10,8 @@ + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds ++ * ++ * mrdata: -added suspend fix + */ + + #include <linux/module.h> +@@ -60,6 +62,10 @@ + INT_MODULE_PARM(unreset_delay, 10); /* centiseconds */ + INT_MODULE_PARM(unreset_check, 10); /* centiseconds */ + INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */ ++// INT_MODULE_PARM(unreset_delay, 20); /* centiseconds */ ++// INT_MODULE_PARM(unreset_check, 100); /* centiseconds */ ++// INT_MODULE_PARM(unreset_limit, 300); /* unreset_check's */ ++ + + /* Access speed for attribute memory windows */ + INT_MODULE_PARM(cis_speed, 300); /* ns */ +@@ -365,6 +371,7 @@ + skt->ops->set_socket(skt, &skt->socket); + + msleep(unreset_delay * 10); ++ + for (i = 0; i < unreset_limit; i++) { + skt->ops->get_status(skt, &status); + +@@ -430,7 +437,7 @@ + + msleep(initial_delay * 10); + +- for (i = 0; i < 100; i++) { ++ for (i = 0; i < 100; i++) { + skt->ops->get_status(skt, &status); + if (!(status & SS_DETECT)) + return CS_NO_CARD; +diff -uNr linux-2.6.21.vanilla/drivers/pcmcia/sa1100_generic.c linux-2.6.21/drivers/pcmcia/sa1100_generic.c +--- linux-2.6.21.vanilla/drivers/pcmcia/sa1100_generic.c 2007-05-30 18:00:30.000000000 +0200 ++++ linux-2.6.21/drivers/pcmcia/sa1100_generic.c 2007-05-30 18:00:40.000000000 +0200 +@@ -28,6 +28,9 @@ + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + ++ 2007 mrnice: added thesings changes from device_driver ++ to platform_driver - many thx to thesing ++ + ======================================================================*/ + + #include <linux/module.h> +@@ -81,13 +84,15 @@ + return ret; + } + +-static struct device_driver sa11x0_pcmcia_driver = { +- .probe = sa11x0_drv_pcmcia_probe, +- .remove = soc_common_drv_pcmcia_remove, +- .name = "sa11x0-pcmcia", +- .bus = &platform_bus_type, +- .suspend = pcmcia_socket_dev_suspend, +- .resume = pcmcia_socket_dev_resume, ++static struct platform_driver sa11x0_pcmcia_driver = { ++ .driver = { ++ .name = "sa11x0-pcmcia", ++ .probe = sa11x0_drv_pcmcia_probe, ++ .remove = soc_common_drv_pcmcia_remove, ++ .suspend= pcmcia_socket_dev_suspend, ++ .resume = pcmcia_socket_dev_resume, ++ //.bus = &platform_bus_type, ++ }, + }; + + /* sa11x0_pcmcia_init() +@@ -100,7 +105,7 @@ + */ + static int __init sa11x0_pcmcia_init(void) + { +- return driver_register(&sa11x0_pcmcia_driver); ++ return platform_driver_register(&sa11x0_pcmcia_driver); + } + + /* sa11x0_pcmcia_exit() +@@ -110,7 +115,7 @@ + */ + static void __exit sa11x0_pcmcia_exit(void) + { +- driver_unregister(&sa11x0_pcmcia_driver); ++ platform_driver_unregister(&sa11x0_pcmcia_driver); + } + + MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>"); +diff -uNr linux-2.6.21.vanilla/drivers/pcmcia/sa1100_simpad.c linux-2.6.21/drivers/pcmcia/sa1100_simpad.c +--- linux-2.6.21.vanilla/drivers/pcmcia/sa1100_simpad.c 2007-05-30 18:00:30.000000000 +0200 ++++ linux-2.6.21/drivers/pcmcia/sa1100_simpad.c 2007-05-30 18:08:53.000000000 +0200 +@@ -3,6 +3,8 @@ + * + * PCMCIA implementation routines for simpad + * ++ * mrdata: -added cs3_ro stuff to get fix voltage issue ++ * + */ + #include <linux/module.h> + #include <linux/kernel.h> +@@ -14,7 +16,8 @@ + #include <asm/irq.h> + #include <asm/arch/simpad.h> + #include "sa1100_generic.h" +- ++ ++extern long get_cs3_ro(void); + extern long get_cs3_shadow(void); + extern void set_cs3_bit(int value); + extern void clear_cs3_bit(int value); +@@ -25,7 +28,6 @@ + + static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt) + { +- + clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); + + skt->irq = IRQ_GPIO_CF_IRQ; +@@ -47,21 +49,15 @@ + struct pcmcia_state *state) + { + unsigned long levels = GPLR; +- long cs3reg = get_cs3_shadow(); ++ long cs3_ro_reg = get_cs3_ro(); + +- state->detect=((levels & GPIO_CF_CD)==0)?1:0; +- state->ready=(levels & GPIO_CF_IRQ)?1:0; +- state->bvd1=1; /* Not available on Simpad. */ +- state->bvd2=1; /* Not available on Simpad. */ +- state->wrprot=0; /* Not available on Simpad. */ +- +- if((cs3reg & 0x0c) == 0x0c) { +- state->vs_3v=0; +- state->vs_Xv=0; +- } else { +- state->vs_3v=1; +- state->vs_Xv=0; +- } ++ state->detect = ((levels & GPIO_CF_CD) == 0) ? 1 : 0 ; ++ state->ready = (levels & GPIO_CF_IRQ) ? 1 : 0 ; ++ state->bvd1 = (cs3_ro_reg & PCMCIA_BVD1) ? 1 : 0 ; /* alt: =1 Not available on Simpad. */ ++ state->bvd2 = (cs3_ro_reg & PCMCIA_BVD2) ? 1 : 0 ; /* alt: =1 Not available on Simpad. */ ++ state->wrprot = 0 ; /* Not available on Simpad. */ ++ state->vs_3v = (cs3_ro_reg & PCMCIA_VS1) ? 0 : 1 ; ++ state->vs_Xv = (cs3_ro_reg & PCMCIA_VS2) ? 0 : 1 ; + } + + static int +@@ -104,6 +100,7 @@ + + static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt) + { ++ clear_cs3_bit(PCMCIA_RESET); + soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); + } + diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-serial-gpio_keys-and-cs3-ro.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-serial-gpio_keys-and-cs3-ro.patch new file mode 100644 index 0000000000..081afc8936 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-serial-gpio_keys-and-cs3-ro.patch @@ -0,0 +1,332 @@ +diff -uNr linux-2.6.21.vanilla/arch/arm/mach-sa1100/simpad.c linux-2.6.21/arch/arm/mach-sa1100/simpad.c +--- linux-2.6.21.vanilla/arch/arm/mach-sa1100/simpad.c 2007-05-30 18:00:29.000000000 +0200 ++++ linux-2.6.21/arch/arm/mach-sa1100/simpad.c 2007-05-30 18:02:15.000000000 +0200 +@@ -1,5 +1,15 @@ + /* + * linux/arch/arm/mach-sa1100/simpad.c ++ * ++ * 2007/04/11 mrdata: ++ * - insert simpad_uart_set_mctrl() ++ * simpad_uart_get_mctrl() ++ * - internal RS232/DECT/Bluetooth ++ * works again (based on 2.4 simpad-serial.patch) ++ * - added cs3_ro ++ * ++ * 2007/04/12 Bernhard Guillon: ++ * -added gpio_keys (based on h3000.c from hh.org) + */ + + #include <linux/module.h> +@@ -9,6 +19,9 @@ + #include <linux/proc_fs.h> + #include <linux/string.h> + #include <linux/pm.h> ++ ++#include <linux/apm-emulation.h> ++ + #include <linux/platform_device.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> +@@ -27,12 +40,21 @@ + + #include <linux/serial_core.h> + #include <linux/ioport.h> ++#include <linux/input.h> ++#include <linux/gpio_keys.h> + #include <asm/io.h> + + #include "generic.h" + ++long cs3_ro; + long cs3_shadow; + ++long get_cs3_ro(void) ++{ ++ cs3_ro = *(CS3BUSTYPE *)(CS3_BASE); ++ return cs3_ro; ++} ++ + long get_cs3_shadow(void) + { + return cs3_shadow; +@@ -55,9 +77,12 @@ + *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow; + } + ++EXPORT_SYMBOL(get_cs3_ro); ++EXPORT_SYMBOL(get_cs3_shadow); + EXPORT_SYMBOL(set_cs3_bit); + EXPORT_SYMBOL(clear_cs3_bit); + ++ + static struct map_desc simpad_io_desc[] __initdata = { + { /* MQ200 */ + .virtual = 0xf2800000, +@@ -73,23 +98,71 @@ + }; + + ++static void simpad_uart_set_mctrl(struct uart_port *port, u_int mctrl) ++{ ++ if (port->mapbase == _Ser1UTCR0) { ++ /* internal serial port (ttySA1, DECT/Bluetooth) */ ++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART1_RTS; ++ else GPSR = GPIO_UART1_RTS; ++ ++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART1_DTR; ++ else GPSR = GPIO_UART1_DTR; ++ } ++ ++ else if (port->mapbase == _Ser3UTCR0) { ++ /* external serial port (ttySA0, RS232) */ ++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART3_RTS; ++ else GPSR = GPIO_UART3_RTS; ++ ++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART3_DTR; ++ else GPSR = GPIO_UART3_DTR; ++ } ++} ++ ++ ++static u_int simpad_uart_get_mctrl(struct uart_port *port) ++{ ++ u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; ++ ++ if (port->mapbase == _Ser1UTCR0) { ++ /* internal serial port (ttySA1, DECT/Bluetooth) */ ++ int gplr = GPLR; ++ if (gplr & GPIO_UART1_DCD) ret &= ~TIOCM_CD; ++ if (gplr & GPIO_UART1_CTS) ret &= ~TIOCM_CTS; ++ if (gplr & GPIO_UART1_DSR) ret &= ~TIOCM_DSR; ++ } ++ ++ else if (port->mapbase == _Ser3UTCR0) { ++ /* external serial port (ttySA0, RS232) */ ++ int gplr = GPLR; ++ if (gplr & GPIO_UART3_DCD) ret &= ~TIOCM_CD; ++ if (gplr & GPIO_UART3_CTS) ret &= ~TIOCM_CTS; ++ if (gplr & GPIO_UART3_DSR) ret &= ~TIOCM_DSR; ++ } ++ return ret; ++} ++ ++ + static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate) + { +- if (port->mapbase == (u_int)&Ser1UTCR0) { +- if (state) +- { +- clear_cs3_bit(RS232_ON); +- clear_cs3_bit(DECT_POWER_ON); +- }else +- { +- set_cs3_bit(RS232_ON); +- set_cs3_bit(DECT_POWER_ON); +- } +- } ++ if (port->mapbase == (u_int)&Ser3UTCR0) { ++ if (state) ++ { ++ clear_cs3_bit(RS232_ON); ++ /* clear_cs3_bit(DECT_POWER_ON); */ ++ }else ++ { ++ set_cs3_bit(RS232_ON); ++ /* set_cs3_bit(DECT_POWER_ON); */ ++ } ++ } + } + ++ + static struct sa1100_port_fns simpad_port_fns __initdata = { +- .pm = simpad_uart_pm, ++ .set_mctrl = simpad_uart_set_mctrl, ++ .get_mctrl = simpad_uart_get_mctrl, ++ .pm = simpad_uart_pm, + }; + + +@@ -135,7 +208,6 @@ + }; + + +- + static void __init simpad_map_io(void) + { + sa1100_map_io(); +@@ -144,13 +216,12 @@ + + set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON | + ENABLE_5V | RESET_SIMCARD | DECT_POWER_ON); +- +- ++ + sa1100_register_uart_fns(&simpad_port_fns); + sa1100_register_uart(0, 3); /* serial interface */ + sa1100_register_uart(1, 1); /* DECT */ + +- // Reassign UART 1 pins ++ /* Reassign UART 1 pins */ + GAFR |= GPIO_UART_TXD | GPIO_UART_RXD; + GPDR |= GPIO_UART_TXD | GPIO_LDD13 | GPIO_LDD15; + GPDR &= ~GPIO_UART_RXD; +@@ -160,7 +231,6 @@ + * Set up registers for sleep mode. + */ + +- + PWER = PWER_GPIO0| PWER_RTC; + PGSR = 0x818; + PCFR = 0; +@@ -171,9 +241,10 @@ + sa11x0_set_mcp_data(&simpad_mcp_data); + } + ++ + static void simpad_power_off(void) + { +- local_irq_disable(); // was cli ++ local_irq_disable(); /* was cli */ + set_cs3(0x800); /* only SD_MEDIAQ */ + + /* disable internal oscillator, float CS lines */ +@@ -191,31 +262,52 @@ + while(1); + + local_irq_enable(); /* we won't ever call it */ ++} + + +-} ++/* ++ * gpio_keys ++*/ ++ ++static struct gpio_keys_button simpad_button_table[] = { ++ { KEY_POWER, IRQ_GPIO_POWER_BUTTON, 1, "power button" }, ++}; ++ ++static struct gpio_keys_platform_data simpad_keys_data = { ++ .buttons = simpad_button_table, ++ .nbuttons = ARRAY_SIZE(simpad_button_table), ++}; ++ ++static struct platform_device simpad_keys = { ++ .name = "gpio-keys", ++ .dev = { ++ .platform_data = &simpad_keys_data, ++ }, ++}; + + + /* + * MediaQ Video Device + */ ++ + static struct platform_device simpad_mq200fb = { + .name = "simpad-mq200", + .id = 0, + }; + ++ + static struct platform_device *devices[] __initdata = { +- &simpad_mq200fb ++ &simpad_keys, ++ &simpad_mq200fb, + }; + + +- + static int __init simpad_init(void) + { + int ret; + + pm_power_off = simpad_power_off; +- ++ + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); + if(ret) + printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device"); +diff -uNr linux-2.6.21.vanilla/include/asm-arm/arch-sa1100/simpad.h linux-2.6.21/include/asm-arm/arch-sa1100/simpad.h +--- linux-2.6.21.vanilla/include/asm-arm/arch-sa1100/simpad.h 2007-05-30 18:00:30.000000000 +0200 ++++ linux-2.6.21/include/asm-arm/arch-sa1100/simpad.h 2007-05-30 18:02:56.000000000 +0200 +@@ -12,11 +12,12 @@ + #define __ASM_ARCH_SIMPAD_H + + +-#define GPIO_UART1_RTS GPIO_GPIO14 ++#define GPIO_UART1_RTS GPIO_GPIO9 + #define GPIO_UART1_DTR GPIO_GPIO7 + #define GPIO_UART1_CTS GPIO_GPIO8 + #define GPIO_UART1_DCD GPIO_GPIO23 + #define GPIO_UART1_DSR GPIO_GPIO6 ++#define GPIO_UART1_RI GPIO_GPIO19 + + #define GPIO_UART3_RTS GPIO_GPIO12 + #define GPIO_UART3_DTR GPIO_GPIO16 +@@ -48,9 +49,9 @@ + #define GPIO_SMART_CARD GPIO_GPIO10 + #define IRQ_GPIO_SMARD_CARD IRQ_GPIO10 + +-// CS3 Latch is write only, a shadow is necessary ++// CS3 Latch is write only 16-bit , a shadow is necessary + +-#define CS3BUSTYPE unsigned volatile long ++#define CS3BUSTYPE unsigned volatile long + #define CS3_BASE 0xf1000000 + + #define VCC_5V_EN 0x0001 // For 5V PCMCIA +@@ -70,43 +71,17 @@ + #define ENABLE_5V 0x4000 // Enable 5V circuit + #define RESET_SIMCARD 0x8000 + +-#define RS232_ENABLE 0x0440 +-#define PCMCIAMASK 0x402f ++// CS3 Latch is readable only 8-bit interest + ++#define PCMCIA_BVD1 0x0001 ++#define PCMCIA_BVD2 0x0002 ++#define PCMCIA_VS1 0x0004 // PCMCIA card voltage select ++#define PCMCIA_VS2 0x0008 // PCMCIA card voltage select, if both are in high state -> 5V PCMCIA card ++#define LOCK_IND 0x0010 ++#define CHARGING_STATE 0x0020 // Ladestatus ++#define PCMCIA_SHORT 0x0040 // low active + +-struct simpad_battery { +- unsigned char ac_status; /* line connected yes/no */ +- unsigned char status; /* battery loading yes/no */ +- unsigned char percentage; /* percentage loaded */ +- unsigned short life; /* life till empty */ +-}; +- +-/* These should match the apm_bios.h definitions */ +-#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00 +-#define SIMPAD_AC_STATUS_AC_ONLINE 0x01 +-#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */ +-#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff +- +-/* These bitfields are rarely "or'd" together */ +-#define SIMPAD_BATT_STATUS_HIGH 0x01 +-#define SIMPAD_BATT_STATUS_LOW 0x02 +-#define SIMPAD_BATT_STATUS_CRITICAL 0x04 +-#define SIMPAD_BATT_STATUS_CHARGING 0x08 +-#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10 +-#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */ +-#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */ +-#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */ +-#define SIMPAD_BATT_STATUS_NOBATT 0x80 +-#define SIMPAD_BATT_STATUS_UNKNOWN 0xff +- +-extern int simpad_get_battery(struct simpad_battery* ); ++#define RS232_ENABLE 0x0440 ++#define PCMCIAMASK 0x402f + + #endif // __ASM_ARCH_SIMPAD_H +- +- +- +- +- +- +- +- diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-switches.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-switches.patch new file mode 100644 index 0000000000..552bb8b223 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-switches.patch @@ -0,0 +1,359 @@ +diff -uNr linux-2.6.21.vanilla/drivers/mfd/Kconfig linux-2.6.21/drivers/mfd/Kconfig +--- linux-2.6.21.vanilla/drivers/mfd/Kconfig 2007-04-26 05:08:32.000000000 +0200 ++++ linux-2.6.21/drivers/mfd/Kconfig 2007-05-29 12:56:32.000000000 +0200 +@@ -37,4 +37,7 @@ + tristate "Touchscreen interface support" + depends on MCP_UCB1200 && INPUT + ++config MCP_UCB1200_SWITCHES ++ tristate "SIMpad Switches support" ++ depends on MCP_UCB1200 && INPUT + endmenu +diff -uNr linux-2.6.21.vanilla/drivers/mfd/Makefile linux-2.6.21/drivers/mfd/Makefile +--- linux-2.6.21.vanilla/drivers/mfd/Makefile 2007-04-26 05:08:32.000000000 +0200 ++++ linux-2.6.21/drivers/mfd/Makefile 2007-05-29 12:56:32.000000000 +0200 +@@ -8,7 +8,7 @@ + obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o + obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o +- ++obj-$(CONFIG_MCP_UCB1200_SWITCHES) += ucb1x00-switches.o + ifeq ($(CONFIG_SA1100_ASSABET),y) + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o + endif +diff -Naur linux-2.6.21/drivers/mfd.old/ucb1x00-switches.c linux-2.6.21/drivers/mfd/ucb1x00-switches.c +--- linux-2.6.21/drivers/mfd/ucb1x00-switches.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/mfd/ucb1x00-switches.c 2007-07-04 23:59:39.000000000 +0200 +@@ -0,0 +1,332 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-switches.c ++ * ++ * Copyright (C) 2007 Bernhard Guillon. ++ * ++ * 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. ++ * ++ * This driver is for the Switches of Siemens SIMpad (CL4,SL4,SLC), T-Sinus-Pad and ++ * Swisscom WP50 devices. ++ * ++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8. ++ * ++ * This driver is based on the 2.4 ucb1x00-switches, the 2.6 ucb1x00-assabet ++ * and the ucb1x00-ts driver. ++ * ++ * 2007/06/21 mrdata: ++ * - create new thread kswd() to handle irq_events for ucb1300-gpio's ++ * - found out, that not every key-press or key-release ++ * generate a irq_event ++ * -> establish key_state handling ++ * key_state, key_state_last <-> KEY_PRESS, KEY_RELEASE ++ * -> after irq_event polling the ucb1300-gpio's till all keys ++ * in key_state = KEY_RELEASE ++ * ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/device.h> ++#include <linux/sched.h> ++#include <linux/freezer.h> ++#include <linux/kthread.h> ++ ++#include <asm/dma.h> ++ ++#include "ucb1x00.h" ++ ++#define KEY_PRESS 1 ++#define KEY_RELEASE 0 ++ ++static int key [6] = { KEY_PROG1,KEY_PROG2,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT }; ++ ++static unsigned short int key_state [6] = { 0, 0, 0, 0, 0, 0}; ++static unsigned short int key_state_last [6] = { 1, 1, 1, 1, 1, 1}; ++ ++struct ucb1x00_switches { ++ struct input_dev *idev; ++ struct ucb1x00 *ucb; ++ ++ wait_queue_head_t irq_wait; ++ struct task_struct *rtask; ++ ++ int idx; ++ ++ unsigned int valid:1; ++}; ++ ++static int ucb1x00_thread(void *_switches_id) ++{ ++ unsigned short int this; ++ int idx_tmp; ++ int i; ++ struct ucb1x00_switches *switches = _switches_id; ++ struct input_dev *idev = switches->idev; ++ struct task_struct *tsk = current; ++ DECLARE_WAITQUEUE(wait, tsk); ++ ++ add_wait_queue(&switches->irq_wait, &wait); ++ ++ while (!kthread_should_stop()) ++ { ++ signed long timeout; ++ ++ if ((switches->idx >= 0) && (switches->idx <= 5) && (switches->valid == 1)) ++ { ++ switches->valid = 0; ++ ++ idx_tmp = switches->idx; ++ ++ ucb1x00_enable(switches->ucb); ++ ++ this = ~ucb1x00_io_read(switches->ucb); ++ ++ ucb1x00_disable(switches->ucb); ++ ++ if (key_state[idx_tmp] == KEY_RELEASE) ++ { ++ key_state_last[idx_tmp] = KEY_RELEASE; ++ key_state[idx_tmp] = KEY_PRESS; ++ ++ input_report_key(idev, key[idx_tmp], KEY_PRESS); ++ input_sync(idev); ++ } ++ ++ for (i = 0; i < 6; i++) ++ { ++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS)) ++ { ++ key_state_last[i] = KEY_RELEASE; ++ key_state[i] = KEY_PRESS; ++ ++ input_report_key(idev, key[i], KEY_PRESS); ++ input_sync(idev); ++ } ++ } ++ ++ for(;;) ++ { ++ ucb1x00_enable(switches->ucb); ++ this = ~ucb1x00_io_read(switches->ucb); ++ ucb1x00_disable(switches->ucb); ++ ++ for (i = 0; i < 6; i++) ++ { ++ if ((key_state[i] == KEY_PRESS) && (((this & (1 << i)) ? 1 : 0) == KEY_RELEASE)) ++ { ++ key_state_last[i] = KEY_PRESS; ++ key_state[i] = KEY_RELEASE; ++ ++ input_report_key(idev, key[i], KEY_RELEASE); ++ input_sync(idev); ++ } ++ ++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS)) ++ { ++ key_state_last[i] = KEY_RELEASE; ++ key_state[i] = KEY_PRESS; ++ ++ input_report_key(idev, key[i], KEY_PRESS); ++ input_sync(idev); ++ } ++ ++ } ++ ++ // left loop, if no key press detect ++ if ((this | 0xff80) == 0xff80) ++ { ++ break; ++ } ++ ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ ++ try_to_freeze(); ++ ++ timeout = HZ / 100; ++ ++ schedule_timeout(timeout); ++ } ++ } ++ ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ ++ try_to_freeze(); ++ ++ timeout = MAX_SCHEDULE_TIMEOUT; ++ ++ schedule_timeout(timeout); ++ } ++ ++ remove_wait_queue(&switches->irq_wait, &wait); ++ ++ switches->rtask = NULL; ++ ++ return 0; ++} ++ ++ ++static void ucb1x00_dev_irq(int idx, void *id) ++{ ++ struct ucb1x00_switches *switches = id; ++ ++ switches->idx = idx; ++ switches->valid = 1; ++ ++ wake_up(&switches->irq_wait); ++} ++ ++static int ucb1x00_switches_add(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_switches *switches; ++ struct input_dev *idev; ++ int err,i; ++ ++ switches = kzalloc(sizeof(struct ucb1x00_switches), GFP_KERNEL); ++ idev = input_allocate_device(); ++ ++ if (!switches || !idev) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ switches->ucb = dev->ucb; ++ ++ idev->private = switches; ++ idev->name = "SIMpad Switches"; ++ idev->id.product = switches->ucb->id; ++ ++ __set_bit(EV_KEY, idev->evbit); ++ __set_bit(EV_REP, idev->evbit); ++ __set_bit(KEY_PROG1, idev->keybit); ++ __set_bit(KEY_PROG2, idev->keybit); ++ __set_bit(KEY_UP, idev->keybit); ++ __set_bit(KEY_DOWN, idev->keybit); ++ __set_bit(KEY_LEFT, idev->keybit); ++ __set_bit(KEY_RIGHT, idev->keybit); ++ ++ err = input_register_device(idev); ++ if (err) ++ goto fail; ++ switches->idev = idev; ++ dev->priv = switches; ++ ++ BUG_ON(switches->rtask); ++ ++ init_waitqueue_head(&switches->irq_wait); ++ ++ ucb1x00_enable(switches->ucb); ++ ++ ucb1x00_io_set_dir(switches->ucb, ++ UCB_IO_0 | UCB_IO_1 | UCB_IO_2 | ++ UCB_IO_3 | UCB_IO_4 | UCB_IO_5, ++ UCB_IO_8 | UCB_IO_9); ++ ++ ucb1x00_disable(switches->ucb); ++ ++ for (i = 0; i < 6; ++i) { ++ ucb1x00_enable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ if (ucb1x00_hook_irq(switches->ucb, i, ucb1x00_dev_irq, switches) < 0) { ++ printk(KERN_ERR "unable to hook IRQ for " ++ "UCB1300 SWITCH_%d\n", i); ++ return -EBUSY; ++ } ++ } ++ ++ switches->rtask = kthread_run(ucb1x00_thread, switches, "kswd"); ++ if (!IS_ERR(switches->rtask)) ++ { ++ return 0; ++ } ++ else ++ { ++ input_unregister_device(switches->idev); ++ ++ for (i = 5; i >= 0; --i) { ++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ /* Only error conditions are ENOENT and EINVAL; silently ++ * ignore: ++ */ ++ ucb1x00_free_irq(switches->ucb, i, NULL); ++ } ++ switches->rtask = NULL; ++ ucb1x00_disable(switches->ucb); ++ kfree(switches); ++ ++ return -EFAULT; ++ } ++ ++fail: ++ input_free_device(idev); ++ kfree(switches); ++ return err; ++ ++} ++ ++static void ucb1x00_switches_remove(struct ucb1x00_dev *dev) ++{ ++ int i; ++ struct ucb1x00_switches *switches = dev->priv; ++ ++ if (switches->rtask) ++ kthread_stop(switches->rtask); ++ ++ switches->rtask = NULL; ++ ++ input_unregister_device(switches->idev); ++ ++ for (i = 5; i >= 0; --i) { ++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ /* Only error conditions are ENOENT and EINVAL; silently ++ * ignore: ++ */ ++ ucb1x00_free_irq(switches->ucb, i, NULL); ++ } ++ ucb1x00_disable(switches->ucb); ++ kfree(switches); ++} ++ ++#ifdef CONFIG_PM ++static int ucb1x00_switches_resume(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_switches *switches = dev->priv; ++ ++ if (switches->rtask != NULL) ++ { ++ switches->valid = 0; ++ wake_up(&switches->irq_wait); ++ ++ printk(KERN_DEBUG "ucb1x00-switches.c -> _switches_resume() kswd - restart *DONE*\n"); ++ } ++ return 0; ++} ++#else ++#define ucb1x00_switches_resume NULL ++#endif ++ ++static struct ucb1x00_driver ucb1x00_switches_driver = { ++ .add = ucb1x00_switches_add, ++ .remove = ucb1x00_switches_remove, ++ .resume = ucb1x00_switches_resume, ++}; ++ ++static int __init ucb1x00_switches_init(void) ++{ ++ return ucb1x00_register_driver(&ucb1x00_switches_driver); ++} ++ ++static void __exit ucb1x00_switches_exit(void) ++{ ++ ucb1x00_unregister_driver(&ucb1x00_switches_driver); ++} ++ ++module_init(ucb1x00_switches_init); ++module_exit(ucb1x00_switches_exit); ++ ++MODULE_AUTHOR("Bernhard Guillon <Bernhard.Guillon@opensimpad.org>"); ++MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad"); ++MODULE_LICENSE("GPL"); diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-ts-supend-and-accuracy.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-ts-supend-and-accuracy.patch new file mode 100644 index 0000000000..8c2c89235f --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-ucb1x00-ts-supend-and-accuracy.patch @@ -0,0 +1,121 @@ +diff -uNr linux-2.6.21.vanilla/drivers/mfd/ucb1x00-ts.c linux-2.6.21/drivers/mfd/ucb1x00-ts.c +--- linux-2.6.21.vanilla/drivers/mfd/ucb1x00-ts.c 2007-05-30 18:00:30.000000000 +0200 ++++ linux-2.6.21/drivers/mfd/ucb1x00-ts.c 2007-05-30 18:14:42.000000000 +0200 +@@ -16,6 +16,10 @@ + * It is important to note that the signal connected to the ADCSYNC + * pin should provide pulses even when the LCD is blanked, otherwise + * a pen touch needed to unblank the LCD will never be read. ++ * ++ * mrdata: -added some accuracy improvement based on thesings collie patch ++ * -added suspend fix ++ * + */ + #include <linux/module.h> + #include <linux/moduleparam.h> +@@ -105,6 +109,8 @@ + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ++ udelay(55); ++ + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); + } + } +@@ -131,7 +137,7 @@ + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + +- udelay(55); ++ udelay(165); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); + } +@@ -159,7 +165,7 @@ + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + +- udelay(55); ++ udelay(165); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); + } +@@ -225,13 +231,17 @@ + signed long timeout; + + ts->restart = 0; +- ++ + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ucb1x00_adc_enable(ts->ucb); + y = ucb1x00_ts_read_ypos(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ucb1x00_adc_enable(ts->ucb); + p = ucb1x00_ts_read_pressure(ts); +- ++ + /* + * Switch back to interrupt mode. + */ +@@ -240,15 +250,19 @@ + + msleep(10); + ++ if ((x < 60) || (y < 60)) { ++ p = 0; ++ } ++ + ucb1x00_enable(ts->ucb); + +- + if (ucb1x00_ts_pen_down(ts)) { ++ + set_task_state(tsk, TASK_INTERRUPTIBLE); + + ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); + ucb1x00_disable(ts->ucb); +- ++ + /* + * If we spat out a valid sample set last time, + * spit out a "pen off" sample here. +@@ -259,7 +273,9 @@ + } + + timeout = MAX_SCHEDULE_TIMEOUT; ++ + } else { ++ + ucb1x00_disable(ts->ucb); + + /* +@@ -278,6 +294,14 @@ + + try_to_freeze(); + ++ /* ++ * While suspend the ktsd-thread goes sleep -> try_to_freeze() ++ * While resume the ktsd-thread do wakup and must rune one time ++ * again to do a clean re-setup -> enable_irq: UCB_IRQ_TSPX ++ */ ++ if(ts->restart) ++ timeout = HZ / 100; ++ + schedule_timeout(timeout); + } + +@@ -360,8 +384,12 @@ + * TS interrupt mode is set up again + * after sleep. + */ ++ + ts->restart = 1; + wake_up(&ts->irq_wait); ++ ++ printk(KERN_INFO "ucb1x00-ts.c -> ucb1x00_ts_resume() ktsd - restart *DONE*\n"); ++ + } + return 0; + } diff --git a/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-usb-gadget.patch b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-usb-gadget.patch new file mode 100644 index 0000000000..00d062bd7d --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.21-SIMpad-usb-gadget.patch @@ -0,0 +1,3329 @@ +diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/Kconfig linux-2.6.21/drivers/usb/gadget/Kconfig +--- linux-2.6.21.vanilla/drivers/usb/gadget/Kconfig 2007-04-26 05:08:32.000000000 +0200 ++++ linux-2.6.21/drivers/usb/gadget/Kconfig 2007-06-05 11:58:28.000000000 +0200 +@@ -205,6 +205,21 @@ + depends on USB_GADGET_AT91 + default USB_GADGET + ++config USB_GADGET_SA1100 ++ boolean "SA1100 USB Device Port" ++ depends on ARCH_SA1100 ++ select USB_GADGET_SELECTED ++ help ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "sa1100_udc" and force all ++ gadget drivers to also be dynamically linked. ++ ++config USB_SA1100 ++ tristate ++ depends on USB_GADGET_SA1100 ++ default USB_GADGET ++ + config USB_GADGET_DUMMY_HCD + boolean "Dummy HCD (DEVELOPMENT)" + depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL +diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/Makefile linux-2.6.21/drivers/usb/gadget/Makefile +--- linux-2.6.21.vanilla/drivers/usb/gadget/Makefile 2007-04-26 05:08:32.000000000 +0200 ++++ linux-2.6.21/drivers/usb/gadget/Makefile 2007-06-05 11:58:28.000000000 +0200 +@@ -8,6 +8,7 @@ + obj-$(CONFIG_USB_OMAP) += omap_udc.o + obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o + obj-$(CONFIG_USB_AT91) += at91_udc.o ++obj-$(CONFIG_USB_SA1100) += sa1100_udc.o + + # + # USB gadget drivers +diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/ether.c linux-2.6.21/drivers/usb/gadget/ether.c +--- linux-2.6.21.vanilla/drivers/usb/gadget/ether.c 2007-04-26 05:08:32.000000000 +0200 ++++ linux-2.6.21/drivers/usb/gadget/ether.c 2007-06-05 11:59:24.000000000 +0200 +@@ -1456,7 +1456,7 @@ + goto done_set_intf; + } + +-#ifdef DEV_CONFIG_CDC ++//#ifdef DEV_CONFIG_CDC + switch (wIndex) { + case 0: /* control/master intf */ + if (wValue != 0) +@@ -1498,12 +1498,12 @@ + value = 0; + break; + } +-#else ++//#else + /* FIXME this is wrong, as is the assumption that + * all non-PXA hardware talks real CDC ... + */ +- dev_warn (&gadget->dev, "set_interface ignored!\n"); +-#endif /* DEV_CONFIG_CDC */ ++// dev_warn (&gadget->dev, "set_interface ignored!\n"); ++//#endif /* DEV_CONFIG_CDC */ + + done_set_intf: + spin_unlock (&dev->lock); +diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/sa1100_udc.c linux-2.6.21/drivers/usb/gadget/sa1100_udc.c +--- linux-2.6.21.vanilla/drivers/usb/gadget/sa1100_udc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/usb/gadget/sa1100_udc.c 2007-06-05 11:58:29.000000000 +0200 +@@ -0,0 +1,2591 @@ ++/* ++ * SA1100 USB Device Controller (UDC) driver. ++ * ++ * Copyright (C) Compaq Computer Corporation, 1998, 1999 ++ * Copyright (C) Extenex Corporation, 2001 ++ * Copyright (C) David Brownell, 2003 ++ * Copyright (C) Nick Bane, 2005, 2006 ++ * Many fragments from pxa2xx_udc.c and mach-sa1100 driver with various ++ * GPL Copyright authors incl Russel king and Nicolas Pitre ++ * Port to 2.6.12 by N C Bane ++ * ++ * This file provides interrupt routing and overall coordination for the ++ * sa1100 USB endpoints: ep0, ep1out-bulk, ep2out-bulk, as well as device ++ * initialization and some parts of USB "Chapter 9" device behavior. ++ * ++ * It implements the "USB gadget controller" API, abstracting most hardware ++ * details so that drivers running on top of this API are mostly independent ++ * of hardware. A key exception is that ep0 logic needs to understand which ++ * endpoints a given controller has, and their capabilities. Also, hardware ++ * that doesn't fully support USB (like sa1100) may need workarounds in the ++ * protocols implemented by device functions. ++ * ++ * See linux/Documentation/arm/SA1100/SA1100_USB for more info, or the ++ * kerneldoc for the API exposed to gadget drivers. ++ * ++ */ ++#define DEBUG 1 ++#define VERBOSE 1 ++ ++//#define SA1100_USB_DEBUG ++#ifdef SA1100_USB_DEBUG ++static int sa1100_usb_debug=0; ++#endif ++ ++#define NCB_DMA_FIX ++#ifdef NCB_DMA_FIX ++#include <linux/slab.h> ++#define SEND_BUFFER_SIZE 4096 /* this is probably a bit big */ ++#define RECEIVE_BUFFER_SIZE 256 /* 64 may be all that is necessary */ ++static char *send_buffer=NULL; ++static char *receive_buffer=NULL; ++#endif ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/ioport.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/smp_lock.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/timer.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/version.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++ ++#include <asm/byteorder.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/dma.h> ++#include <asm/system.h> ++#include <asm/mach-types.h> ++#include <asm/unaligned.h> ++ ++#include <linux/usb.h>
++#include <linux/usb_ch9.h> ++#include <linux/usb_gadget.h> ++ ++#if CONFIG_PROC_FS
++#include <linux/proc_fs.h> ++#endif ++ ++#if defined(CONFIG_SA1100_BALLOON) ++#include <asm/arch/balloon.h> ++#endif ++ ++#define DRIVER_VERSION __DATE__ ++ ++#define DMA_ADDR_INVALID (~(dma_addr_t)0) ++ ++ ++static const char driver_name [] = "sa1100_udc"; ++static const char driver_desc [] = "SA-1110 USB Device Controller"; ++ ++static const char ep0name [] = "ep0"; ++ ++#ifdef DEBUG ++static char *type_string (u8 bmAttributes) ++{ ++ switch ( (bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: return "bulk"; ++ //case USB_ENDPOINT_XFER_ISOC: return "iso"; ++ case USB_ENDPOINT_XFER_INT: return "intr"; ++ }; ++ return "control"; ++} ++#endif ++ ++#include <linux/dma-mapping.h> ++struct usb_stats_t { ++ unsigned long ep0_fifo_write_failures; ++ unsigned long ep0_bytes_written; ++ unsigned long ep0_fifo_read_failures; ++ unsigned long ep0_bytes_read; ++}; ++ ++struct usb_info_t ++{ ++ dma_regs_t *dmaregs_tx, *dmaregs_rx; ++ int state; ++ unsigned char address; ++ struct usb_stats_t stats; ++}; ++ ++enum { kError=-1, kEvSuspend=0, kEvReset=1, ++ kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 }; ++int usbctl_next_state_on_event( int event ) { ++ return 0; ++} ++static struct usb_info_t usbd_info; ++ ++/* receiver */ ++void ep1_reset(void); ++void ep1_stall(void); ++int sa1100_usb_recv (struct usb_request *req, void (*callback) (int,int)); ++ ++/* xmitter */ ++void ep2_reset(void); ++void ep2_stall(void); ++int sa1100_usb_send (struct usb_request *req, void (*callback) (int,int)); ++ ++/* UDC register utility functions */ ++#define UDC_write(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) = (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: write %#x to %p (%#x) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while((reg) != (val)); \ ++} ++ ++#define UDC_set(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) |= (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: set %#x of %p (%#x) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while(!((reg) & (val))); \ ++} ++ ++#define UDC_clear(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) &= ~(val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while((reg) & (val)); \ ++} ++ ++#define UDC_flip(reg, val) { \ ++ int i = 10000; \ ++ (reg) = (val); \ ++ do { \ ++ (reg) = (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while(((reg) & (val))); \ ++} ++ ++#include "sa1100_udc.h" ++ ++static struct sa1100_udc *the_controller; ++static void nuke (struct sa1100_ep *, int status); ++static void done (struct sa1100_ep *ep, struct sa1100_request *req, int status); ++static inline void ep0_idle (struct sa1100_udc *dev) ++{ ++ dev->ep0state = EP0_IDLE; ++} ++ ++// ep0 handlers ++ ++// 1 == lots of trace noise, 0 = only "important' stuff ++//#define VERBOSITY 1 ++ ++#ifndef MIN ++#define MIN( a, b ) ((a)<(b)?(a):(b)) ++#endif ++ ++#if 1 && !defined( ASSERT ) ++# define ASSERT(expr) \ ++ if(!(expr)) { \ ++ printk( "Assertion failed! %s,%s,%s,line=%d\n",\ ++ #expr,__FILE__,__FUNCTION__,__LINE__); \ ++ } ++#else ++# define ASSERT(expr) ++#endif ++ ++#if VERBOSITY ++#define PRINTKD(fmt, args...) printk( fmt , ## args) ++#else ++#define PRINTKD(fmt, args...) ++#endif ++ ++/* USB Device Requests */ ++typedef struct ++{ ++ __u8 bmRequestType; ++ __u8 bRequest; ++ __u16 wValue; ++ __u16 wIndex; ++ __u16 wLength; ++} usb_dev_request_t __attribute__ ((packed)); ++ ++/* other subroutines */ ++unsigned int (*wrint)(void); ++void ep0_int_hndlr( void ); ++static void ep0_queue(void *buf, unsigned int req, unsigned int act); ++static void write_fifo( void ); ++static int read_fifo( struct usb_ctrlrequest * p ); ++ ++/* some voodo helpers 01Mar01ww */ ++static void set_cs_bits( __u32 set_bits ); ++static void set_de( void ); ++static void set_ipr( void ); ++static void set_ipr_and_de( void ); ++static bool clear_opr( void ); ++ ++/*************************************************************************** ++Inline Helpers ++***************************************************************************/ ++ ++/* Data extraction from usb_request_t fields */ ++enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 }; ++static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); } ++ ++static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); } ++inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); } ++ ++/* following is hook for self-powered flag in GET_STATUS. Some devices ++ .. might like to override and return real info */ ++static inline bool self_powered_hook( void ) { return true; } ++ ++#if VERBOSITY ++/* "pcs" == "print control status" */ ++static inline void pcs( void ) ++{ ++ __u32 foo = Ser0UDCCS0; ++ printk( "%8.8X: %s %s %s %s\n", ++ foo, ++ foo & UDCCS0_SE ? "SE" : "", ++ foo & UDCCS0_OPR ? "OPR" : "", ++ foo & UDCCS0_IPR ? "IPR" : "", ++ foo & UDCCS0_SST ? "SST" : "" ++ ); ++} ++static inline void preq( struct usb_ctrlrequest * pReq ) ++{ ++ static char * tnames[] = { "dev", "intf", "ep", "oth" }; ++ static char * rnames[] = { "std", "class", "vendor", "???" }; ++ char * psz; ++ switch( pReq->bRequest ) { ++ case USB_REQ_GET_STATUS: psz = "get stat"; break; ++ case USB_REQ_CLEAR_FEATURE: psz = "clr feat"; break; ++ case USB_REQ_SET_FEATURE: psz = "set feat"; break; ++ case USB_REQ_SET_ADDRESS: psz = "set addr"; break; ++ case USB_REQ_GET_DESCRIPTOR: psz = "get desc"; break; ++ case USB_REQ_SET_DESCRIPTOR: psz = "set desc"; break; ++ case USB_REQ_GET_CONFIGURATION: psz = "get cfg"; break; ++ case USB_REQ_SET_CONFIGURATION: psz = "set cfg"; break; ++ case USB_REQ_GET_INTERFACE: psz = "get intf"; break; ++ case USB_REQ_SET_INTERFACE: psz = "set intf"; break; ++ default: psz = "unknown"; break; ++ } ++ printk( "- [%s: %s req to %s. dir=%s]\n", psz, ++ rnames[ (pReq->bRequestType >> 5) & 3 ], ++ tnames[ pReq->bRequestType & 3 ], ++ ( pReq->bRequestType & 0x80 ) ? "in" : "out" ); ++} ++ ++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) ++{ ++ printk("%s: bRequestType=0x%02x bRequest=0x%02x " ++ "wValue=0x%04x wIndex=0x%04x wLength=0x%04x\n", ++ prefix, req->bRequestType, req->bRequest, ++ le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex), ++ le16_to_cpu(req->wLength)); ++} ++#else ++static inline void pcs( void ){} ++//static inline void preq( void ){} ++static inline void preq( void *x ){} ++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) {} ++#endif ++ ++/*************************************************************************** ++Globals ++***************************************************************************/ ++static const char pszMe[] = "usbep0: "; ++ ++ ++/* global write struct to keep write ++ ..state around across interrupts */ ++static struct { ++ unsigned char *p; ++ int bytes_left; ++} wr; ++ ++/*************************************************************************** ++Public Interface ++***************************************************************************/ ++ ++/* reset received from HUB (or controller just went nuts and reset by itself!) ++ so udc core has been reset, track this state here */ ++void ++ep0_reset(void) ++{ ++ /* reset state machine */ ++ wr.p = NULL; ++ wr.bytes_left = 0; ++ usbd_info.address=0; ++// needed? Ser0UDCAR = 0; ++} ++ ++ ++/* handle interrupt for endpoint zero */ ++ ++inline void ep0_clear_write(void) { ++ wr.p=NULL; ++ wr.bytes_left=0; ++} ++ ++/* this is a config packet parser based on that from the updated HH 2.6 udc */ ++static void ++ep0_read_packet( void ) ++{ ++ unsigned char status_buf[2]; /* returned in GET_STATUS */ ++ struct usb_ctrlrequest req; ++ int request_type; ++ int n; ++ __u32 address; ++ ++ /* reset previous count */ ++ the_controller->ep0_req_len=-1; ++ ++ /* read the setup request */ ++ n = read_fifo( &req ); ++ usbctl_dump_request("ep0_read_packet",&req); ++ ++ if ( n != sizeof( req ) ) { ++ printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. " ++ " Stalling out...\n", ++ pszMe, sizeof( req ), n ); ++ /* force stall, serviced out */ ++ set_cs_bits( UDCCS0_FST | UDCCS0_SO ); ++ goto sh_sb_end; ++ } ++ ++ /* Is it a standard request? (not vendor or class request) */ ++ request_type = type_code_from_request( req.bRequestType ); ++ if ( request_type != 0 ) { ++ printk( "%ssetup begin: unsupported bRequestType: %d ignored\n", ++ pszMe, request_type ); ++ set_cs_bits( UDCCS0_DE | UDCCS0_SO ); ++ goto sh_sb_end; ++ } ++ ++ /* save requested reply size */ ++ the_controller->ep0_req_len=le16_to_cpu(req.wLength); ++ PRINTKD("%s: request length is %d\n",__FUNCTION__,the_controller->ep0_req_len); ++ ++#if VERBOSITY ++ { ++ unsigned char * pdb = (unsigned char *) &req; ++ PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ", ++ pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7] ++ ); ++ preq( &req ); ++ } ++#endif ++ ++ /* Handle it */ ++ switch( req.bRequest ) { ++ ++ /* This first bunch have no data phase */ ++ ++ case USB_REQ_SET_ADDRESS: ++ address = (__u32) (req.wValue & 0x7F); ++ /* when SO and DE sent, UDC will enter status phase and ack, ++ ..propagating new address to udc core. Next control transfer ++ ..will be on the new address. You can't see the change in a ++ ..read back of CAR until then. (about 250us later, on my box). ++ ..The original Intel driver sets S0 and DE and code to check ++ ..that address has propagated here. I tried this, but it ++ ..would only work sometimes! The rest of the time it would ++ ..never propagate and we'd spin forever. So now I just set ++ ..it and pray... ++ */ ++ Ser0UDCAR = address; ++ usbd_info.address = address; ++ usbctl_next_state_on_event( kEvAddress ); ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ printk( "%sI have been assigned address: %d\n", pszMe, address ); ++ break; ++ ++ ++ case USB_REQ_SET_CONFIGURATION: ++ if ( req.wValue == 1 ) { ++ /* configured */ ++ if (usbctl_next_state_on_event( kEvConfig ) != kError) { ++ /* (re)set the out and in max packet sizes */ ++ PRINTKD( "%s: calling the_controller.driver->setup with SET_CONFIGURATION\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ __u32 in = __le16_to_cpu( the_controller->ep[1].ep.maxpacket ); ++ __u32 out = __le16_to_cpu( the_controller->ep[2].ep.maxpacket ); ++ Ser0UDCOMP = ( out - 1 ); ++ Ser0UDCIMP = ( in - 1 ); ++ // we are configured ++ usbd_info.state = USB_STATE_CONFIGURED; ++ // enable rx and tx interrupts ++ Ser0UDCCR &= ~(UDCCR_RIM | UDCCR_TIM); ++ ++ printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in ); ++ break; ++ } ++ } else if ( req.wValue == 0 ) { ++ /* de-configured */ ++ if (usbctl_next_state_on_event( kEvDeConfig ) != kError ) ++ printk( "%sDe-Configured\n", pszMe ); ++ usbd_info.state = 0; ++ Ser0UDCCR |= UDCCR_RIM | UDCCR_TIM; ++ ep1_reset (); ++ ep2_reset (); ++ printk("%s: de-configured. Tx and Rx interrupts disabled. ep1 and ep2 reset\n",__FUNCTION__); ++ } else { ++ printk( "%ssetup phase: Unknown " ++ "\"set configuration\" data %d\n", ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ case USB_REQ_CLEAR_FEATURE: ++ /* could check data length, direction...26Jan01ww */ ++ if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */ ++ int ep = windex_to_ep_num( req.wIndex ); ++ if ( ep == 1 ) { ++ printk( "%sclear feature \"endpoint halt\" " ++ " on receiver\n", pszMe ); ++ ep1_reset(); ++ } ++ else if ( ep == 2 ) { ++ printk( "%sclear feature \"endpoint halt\" " ++ "on xmitter\n", pszMe ); ++ ep2_reset(); ++ } else { ++ printk( "%sclear feature \"endpoint halt\" " ++ "on unsupported ep # %d\n", ++ pszMe, ep ); ++ } ++ } else { ++ printk( "%sUnsupported feature selector (%d) " ++ "in clear feature. Ignored.\n" , ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ case USB_REQ_SET_FEATURE: ++ if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */ ++ int ep = windex_to_ep_num( req.wValue ); ++ if ( ep == 1 ) { ++ printk( "%set feature \"endpoint halt\" " ++ "on receiver\n", pszMe ); ++ ep1_stall(); ++ } ++ else if ( ep == 2 ) { ++ printk( "%sset feature \"endpoint halt\" " ++ " on xmitter\n", pszMe ); ++ ep2_stall(); ++ } else { ++ printk( "%sset feature \"endpoint halt\" " ++ "on unsupported ep # %d\n", ++ pszMe, ep ); ++ } ++ } ++ else { ++ printk( "%sUnsupported feature selector " ++ "(%d) in set feature\n", ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ /* The rest have a data phase that writes back to the host */ ++ case USB_REQ_GET_STATUS: ++ /* return status bit flags */ ++ status_buf[0] = status_buf[1] = 0; ++ n = request_target(req.bRequestType); ++ switch( n ) { ++ case kTargetDevice: ++ if ( self_powered_hook() ) ++ status_buf[0] |= 1; ++ break; ++ case kTargetInterface: ++ break; ++ case kTargetEndpoint: ++ /* return stalled bit */ ++ n = windex_to_ep_num( req.wIndex ); ++ if ( n == 1 ) ++ status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4; ++ else if ( n == 2 ) ++ status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5; ++ else { ++ printk( "%sUnknown endpoint (%d) " ++ "in GET_STATUS\n", pszMe, n ); ++ } ++ break; ++ default: ++ printk( "%sUnknown target (%d) in GET_STATUS\n", ++ pszMe, n ); ++ /* fall thru */ ++ break; ++ } ++ PRINTKD("%s: GET_STATUS writing %d\n",__FUNCTION__,req.wLength); ++ ep0_queue( status_buf, req.wLength, sizeof( status_buf )); ++ break; ++ case USB_REQ_GET_DESCRIPTOR: ++ PRINTKD( "%s: calling the_controller.driver->setup with GET_DESCRIPTOR\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ break; ++ case USB_REQ_GET_CONFIGURATION: ++ PRINTKD( "%s: calling the_controller.driver->setup with GET_CONFIGURATION\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ break; ++ case USB_REQ_GET_INTERFACE: ++ PRINTKD( "%s: calling the_controller->driver->setup with GET_INTERFACE\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ break; ++ case USB_REQ_SET_INTERFACE: ++ PRINTKD( "%s: calling the_controller->driver->setup with SET_INTERFACE\n", __FUNCTION__ ); ++ the_controller->driver->setup(&the_controller->gadget, &req); ++ break; ++ default : ++ printk("%sunknown request 0x%x\n", pszMe, req.bRequest); ++ break; ++ } /* switch( bRequest ) */ ++ ++sh_sb_end: ++ return; ++ ++} ++ ++void ++ep0_int_hndlr( void ) ++{ ++ u32 cs_reg_in; ++ ++ pcs(); ++ ++ cs_reg_in = Ser0UDCCS0; ++ ++ ++ /* ++ * If "setup end" has been set, the usb controller has terminated ++ * a setup transaction before we set DE. This happens during ++ * enumeration with some hosts. For example, the host will ask for ++ * our device descriptor and specify a return of 64 bytes. When we ++ * hand back the first 8, the host will know our max packet size ++ * and turn around and issue a new setup immediately. This causes ++ * the UDC to auto-ack the new setup and set SE. We must then ++ * "unload" (process) the new setup, which is what will happen ++ * after this preamble is finished executing. ++ */ ++ if (cs_reg_in & UDCCS0_SE) { ++ PRINTKD("UDC: early termination of setup\n"); ++ ++ /* ++ * Clear setup end ++ */ ++ set_cs_bits(UDCCS0_SSE); ++ ++ /* ++ * Clear any pending write. ++ */ ++ ep0_clear_write(); ++ } ++ ++ /* ++ * UDC sent a stall due to a protocol violation. ++ */ ++ if (cs_reg_in & UDCCS0_SST) { ++// usb->ep0_stall_sent++; ++ ++ PRINTKD("UDC: write_preamble: UDC sent stall\n"); ++ ++ /* ++ * Clear sent stall ++ */ ++ set_cs_bits(UDCCS0_SST); ++ ++ /* ++ * Clear any pending write. ++ */ ++ ep0_clear_write(); ++ } ++ ++ switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) { ++ case UDCCS0_OPR | UDCCS0_IPR: ++ PRINTKD("UDC: write_preamble: see OPR. Stopping write to " ++ "handle new SETUP\n"); ++ ++ /* ++ * very rarely, you can get OPR and ++ * leftover IPR. Try to clear ++ */ ++ UDC_clear(Ser0UDCCS0, UDCCS0_IPR); ++ ++ /* ++ * Clear any pending write. ++ */ ++ ep0_clear_write(); ++ ++ /*FALLTHROUGH*/ ++ case UDCCS0_OPR: ++ /* ++ * A new setup request is pending. Handle ++ * it. Note that we don't try to read a ++ * packet if SE was set and OPR is clear. ++ */ ++ ep0_read_packet(); ++ break; ++ ++ case 0: ++ // if data pending ... ++ if (wr.p) { ++ unsigned int cs_bits = 0; ++ if (wr.bytes_left != 0) { ++ /* ++ * More data to go ++ */ ++ write_fifo(); ++ // packet ready ++ cs_bits |= UDCCS0_IPR; ++ } ++ ++ if (wr.bytes_left == 0) { ++ /* ++ * All data sent. ++ */ ++ cs_bits |= wrint(); ++ // a null packet may be following ++ if (!wrint) ++ ep0_clear_write(); ++ } ++ set_cs_bits(cs_bits); ++ } ++ else ++ PRINTKD("%s: No data - probably an ACK\n",__FUNCTION__); ++ break; ++ ++ case UDCCS0_IPR: ++ PRINTKD("UDC: IPR set, not writing\n"); ++// usb->ep0_early_irqs++; ++ break; ++ } ++ ++ pcs(); ++ PRINTKD( "-end-\n" ); ++} ++ ++static unsigned int ep0_sh_write_data(void) ++{ ++ /* ++ * If bytes left is zero, we are coming in on the ++ * interrupt after the last packet went out. And ++ * we know we don't have to empty packet this ++ * transfer so just set DE and we are done ++ */ ++ PRINTKD("UDC: normal packet ended\n"); ++ wrint=NULL; ++ return UDCCS0_DE; ++} ++ ++static unsigned int ep0_sh_write_with_empty_packet(void) ++{ ++ /* ++ * If bytes left is zero, we are coming in on the ++ * interrupt after the last packet went out. ++ * We must do short packet suff, so set DE and IPR ++ */ ++ PRINTKD("UDC: short packet sent\n"); ++ wrint=NULL; ++ return UDCCS0_IPR | UDCCS0_DE; ++} ++ ++static unsigned int ep0_sh_write_data_then_empty_packet(void) ++{ ++ PRINTKD("UDC: last packet full. Send empty packet next\n"); ++ wrint=ep0_sh_write_with_empty_packet; ++ return 0; ++} ++ ++static void ep0_queue(void *buf, unsigned int len, unsigned int req_len) ++{ ++ __u32 cs_reg_bits = UDCCS0_IPR; ++ ++ PRINTKD("a=%d r=%d\n", len, req_len); ++ ++ if (len == 0) { ++ // no output packet to wait for ++ printk("%s: zero byte packet being queued. Setting DE and OPR end exiting\n",__FUNCTION__); ++ set_cs_bits(UDCCS0_DE | UDCCS0_SO); ++ return; ++ } ++ ++ /* ++ * thou shalt not enter data phase until ++ * Out Packet Ready is clear ++ */ ++ if (!clear_opr()) { ++ printk("UDC: SO did not clear OPR\n"); ++ set_cs_bits(UDCCS0_DE | UDCCS0_SO); ++ return; ++ } ++ ++ // note data to xmit stored ++ wr.p=buf; ++ wr.bytes_left=min(len, req_len); ++ ++ // write the first block ++ write_fifo(); ++ ++ // done already? ++ if (wr.bytes_left == 0) { ++ /* ++ * out in one, so data end ++ */ ++ cs_reg_bits |= UDCCS0_DE; ++ ep0_clear_write(); ++ // rest is a shorter than expected reply? ++ } else if (len < req_len) { ++ /* ++ * we are going to short-change host ++ * so need nul to not stall ++ */ ++ if (len % 8) { ++ PRINTKD("%s: %d more to go ending in a short packet.\n",__FUNCTION__,wr.bytes_left); ++ wrint=ep0_sh_write_with_empty_packet; ++ } ++ // unless we are on a packet boundary. Then send full packet plus null packet. ++ else { ++ PRINTKD("%s: %d more to go then add empty packet.\n",__FUNCTION__,wr.bytes_left); ++ wrint=ep0_sh_write_data_then_empty_packet; ++ } ++ } else { ++ /* ++ * we have as much or more than requested ++ */ ++ PRINTKD("%s: %d more to go.\n",__FUNCTION__,wr.bytes_left); ++ wrint=ep0_sh_write_data; ++ } ++ ++ /* ++ * note: IPR was set uncondtionally at start of routine ++ */ ++ set_cs_bits(cs_reg_bits); ++} ++ ++/* ++ * write_fifo() ++ * Stick bytes in the 8 bytes endpoint zero FIFO. ++ * This version uses a variety of tricks to make sure the bytes ++ * are written correctly. 1. The count register is checked to ++ * see if the byte went in, and the write is attempted again ++ * if not. 2. An overall counter is used to break out so we ++ * don't hang in those (rare) cases where the UDC reverses ++ * direction of the FIFO underneath us without notification ++ * (in response to host aborting a setup transaction early). ++ * ++ */ ++static void write_fifo( void ) ++{ ++ int bytes_this_time = MIN( wr.bytes_left, 8 ); ++ int bytes_written = 0; ++ ++ PRINTKD( "WF=%d: ", bytes_this_time ); ++ ++ while( bytes_this_time-- ) { ++ unsigned int cwc; ++ int i; ++ PRINTKD( "%2.2X ", *wr.p ); ++ cwc = Ser0UDCWC & 15; ++ i = 10; ++ do { ++ Ser0UDCD0 = *wr.p; ++ udelay( 20 ); /* voodo 28Feb01ww */ ++ } while( (Ser0UDCWC &15) == cwc && --i ); ++ ++ if ( i == 0 ) { ++ printk( "%swrite_fifo: write failure\n", pszMe ); ++ usbd_info.stats.ep0_fifo_write_failures++; ++ } ++ ++ wr.p++; ++ bytes_written++; ++ } ++ wr.bytes_left -= bytes_written; ++ ++ /* following propagation voodo so maybe caller writing IPR in ++ ..a moment might actually get it to stick 28Feb01ww */ ++ udelay( 300 ); ++ ++ usbd_info.stats.ep0_bytes_written += bytes_written; ++ PRINTKD( "L=%d WCR=%8.8X\n", wr.bytes_left, Ser0UDCWC ); ++} ++/* ++ * read_fifo() ++ * Read 1-8 bytes out of FIFO and put in request. ++ * Called to do the initial read of setup requests ++ * from the host. Return number of bytes read. ++ * ++ * Like write fifo above, this driver uses multiple ++ * reads checked agains the count register with an ++ * overall timeout. ++ * ++ */ ++static int ++read_fifo( struct usb_ctrlrequest * request ) ++{ ++ int bytes_read = 0; ++ int fifo_count; ++ ++ unsigned char * pOut = (unsigned char*) request; ++ ++ fifo_count = ( Ser0UDCWC & 0xFF ); ++ ++ ASSERT( fifo_count <= 8 ); ++ PRINTKD( "RF=%d ", fifo_count ); ++ ++ while( fifo_count-- ) { ++ unsigned int cwc; ++ int i; ++ ++ cwc = Ser0UDCWC & 15; ++ ++ i = 10; ++ do { ++ *pOut = (unsigned char) Ser0UDCD0; ++ udelay( 20 ); ++ } while( ( Ser0UDCWC & 15 ) == cwc && --i ); ++ ++ if ( i == 0 ) { ++ printk( "%sread_fifo(): read failure\n", pszMe ); ++ usbd_info.stats.ep0_fifo_read_failures++; ++ } ++ pOut++; ++ bytes_read++; ++ } ++ ++ PRINTKD( "fc=%d\n", bytes_read ); ++ usbd_info.stats.ep0_bytes_read++; ++ return bytes_read; ++} ++ ++/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */ ++ ++#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE ) ++#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS )) ++#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE) ++ ++static void set_cs_bits( __u32 bits ) ++{ ++ if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST | UDCCS0_SST) ) ++ Ser0UDCCS0 = bits; ++ else if ( (bits & BOTH_BITS) == BOTH_BITS ) ++ set_ipr_and_de(); ++ else if ( bits & UDCCS0_IPR ) ++ set_ipr(); ++ else if ( bits & UDCCS0_DE ) ++ set_de(); ++} ++ ++static void set_de( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= UDCCS0_DE; ++ } else { ++ PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( Ser0UDCCS0 & UDCCS0_DE ) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n", ++ pszMe, UDCCS0_DE, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++static void set_ipr( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= UDCCS0_IPR; ++ } else { ++ PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( Ser0UDCCS0 & UDCCS0_IPR ) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n", ++ pszMe, UDCCS0_IPR, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++static void set_ipr_and_de( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= BOTH_BITS; ++ } else { ++ PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n", ++ pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++static bool clear_opr( void ) ++{ ++ int i = 10000; ++ bool is_clear; ++ do { ++ Ser0UDCCS0 = UDCCS0_SO; ++ is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR ); ++ if ( i-- <= 0 ) { ++ printk( "%sclear_opr(): failed\n", pszMe ); ++ break; ++ } ++ } while( ! is_clear ); ++ return is_clear; ++} ++ ++ ++ ++// ep1 handlers ++ ++static char *ep1_buf; ++static int ep1_len; ++static void (*ep1_callback)(int flag, int size); ++static char *ep1_curdmabuf; ++static dma_addr_t ep1_curdmapos; ++static int ep1_curdmalen; ++static int ep1_remain; ++static int ep1_used; ++ ++static dma_regs_t *dmaregs_rx = NULL; ++static int rx_pktsize; ++ ++static int naking; ++ ++static void ++ep1_start(void) ++{ ++ sa1100_reset_dma(dmaregs_rx); ++ if (!ep1_curdmalen) { ++ ep1_curdmalen = rx_pktsize; ++ if (ep1_curdmalen > ep1_remain) ++ ep1_curdmalen = ep1_remain; ++ ep1_curdmapos = dma_map_single(NULL, ep1_curdmabuf, ep1_curdmalen, ++ DMA_FROM_DEVICE); ++ } ++ ++ UDC_write( Ser0UDCOMP, ep1_curdmalen-1 ); ++ ++ sa1100_start_dma(dmaregs_rx, ep1_curdmapos, ep1_curdmalen); ++ ++ if ( naking ) { ++ /* turn off NAK of OUT packets, if set */ ++ UDC_flip( Ser0UDCCS1, UDCCS1_RPC ); ++ naking = 0; ++ } ++} ++ ++static void ++ep1_done(int flag) ++{ ++ int size = ep1_len - ep1_remain; ++ ++ if (!ep1_len) ++ return; ++ if (ep1_curdmalen) ++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, ++ DMA_FROM_DEVICE); ++ ep1_len = ep1_curdmalen = 0; ++ if (ep1_callback) ++ ep1_callback(flag, size); ++} ++ ++void ++ep1_state_change_notify( int new_state ) ++{ ++ ++} ++ ++void ++ep1_stall( void ) ++{ ++ /* SET_FEATURE force stall at UDC */ ++ UDC_set( Ser0UDCCS1, UDCCS1_FST ); ++} ++ ++int ++ep1_init(dma_regs_t *dmaregs) ++{ ++ dmaregs_rx = dmaregs; ++ sa1100_reset_dma(dmaregs_rx); ++ ep1_done(-EAGAIN); ++ return 0; ++} ++ ++void ++ep1_reset(void) ++{ ++ if (dmaregs_rx) ++ sa1100_reset_dma(dmaregs_rx); ++ UDC_clear(Ser0UDCCS1, UDCCS1_FST); ++ ep1_done(-EINTR); ++} ++ ++void ++ep1_int_hndlr(int udcsr) ++{ ++ dma_addr_t dma_addr; ++ unsigned int len; ++ int status = Ser0UDCCS1; ++ ++ if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking ); ++ ++ if (status & UDCCS1_RPC) { ++ ++ if (!ep1_curdmalen) { ++ printk("usb_recv: RPC for non-existent buffer\n"); ++ naking=1; ++ return; ++ } ++ ++ sa1100_stop_dma(dmaregs_rx); ++ ++ if (status & UDCCS1_SST) { ++ printk("usb_recv: stall sent OMP=%d\n",Ser0UDCOMP); ++ UDC_flip(Ser0UDCCS1, UDCCS1_SST); ++ ep1_done(-EIO); // UDC aborted current transfer, so we do ++ return; ++ } ++ ++ if (status & UDCCS1_RPE) { ++ printk("usb_recv: RPError %x\n", status); ++ UDC_flip(Ser0UDCCS1, UDCCS1_RPC); ++ ep1_done(-EIO); ++ return; ++ } ++ ++ dma_addr=sa1100_get_dma_pos(dmaregs_rx); ++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, ++ DMA_FROM_DEVICE); ++ len = dma_addr - ep1_curdmapos; ++#ifdef SA1100_USB_DEBUG ++ if (sa1100_usb_debug) { ++ int i; ++ printk("usb rx %d :\n ",len); ++ if (sa1100_usb_debug>1) { ++ for (i=0; i<len; i++) { ++ if ((i % 32)==31) ++ printk("\n "); ++// printk("%2.2x ",((char *)ep1_buf)[ep1_used+i]); ++ printk("%2.2x ",((char *)ep1_curdmapos)[i]); ++ } ++ } ++ printk("\n"); ++ } ++#endif ++ if (len < ep1_curdmalen) { ++ char *buf = ep1_curdmabuf + len; ++ while (Ser0UDCCS1 & UDCCS1_RNE) { ++ if (len >= ep1_curdmalen) { ++ printk("usb_recv: too much data in fifo\n"); ++ break; ++ } ++ *buf++ = Ser0UDCDR; ++ len++; ++ } ++ } else if (Ser0UDCCS1 & UDCCS1_RNE) { ++ printk("usb_recv: fifo screwed, shouldn't contain data\n"); ++ len = 0; ++ } ++ ++#if defined(NCB_DMA_FIX) ++// if (len && (ep1_buf != ep1_curdmabuf)) ++// memcpy(ep1_buf,ep1_curdmabuf,len); ++ if (len) ++ memcpy(&(((unsigned char *)ep1_buf)[ep1_used]),ep1_curdmabuf,len); ++#endif ++ ++ ep1_curdmalen = 0; /* dma unmap already done */ ++ ep1_remain -= len; ++ ep1_used += len; ++// ep1_curdmabuf += len; // use same buffer again ++ naking = 1; ++//printk("%s: received %d, %d remaining\n",__FUNCTION__,len,ep1_remain); ++ if (len && (len == rx_pktsize)) ++ ep1_start(); ++ else ++ ep1_done((len) ? 0 : -EPIPE); ++ } ++ /* else, you can get here if we are holding NAK */ ++} ++ ++int ++sa1100_usb_recv(struct usb_request *req, void (*callback)(int flag, int size)) ++{ ++ unsigned long flags; ++ char *buf=req->buf; ++ int len=req->length; ++ ++ if (ep1_len) ++ return -EBUSY; ++ ++ local_irq_save(flags); ++ ep1_buf = buf; ++ ep1_len = len; ++ ep1_callback = callback; ++ ep1_remain = len; ++ ep1_used = 0; ++#ifdef NCB_DMA_FIX ++// if (((size_t)buf)&3) ++ if (1) ++ ep1_curdmabuf = receive_buffer; ++ else ++#else ++ ep1_curdmabuf = buf; ++#endif ++ ep1_curdmalen = 0; ++ ep1_start(); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++// ep2 handlers ++ ++static char *ep2_buf; ++static int ep2_len; ++static void (*ep2_callback)(int status, int size); ++static dma_addr_t ep2_dma; ++static dma_addr_t ep2_curdmapos; ++static int ep2_curdmalen; ++static int ep2_remain; ++static dma_regs_t *dmaregs_tx = NULL; ++static int tx_pktsize; ++ ++/* device state is changing, async */ ++void ++ep2_state_change_notify( int new_state ) ++{ ++} ++ ++/* set feature stall executing, async */ ++void ++ep2_stall( void ) ++{ ++ UDC_set( Ser0UDCCS2, UDCCS2_FST ); /* force stall at UDC */ ++} ++ ++static void ++ep2_start(void) ++{ ++ if (!ep2_len) ++ return; ++ ++ ep2_curdmalen = tx_pktsize; ++ if (ep2_curdmalen > ep2_remain) ++ ep2_curdmalen = ep2_remain; ++ ++ /* must do this _before_ queue buffer.. */ ++ UDC_flip( Ser0UDCCS2,UDCCS2_TPC ); /* stop NAKing IN tokens */ ++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 ); ++ ++#if 0 ++ /* Remove if never seen...8Mar01ww */ ++ { ++ int massive_attack = 20; ++ while ( Ser0UDCIMP != ep2_curdmalen-1 && massive_attack-- ) { ++ printk( "usbsnd: Oh no you don't! Let me spin..." ); ++ udelay( 500 ); ++ printk( "and try again...\n" ); ++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 ); ++ } ++ if ( massive_attack != 20 ) { ++ if ( Ser0UDCIMP != ep2_curdmalen-1 ) ++ printk( "usbsnd: Massive attack FAILED :-( %d\n", ++ 20 - massive_attack ); ++ else ++ printk( "usbsnd: Massive attack WORKED :-) %d\n", ++ 20 - massive_attack ); ++ } ++ } ++ /* End remove if never seen... 8Mar01ww */ ++#endif ++ ++ Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug ++ sa1100_start_dma(dmaregs_tx, ep2_curdmapos, ep2_curdmalen); ++} ++ ++static void ++ep2_done(int flag) ++{ ++ int size = ep2_len - ep2_remain; ++ if (ep2_len) { ++ dma_unmap_single(NULL, ep2_dma, ep2_len, DMA_TO_DEVICE); ++ ep2_len = 0; ++ if (ep2_callback) ++ ep2_callback(flag, size); ++ } ++} ++ ++int ++ep2_init(dma_regs_t *dmaregs) ++{ ++ dmaregs_tx = dmaregs; ++ sa1100_reset_dma(dmaregs_tx); ++ ep2_done(-EAGAIN); ++ return 0; ++} ++ ++void ++ep2_reset(void) ++{ ++ UDC_clear(Ser0UDCCS2, UDCCS2_FST); ++ if (dmaregs_tx) ++ sa1100_reset_dma(dmaregs_tx); ++ ep2_done(-EINTR); ++} ++ ++void ++ep2_int_hndlr(int udcsr) ++{ ++ int status = Ser0UDCCS2; ++ ++ if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug. ++ Ser0UDCAR = usbd_info.address; ++ ++ if (status & UDCCS2_TPC) { ++ ++ UDC_flip(Ser0UDCCS2, UDCCS2_SST); ++ ++ sa1100_reset_dma(dmaregs_tx); ++ ++ if (status & (UDCCS2_TPE | UDCCS2_TUR)) { ++ printk("usb_send: transmit error %x\n", status); ++ ep2_done(-EIO); ++ } else { ++#if 1 // 22Feb01ww/Oleg ++ ep2_curdmapos += ep2_curdmalen; ++ ep2_remain -= ep2_curdmalen; ++#else ++ ep2_curdmapos += Ser0UDCIMP + 1; // this is workaround ++ ep2_remain -= Ser0UDCIMP + 1; // for case when setting of Ser0UDCIMP was failed ++#endif ++ ++ if (ep2_remain != 0) ++ ep2_start(); ++ else ++ ep2_done(0); ++ } ++ } else { ++ printk("usb_send: Not TPC: UDCCS2 = %x\n", status); ++ } ++} ++ ++int ++sa1100_usb_send(struct usb_request *req, void (*callback)(int status, int size)) ++{ ++ char *buf=req->buf; ++ int len=req->length; ++ unsigned long flags; ++ ++ if (usbd_info.state != USB_STATE_CONFIGURED) { ++ PRINTKD("%s: return -ENODEV\n",__FUNCTION__); ++ return -ENODEV; ++ } ++ ++ if (ep2_len) { ++ PRINTKD("%s: return -EBUSY\n",__FUNCTION__); ++ return -EBUSY; ++ } ++ ++ local_irq_save(flags); ++#ifdef NCB_DMA_FIX ++ // if misaligned, copy to aligned buffer ++// if (((size_t)buf)&3) { ++ if (1) { ++ PRINTKD("%s: copying %d bytes to send_buffer\n",__FUNCTION__,len); ++ memcpy(send_buffer,buf,len); ++ ep2_buf = send_buffer; ++ } ++ else ++#endif ++ ep2_buf = buf; ++ ++ ep2_len = len; ++ ep2_dma = dma_map_single(NULL, ep2_buf, len,DMA_TO_DEVICE); ++ PRINTKD("%s: mapped dma to buffer(%p0\n",__FUNCTION__,buf); ++ ++ ep2_callback = callback; ++ ep2_remain = len; ++ ep2_curdmapos = ep2_dma; ++ ++ PRINTKD("%s: calling ep2_start\n",__FUNCTION__); ++ ep2_start(); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++/*-------------------------------------------------------------------------*/ ++ ++static int ++sa1100_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) ++{ ++ struct sa1100_udc *dev; ++ struct sa1100_ep *ep; ++ u32 max; ++ int type; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!_ep || !desc || ep->desc || _ep->name == ep0name ++ || desc->bDescriptorType != USB_DT_ENDPOINT)
{ ++ PRINTKD("%s: _ep = %p, desc = %p\n",__FUNCTION__,_ep,desc); ++ if (_ep && desc) ++ PRINTKD("%s: ep->desc = %p, _ep->name = %s desc->bDescriptorType = %s\n",__FUNCTION__,ep->desc,_ep->name, ++ (desc->bDescriptorType == USB_DT_ENDPOINT) ? "USB_DT_ENDPOINT":"bad!!"); ++ return -EINVAL; ++ } ++ ++ dev = ep->dev; ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN; ++ ++ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++ max = le16_to_cpu (desc->wMaxPacketSize); ++ switch (max) { ++ case 64: case 32: ++ /* note: maxpacket > 16 means DMA might overrun/underrun */ ++ case 16: case 8: ++ break; ++ default: ++ if (type == USB_ENDPOINT_XFER_INT && max < 64) ++ break; ++ return -EDOM; ++ } ++ ++ switch (type) { ++ case USB_ENDPOINT_XFER_BULK: ++ case USB_ENDPOINT_XFER_INT: ++ if (ep == &dev->ep[2]) { ++ if (desc->bEndpointAddress != (USB_DIR_IN|2)
) { ++ PRINTKD("%s: ep[2] has invalid endpoint\n",__FUNCTION__); ++ return -EINVAL; ++ } ++ tx_pktsize = max; ++ Ser0UDCOMP = max - 1; ++ PRINTKD("%s: ep2 max packet size is %d\n",__FUNCTION__,max); ++ break; ++ } else if (ep == &dev->ep[1]) { ++ if (desc->bEndpointAddress != (USB_DIR_OUT|1))
{ ++ PRINTKD("%s: ep[1] has invalid endpoint\n",__FUNCTION__); ++ return -EINVAL; ++ } ++ rx_pktsize = max; ++ Ser0UDCIMP = max - 1; ++ PRINTKD("%s: ep1 max packet size is %d\n",__FUNCTION__,max); ++ break; ++ } ++ // FALLTHROUGH ++ default: ++ PRINTKD("%s: Invalid endpoint\n",__FUNCTION__); ++ return -EINVAL; ++ } ++ ++ _ep->maxpacket = max; ++ ep->desc = desc; ++ ++ DEBUG (dev, "enabled %s %s max %04x\n", _ep->name, ++ type_string (desc->bmAttributes), max); ++ ++ return 0; ++} ++ ++static int sa1100_disable (struct usb_ep *_ep) ++{ ++ struct sa1100_ep *ep; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!_ep || !ep->desc || _ep->name == ep0name) ++ return -EINVAL; ++ ++ nuke (ep, -ESHUTDOWN); ++ ++ DEBUG (ep->dev, "disabled %s\n", _ep->name); ++ ++ ep->desc = NULL; ++ ep->stopped = 1; ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_request * ++sa1100_alloc_request (struct usb_ep *_ep, int gfp_flags) ++{ ++ struct sa1100_request *req; ++ ++ if (!_ep) ++ return 0; ++ ++ req = kmalloc (sizeof *req, gfp_flags); ++ if (!req) ++ return 0; ++ ++ memset (req, 0, sizeof *req); ++ req->req.dma = DMA_ADDR_INVALID; ++ INIT_LIST_HEAD (&req->queue); ++ return &req->req; ++} ++ ++static void ++sa1100_free_request (struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct sa1100_request *req; ++ ++#if 0 // NCB ++ struct sa1100_ep *ep; ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) ++ return; ++
#endif ++ ++ req = container_of (_req, struct sa1100_request, req); ++ WARN_ON (!list_empty (&req->queue)); ++ kfree(req); //NCB - see pxa2xx_udc ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* allocate buffers this way to eliminate per-io buffer copies */ ++ ++static void * ++sa1100_alloc_buffer ( ++ struct usb_ep *_ep, ++ unsigned bytes, ++ dma_addr_t *dma, ++ int gfp_flags ++) ++{ ++ void *retval; ++#if 1 // NCB see pxa2xx_udc ++ retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); ++ if (retval) ++// used in pxa .. *dma = virt_to_bus (retval); ++ *dma = virt_to_phys (retval); ++#else ++ struct sa1100_ep *ep; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!ep || (!ep->desc && _ep->name != ep0name)) ++ return 0; ++ ++ *dma = DMA_ADDR_INVALID; ++ retval = kmalloc (bytes, GFP_DMA | gfp_flags); ++ if (retval) ++ *dma = virt_to_phys (retval); ++
#endif ++ return retval; ++} ++ ++static void ++sa1100_free_buffer ( ++ struct usb_ep *_ep, ++ void *buf, ++ dma_addr_t dma, ++ unsigned bytes ++) { ++ kfree (buf); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ++done (struct sa1100_ep *ep, struct sa1100_request *req, int status) ++{ ++ unsigned stopped = ep->stopped; ++ ++ ++ list_del_init (&req->queue); ++ ++ if (req->req.status == -EINPROGRESS) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ if (status && status != -ESHUTDOWN) ++ VDEBUG (ep->dev, "complete %s req %p stat %d len %u/%u\n", ++ ep->ep.name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ PRINTKD("%s: calling complete on req\n",__FUNCTION__); ++ req->req.complete (&ep->ep, &req->req); ++ ep->stopped = stopped; ++} ++ ++#if 0 // NCB ++void control_done (int value) ++{ ++ struct list_head *entry, *tmp; ++ struct sa1100_request *req; ++ ++ list_for_each_safe (entry, tmp, &the_controller.ep[0].queue) { ++ req = list_entry (entry, struct sa1100_request, queue); ++ ++ /* HACK -- assume no control errors */ ++ if (value == 0) ++ req->req.actual = req->req.length; ++#if 1 ++ PRINTKD("%s: calling done with ep=%p, req=%p\n",__FUNCTION__,&the_udc.ep[0],req); ++ done (&the_udc.ep[0], req, value); ++#else ++ done (&the_udc.ep0, req, value); ++#endif ++ value = -EPROTO; ++ } ++} ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* FIXME move away from the old non-queued api. ++ * - forces extra work on us ++ * - stores request state twice ++ * - doesn't let gadget driver handle dma mapping ++ * - status codes need mapping ++ */ ++ ++static int map_status (int status) ++{ ++ switch (status) { ++ case 0: ++ case -EIO: /* ep[12]_int_handler */ ++ return status; ++ case -EPIPE: /* ep1_int_handler */ ++ return 0; ++ // case -EAGAIN: /* ep[12]_init */ ++ // case -EINTR: /* ep[12]_reset */ ++ default: ++ return -ESHUTDOWN; ++ } ++} ++ ++static void tx_callback (int status, int size) ++{ ++ struct sa1100_ep *ep = &the_controller->ep[2]; ++ struct sa1100_request *req; ++ ++// PRINTKD("%s: doing ...\n",__FUNCTION__); ++PRINTKD("%s: doing ... status=%d size=%d\n",__FUNCTION__,status,size); ++//PRINTKD("%s: doing ... status=%d\n",__FUNCTION__,status); ++ if (list_empty (&ep->queue)) { ++ if (status != -EAGAIN) ++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n", ++ ep->ep.name, status, size); ++// DEBUG (ep->dev, "%s, bogus tx callback %d\n", ++// ep->ep.name, status); ++else ++PRINTKD("%s: list empty.\n",__FUNCTION__); ++ return; ++ } ++ req = list_entry (ep->queue.next, struct sa1100_request, queue); ++ req->req.actual = size; ++ done (ep, req, map_status (status)); ++ ++PRINTKD("%s: ep->stopped=%s\n",__FUNCTION__,ep->stopped ? "true":"false"); ++ if (ep->stopped || list_empty (&ep->queue)) ++ return; ++ req = list_entry (ep->queue.next, struct sa1100_request, queue); ++PRINTKD("%s: calling sa1100_usb_send\n",__FUNCTION__); ++ sa1100_usb_send (&req->req, tx_callback); ++} ++ ++static void rx_callback (int status, int size) ++{ ++ struct sa1100_ep *ep = &the_controller->ep[1]; ++ struct sa1100_request *req; ++ ++// PRINTKD("%s: doing ...\n",__FUNCTION__); ++PRINTKD("%s: doing ... status=%d\n",__FUNCTION__,status); ++ if (list_empty (&ep->queue)) { ++ if (status != -EAGAIN) ++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n", ++ ep->ep.name, status, size); ++ return; ++ } ++ req = list_entry (ep->queue.next, struct sa1100_request, queue); ++ req->req.actual = size; ++ done (ep, req, map_status (status)); ++ ++ if (ep->stopped || list_empty (&ep->queue)) ++ return; ++ req = list_entry (ep->queue.next, struct sa1100_request, queue); ++ sa1100_usb_recv (&req->req, rx_callback); ++} ++ ++ ++static int ++sa1100_queue (struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) ++{ ++ struct sa1100_request *req; ++ struct sa1100_ep *ep; ++ struct sa1100_udc *dev; ++ unsigned long flags; ++ ++ req = container_of (_req, struct sa1100_request, req); ++ if (!_req || !_req->complete || !_req->buf ++ || !list_empty (&req->queue)) ++ return -EINVAL; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!_ep || (!ep->desc && _ep->name != ep0name)) ++ return -EINVAL; ++ dev = ep->dev; ++ ++ // handle ep0 ++ if (_ep->name == ep0name)
{ ++ ep0_queue( _req->buf, _req->length, dev->ep0_req_len >=0 ? dev->ep0_req_len: _req->length ); ++ return 0; ++ } ++ ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ ++ /* sa1100 udc can't write zlps */ ++ if (ep == &dev->ep[2] && _req->length == 0) ++ return -ERANGE; ++ ++ /* the old sa1100 api doesn't use 'unsigned' for lengths */ ++ if (_req->length > INT_MAX) ++ return -ERANGE; ++ ++#if 0 ++ VDEBUG (dev, "%s queue req %p, len %d buf %p\n", ++ _ep->name, _req, _req->length, _req->buf); ++#endif ++ ++ local_irq_save (flags); ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ ++// NCB if (list_empty) { ++ if (list_empty (&ep->queue) && !ep->stopped) { ++ /* FIXME this does DMA mapping wrong. caller is allowed ++ * to provide buffers that don't need mapping, but this ++ * doesn't use them. ++ */ ++ if (ep == &ep->dev->ep[2])
{ ++ PRINTKD("%s: sa1100_usb_send buf %p length %d\n",__FUNCTION__,_req->buf,_req->length); ++ sa1100_usb_send (_req, tx_callback); ++ } ++ else if (ep == &ep->dev->ep[1])
{ ++ PRINTKD("%s: sa1100_usb_recv buf %p length %d\n",__FUNCTION__,_req->buf,_req->length); ++ sa1100_usb_recv (_req, rx_callback); ++ } ++ /* ep0 rx/tx is handled separately */ ++ } ++ list_add_tail (&req->queue, &ep->queue); ++ ++ local_irq_restore (flags); ++ ++ return 0; ++} ++ ++/* dequeue ALL requests */ ++static void nuke (struct sa1100_ep *ep, int status) ++{ ++ struct sa1100_request *req; ++ ++ /* called with irqs blocked */ ++ ep->stopped = 1; ++ if (ep == &ep->dev->ep[1]) ++ ep1_reset (); ++ else if (ep == &ep->dev->ep[2]) ++ ep2_reset (); ++ while (!list_empty (&ep->queue)) { ++ req = list_entry (ep->queue.next, ++ struct sa1100_request, ++ queue); ++ done (ep, req, status); ++ } ++} ++ ++/* dequeue JUST ONE request */ ++static int sa1100_dequeue (struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct sa1100_ep *ep; ++ struct sa1100_request *req; ++ unsigned long flags; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ req = container_of (_req, struct sa1100_request, req); ++ if (!_ep || (!ep->desc && _ep->name != ep0name) || !_req) ++ return -EINVAL; ++ ++ local_irq_save (flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry (req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ if (&req->req != _req) { ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ ++#if 0 ++//#ifdef USE_DMA ++ if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { ++ cancel_dma(ep); ++ done(ep, req, -ECONNRESET); ++ /* restart i/o */ ++ if (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, ++ struct pxa2xx_request, queue); ++ kick_dma(ep, req); ++ } ++ } else ++//#endif ++#endif ++ done(ep, req, -ECONNRESET); ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++sa1100_set_halt (struct usb_ep *_ep, int value) ++{ ++ struct sa1100_ep *ep; ++ ++ ep = container_of (_ep, struct sa1100_ep, ep); ++ if (!_ep || (!ep->desc && _ep->name != ep0name)) ++ return -EINVAL; ++ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ if ( (ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC) ++ return -EINVAL; ++ ++ VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear"); ++ ++ /* set/clear, then synch memory views with the device */ ++ if (value) { ++ if (ep == &ep->dev->ep[1]) ++ ep1_stall (); ++ else ++ ep2_stall (); ++ } else { ++ if (ep == &ep->dev->ep[1]) ++ ep1_reset (); ++ else ++ ep2_reset (); ++ } ++ ++ return 0; ++} ++ ++static struct usb_ep_ops sa1100_ep_ops = { ++ .enable = sa1100_enable, ++ .disable = sa1100_disable, ++ ++ .alloc_request = sa1100_alloc_request, ++ .free_request = sa1100_free_request, ++ ++ .alloc_buffer = sa1100_alloc_buffer, ++ .free_buffer = sa1100_free_buffer, ++ ++ .queue = sa1100_queue, ++ .dequeue = sa1100_dequeue, ++ ++ .set_halt = sa1100_set_halt, ++ // .fifo_status = sa1100_fifo_status, ++ // .fifo_flush = sa1100_fifo_flush, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int sa1100_get_frame (struct usb_gadget *_gadget) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static int sa1100_wakeup (struct usb_gadget *_gadget) ++{ ++ struct sa1100_udc *dev; ++ ++ if (!_gadget) ++ return 0; ++ dev = container_of (_gadget, struct sa1100_udc, gadget); ++ ++ // FIXME ++ ++ return 0; ++} ++ ++static const struct usb_gadget_ops sa1100_ops = { ++ .get_frame = sa1100_get_frame, ++ .wakeup = sa1100_wakeup, ++ ++ // .set_selfpowered = sa1100_set_selfpowered, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline void enable_resume_mask_suspend (void) ++{ ++ int i = 0; ++ ++ while (1) { ++ Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events ++ udelay (i); ++ if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR)) ++ break; ++ if (++i == 50) { ++ WARN (&the_controller, "%s Could not set SUSIM %8.8X\n", ++ __FUNCTION__, Ser0UDCCR); ++ break; ++ } ++ } ++ ++ i = 0; ++ while (1) { ++ Ser0UDCCR &= ~UDCCR_RESIM; ++ udelay (i); ++ if ( (Ser0UDCCR & UDCCR_RESIM) == 0 ++ || (Ser0UDCSR & UDCSR_RSTIR)) ++ break; ++ if (++i == 50) { ++ WARN (&the_controller, "%s Could not clear RESIM %8.8X\n", ++ __FUNCTION__, Ser0UDCCR); ++ break; ++ } ++ } ++} ++ ++static inline void enable_suspend_mask_resume (void) ++{ ++ int i = 0; ++ while (1) { ++ Ser0UDCCR |= UDCCR_RESIM; // mask future resume events ++ udelay (i); ++ if (Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR)) ++ break; ++ if (++i == 50) { ++ WARN (&the_controller, "%s could not set RESIM %8.8X\n", ++ __FUNCTION__, Ser0UDCCR); ++ break; ++ } ++ } ++ i = 0; ++ while (1) { ++ Ser0UDCCR &= ~UDCCR_SUSIM; ++ udelay (i); ++ if ( (Ser0UDCCR & UDCCR_SUSIM) == 0 ++ || (Ser0UDCSR & UDCSR_RSTIR)) ++ break; ++ if (++i == 50) { ++ WARN (&the_controller, "%s Could not clear SUSIM %8.8X\n", ++ __FUNCTION__, Ser0UDCCR); ++ break; ++ } ++ } ++} ++ ++// HACK DEBUG 3Mar01ww ++// Well, maybe not, it really seems to help! 08Mar01ww ++static void core_kicker (void) ++{ ++ u32 car = Ser0UDCAR; ++ u32 imp = Ser0UDCIMP; ++ u32 omp = Ser0UDCOMP; ++ ++ UDC_set (Ser0UDCCR, UDCCR_UDD); ++ udelay (300); ++ UDC_clear (Ser0UDCCR, UDCCR_UDD); ++ ++ Ser0UDCAR = car; ++ Ser0UDCIMP = imp; ++ Ser0UDCOMP = omp; ++} ++ ++// NCB static void ++static irqreturn_t ++udc_int_hndlr (int irq, void *_dev, struct pt_regs *regs) ++{ ++ struct sa1100_udc *dev = _dev; ++ u32 status = Ser0UDCSR; ++ ++ u32 control = Ser0UDCCR; ++
PRINTKD("%s: status = 0x%x and control = 0x%x\n",__FUNCTION__,status,control); ++ /* ReSeT Interrupt Request - UDC has been reset */ ++ if (status & UDCSR_RSTIR) { ++ PRINTKD("%s: processing UDCSR_RSTIR\n",__FUNCTION__); ++ if (usbctl_next_state_on_event (kEvReset) != kError) { ++ /* starting 20ms or so reset sequence now... */ ++ INFO (dev, "Resetting\n"); ++ ep0_reset (); // just set state to idle ++ ep1_reset (); // flush dma, clear false stall ++ ep2_reset (); // flush dma, clear false stall ++ } ++ // mask reset ints, they flood during sequence, enable ++ // suspend and resume ++ Ser0UDCCR |= UDCCR_REM; // mask reset ++ Ser0UDCCR &= ~ (UDCCR_SUSIM | UDCCR_RESIM); // enable suspend and resume ++ UDC_flip ( Ser0UDCSR, status); // clear all pending sources ++ PRINTKD("%s: setting USB_FULL_SPEED\n",__FUNCTION__); ++ dev->gadget.speed = USB_SPEED_FULL; ++ return IRQ_HANDLED; // NCB ++ } ++ ++ /* else we have done something other than reset, ++ * so be sure reset enabled ++ */ ++ UDC_clear (Ser0UDCCR, UDCCR_REM); ++ ++ /* RESume Interrupt Request */ ++ if (status & UDCSR_RESIR) { ++ struct usb_gadget_driver *driver = dev->driver; ++ ++ PRINTKD("%s: processing UDCSR_RESIR\n",__FUNCTION__); ++ if (driver->resume) ++ driver->resume (&dev->gadget); ++ core_kicker (); ++ enable_suspend_mask_resume (); ++ } ++ ++ /* SUSpend Interrupt Request */ ++ if (status & UDCSR_SUSIR) { ++ struct usb_gadget_driver *driver = dev->driver; ++ ++ PRINTKD("%s: processing UDCSR_SUSIR\n",__FUNCTION__); ++ if (driver->suspend) ++ driver->suspend (&dev->gadget); ++ enable_resume_mask_suspend (); ++ } ++ ++ UDC_flip (Ser0UDCSR, status); // clear all pending sources ++ ++ if (status & UDCSR_EIR)
++ ep0_int_hndlr (); ++ ++ ++ if (status & UDCSR_RIR)
{ ++ PRINTKD("%s: processing ep1_int_hndlr\n",__FUNCTION__); ++ ep1_int_hndlr (status); ++ } ++ if (status & UDCSR_TIR)
{ ++ PRINTKD("%s: processing ep2_int_hndlr\n",__FUNCTION__); ++ ep2_int_hndlr (status); ++ } ++ ++ return IRQ_HANDLED; // NCB ++ ++} ++ ++/* soft_connect_hook () ++ * Some devices have platform-specific circuitry to make USB ++ * not seem to be plugged in, even when it is. This allows ++ * software to control when a device 'appears' on the USB bus ++ * (after Linux has booted and this driver has loaded, for ++ * example). If you have such a circuit, control it here. ++ */ ++#ifdef CONFIG_SA1100_EXTENEX1 ++static void soft_connect_hook (int enable) ++{ ++ if (machine_is_extenex1 ()) { ++ if (enable) { ++ PPDR |= PPC_USB_SOFT_CON; ++ PPSR |= PPC_USB_SOFT_CON; ++ } else { ++ PPSR &= ~PPC_USB_SOFT_CON; ++ PPDR &= ~PPC_USB_SOFT_CON; ++ } ++ } ++} ++#elif defined(CONFIG_SA1100_BALLOON) ++static void soft_connect_hook (int enable) ++{ ++ if (machine_is_balloon()) { ++ if (enable) ++ balloon_udc_connect(); ++ else ++ balloon_udc_disconnect(); ++ } ++} ++#else ++#define soft_connect_hook(x) do { } while (0); ++#endif ++ ++/* "function" sysfs attribute */ ++static ssize_t ++show_function (struct device *_dev, char *buf) ++{ ++ struct sa1100_udc *dev = dev_get_drvdata (_dev); ++ ++ if (!dev->driver ++ || !dev->driver->function ++ || strlen (dev->driver->function) > PAGE_SIZE) ++ return 0; ++ return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); ++} ++static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); ++ ++/* disable the UDC at the source */ ++static void udc_disable (struct sa1100_udc *dev) ++{ ++ soft_connect_hook (0); ++ UDC_set (Ser0UDCCR, UDCCR_UDD); ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ep0_idle(dev); ++} ++ ++static void udc_reinit(struct sa1100_udc *dev) { ++ ++ u32 i; ++ ++ /* Initialize the gadget controller data structure */ ++ INIT_LIST_HEAD (&dev->gadget.ep_list); ++ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); ++ ep0_idle(dev); ++ for ( i = 0 ; i < 3 ; i++) { ++ struct sa1100_ep *ep = &dev->ep[i]; ++ if (i != 0) ++ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); ++ ep->desc = NULL; ++ ep->stopped = 0; ++ INIT_LIST_HEAD(&ep->queue); ++ } ++} ++ ++/* enable the udc at the source */ ++static void udc_enable (struct sa1100_udc *dev) ++{ ++ UDC_clear (Ser0UDCCR, UDCCR_UDD); ++ ep0_idle(dev); ++} ++ ++static void ep0_start (struct sa1100_udc *dev) ++{ ++ udc_enable (dev); ++ udelay (100); ++ ++ /* clear stall - receiver seems to start stalled? 19Jan01ww */ ++ /* also clear other stuff just to be thurough 22Feb01ww */ ++ UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC ); ++ UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC ); ++ ++ /* mask everything */ ++ Ser0UDCCR = 0xFC; ++ ++ /* flush DMA and fire through some -EAGAINs */ ++ ep1_init (dev->ep[1].dmaregs); ++ ep2_init (dev->ep[2].dmaregs); ++ ++ /* enable any platform specific hardware */ ++ soft_connect_hook (1); ++ ++ /* clear all top-level sources */ ++ Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR | ++ UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ; ++ ++ /* EXERIMENT - a short line in the spec says toggling this ++ * bit diddles the internal state machine in the udc to ++ * expect a suspend ++ */ ++ Ser0UDCCR |= UDCCR_RESIM; ++ /* END EXPERIMENT 10Feb01ww */ ++ ++ /* enable any platform specific hardware */ ++ soft_connect_hook (1); ++ ++ /* Enable interrupts. If you are unplugged you will immediately ++ * get a suspend interrupt. If you are plugged and have a soft ++ * connect-circuit, you will get a reset. If you are plugged ++ * without a soft-connect, I think you also get suspend. In short, ++ * start with suspend masked and everything else enabled ++ */ ++ UDC_write( Ser0UDCCR, UDCCR_SUSIM ); ++} ++ ++ ++/* when a driver is successfully registered, it will receive ++ * control requests including set_configuration (), which enables ++ * non-control requests. then usb traffic follows until a ++ * disconnect is reported. then a host may connect again, or ++ * the driver might get unbound. ++ */ ++int usb_gadget_register_driver (struct usb_gadget_driver *driver) ++{ ++ struct sa1100_udc *dev = the_controller; ++ int retval; ++ ++ if (!driver ++ || !driver->bind ++ || !driver->unbind ++ || !driver->setup) ++ return -EINVAL; ++ if (!dev) ++ return -ENODEV; ++ if (dev->driver) ++ return -EBUSY; ++ ++ /* hook up the driver ... */ ++ dev->driver = driver; ++ dev->gadget.dev.driver = &driver->driver; ++ ++// device_add (&dev->gadget.dev); ++ retval = driver->bind (&dev->gadget); ++ if (retval) { ++ DEBUG (dev, "bind to driver %s --> %d\n", ++ driver->driver.name, retval); ++ device_del(&dev->gadget.dev); ++ dev->driver = NULL; ++ dev->gadget.dev.driver = NULL; ++ return retval; ++ } ++
device_create_file(dev->dev, &dev_attr_function); ++ ++ /* ... then enable host detection and ep0; and we're ready ++ * for set_configuration as well as eventual disconnect. ++ */ ++ ep0_start (dev); ++ ++ DEBUG (dev, "%s ready\n", driver->driver.name); ++ ++ return 0; ++} ++EXPORT_SYMBOL (usb_gadget_register_driver); ++ ++static void ++stop_activity (struct sa1100_udc *dev, struct usb_gadget_driver *driver) ++{ ++ int i; ++ ++ /* don't disconnect if it's not connected */ ++ if (dev->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = NULL; ++
dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ /* mask everything */ ++ Ser0UDCCR = 0xFC; ++ ++ /* stop hardware; prevent new request submissions; ++ * and kill any outstanding requests. ++ */ ++ for (i = 0; i < 3; i++) { ++ struct sa1100_ep *ep = &dev->ep[i]; ++ ++ ep->stopped = 1; ++ nuke(ep, -ESHUTDOWN); ++ } ++ udc_disable (dev); ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (driver) { ++// spin_unlock (&dev->lock); ++ driver->disconnect (&dev->gadget); ++// spin_lock (&dev->lock); ++ } ++ /* re-init driver-visible data structures */ ++ udc_reinit(dev); ++} ++ ++int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) ++{ ++ struct sa1100_udc *dev = the_controller; ++ ++ if (!dev) ++ return -ENODEV; ++ if (!driver || driver != dev->driver) ++ return -EINVAL; ++ ++ local_irq_disable(); ++ stop_activity (dev, driver); ++ local_irq_enable(); ++ driver->unbind (&dev->gadget); ++ dev->driver = 0; ++ ++//printk("%s: deleting device\n",__FUNCTION__); ++// device_del (&dev->gadget.dev); ++ device_remove_file(dev->dev, &dev_attr_function); ++ ++ DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name); ++ return 0; ++} ++EXPORT_SYMBOL (usb_gadget_unregister_driver); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/*-------------------------------------------------------------------------*/ ++ ++////////////////////////////////////////////////////////////////////////////// ++// Proc Filesystem Support ++////////////////////////////////////////////////////////////////////////////// ++ ++#if CONFIG_PROC_FS ++ ++#define SAY(fmt,args...) p += sprintf (p, fmt, ## args) ++#define SAYV(num) p += sprintf (p, num_fmt, "Value", num) ++#define SAYC(label,yn) p += sprintf (p, yn_fmt, label, yn) ++#define SAYS(label,v) p += sprintf (p, cnt_fmt, label, v) ++ ++static int usbctl_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ const char * num_fmt = "%25.25s: %8.8lX\n"; ++ const char * cnt_fmt = "%25.25s: %lu\n"; ++ const char * yn_fmt = "%25.25s: %s\n"; ++ const char * yes = "YES"; ++ const char * no = "NO"; ++ unsigned long v; ++ char * p = page; ++ int len; ++ ++ SAY ("SA1100 USB Controller Core\n"); ++ ++ SAYS ("ep0 bytes read", usbd_info.stats.ep0_bytes_read); ++ SAYS ("ep0 bytes written", usbd_info.stats.ep0_bytes_written); ++ SAYS ("ep0 FIFO read failures", usbd_info.stats.ep0_fifo_read_failures); ++ SAYS ("ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures); ++ ++ SAY ("\n"); ++ ++ v = Ser0UDCAR; ++ SAY ("%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v); ++ v = Ser0UDCIMP; ++ SAY ("%25.25s: %ld (%8.8lX)\n", "IN max packet size", v+1, v); ++ v = Ser0UDCOMP; ++ SAY ("%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v); ++ ++ v = Ser0UDCCR; ++ SAY ("\nUDC Mask Register\n"); ++ SAYV (v); ++ SAYC ("UDC Active", (v & UDCCR_UDA) ? yes : no); ++ SAYC ("Suspend interrupts masked", (v & UDCCR_SUSIM) ? yes : no); ++ SAYC ("Resume interrupts masked", (v & UDCCR_RESIM) ? yes : no); ++ SAYC ("Reset interrupts masked", (v & UDCCR_REM) ? yes : no); ++ ++ v = Ser0UDCSR; ++ SAY ("\nUDC Interrupt Request Register\n"); ++ SAYV (v); ++ SAYC ("Reset pending", (v & UDCSR_RSTIR) ? yes : no); ++ SAYC ("Suspend pending", (v & UDCSR_SUSIR) ? yes : no); ++ SAYC ("Resume pending", (v & UDCSR_RESIR) ? yes : no); ++ SAYC ("ep0 pending", (v & UDCSR_EIR) ? yes : no); ++ SAYC ("receiver pending", (v & UDCSR_RIR) ? yes : no); ++ SAYC ("tramsitter pending", (v & UDCSR_TIR) ? yes : no); ++ ++#ifdef CONFIG_SA1100_EXTENEX1 ++ SAYC ("\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden"); ++#endif ++ ++#if 1 ++ SAY ("\nDMA Tx registers\n"); ++ { ++ dma_regs_t *r=the_controller->ep[2].dmaregs; ++ SAY (" DDAR"); ++ SAYV(r->DDAR); ++ SAY (" DCSR"); ++ SAYV(r->RdDCSR); ++ SAY (" DBSA (address buf A) "); ++ SAYV(r->DBSA); ++ SAY (" DBTA (transfer count A) "); ++ SAYV(r->DBTA); ++ SAY (" DBSB (address buf B) "); ++ SAYV(r->DBSB); ++ SAY (" DBTB (transfer count B) "); ++ SAYV(r->DBTB); ++ ++ } ++ SAY ("\nDMA Rx registers\n"); ++ { ++ dma_regs_t *r=the_controller->ep[1].dmaregs; ++ SAY (" DDAR"); ++ SAYV(r->DDAR); ++ SAY (" DCSR"); ++ SAYV(r->RdDCSR); ++ SAY (" DBSA (address buf A) "); ++ SAYV(r->DBSA); ++ SAY (" DBTA (transfer count A) "); ++ SAYV(r->DBTA); ++ SAY (" DBSB (address buf B) "); ++ SAYV(r->DBSB); ++ SAY (" DBTB (transfer count B) "); ++ SAYV(r->DBTB); ++ ++ } ++#endif ++#if 1 ++ v = Ser0UDCCS0; ++ SAY ("\nUDC Endpoint Zero Status Register\n"); ++ SAYV (v); ++ SAYC ("Out Packet Ready", (v & UDCCS0_OPR) ? yes : no); ++ SAYC ("In Packet Ready", (v & UDCCS0_IPR) ? yes : no); ++ SAYC ("Sent Stall", (v & UDCCS0_SST) ? yes : no); ++ SAYC ("Force Stall", (v & UDCCS0_FST) ? yes : no); ++ SAYC ("Data End", (v & UDCCS0_DE) ? yes : no); ++ SAYC ("Data Setup End", (v & UDCCS0_SE) ? yes : no); ++ SAYC ("Serviced (SO)", (v & UDCCS0_SO) ? yes : no); ++ ++ v = Ser0UDCCS1; ++ SAY ("\nUDC Receiver Status Register\n"); ++ SAYV (v); ++ SAYC ("Receive Packet Complete", (v & UDCCS1_RPC) ? yes : no); ++ SAYC ("Sent Stall", (v & UDCCS1_SST) ? yes : no); ++ SAYC ("Force Stall", (v & UDCCS1_FST) ? yes : no); ++ SAYC ("Receive Packet Error", (v & UDCCS1_RPE) ? yes : no); ++ SAYC ("Receive FIFO not empty", (v & UDCCS1_RNE) ? yes : no); ++ ++ v = Ser0UDCCS2; ++ SAY ("\nUDC Transmitter Status Register\n"); ++ SAYV (v); ++ SAYC ("FIFO has < 8 of 16 chars", (v & UDCCS2_TFS) ? yes : no); ++ SAYC ("Transmit Packet Complete", (v & UDCCS2_TPC) ? yes : no); ++ SAYC ("Transmit FIFO underrun", (v & UDCCS2_TUR) ? yes : no); ++ SAYC ("Transmit Packet Error", (v & UDCCS2_TPE) ? yes : no); ++ SAYC ("Sent Stall", (v & UDCCS2_SST) ? yes : no); ++ SAYC ("Force Stall", (v & UDCCS2_FST) ? yes : no); ++#endif ++ ++ len = (p - page) - off; ++ if (len < 0) ++ len = 0; ++ *eof = (len <=count) ? 1 : 0; ++ *start = page + off; ++ return len; ++} ++ ++static inline void register_proc_entry (void) ++{ ++ create_proc_read_entry (driver_name, 0, NULL, ++ usbctl_read_proc, NULL); ++} ++ ++static inline void unregister_proc_entry (void) ++{ ++ remove_proc_entry (driver_name, NULL); ++} ++ ++#else ++ ++#define register_proc_entry() do {} while (0) ++#define unregister_proc_entry() do {} while (0) ++ ++#endif /* CONFIG_PROC_FS */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++MODULE_DESCRIPTION ("sa1100_udc"); ++MODULE_AUTHOR ("Various"); ++MODULE_LICENSE ("GPL"); ++ ++static struct sa1100_udc memory = { ++ .gadget = { ++ .ops = &sa1100_ops, ++ .ep0 = &memory.ep[0].ep, ++ .name = driver_name, ++/* ++ .dev = { ++ .bus_id = "gadget", ++ .release = nop_release, ++ }, ++*/ ++ }, ++ ++ /* control endpoint */ ++ .ep[0] = { ++ .ep = { ++ .name = ep0name, ++ .ops = &sa1100_ep_ops, ++ .maxpacket = EP0_FIFO_SIZE, ++// .maxpacket = 8, ++ }, ++ .dev = &memory, ++/* ++ .reg_udccs = &UDCCS0, ++ .reg_uddr = &UDDR0, ++*/ ++ }, ++ ++ /* first group of endpoints */ ++ .ep[1] = { ++ .ep = { ++ .name = "ep1out-bulk", ++ .ops = &sa1100_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++// .maxpacket = 64, ++ }, ++ .dev = &memory, ++/* ++ .fifo_size = BULK_FIFO_SIZE, ++ .bEndpointAddress = USB_DIR_IN | 1, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .reg_udccs = &UDCCS1, ++ .reg_uddr = &UDDR1, ++ drcmr (25) ++*/ ++ }, ++ .ep[2] = { ++ .ep = { ++ .name = "ep2in-bulk", ++ .ops = &sa1100_ep_ops, ++ .maxpacket = BULK_FIFO_SIZE, ++// .maxpacket = 64, ++ }, ++ .dev = &memory, ++/* ++ .fifo_size = BULK_FIFO_SIZE, ++ .bEndpointAddress = 2, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .reg_udccs = &UDCCS2, ++ .reg_ubcr = &UBCR2, ++ .reg_uddr = &UDDR2, ++ drcmr (26) ++*/ ++ } ++}; ++
++static int __init sa1100_udc_probe ( struct device *_dev) ++{ ++ struct sa1100_udc *dev=&memory; ++ int retval = 0; ++ ++ /* setup dev */ ++ dev->dev = _dev; ++// dev->mach = _dev->platform_data; ++ ++ device_initialize(&dev->gadget.dev); ++ dev->gadget.dev.parent = _dev; ++ dev->gadget.dev.dma_mask = _dev->dma_mask; ++ ++ the_controller = dev; ++ dev_set_drvdata(_dev, dev); ++ ++ /* controller stays disabled until gadget driver is bound */ ++ udc_disable (dev); ++ udc_reinit(dev); ++ ++// spin_lock_init(&the_udc.lock); ++ register_proc_entry (); ++ ++ /* setup dma channels and IRQ */ ++ retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive", ++ NULL, NULL, &dev->ep[1].dmaregs); ++ if (retval) { ++ ERROR (dev, "couldn't get rx dma, err %d\n", retval); ++ goto err_rx_dma; ++ } ++ retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit", ++ NULL, NULL, &dev->ep[2].dmaregs); ++ if (retval) { ++ ERROR (dev, "couldn't get tx dma, err %d\n", retval); ++ goto err_tx_dma; ++ } ++ retval = request_irq (IRQ_Ser0UDC, udc_int_hndlr, SA_INTERRUPT, ++ driver_name, dev); ++ if (retval) { ++ ERROR (dev, "couldn't get irq, err %d\n", retval); ++ goto err_irq; ++ } ++ ++ INFO (dev, "initialized, rx %p tx %p irq %d\n", ++ dev->ep[1].dmaregs, dev->ep[2].dmaregs, IRQ_Ser0UDC); ++ return 0; ++ ++err_irq: ++ sa1100_free_dma (dev->ep[2].dmaregs); ++ usbd_info.dmaregs_rx = 0; ++err_tx_dma: ++ sa1100_free_dma (dev->ep[1].dmaregs); ++ usbd_info.dmaregs_tx = 0; ++err_rx_dma: ++ return retval; ++} ++ ++static int __exit sa1100_udc_remove (struct device *_dev) ++{ ++ struct sa1100_udc *dev = dev_get_drvdata(_dev); ++ ++ udc_disable (dev); ++ unregister_proc_entry (); ++ usb_gadget_unregister_driver (dev->driver); ++ sa1100_free_dma (dev->ep[1].dmaregs); ++ sa1100_free_dma (dev->ep[2].dmaregs); ++ free_irq (IRQ_Ser0UDC, dev); ++ dev_set_drvdata(_dev,NULL); ++ the_controller = NULL; ++ return 0; ++} ++ ++static struct device_driver udc_driver = { ++ .name = "sa11x0-udc", ++ .bus = &platform_bus_type, ++ .probe = sa1100_udc_probe, ++ .remove = __exit_p(sa1100_udc_remove), ++// .suspend = sa1100_udc_suspend, ++// .resume = sa1100_udc_resume, ++}; ++ ++static int __init udc_init(void) ++{ ++ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); ++#ifdef NCB_DMA_FIX ++ send_buffer = (char*) kmalloc( SEND_BUFFER_SIZE, GFP_KERNEL | GFP_DMA ); ++ receive_buffer = (char*) kmalloc( RECEIVE_BUFFER_SIZE, GFP_KERNEL | GFP_DMA ); ++#endif ++ return driver_register(&udc_driver); ++} ++module_init(udc_init); ++ ++static void __exit udc_exit(void) ++{ ++#ifdef NCB_DMA_FIX ++ if (send_buffer) { ++ kfree(send_buffer); ++ send_buffer=NULL; ++ } ++ if (receive_buffer) { ++ kfree(receive_buffer); ++ receive_buffer=NULL; ++ } ++#endif ++ driver_unregister(&udc_driver); ++} ++module_exit(udc_exit); ++ +diff -uNr linux-2.6.21.vanilla/drivers/usb/gadget/sa1100_udc.h linux-2.6.21/drivers/usb/gadget/sa1100_udc.h +--- linux-2.6.21.vanilla/drivers/usb/gadget/sa1100_udc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/drivers/usb/gadget/sa1100_udc.h 2007-06-05 11:58:29.000000000 +0200 +@@ -0,0 +1,107 @@ ++/*
++ * internals of "new style" UDC controller
++ * <linux/usb_gadget.h> replaces ARM-specific "sa1100_usb.h".
++ */
++
++struct sa1100_ep {
++ struct usb_ep ep;
++ struct sa1100_udc *dev;
++ //unsigned long irqs;
++
++ const struct usb_endpoint_descriptor *desc;
++ struct list_head queue;
++ dma_regs_t *dmaregs; ++ unsigned stopped : 1;
++};
++
++struct sa1100_request {
++ struct usb_request req;
++ struct list_head queue;
++// NCB unsigned mapped : 1;
++};
++
++enum ep0_state { ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_END_XFER, ++ EP0_STALL, ++}; ++ ++//#define EP0_FIFO_SIZE ((unsigned)16) ++#define EP0_FIFO_SIZE ((unsigned)8) ++#define BULK_FIFO_SIZE ((unsigned)64) ++//#define ISO_FIFO_SIZE ((unsigned)256) ++//#define INT_FIFO_SIZE ((unsigned)8) ++ ++struct udc_stats { ++ struct ep0stats { ++ unsigned long ops; ++ unsigned long bytes; ++ } read, write; ++ unsigned long irqs; ++}; ++ ++struct sa1100_udc {
++ struct usb_gadget gadget;
++ struct usb_gadget_driver *driver; ++ struct device *dev; ++ enum ep0_state ep0state; ++ struct udc_stats stats; ++// NCB spinlock_t lock;
++// NCB dma_regs_t *dmaregs_tx, *dmaregs_rx;
++ unsigned got_irq : 1, ++ vbus : 1, ++ pullup : 1, ++ has_cfr : 1, ++ req_pending : 1, ++ req_std : 1, ++ req_config : 1; ++ ++#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) ++ struct timer_list timer; ++ u64 dma_mask; ++ unsigned char address;
++ struct sa1100_ep ep[3]; ++ int ep0_req_len; ++};
++
++/*-------------------------------------------------------------------------*/
++
++#define xprintk(dev,level,fmt,args...) \
++ printk(level "%s: " fmt , driver_name , ## args)
++
++#ifdef DEBUG
++#undef DEBUG
++#define DEBUG(dev,fmt,args...) \
++ xprintk(dev , KERN_DEBUG , fmt , ## args)
++#else
++#define DEBUG(dev,fmt,args...) \
++ do { } while (0)
++#endif /* DEBUG */
++
++#ifdef VERBOSE
++#define VDEBUG DEBUG
++#else
++#define VDEBUG(dev,fmt,args...) \
++ do { } while (0)
++#endif /* VERBOSE */
++
++#define ERROR(dev,fmt,args...) \
++ xprintk(dev , KERN_ERR , fmt , ## args)
++#define WARN(dev,fmt,args...) \
++ xprintk(dev , KERN_WARNING , fmt , ## args)
++#define INFO(dev,fmt,args...) \
++ xprintk(dev , KERN_INFO , fmt , ## args)
++
++/*-------------------------------------------------------------------------*/
++
++#ifndef container_of
++#define container_of list_entry
++#endif
++
++#ifndef WARN_ON
++#define WARN_ON(x) do { } while (0)
++#endif
++
++
+diff -uNr linux-2.6.21.vanilla/include/linux/usb_ch9.h linux-2.6.21/include/linux/usb_ch9.h +--- linux-2.6.21.vanilla/include/linux/usb_ch9.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.21/include/linux/usb_ch9.h 2007-06-05 11:58:29.000000000 +0200 +@@ -0,0 +1,555 @@ ++/* ++ * This file holds USB constants and structures that are needed for USB ++ * device APIs. These are used by the USB device model, which is defined ++ * in chapter 9 of the USB 2.0 specification. Linux has several APIs in C ++ * that need these: ++ * ++ * - the master/host side Linux-USB kernel driver API; ++ * - the "usbfs" user space API; and ++ * - the Linux "gadget" slave/device/peripheral side driver API. ++ * ++ * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems ++ * act either as a USB master/host or as a USB slave/device. That means ++ * the master and slave side APIs benefit from working well together. ++ * ++ * There's also "Wireless USB", using low power short range radios for ++ * peripheral interconnection but otherwise building on the USB framework. ++ */ ++ ++#ifndef __LINUX_USB_CH9_H ++#define __LINUX_USB_CH9_H ++ ++#include <linux/types.h> /* __u8 etc */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* CONTROL REQUEST SUPPORT */ ++ ++/* ++ * USB directions ++ * ++ * This bit flag is used in endpoint descriptors' bEndpointAddress field. ++ * It's also one of three fields in control requests bRequestType. ++ */ ++#define USB_DIR_OUT 0 /* to device */ ++#define USB_DIR_IN 0x80 /* to host */ ++ ++/* ++ * USB types, the second of three bRequestType fields ++ */ ++#define USB_TYPE_MASK (0x03 << 5) ++#define USB_TYPE_STANDARD (0x00 << 5) ++#define USB_TYPE_CLASS (0x01 << 5) ++#define USB_TYPE_VENDOR (0x02 << 5) ++#define USB_TYPE_RESERVED (0x03 << 5) ++ ++/* ++ * USB recipients, the third of three bRequestType fields ++ */ ++#define USB_RECIP_MASK 0x1f ++#define USB_RECIP_DEVICE 0x00 ++#define USB_RECIP_INTERFACE 0x01 ++#define USB_RECIP_ENDPOINT 0x02 ++#define USB_RECIP_OTHER 0x03 ++ ++/* ++ * Standard requests, for the bRequest field of a SETUP packet. ++ * ++ * These are qualified by the bRequestType field, so that for example ++ * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved ++ * by a GET_STATUS request. ++ */ ++#define USB_REQ_GET_STATUS 0x00 ++#define USB_REQ_CLEAR_FEATURE 0x01 ++#define USB_REQ_SET_FEATURE 0x03 ++#define USB_REQ_SET_ADDRESS 0x05 ++#define USB_REQ_GET_DESCRIPTOR 0x06 ++#define USB_REQ_SET_DESCRIPTOR 0x07 ++#define USB_REQ_GET_CONFIGURATION 0x08 ++#define USB_REQ_SET_CONFIGURATION 0x09 ++#define USB_REQ_GET_INTERFACE 0x0A ++#define USB_REQ_SET_INTERFACE 0x0B ++#define USB_REQ_SYNCH_FRAME 0x0C ++ ++#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */ ++#define USB_REQ_GET_ENCRYPTION 0x0E ++#define USB_REQ_SET_HANDSHAKE 0x0F ++#define USB_REQ_GET_HANDSHAKE 0x10 ++#define USB_REQ_SET_CONNECTION 0x11 ++#define USB_REQ_SET_SECURITY_DATA 0x12 ++#define USB_REQ_GET_SECURITY_DATA 0x13 ++#define USB_REQ_SET_WUSB_DATA 0x14 ++#define USB_REQ_LOOPBACK_DATA_WRITE 0x15 ++#define USB_REQ_LOOPBACK_DATA_READ 0x16 ++#define USB_REQ_SET_INTERFACE_DS 0x17 ++ ++/* ++ * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and ++ * are read as a bit array returned by USB_REQ_GET_STATUS. (So there ++ * are at most sixteen features of each type.) ++ */ ++#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ ++#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ ++#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ ++#define USB_DEVICE_BATTERY 2 /* (wireless) */ ++#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ ++#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/ ++#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ ++#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ ++#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ ++ ++#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ ++ ++ ++/** ++ * struct usb_ctrlrequest - SETUP data for a USB device control request ++ * @bRequestType: matches the USB bmRequestType field ++ * @bRequest: matches the USB bRequest field ++ * @wValue: matches the USB wValue field (le16 byte order) ++ * @wIndex: matches the USB wIndex field (le16 byte order) ++ * @wLength: matches the USB wLength field (le16 byte order) ++ * ++ * This structure is used to send control requests to a USB device. It matches ++ * the different fields of the USB 2.0 Spec section 9.3, table 9-2. See the ++ * USB spec for a fuller description of the different fields, and what they are ++ * used for. ++ * ++ * Note that the driver for any interface can issue control requests. ++ * For most devices, interfaces don't coordinate with each other, so ++ * such requests may be made at any time. ++ */ ++struct usb_ctrlrequest { ++ __u8 bRequestType; ++ __u8 bRequest; ++ __le16 wValue; ++ __le16 wIndex; ++ __le16 wLength; ++} __attribute__ ((packed)); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * STANDARD DESCRIPTORS ... as returned by GET_DESCRIPTOR, or ++ * (rarely) accepted by SET_DESCRIPTOR. ++ * ++ * Note that all multi-byte values here are encoded in little endian ++ * byte order "on the wire". But when exposed through Linux-USB APIs, ++ * they've been converted to cpu byte order. ++ */ ++ ++/* ++ * Descriptor types ... USB 2.0 spec table 9.5 ++ */ ++#define USB_DT_DEVICE 0x01 ++#define USB_DT_CONFIG 0x02 ++#define USB_DT_STRING 0x03 ++#define USB_DT_INTERFACE 0x04 ++#define USB_DT_ENDPOINT 0x05 ++#define USB_DT_DEVICE_QUALIFIER 0x06 ++#define USB_DT_OTHER_SPEED_CONFIG 0x07 ++#define USB_DT_INTERFACE_POWER 0x08 ++/* these are from a minor usb 2.0 revision (ECN) */ ++#define USB_DT_OTG 0x09 ++#define USB_DT_DEBUG 0x0a ++#define USB_DT_INTERFACE_ASSOCIATION 0x0b ++/* these are from the Wireless USB spec */ ++#define USB_DT_SECURITY 0x0c ++#define USB_DT_KEY 0x0d ++#define USB_DT_ENCRYPTION_TYPE 0x0e ++#define USB_DT_BOS 0x0f ++#define USB_DT_DEVICE_CAPABILITY 0x10 ++#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11 ++ ++/* conventional codes for class-specific descriptors */ ++#define USB_DT_CS_DEVICE 0x21 ++#define USB_DT_CS_CONFIG 0x22 ++#define USB_DT_CS_STRING 0x23 ++#define USB_DT_CS_INTERFACE 0x24 ++#define USB_DT_CS_ENDPOINT 0x25 ++ ++/* All standard descriptors have these 2 fields at the beginning */ ++struct usb_descriptor_header { ++ __u8 bLength; ++ __u8 bDescriptorType; ++} __attribute__ ((packed)); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_DEVICE: Device descriptor */ ++struct usb_device_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __le16 bcdUSB; ++ __u8 bDeviceClass; ++ __u8 bDeviceSubClass; ++ __u8 bDeviceProtocol; ++ __u8 bMaxPacketSize0; ++ __le16 idVendor; ++ __le16 idProduct; ++ __le16 bcdDevice; ++ __u8 iManufacturer; ++ __u8 iProduct; ++ __u8 iSerialNumber; ++ __u8 bNumConfigurations; ++} __attribute__ ((packed)); ++ ++#define USB_DT_DEVICE_SIZE 18 ++ ++ ++/* ++ * Device and/or Interface Class codes ++ * as found in bDeviceClass or bInterfaceClass ++ * and defined by www.usb.org documents ++ */ ++#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ ++#define USB_CLASS_AUDIO 1 ++#define USB_CLASS_COMM 2 ++#define USB_CLASS_HID 3 ++#define USB_CLASS_PHYSICAL 5 ++#define USB_CLASS_STILL_IMAGE 6 ++#define USB_CLASS_PRINTER 7 ++#define USB_CLASS_MASS_STORAGE 8 ++#define USB_CLASS_HUB 9 ++#define USB_CLASS_CDC_DATA 0x0a ++#define USB_CLASS_CSCID 0x0b /* chip+ smart card */ ++#define USB_CLASS_CONTENT_SEC 0x0d /* content security */ ++#define USB_CLASS_VIDEO 0x0e ++#define USB_CLASS_WIRELESS_CONTROLLER 0xe0 ++#define USB_CLASS_APP_SPEC 0xfe ++#define USB_CLASS_VENDOR_SPEC 0xff ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_CONFIG: Configuration descriptor information. ++ * ++ * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the ++ * descriptor type is different. Highspeed-capable devices can look ++ * different depending on what speed they're currently running. Only ++ * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG ++ * descriptors. ++ */ ++struct usb_config_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __le16 wTotalLength; ++ __u8 bNumInterfaces; ++ __u8 bConfigurationValue; ++ __u8 iConfiguration; ++ __u8 bmAttributes; ++ __u8 bMaxPower; ++} __attribute__ ((packed)); ++ ++#define USB_DT_CONFIG_SIZE 9 ++ ++/* from config descriptor bmAttributes */ ++#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */ ++#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */ ++#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */ ++#define USB_CONFIG_ATT_BATTERY (1 << 4) /* battery powered */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_STRING: String descriptor */ ++struct usb_string_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __le16 wData[1]; /* UTF-16LE encoded */ ++} __attribute__ ((packed)); ++ ++/* note that "string" zero is special, it holds language codes that ++ * the device supports, not Unicode characters. ++ */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_INTERFACE: Interface descriptor */ ++struct usb_interface_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bInterfaceNumber; ++ __u8 bAlternateSetting; ++ __u8 bNumEndpoints; ++ __u8 bInterfaceClass; ++ __u8 bInterfaceSubClass; ++ __u8 bInterfaceProtocol; ++ __u8 iInterface; ++} __attribute__ ((packed)); ++ ++#define USB_DT_INTERFACE_SIZE 9 ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_ENDPOINT: Endpoint descriptor */ ++struct usb_endpoint_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bEndpointAddress; ++ __u8 bmAttributes; ++ __le16 wMaxPacketSize; ++ __u8 bInterval; ++ ++ /* NOTE: these two are _only_ in audio endpoints. */ ++ /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ ++ __u8 bRefresh; ++ __u8 bSynchAddress; ++} __attribute__ ((packed)); ++ ++#define USB_DT_ENDPOINT_SIZE 7 ++#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ ++ ++ ++/* ++ * Endpoints ++ */ ++#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ ++#define USB_ENDPOINT_DIR_MASK 0x80 ++ ++#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ ++#define USB_ENDPOINT_XFER_CONTROL 0 ++#define USB_ENDPOINT_XFER_ISOC 1 ++#define USB_ENDPOINT_XFER_BULK 2 ++#define USB_ENDPOINT_XFER_INT 3 ++#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */ ++struct usb_qualifier_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __le16 bcdUSB; ++ __u8 bDeviceClass; ++ __u8 bDeviceSubClass; ++ __u8 bDeviceProtocol; ++ __u8 bMaxPacketSize0; ++ __u8 bNumConfigurations; ++ __u8 bRESERVED; ++} __attribute__ ((packed)); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_OTG (from OTG 1.0a supplement) */ ++struct usb_otg_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bmAttributes; /* support for HNP, SRP, etc */ ++} __attribute__ ((packed)); ++ ++/* from usb_otg_descriptor.bmAttributes */ ++#define USB_OTG_SRP (1 << 0) ++#define USB_OTG_HNP (1 << 1) /* swap host/device roles */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_DEBUG: for special highspeed devices, replacing serial console */ ++struct usb_debug_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ /* bulk endpoints with 8 byte maxpacket */ ++ __u8 bDebugInEndpoint; ++ __u8 bDebugOutEndpoint; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */ ++struct usb_interface_assoc_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bFirstInterface; ++ __u8 bInterfaceCount; ++ __u8 bFunctionClass; ++ __u8 bFunctionSubClass; ++ __u8 bFunctionProtocol; ++ __u8 iFunction; ++} __attribute__ ((packed)); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_SECURITY: group of wireless security descriptors, including ++ * encryption types available for setting up a CC/association. ++ */ ++struct usb_security_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __le16 wTotalLength; ++ __u8 bNumEncryptionTypes; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_KEY: used with {GET,SET}_SECURITY_DATA; only public keys ++ * may be retrieved. ++ */ ++struct usb_key_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 tTKID[3]; ++ __u8 bReserved; ++ __u8 bKeyData[0]; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_ENCRYPTION_TYPE: bundled in DT_SECURITY groups */ ++struct usb_encryption_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bEncryptionType; ++#define USB_ENC_TYPE_UNSECURE 0 ++#define USB_ENC_TYPE_WIRED 1 /* non-wireless mode */ ++#define USB_ENC_TYPE_CCM_1 2 /* aes128/cbc session */ ++#define USB_ENC_TYPE_RSA_1 3 /* rsa3072/sha1 auth */ ++ __u8 bEncryptionValue; /* use in SET_ENCRYPTION */ ++ __u8 bAuthKeyIndex; ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_BOS: group of wireless capabilities */ ++struct usb_bos_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __le16 wTotalLength; ++ __u8 bNumDeviceCaps; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_DEVICE_CAPABILITY: grouped with BOS */ ++struct usb_dev_cap_header { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDevCapabilityType; ++}; ++ ++#define USB_CAP_TYPE_WIRELESS_USB 1 ++ ++struct usb_wireless_cap_descriptor { /* Ultra Wide Band */ ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDevCapabilityType; ++ ++ __u8 bmAttributes; ++#define USB_WIRELESS_P2P_DRD (1 << 1) ++#define USB_WIRELESS_BEACON_MASK (3 << 2) ++#define USB_WIRELESS_BEACON_SELF (1 << 2) ++#define USB_WIRELESS_BEACON_DIRECTED (2 << 2) ++#define USB_WIRELESS_BEACON_NONE (3 << 2) ++ __le16 wPHYRates; /* bit rates, Mbps */ ++#define USB_WIRELESS_PHY_53 (1 << 0) /* always set */ ++#define USB_WIRELESS_PHY_80 (1 << 1) ++#define USB_WIRELESS_PHY_107 (1 << 2) /* always set */ ++#define USB_WIRELESS_PHY_160 (1 << 3) ++#define USB_WIRELESS_PHY_200 (1 << 4) /* always set */ ++#define USB_WIRELESS_PHY_320 (1 << 5) ++#define USB_WIRELESS_PHY_400 (1 << 6) ++#define USB_WIRELESS_PHY_480 (1 << 7) ++ __u8 bmTFITXPowerInfo; /* TFI power levels */ ++ __u8 bmFFITXPowerInfo; /* FFI power levels */ ++ __le16 bmBandGroup; ++ __u8 bReserved; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with ++ * each endpoint descriptor for a wireless device ++ */ ++struct usb_wireless_ep_comp_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bMaxBurst; ++ __u8 bMaxSequence; ++ __le16 wMaxStreamDelay; ++ __le16 wOverTheAirPacketSize; ++ __u8 bOverTheAirInterval; ++ __u8 bmCompAttributes; ++#define USB_ENDPOINT_SWITCH_MASK 0x03 /* in bmCompAttributes */ ++#define USB_ENDPOINT_SWITCH_NO 0 ++#define USB_ENDPOINT_SWITCH_SWITCH 1 ++#define USB_ENDPOINT_SWITCH_SCALE 2 ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_REQ_SET_HANDSHAKE is a four-way handshake used between a wireless ++ * host and a device for connection set up, mutual authentication, and ++ * exchanging short lived session keys. The handshake depends on a CC. ++ */ ++struct usb_handshake { ++ __u8 bMessageNumber; ++ __u8 bStatus; ++ __u8 tTKID[3]; ++ __u8 bReserved; ++ __u8 CDID[16]; ++ __u8 nonce[16]; ++ __u8 MIC[8]; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB_REQ_SET_CONNECTION modifies or revokes a connection context (CC). ++ * A CC may also be set up using non-wireless secure channels (including ++ * wired USB!), and some devices may support CCs with multiple hosts. ++ */ ++struct usb_connection_context { ++ __u8 CHID[16]; /* persistent host id */ ++ __u8 CDID[16]; /* device id (unique w/in host context) */ ++ __u8 CK[16]; /* connection key */ ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* USB 2.0 defines three speeds, here's how Linux identifies them */ ++ ++enum usb_device_speed { ++ USB_SPEED_UNKNOWN = 0, /* enumerating */ ++ USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ ++ USB_SPEED_HIGH, /* usb 2.0 */ ++ USB_SPEED_VARIABLE, /* wireless (usb 2.5) */ ++}; ++ ++enum usb_device_state { ++ /* NOTATTACHED isn't in the USB spec, and this state acts ++ * the same as ATTACHED ... but it's clearer this way. ++ */ ++ USB_STATE_NOTATTACHED = 0, ++ ++ /* chapter 9 and authentication (wireless) device states */ ++ USB_STATE_ATTACHED, ++ USB_STATE_POWERED, /* wired */ ++ USB_STATE_UNAUTHENTICATED, /* auth */ ++ USB_STATE_RECONNECTING, /* auth */ ++ USB_STATE_DEFAULT, /* limited function */ ++ USB_STATE_ADDRESS, ++ USB_STATE_CONFIGURED, /* most functions */ ++ ++ USB_STATE_SUSPENDED ++ ++ /* NOTE: there are actually four different SUSPENDED ++ * states, returning to POWERED, DEFAULT, ADDRESS, or ++ * CONFIGURED respectively when SOF tokens flow again. ++ */ ++}; ++ ++#endif /* __LINUX_USB_CH9_H */ diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-GPIO-MMC-mod.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-GPIO-MMC-mod.patch new file mode 100644 index 0000000000..70a1555158 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-GPIO-MMC-mod.patch @@ -0,0 +1,1699 @@ +diff -Nur linux-2.6.24.vanilla/drivers/mmc/card/Makefile linux-2.6.24/drivers/mmc/card/Makefile +--- linux-2.6.24.vanilla/drivers/mmc/card/Makefile 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/mmc/card/Makefile 2008-02-23 03:10:45.000000000 +0100 +@@ -6,8 +6,11 @@ + EXTRA_CFLAGS += -DDEBUG + endif + ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++# nothing to do ++else + obj-$(CONFIG_MMC_BLOCK) += mmc_block.o + mmc_block-objs := block.o queue.o + + obj-$(CONFIG_SDIO_UART) += sdio_uart.o +- ++endif +diff -Nur linux-2.6.24.vanilla/drivers/mmc/core/Makefile linux-2.6.24/drivers/mmc/core/Makefile +--- linux-2.6.24.vanilla/drivers/mmc/core/Makefile 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/mmc/core/Makefile 2008-02-23 03:10:45.000000000 +0100 +@@ -6,9 +6,13 @@ + EXTRA_CFLAGS += -DDEBUG + endif + ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++# nothing to do ++else + obj-$(CONFIG_MMC) += mmc_core.o + mmc_core-y := core.o sysfs.o bus.o host.o \ + mmc.o mmc_ops.o sd.o sd_ops.o \ + sdio.o sdio_ops.o sdio_bus.o \ + sdio_cis.o sdio_io.o sdio_irq.o ++endif + +diff -Nur linux-2.6.24.vanilla/drivers/mmc/host/Kconfig linux-2.6.24/drivers/mmc/host/Kconfig +--- linux-2.6.24.vanilla/drivers/mmc/host/Kconfig 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/mmc/host/Kconfig 2008-02-23 03:10:45.000000000 +0100 +@@ -4,6 +4,7 @@ + + comment "MMC/SD Host Controller Drivers" + ++ + config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA +@@ -130,3 +131,9 @@ + + If unsure, or if your system has no SPI master driver, say N. + ++config MMC_SPI_BLOCK ++ tristate "MMC/SD over GPIO (Software SPI) for SIMpad (EXPERIMENTAL)" ++ depends on SA1100_SIMPAD && EXPERIMENTAL ++ help ++ Say Y here to enable MMC block device over GPIO ++ if you have done the MMC-Mod. For Module say M. +diff -Nur linux-2.6.24.vanilla/drivers/mmc/host/Makefile linux-2.6.24/drivers/mmc/host/Makefile +--- linux-2.6.24.vanilla/drivers/mmc/host/Makefile 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/mmc/host/Makefile 2008-02-23 03:10:45.000000000 +0100 +@@ -6,6 +6,9 @@ + EXTRA_CFLAGS += -DDEBUG + endif + ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++obj-$(CONFIG_MMC_SPI_BLOCK) += mmc_spi_block.o ++else + obj-$(CONFIG_MMC_ARMMMCI) += mmci.o + obj-$(CONFIG_MMC_PXA) += pxamci.o + obj-$(CONFIG_MMC_IMX) += imxmmc.o +@@ -17,4 +20,4 @@ + obj-$(CONFIG_MMC_AT91) += at91_mci.o + obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o + obj-$(CONFIG_MMC_SPI) += mmc_spi.o +- ++endif +diff -Nur linux-2.6.24.vanilla/drivers/mmc/host/mmc_spi_block.c linux-2.6.24/drivers/mmc/host/mmc_spi_block.c +--- linux-2.6.24.vanilla/drivers/mmc/host/mmc_spi_block.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/mmc/host/mmc_spi_block.c 2008-02-23 03:10:45.000000000 +0100 +@@ -0,0 +1,1622 @@ ++/* ++ * Copyright (c) Cl�ent Ballabriga, 2005 - GPL ++ * Copyright (c) Guylhem Aznar, 2005 - GPL ++ * ++ * Please check http://externe.net/zaurus/simpad-bluetooth reference design first. ++ * ++ * Based on Madsuk/Rohde work on a MMC driver for the WRT54G. ++ * ++ * This is an ugly hack of a driver. I am surprised if it ever works! ++ * So please use a real driver or contribute one to the 2.4/2.6 mmc framework ++ * ++ * mrdata: ported to 2.6 ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#include <linux/sched.h> ++#include <linux/kernel.h> ++#include <linux/fs.h> ++#include <linux/errno.h> ++#include <linux/hdreg.h> ++#include <linux/kdev_t.h> ++#include <linux/blkdev.h> ++#include <linux/spinlock.h> ++#include <linux/time.h> ++#include <linux/delay.h> ++#include <linux/timer.h> ++ ++#include <linux/platform_device.h> ++ ++#include <asm/hardware.h> ++#include <asm/arch/simpad.h> ++#include <asm/arch/gpio.h> ++ ++static int major = 121; ++ ++#define DEVICE_NAME "mmc_spi" ++ ++static int hd_sizes[1<<6]; ++static int hd_blocksizes[1<<6]; ++static int hd_hardsectsizes[1<<6]; ++static int hd_maxsect[1<<6]; ++static struct hd_struct hd[1<<6]; ++ ++static struct gendisk *mmc_disk; ++ ++static struct platform_device *mmc_dev; /* the one and only instance */ ++ ++static spinlock_t mmc_spi_lock; ++ ++/* ++ * ******************************************************************* ++ * ++ * This is the only configurable part. ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++// #define DEBUG 1 ++// #define DEBUG_HD 1 ++// #define CHECK_MEDIA_CHANGE // for developement ONLY, not working yet ++ ++/* Let that include where it is or compilation fails on INIT_REQUEST/CURRENT */ ++ ++ ++/* ++ * If you are using different GPIOs in your hardware hack, you must ++ * first make sure they are unused for other functions and then ++ * configure them here. ++ * ++ * On the simpad I use spare pins from the UART1 (internal serial port -> DECT 20-polig): ++ * ++ * Funktion PIN ## Original direction GPIO ## SPI function New direction SD/MMC ++ * - DCD PIN 08 (in) GPIO 23 DO - new name: DI -> MISO (in) PIN 7 Data Out ++ * - DTR PIN 11 (out) GPIO 07 CS (out) PIN 1 Chip Select ++ * - RI PIN 14 (in) GPIO 19 CLK (out) PIN 5 Clock ++ * - DSR PIN 16 (in) GPIO 06 DI - new name: DO -> MOSI (out) PIN 2 Data In ++ * ++ * ++ * SPI: MISO = Master In / Slave OUT MOSI = Master Out / Slave In ++ * ++ * Don't worry about in/out original function - the GPIOs will be ++ * reprogrammed. ++ */ ++ ++#define GPIO_SD_DI 23 ++#define GPIO_SD_CS 7 ++#define GPIO_SD_CLK 19 ++#define GPIO_SD_DO 6 ++ ++// #define FAST_GPIO_SD_DI GPIO_GPIO23 ++// #define FAST_GPIO_SD_CS GPIO_GPIO7 ++// #define FAST_GPIO_SD_CLK GPIO_GPIO19 ++// #define FAST_GPIO_SD_DO GPIO_GPIO6 ++ ++#define FAST_GPIO_SD_DI GPIO_UART1_DCD ++#define FAST_GPIO_SD_CS GPIO_UART1_DTR ++#define FAST_GPIO_SD_CLK GPIO_UART1_RI ++#define FAST_GPIO_SD_DO GPIO_UART1_DSR ++ ++/* ++ * ******************************************************************* ++ * ++ * Do not change anything below ! ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++/* GPIO states */ ++#define LOW 0 ++#define HIGH 1 ++ ++#define INPUT 0 ++#define OUTPUT 1 ++ ++#define PRESENT 1 ++#define ABSENT 0 ++ ++typedef unsigned int uint32; ++typedef unsigned long u32_t; ++typedef unsigned short u16_t; ++typedef unsigned char u8_t; ++ ++// static struct timer_list mmc_timer; ++ ++// static struct timeval s_zeit, e_zeit; ++ ++/* start with no card */ ++static int mmc_media_detect = 0; ++// static int mmc_media_changed = 1; ++ ++ ++///////////////////// ++// prototypes ++static int mmc_open(struct inode *inode, struct file *filp); ++static int mmc_release(struct inode *inode, struct file *filp); ++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); ++static void mmc_spi_request(struct request_queue *q); ++ ++ ++/* ++ * ******************************************************************* ++ * ++ * Begin GPIO hardware access functions. ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++#define gpio_read(a) ((GPLR & a) ? 1 : 0) ++#define gpio_write_high(a) GPSR = a ++#define gpio_write_low(a) GPCR = a ++ ++/* set MMC_Chip_Select to HIGH (MMC/SD-Card inactiv) */ ++#define MMC_Disable() gpio_write_high( FAST_GPIO_SD_CS) ++ ++/* set MMC_Chip_Select to LOW (MMC/SD-Card activ) */ ++#define MMC_Enable() gpio_write_low( FAST_GPIO_SD_CS) ++ ++/* ++ * ******************************************************************* ++ * ++ * Begin SPI hardware access functions. ++ * ++ * ******************************************************************* ++ * ++ */ ++static int mmc_spi_media_detect(void) ++{ ++// FIXME: add card detection/test by SPI ++ ++ return 1; ++} ++ ++static int mmc_spi_hardware_init(void) ++{ ++ printk("\nmmc: GPIO init\n"); ++ ++ /* cut existing functions */ ++ gpio_set_alternative_function(GPIO_SD_CLK, 0); ++ gpio_set_alternative_function(GPIO_SD_DI, 0); ++ gpio_set_alternative_function(GPIO_SD_DO, 0); ++ gpio_set_alternative_function(GPIO_SD_CS, 0); ++ ++ /* remap directions and set state of spi pins */ ++ gpio_direction_output(GPIO_SD_CLK, 0); ++ gpio_direction_input(GPIO_SD_DI); ++ gpio_direction_output(GPIO_SD_DO, 0); ++ gpio_direction_output(GPIO_SD_CS, 0); ++ ++ printk("mmc: initialising MMC\n"); ++ ++ /* Start */ ++ MMC_Disable(); ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ gpio_write_high( FAST_GPIO_SD_DO); ++ return 0; ++} ++ ++/* return what has been read, write the parameter */ ++/* Clockrate round about 1,2 MHz */ ++ ++static unsigned char mmc_spi_readwrite(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ return (result); ++} ++ ++/* return what has been read, write the parameter */ ++/* Clockrate round 200 kHz */ ++ ++static unsigned char mmc_spi_readwrite_slow(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ udelay(10); ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ udelay(10); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ udelay(10); ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ udelay(10); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ udelay(10); ++ ++ // printk("Send Byte = 0x%2X Receive Byte = 0x%2X \n", data_out, result); ++ ++ return (result); ++} ++ ++/* return what has been read */ ++ ++static unsigned char mmc_spi_read_only(void) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ return (result); ++} ++ ++/* write the parameter */ ++/* Clockrate round about 3,6 MHz */ ++ ++static unsigned char mmc_spi_write_only(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ return (result); ++} ++ ++ ++/** ++ * this function was contributed by: rcichielo from openwrt forums ++ * ++ * Comments added by Marc DENTY on 2007-03-20 ++ * ++ * Sequence to read a card's "CID" bytes (name, serial number etc) ++ * ++ * Send: 4ah,00h,00h,00h,00h,00h - CMD10, no args, null CRC ++ * Read: xx - NCR Time ++ * Read: xx - Command Response (Should be 00h) ++ * Read: until FEh is received - Wait for Data token ++ * Read: yy * 16 - Get 16 bytes from CID ++ * Read: zz - Read CRC lo byte ++ * Read: zz - Read CRC hi byte ++ * ++ * Useful locations in the returned data packet: ++ * ++ * 03h-08h Manufacturers's name in ascii ++ * 0ah-0dh Card's 32 bit serial number ++ */ ++/** ++ * Comments added by Cyril CATTIAUX on 2007-03-21 ++ * ++ * CID format specification (from Sandisk SD Product Manual v1.9) ++ * ++ * cid[00 ] Manufacturer ID (unsigned byte) ++ * cid[01-02] OEM/Application ID (ASCII) ++ * cid[03-07] Product Name (ASCII) ++ * cid[08 ] Product Revistion (BCD coded number) ++ * cid[09-12] Serial Number (32-bit unsigned int) ++ * cid[13-14] Reserved(bit 12->15) - Manufacture Date(bit 0->11) ++ * cid[15 ] CRC7(bit 1->7) - Not used, allways 1 (bit 0) ++*/ ++static int mmc_read_cid(unsigned char *cid) ++{ ++ unsigned char result = 0; ++ int i; ++ ++ MMC_Enable(); ++ ++ /* wait */ ++ for (i = 0; i < 4; i++) ++ { ++ result=mmc_spi_readwrite(0xff); ++ } ++ ++ /* issue CID (card identification data) read request */ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0x40 | 10); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x95); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result=mmc_spi_readwrite(0xff); ++ ++ if(result == 0x00) ++ break; ++ } ++ ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return(1); ++ } ++ ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xfe) break; ++ } ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return(2); ++ } ++ ++ for (i = 0; i < 16; i++) { ++ result = mmc_spi_readwrite(0xff); ++ cid[i] = result; ++ } ++ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0xff); ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ ++ return 0; ++} ++ ++ ++/** ++ * Comments added by Cyril CATTIAUX on 2007-03-21 ++ * ++ * CID format specification (from Sandisk SD Product Manual v1.9) ++ * ++ * cid[00 ] Manufacturer ID (unsigned byte) ++ * cid[01-02] OEM/Application ID (ASCII) ++ * cid[03-07] Product Name (ASCII) ++ * cid[08 ] Product Revision (BCD coded 2 digit number) ++ * cid[09-12] Serial Number (32-bit unsigned int) ++ * cid[13-14] Manufacture Date(bit 0->11) (BCD coded 3 digit number YYM offset from 2000) - Reserved(bit 12->15) ++ * cid[15 ] Not used, allways 1 (bit 0) - CRC7(bit 1->7) ++*/ ++static void mmc_show_cid_info(void) ++{ ++ int i, result; ++ unsigned short tmps; ++ unsigned char cid[16]; ++ ++ char manufacturer_id; ++ char oem_id[3]; ++ char product_name[6]; ++ unsigned char product_revision_h, product_revision_l; ++ unsigned int product_sn; ++ unsigned short product_date_y; ++ unsigned char product_date_m; ++ ++ result = mmc_read_cid(cid); ++ ++ if (result == 0) ++ { ++ printk("mmc_init: MMC/SD Card ID: "); ++ for (i=0; i<16; i++) { ++ printk("%02X ", cid[i]); ++ } ++ manufacturer_id=cid[0]; ++ strncpy(oem_id, &cid[1], 2); ++ oem_id[2]='\0'; ++ strncpy(product_name, &cid[3], 5); ++ product_name[5]='\0'; ++ product_revision_h=(cid[8] >> 4) & 0xf; ++ product_revision_l=cid[8] & 0xf; ++ product_sn=(cid[9]<<24) + (cid[10]<<16) + (cid[11]<<8) + cid[12]; ++ tmps=((cid[13]<<8) + cid[14]) & 0x0fff; ++ product_date_y=2000 + (((tmps >> 8) & 0xf) * 10) + ((tmps >> 4) & 0xf); ++ product_date_m=tmps & 0xf; ++ ++ printk("\nManufacturer ID : %02X\n", manufacturer_id); ++ printk("OEM/Application ID: %s\n", oem_id); ++ printk("Product name : %s\n", product_name); ++ printk("Product revision : %d.%d\n", product_revision_h, product_revision_l); ++ printk("Product SN : %08X\n", product_sn); ++ printk("Product Date : %d-%d\n", product_date_y, product_date_m); ++ ++ } else { ++ printk("mmc_init: impossible to get card indentification info for reason code: %02x", result); ++ } ++} ++ ++ ++/* ++static int mmc_spi_hw_test(void) ++{ ++ unsigned char result, k; ++ ++ unsigned int i, j, t; ++ ++ printk("mmc_spi_hw_test -> \n\n"); ++ k = 0x55; ++ for ( i = 0 ; i < 5; i++) { ++ ++ printk("\n0x%2X - ", k); ++ for ( j = 0 ; j < 8; j++ ) { ++ do_gettimeofday( &s_zeit ); ++ result = mmc_spi_readwrite_slow(k); ++ do_gettimeofday( &e_zeit ); ++ ++ if ( result != k ) { ++ printk("!>ERROR<! Transfer = 0x%2X Receive = 0x%2X Trail = %d \n", k, result, j); ++ // i = 255; j = 1000; ++ } ++ ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("Durchlauf: %i Versuch: %i von 8 -> Laufzeit: 0x%X s\n", i , j, t); ++ udelay(200); ++ } ++ printk("ready "); ++ ++ // k++; ++ } ++ printk("ready "); ++ printk("\n\n"); ++ return (0); ++} ++*/ ++ ++/* ++static int mmc_spi_speed_test(void) ++{ ++ unsigned int i, j, k, l, t; ++ ++ MMC_Disable(); ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_readwrite(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_readwrite: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 1; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 1; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_readwrite_slow(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_readwrite_slow: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_read_only(); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_read_only: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_write_only(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_write_only: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ return (1); ++ ++} ++*/ ++ ++ ++static int mmc_spi_card_init(void) ++{ ++ unsigned char result = 0; ++ short i, j; ++ ++// unsigned long flags; ++ ++ // save_flags(flags); ++ // cli(); ++ ++/* ++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CS), gpio_getalt(&gp, GPIO_SD_CS)); ++ printk("GPIO_SD_DI dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DI), gpio_getalt(&gp, GPIO_SD_DI)); ++ printk("GPIO_SD_DO dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DO), gpio_getalt(&gp, GPIO_SD_DO)); ++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CLK), gpio_getalt(&gp, GPIO_SD_CLK)); ++*/ ++ ++ // printk("\nmmc: mmc_spi_hw_test() *START*\n"); ++ ++ // mmc_spi_hw_test(); ++ ++ printk("\nmmc: card init 1/2 (CMD0)\n"); ++ ++ for (j = 0; j < 10; j++) ++ { ++ MMC_Disable(); ++ ++ for (i = 0; i < 10; i++) ++ mmc_spi_readwrite_slow(0xff); ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite_slow(0xff); ++ ++ mmc_spi_readwrite_slow(0x40); ++ ++ for (i = 0; i < 4; i++) { ++ ++ mmc_spi_readwrite_slow(0x00); ++ ++ } ++ ++ mmc_spi_readwrite_slow(0x95); ++ ++ for (i = 0; i < 8; i++) { ++ ++ result = mmc_spi_readwrite_slow(0xff); ++ ++#ifdef DEBUG_HD ++ if (result != 0xff) { ++ if (result > 0x1F && result < 0x80) ++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ else ++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X\n", j, i, result); ++ } ++#endif ++ if (result == 0x01) ++ break; ++ } ++ ++ if (result == 0x01) ++ break; ++ ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ ++ mdelay(60); ++ } ++ ++ if (result != 0x01) { ++ ++ printk("mmc: card init 1/2 error: %d (CMD0) failed\n", result); ++ printk(" -> Hint: MMC/SD-Card realy (fully) inserted ?\n"); ++ ++ return (1); ++ } ++ ++ printk("mmc: card init 1/2 (CMD0) success\n\n"); ++ ++ mdelay(1); ++ ++ printk("mmc: card init 2/2 (CMD1)\n"); ++ for (j = 0; j < 10; j++) { ++ ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x41); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // if (result >= 32 && result <= 127) ++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ // else ++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ ++ mmc_spi_readwrite_slow(0xff); ++ ++ if (result == 0x00) { ++ printk("mmc: card init 2/2 (CMD1) success\n\n"); ++ ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x4d); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 6; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // if (result > 31 && result < 128) ++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ // else ++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result); ++#endif ++ // if (result == 0x00) ++ // break; ++ } ++ // mdelay(60); ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ // mdelay(10); ++ ++ // restore_flags(flags); ++ ++ return (0); ++ } ++ mdelay(60); ++ } ++ return (2); ++} ++ ++ ++static int mmc_spi_card_config(void) ++{ ++ unsigned char result = 0; ++ short i, j; ++ unsigned char csd[32]; ++ unsigned int c_size; ++ unsigned int c_size_mult; ++ unsigned int mult; ++ unsigned int read_bl_len; ++ unsigned int blocknr = 0; ++ unsigned int block_len = 0; ++ unsigned int size = 0; ++ unsigned char rd_buffer[528]; ++// unsigned long flags; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite_slow(0xff); ++ result = mmc_spi_readwrite_slow(0x51); ++ // mmc_spi_readwrite_slow(0x4A); ++ // mmc_spi_readwrite_slow(0x40+0x0D); ++ // mmc_spi_readwrite_slow(0x42); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ ++ // printk("mmc: (CMD17) response von 0x51 result = 0x%X\n", result); ++ ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // printk("mmc: (CMD17) response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // restore_flags(flags); ++ // mmc_spi_readwrite_slow(0xff); ++ return (1); ++ } ++ // restore_flags(flags); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ rd_buffer[i] = result; ++#ifdef DEBUG_HD ++ /* ++ if (result >= 32 && result <= 127) ++ printk("mmc: CMD17 response (start token) result = 0x%X Zeichen = %c\n", result, result); ++ else ++ printk("mmc: CMD17 response (start token) result = 0x%X\n", result); ++ */ ++#endif ++ // if (result == 0xfe) ++ // break; ++ } ++ /* ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0xff); ++ return(1); ++ } ++ */ ++ ++ for (i = 8; i < 520; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ rd_buffer[i] = result; ++ } ++ for (i = 0; i < 2; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ } ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ ++ printk("Buffer - Start\n"); ++ ++ for ( i = 0 ; i < 33 ; i++) { ++ printk("\r\n%4X - ", i*16); ++ for ( j = 0 ; j < 16 ; j++) { ++ if ( rd_buffer[i*16+j] < 16) ++ printk("0%X ", rd_buffer[i*16+j]); ++ else ++ printk("%2X ", rd_buffer[i*16+j]); ++ } ++ for ( j = 0 ; j < 16 ; j++) { ++ if ( rd_buffer[i*16+j] < ' ') ++ printk("."); ++ else ++ printk("%c", rd_buffer[i*16+j]); ++ } ++ } ++ ++ printk("\nBuffer - Ende\n"); ++ ++ mmc_show_cid_info(); ++ ++ for(j = 0 ; j < 1; j++) { ++ MMC_Enable(); ++ ++ // mdelay(1); ++ ++ // save_flags(flags); ++ // cli(); ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x49); ++ // mmc_spi_readwrite_slow(0x4A); ++ // mmc_spi_readwrite_slow(0x40+0x0D); ++ // mmc_spi_readwrite_slow(0x42); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // printk("mmc: (CMD9) response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ // restore_flags(flags); ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ return (1); ++ } ++ for (i = 0; i < 22; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ if (result >= 32 && result <= 127) ++ printk("mmc: response (start token) result = 0x%X Zeichen = %c\n", result, result); ++ else ++ printk("mmc: response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0xfe) ++ break; ++ } ++ if (result == 0xfe) ++ break; ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ } ++ mdelay(60); ++ } ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ printk("mmc: mmc card config (CMD9) failed result = 0x%X\n\n", result); ++ return (2); ++ } ++ for (i = 0; i < 16; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ csd[i] = result; ++ } ++ for (i = 0; i < 2; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ } ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ ++ if (result == 0x00) ++ return (3); ++ ++ c_size = (csd[8] & 0xC0) + (csd[7] << 8) + ((csd[6] & 0x03) << 16); ++ c_size >>= 6; ++ c_size_mult = (csd[10] & 0x80) + ((csd[9] & 0x03) << 8); ++ c_size_mult >>= 7; ++ read_bl_len = csd[5] & 0x0f; ++ mult = 1; ++ mult <<= c_size_mult + 2; ++ blocknr = (c_size + 1) * mult; ++ block_len = 1; ++ block_len <<= read_bl_len; ++ size = block_len * blocknr; ++ size >>= 10; ++ ++ for (i = 0; i < (1 << 6); i++) { ++ hd_blocksizes[i] = 1024; ++ hd_hardsectsizes[i] = block_len; ++ hd_maxsect[i] = 256; ++ } ++ hd_sizes[0] = size; ++ hd[0].nr_sects = blocknr; ++ ++ printk("Size = %d, hardsectsize = %d, sectors = %d\n", ++ size, block_len, blocknr); ++ ++ return 0; ++} ++ ++ ++/* ++ * ******************************************************************* ++ * ++ * End of SPI hardware access functions. ++ * ++ * ******************************************************************* ++ */ ++ ++ ++static int mmc_spi_write_block(unsigned int dest_addr, unsigned char *data) ++{ ++ unsigned int address; ++ unsigned char result = 0; ++ unsigned char ab0, ab1, ab2, ab3; ++ int i; ++ ++ address = dest_addr; ++ ++ ab3 = 0xff & (address >> 24); ++ ab2 = 0xff & (address >> 16); ++ ab1 = 0xff & (address >> 8); ++ ab0 = 0xff & address; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite(0xff); ++ ++ mmc_spi_readwrite(0x58); ++ mmc_spi_readwrite(ab3); /* msb */ ++ mmc_spi_readwrite(ab2); ++ mmc_spi_readwrite(ab1); ++ mmc_spi_readwrite(ab0); /* lsb */ ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0x00) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0x00) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (1); ++ } ++ ++ mmc_spi_readwrite(0xfe); ++ ++ for (i = 0; i < 512; i += 32) ++ { ++ mmc_spi_write_only(data[i]); ++ mmc_spi_write_only(data[i+1]); ++ mmc_spi_write_only(data[i+2]); ++ mmc_spi_write_only(data[i+3]); ++ mmc_spi_write_only(data[i+4]); ++ mmc_spi_write_only(data[i+5]); ++ mmc_spi_write_only(data[i+6]); ++ mmc_spi_write_only(data[i+7]); ++ mmc_spi_write_only(data[i+8]); ++ mmc_spi_write_only(data[i+9]); ++ mmc_spi_write_only(data[i+10]); ++ mmc_spi_write_only(data[i+11]); ++ mmc_spi_write_only(data[i+12]); ++ mmc_spi_write_only(data[i+13]); ++ mmc_spi_write_only(data[i+14]); ++ mmc_spi_write_only(data[i+15]); ++ mmc_spi_write_only(data[i+16]); ++ mmc_spi_write_only(data[i+17]); ++ mmc_spi_write_only(data[i+18]); ++ mmc_spi_write_only(data[i+19]); ++ mmc_spi_write_only(data[i+20]); ++ mmc_spi_write_only(data[i+21]); ++ mmc_spi_write_only(data[i+22]); ++ mmc_spi_write_only(data[i+23]); ++ mmc_spi_write_only(data[i+24]); ++ mmc_spi_write_only(data[i+25]); ++ mmc_spi_write_only(data[i+26]); ++ mmc_spi_write_only(data[i+27]); ++ mmc_spi_write_only(data[i+28]); ++ mmc_spi_write_only(data[i+29]); ++ mmc_spi_write_only(data[i+30]); ++ mmc_spi_write_only(data[i+31]); ++ } ++ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 1000000; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xff) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0xff) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (3); ++ } ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (0); ++} ++ ++static int mmc_spi_read_block(unsigned char *data, unsigned int src_addr) ++{ ++ unsigned int address; ++ unsigned char result = 0; ++ unsigned char ab0, ab1, ab2, ab3; ++ // unsigned long flags; ++ // int i, j; ++ int i; ++ ++ address = src_addr; ++ ++ ab3 = 0xff & (address >> 24); ++ ab2 = 0xff & (address >> 16); ++ ab1 = 0xff & (address >> 8); ++ ab0 = 0xff & address; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite(0xff); ++ ++ mmc_spi_readwrite(0x51); ++ mmc_spi_readwrite(ab3); /* msb */ ++ mmc_spi_readwrite(ab2); ++ mmc_spi_readwrite(ab1); ++ mmc_spi_readwrite(ab0); /* lsb */ ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0x00) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0x00) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (1); ++ } ++ ++ for (i = 0; i < 100000; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xfe) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0xfe) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (2); ++ } ++ ++ for (i = 0; i < 512; i += 32 ) ++ { ++ data[i] = mmc_spi_read_only(); ++ data[i+1] = mmc_spi_read_only(); ++ data[i+2] = mmc_spi_read_only(); ++ data[i+3] = mmc_spi_read_only(); ++ data[i+4] = mmc_spi_read_only(); ++ data[i+5] = mmc_spi_read_only(); ++ data[i+6] = mmc_spi_read_only(); ++ data[i+7] = mmc_spi_read_only(); ++ data[i+8] = mmc_spi_read_only(); ++ data[i+9] = mmc_spi_read_only(); ++ data[i+10] = mmc_spi_read_only(); ++ data[i+11] = mmc_spi_read_only(); ++ data[i+12] = mmc_spi_read_only(); ++ data[i+13] = mmc_spi_read_only(); ++ data[i+14] = mmc_spi_read_only(); ++ data[i+15] = mmc_spi_read_only(); ++ data[i+16] = mmc_spi_read_only(); ++ data[i+17] = mmc_spi_read_only(); ++ data[i+18] = mmc_spi_read_only(); ++ data[i+19] = mmc_spi_read_only(); ++ data[i+20] = mmc_spi_read_only(); ++ data[i+21] = mmc_spi_read_only(); ++ data[i+22] = mmc_spi_read_only(); ++ data[i+23] = mmc_spi_read_only(); ++ data[i+24] = mmc_spi_read_only(); ++ data[i+25] = mmc_spi_read_only(); ++ data[i+26] = mmc_spi_read_only(); ++ data[i+27] = mmc_spi_read_only(); ++ data[i+28] = mmc_spi_read_only(); ++ data[i+29] = mmc_spi_read_only(); ++ data[i+30] = mmc_spi_read_only(); ++ data[i+31] = mmc_spi_read_only(); ++ } ++ ++ result = mmc_spi_readwrite(0xff); ++ result = mmc_spi_readwrite(0xff); ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ ++ return (0); ++} ++ ++/* ++static int mmc_revalidate(kdev_t dev) ++{ ++ int target, max_p, start, i; ++ ++ mmc_media_detect = mmc_spi_media_detect(); ++ ++ if (mmc_media_detect == 0) ++ { ++ return -ENODEV; ++ } ++ ++ target = DEVICE_NR(dev); ++ ++ max_p = hd_gendisk.max_p; ++ start = target << 6; ++ for (i = max_p - 1; i >= 0; i--) ++ { ++ int minor = start + i; ++ invalidate_device(MKDEV(MAJOR_NR, minor), 1); ++ hd_gendisk.part[minor].start_sect = 0; ++ hd_gendisk.part[minor].nr_sects = 0; ++ } ++ ++ grok_partitions(&hd_gendisk, target, 1 << 6, hd_sizes[0] * 2); ++ ++ return 0; ++} ++*/ ++ ++ ++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ if (!inode || !inode->i_rdev) ++ return -EINVAL; ++ ++ switch (cmd) { ++#if 0 ++ case BLKGETSIZE: ++ return put_user(hd[MINOR(inode->i_rdev)].nr_sects, ++ (unsigned long *)arg); ++ case BLKGETSIZE64: ++ return put_user((u64) hd[MINOR(inode->i_rdev)]. ++ nr_sects, (u64 *) arg); ++ case BLKRRPART: ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ return mmc_revalidate(inode->i_rdev); ++#endif ++ case HDIO_GETGEO: ++ { ++ struct block_device *bdev = inode->i_bdev; ++ struct hd_geometry *loc, g; ++ loc = (struct hd_geometry *)arg; ++ if (!loc) ++ return -EINVAL; ++ memset(loc, 0, sizeof(struct hd_geometry)); ++ g.heads = 4; ++ g.sectors = 16; ++ g.cylinders = get_capacity(bdev->bd_disk) / (4*16); ++ g.start = get_start_sect(bdev); ++ return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0; ++ } ++ default: ++ return -ENOTTY; ++ } ++} ++ ++ ++/* ++static int mmc_check_media_change(kdev_t dev) ++{ ++ (void) dev; ++ if (mmc_media_changed == 1) ++ { ++ mmc_media_changed = 0; ++ return 1; ++ } ++ else ++ { ++ return 0; ++ } ++} ++*/ ++ ++ ++/* ++static int mmc_init(void) ++{ ++ int result; ++ ++ result = mmc_spi_hardware_init(); ++ ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_spi_hardware_init\n", result); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_hw_test(); ++ ++ if (result != 0) ++ { ++ printk("\n mmc: mmc_spi_hw_test i.O. \n\n"); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_speed_test(); ++ ++ if (result != 0) ++ { ++ printk("\n mmc: mmc_spi_speed_test i.O. \n\n"); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_card_init(); ++ mdelay(50); ++ if (result != 0) ++ { ++ // Give it an extra shot ++ // result = mmc_spi_card_init(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_card_init\n", result); ++ return -1; ++ } ++ } ++ ++ result = mmc_spi_card_config(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_card_config\n", result); ++ return -1; ++ } ++ ++ ++ return 0; ++} ++*/ ++ ++/* ++static void mmc_exit(void) ++{ ++} ++*/ ++ ++ ++/* ++static void mmc_check_media(void) ++{ ++ int old_state, new_state; ++ int result; ++ ++ old_state = mmc_media_detect; ++ new_state = mmc_spi_media_detect(); ++ ++ if (old_state != new_state) ++ { ++ mmc_media_changed = 1; ++ if (new_state == PRESENT) ++ { ++ result = mmc_init(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_init\n", result); ++ } ++ else ++ { ++ mmc_exit(); ++ } ++ } ++ } ++ ++#ifdef CHECK_MEDIA_CHANGE ++ del_timer(&mmc_timer); ++ mmc_timer.expires = jiffies + 10*HZ; ++ add_timer(&mmc_timer); ++#endif ++ ++} ++*/ ++ ++ ++/* NB: There might be several requests in the queue, simply dequeuing only one ++ and not checking for more will cause a stall because the block subsystem ++ will not call this function again unless the queue is "plugged" which can ++ only happen if it runs empty... */ ++static void mmc_spi_request(struct request_queue *q) ++{ ++ struct request *req; ++ int ret; ++ ++ unsigned int mmc_address; ++ unsigned char *buffer_address; ++ int nr_sectors; ++ int i; ++ int result, success; ++ ++ if (blk_queue_plugged(q)) { ++ return; ++ } ++ ++ spin_lock(&mmc_spi_lock); ++ for(;;) { ++ req = elv_next_request(q); ++ if (!req) ++ break; ++ ++ if (!blk_fs_request(req)) { ++ printk("not a blk_fs_request\n"); ++ spin_unlock(&mmc_spi_lock); ++ continue; ++ } ++ ++ mmc_address = req->sector * hd_hardsectsizes[0]; ++ buffer_address = req->buffer; ++ nr_sectors = req->current_nr_sectors; ++ success = 1; ++ if (rq_data_dir(req) == READ) { ++ spin_unlock_irq(q->queue_lock); ++ for (i = 0; i < nr_sectors; i++) { ++ result = mmc_spi_read_block(buffer_address, mmc_address); ++ if (unlikely(result < 0)) { ++ printk(KERN_ERR "mmi_spi_block: error reading block (%d)\n", result); ++ success = 0; ++ break; ++ } ++ mmc_address += hd_hardsectsizes[0]; ++ buffer_address += hd_hardsectsizes[0]; ++ } ++ spin_lock_irq(q->queue_lock); ++ } else { ++ spin_unlock_irq(q->queue_lock); ++ for (i = 0; i < nr_sectors; i++) { ++ result = mmc_spi_write_block(mmc_address, buffer_address); ++ if (unlikely(result < 0)) { ++ printk(KERN_ERR "mmi_spi_block: error writing block (%d)\n", result); ++ success = 0; ++ break; ++ } ++ mmc_address += hd_hardsectsizes[0]; ++ buffer_address += hd_hardsectsizes[0]; ++ } ++ spin_lock_irq(q->queue_lock); ++ } ++ ret = end_that_request_chunk(req, success, nr_sectors * hd_hardsectsizes[0]); ++ if (!ret) { ++ blkdev_dequeue_request(req); ++ end_that_request_last(req, 0); ++ } ++ } ++ spin_unlock(&mmc_spi_lock); ++} ++ ++ ++static int mmc_open(struct inode *inode, struct file *filp) ++{ ++ // int device; ++ (void) filp; ++ mmc_media_detect = mmc_spi_media_detect(); ++ ++ if (mmc_media_detect == 0) ++ return -ENODEV; ++ ++ return 0; ++} ++ ++static int mmc_release(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++ ++static struct block_device_operations mmc_spi_bdops = { ++ .open = mmc_open, ++ .release = mmc_release, ++ .ioctl = mmc_ioctl, ++ .owner = THIS_MODULE, ++#if 0 ++ .check_media_change = mmc_check_media_change, ++ .revalidate = mmc_revalidate, ++#endif ++}; ++ ++static int detect_card(void) ++{ ++ int result; ++ ++ result = mmc_spi_card_init(); ++ if (result != 0) { ++ // Give it an extra shot ++ result = mmc_spi_card_init(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_init (%d)\n", result); ++ return -ENODEV; ++ } ++ } ++ ++ result = mmc_spi_card_config(); ++ // result = mmc_spi_speed_test(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_config (%d)\n", result); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/* Fills in the gendisk structure from the received card ++ data. */ ++static int gendisk_init(struct device *dev, struct gendisk *gd) ++{ ++ if (!gd) ++ return -EINVAL; ++ ++ gd->major = major; ++ gd->first_minor = 0; /* only one device supported */ ++ gd->fops = &mmc_spi_bdops; ++ gd->driverfs_dev = dev; ++ ++ gd->queue = blk_init_queue(mmc_spi_request,NULL); ++ ++ if (!gd->queue) ++ return -ENOMEM; ++ ++ sprintf(gd->disk_name, "mmcblk"); ++ ++ blk_queue_hardsect_size(gd->queue, hd_hardsectsizes[0]); ++ ++ set_capacity(gd, hd_sizes[0]<<1); ++ ++ return 0; ++} ++ ++static int gendisk_fini(struct gendisk *gd) ++{ ++ BUG_ON(!gd); ++ ++ if (gd->queue) ++ blk_cleanup_queue(gd->queue); ++ ++ del_gendisk(gd); ++ ++ return 0; ++} ++ ++/* platform driver device instance routines */ ++static int mmc_spi_probe(struct platform_device *pdev) ++{ ++ int result; ++ printk("$Id: mmc_spi_block.c,v 1.05 2008/01/04 01:02:10 mrdata Exp $\n"); ++ ++ result = mmc_spi_hardware_init(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_spi_hardware_init (%d)\n", result); ++ result = -ENODEV; ++ return result; ++ } ++ ++ result = detect_card(); ++ if (result < 0) ++ return result; ++ ++ mmc_media_detect = 1; ++ ++ result = register_blkdev(major, DEVICE_NAME); ++ if (result < 0) ++ return result; ++ ++ if (!major) ++ major = result; ++ ++ /* allow 8 partitions per device */ ++ BUG_ON(mmc_disk!=NULL); ++ mmc_disk = alloc_disk(1 << 3); ++ if (!mmc_disk) { ++ result = -ENOMEM; ++ goto out; ++ } ++ ++ result = gendisk_init(&pdev->dev, mmc_disk); ++ if (result < 0) ++ goto out; ++ ++ add_disk(mmc_disk); ++ ++ /*init_timer(&mmc_timer); ++ mmc_timer.expires = jiffies + HZ; ++ mmc_timer.function = (void *)mmc_check_media; ++ add_timer(&mmc_timer); */ ++ return 0; ++ ++out: ++ if (mmc_disk) ++ put_disk(mmc_disk); ++ ++ unregister_blkdev(major, DEVICE_NAME); ++ return result; ++} ++ ++static int mmc_spi_remove(struct platform_device *dev) ++{ ++ // int ret; ++ ++ if (mmc_disk) { ++ gendisk_fini(mmc_disk); ++ put_disk(mmc_disk); ++ } ++ ++ // ret = unregister_blkdev(major, DEVICE_NAME); ++ unregister_blkdev(major, DEVICE_NAME); ++ return 0; ++} ++ ++struct platform_driver mmc_spi_driver = { ++ .probe = mmc_spi_probe, ++ .remove = mmc_spi_remove, ++ .driver = { ++ .name = "mmc_spi_block", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++ ++/* module init/exit */ ++static int __init mmc_spi_block_init(void) ++{ ++ int ret; ++ spin_lock_init(&mmc_spi_lock); ++ ++ ret = platform_driver_register(&mmc_spi_driver); ++ if (ret < 0) ++ return ret; ++ ++ /* we just support one device */ ++ mmc_dev = platform_device_register_simple("mmc_spi_block", -1, NULL, 0); ++ if (IS_ERR(mmc_dev)) ++ return PTR_ERR(mmc_dev); ++ ++ return 0; ++} ++ ++ ++static void __exit mmc_spi_block_exit(void) ++{ ++ platform_driver_unregister(&mmc_spi_driver); ++ if (mmc_dev) ++ platform_device_unregister(mmc_dev); ++} ++ ++ ++module_init(mmc_spi_block_init); ++module_exit(mmc_spi_block_exit); ++ ++MODULE_AUTHOR("Madsuk,Rohde,TaGana,Carsten Juttner <carjay@gmx.net>,Guylhem Aznar <mmc-driver@externe.net>,mrdata"); ++MODULE_DESCRIPTION("Driver for MMC/SD-Cards in SPI mode over GPIO (Software SPI)"); ++MODULE_SUPPORTED_DEVICE("SIMpad"); ++MODULE_LICENSE("GPL"); ++ ++module_param(major, int, 0444); ++MODULE_PARM_DESC(major, "specify the major device number for the MMC/SD SPI driver"); diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-battery-old-way-but-also-with-sysfs.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-battery-old-way-but-also-with-sysfs.patch new file mode 100644 index 0000000000..35f192ada2 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-battery-old-way-but-also-with-sysfs.patch @@ -0,0 +1,571 @@ +diff -Nur linux-2.6.24.vanilla/drivers/mfd/Makefile linux-2.6.24/drivers/mfd/Makefile +--- linux-2.6.24.vanilla/drivers/mfd/Makefile 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/Makefile 2008-02-20 21:27:39.000000000 +0100 +@@ -12,3 +12,7 @@ + ifeq ($(CONFIG_SA1100_ASSABET),y) + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o + endif ++ ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++obj-$(CONFIG_MCP_UCB1200) += ucb1x00-simpad.o ++endif +diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-simpad.c linux-2.6.24/drivers/mfd/ucb1x00-simpad.c +--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-simpad.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-simpad.c 2008-02-20 21:27:39.000000000 +0100 +@@ -0,0 +1,226 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-simpad.c ++ * ++ * Copyright (C) 2001-2003 Russell King, All Rights Reserved. ++ * 2007/03/18 mrdata: ++ * - adapted ucb1x00-assabet.c ++ * - transfer ucb1x00-simpad.c from kernel24 ++ * to new structur of kernel26 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ * ++ * We handle the machine-specific bits of the UCB1x00 driver here. ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/proc_fs.h> ++#include <linux/device.h> ++ ++#include <linux/apm-emulation.h> ++ ++#include <asm/dma.h> ++ ++#include <asm/arch/simpad.h> ++#include <asm/arch-sa1100/simpad_pm.h> ++ ++#include "ucb1x00.h" ++#include "ucb1x00-simpad.h" ++ ++#define UCB1X00_ATTR(name,input,designation) \ ++static ssize_t name##_show(struct class_device *dev, char *buf) \ ++{ \ ++ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \ ++ int val; \ ++ ucb1x00_adc_enable(ucb); \ ++ val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC); \ ++ ucb1x00_adc_disable(ucb); \ ++ return sprintf(buf, "%d\n", CALIBRATE_##designation(val)); \ ++} \ ++static CLASS_DEVICE_ATTR(name,0444,name##_show,NULL) ++ ++UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1, BATTERY); ++UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD2, SUPPLY); ++UCB1X00_ATTR(icharger, UCB_ADC_INP_AD3, CHARGING); ++ ++static struct ucb1x00 *ucb_alt; ++ ++#define UCB1X00_WERT(name,input,designation) \ ++static int ucb1x00_simpad_read_##name(struct ucb1x00 *ucb_alt) \ ++{ \ ++ int val; \ ++ ucb1x00_adc_enable(ucb_alt); \ ++ val = ucb1x00_adc_read(ucb_alt, input, UCB_NOSYNC); \ ++ ucb1x00_adc_disable(ucb_alt); \ ++ return CALIBRATE_##designation(val); \ ++} ++ ++UCB1X00_WERT(vbatt, UCB_ADC_INP_AD1, BATTERY); ++UCB1X00_WERT(vcharger, UCB_ADC_INP_AD2, SUPPLY); ++UCB1X00_WERT(icharger, UCB_ADC_INP_AD3, CHARGING); ++ ++static int ucb1x00_simpad_add(struct ucb1x00_dev *dev) ++{ ++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_vbatt); ++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_vcharger); ++ class_device_create_file(&dev->ucb->cdev, &class_device_attr_icharger); ++ ucb_alt = dev->ucb; ++ return 0; ++} ++ ++static void ucb1x00_simpad_remove(struct ucb1x00_dev *dev) ++{ ++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_icharger); ++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vcharger); ++ class_device_remove_file(&dev->ucb->cdev, &class_device_attr_vbatt); ++} ++ ++static struct ucb1x00_driver ucb1x00_simpad_driver = { ++ .add = ucb1x00_simpad_add, ++ .remove = ucb1x00_simpad_remove, ++}; ++ ++static int __init ucb1x00_simpad_init(void) ++{ ++ apm_get_power_status = simpad_apm_get_power_status; ++ return ucb1x00_register_driver(&ucb1x00_simpad_driver); ++} ++ ++static void __exit ucb1x00_simpad_exit(void) ++{ ++ apm_get_power_status = NULL; ++ ucb1x00_unregister_driver(&ucb1x00_simpad_driver); ++} ++ ++/****************************************************************************/ ++/* Functions exported for use by the kernel and kernel modules */ ++/****************************************************************************/ ++ ++int simpad_get_battery(struct simpad_battery_apm *bstat) ++{ ++ int icharger, vcharger, vbatt; ++ ++ if ( ucb_alt ) { ++ icharger = ucb1x00_simpad_read_icharger( ucb_alt ); ++ vcharger = ucb1x00_simpad_read_vcharger( ucb_alt ); ++ vbatt = ucb1x00_simpad_read_vbatt( ucb_alt ); ++ } else { ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_UNKNOWN; ++ bstat->status = SIMPAD_BATT_STATUS_UNKNOWN; ++ bstat->percentage = 0x64; // lets say 100% ++ bstat->life = 330; // lets say a long time ++ return 0; ++ } ++ ++ /* AC status */ ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_OFFLINE; ++ if ( vcharger>MIN_SUPPLY ) { ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_ONLINE; ++ } ++ ++ /* charging */ ++ bstat->status = 0x0; ++ if ( icharger >= CHARGING_LED_LEVEL ) ++ bstat->status = SIMPAD_BATT_STATUS_CHARGING; ++ ++ if ( vbatt > BATT_LOW ) ++ bstat->status |= SIMPAD_BATT_STATUS_HIGH; ++ else if ( vbatt < BATT_CRITICAL ) ++ bstat->status |= SIMPAD_BATT_STATUS_CRITICAL; ++ else ++ bstat->status |= SIMPAD_BATT_STATUS_LOW; ++ ++ if (bstat->status & SIMPAD_BATT_STATUS_CHARGING) { ++ if (icharger > CHARGING_MAX_LEVEL) ++ icharger = CHARGING_MAX_LEVEL; ++ if (icharger < CHARGING_LED_LEVEL) ++ icharger = CHARGING_LED_LEVEL; ++ ++ bstat->percentage = 100 - 100 * (icharger - CHARGING_LED_LEVEL) / ++ (CHARGING_MAX_LEVEL - CHARGING_LED_LEVEL); ++ } else { ++ if (vbatt > BATT_FULL) ++ vbatt = BATT_FULL; ++ if (vbatt < BATT_EMPTY) ++ vbatt = BATT_EMPTY; ++ ++ bstat->percentage = 100 * (vbatt - BATT_EMPTY) / (BATT_FULL - BATT_EMPTY); ++ } ++ ++ /* let's assume: full load is 7h */ ++ /* bstat->life = 420*bstat->percentage/100; */ ++ /* mrdata: think, 4h is more realistic */ ++ bstat->life = 240*(bstat->percentage)/100; ++ ++#if 0 ++ printk("get_battery: ac: %02x / ch: %02x / perc: %02x / life: %d \n", ++ bstat->ac_status, bstat->status, ++ bstat->percentage, bstat->life ); ++#endif ++ ++ return 0; ++} ++ ++void simpad_apm_get_power_status(struct apm_power_info *info) ++{ ++ struct simpad_battery_apm bstat; ++ unsigned char ac = APM_AC_UNKNOWN; ++ unsigned char level = APM_BATTERY_STATUS_UNKNOWN; ++ int status, result; ++ ++ result = simpad_get_battery(&bstat); ++ if (result) { ++ printk("%s: unable to access battery information: result=%d\n", __FUNCTION__, result); ++ return; ++ } ++ ++ switch (bstat.ac_status) { ++ case SIMPAD_AC_STATUS_AC_OFFLINE: ++ ac = APM_AC_OFFLINE; ++ break; ++ case SIMPAD_AC_STATUS_AC_ONLINE: ++ ac = APM_AC_ONLINE; ++ break; ++ case SIMPAD_AC_STATUS_AC_BACKUP: ++ ac = APM_AC_BACKUP; ++ break; ++ } ++ ++ info->ac_line_status = ac; ++ ++ status = bstat.status; ++ if (status & (SIMPAD_BATT_STATUS_CHARGING | SIMPAD_BATT_STATUS_CHARGE_MAIN)) ++ level = APM_BATTERY_STATUS_CHARGING; ++ else if (status & (SIMPAD_BATT_STATUS_HIGH | SIMPAD_BATT_STATUS_FULL)) ++ level = APM_BATTERY_STATUS_HIGH; ++ else if (status & SIMPAD_BATT_STATUS_LOW) ++ level = APM_BATTERY_STATUS_LOW; ++ else if (status & SIMPAD_BATT_STATUS_CRITICAL) ++ level = APM_BATTERY_STATUS_CRITICAL; ++ ++ info->battery_status = level; ++ info->battery_flag = info->battery_status; ++ ++ info->battery_life = bstat.percentage; ++ ++ /* we have a dumb battery - so we know nothing */ ++ info->time = bstat.life; ++ info->units = APM_UNITS_MINS; ++ ++#if 0 ++ printk("apm_get_power: ac: %02x / bs: %02x / bf: %02x / perc: %02x / life: %d\n", ++ info->ac_line_status, info->battery_status, info->battery_flag, ++ info->battery_life, info->time ); ++#endif ++ return; ++} ++ ++module_init(ucb1x00_simpad_init); ++module_exit(ucb1x00_simpad_exit); ++ ++ ++MODULE_AUTHOR("Juergen Messerer <juergen.messerer@freesurf.ch>"); ++MODULE_DESCRIPTION("SIMpad noddy testing only example ADC driver"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-simpad.h linux-2.6.24/drivers/mfd/ucb1x00-simpad.h +--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-simpad.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-simpad.h 2008-02-20 21:27:39.000000000 +0100 +@@ -0,0 +1,86 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-simpad.h ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ */ ++#ifndef UCB1300_SIMPAD_H ++#define UCB1300_SIMPAD_H ++ ++/* ++ * Conversion from AD -> mV ++ * 7.5V = 1023 7.33137mV/Digit ++ * ++ * 400 Units == 9.7V ++ * a = ADC value ++ * 2007/03/24 mrdata: ++ * according UCB1300 datasheet ADC error max. 3 LSB ++ * 5-95% of full scale -> 3*7.33137mV = 21.99mV ++ * // old ++ * 21 = ADC error ++ * 12600 = Divident to get 2*7.3242 ++ * 860 = Divider to get 2*7.3242 ++ * 170 = Voltagedrop over ++ * ++ * // new ++ * 3 = ADC error ++ * 12610 = Divident to get 2*7.33139 ++ * 860 = Divider to get 2*7.33139 ++ * 170 = Voltagedrop over ++ */ ++// #define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170) ++#define CALIBRATE_BATTERY(a) ((((a + 3)*12610)/860) + 170) ++ ++/* ++ * We have two types of batteries a small and a large one ++ * To get the right value we to distinguish between those two ++ * 450 Units == 15 V ++ */ ++#ifdef SMALL_BATTERY ++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 51) ++#define MIN_SUPPLY 8500 /* Less then 8.5V means no powersupply */ ++#else ++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45) ++//#define MIN_SUPPLY 14000 /* Less then 14V means no powersupply */ ++#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */ ++#endif ++ ++/* ++ * Charging Current ++ * if value is >= 50 then charging is on ++ */ ++// #define CALIBRATE_CHARGING(a) (((a) * 1000) / (152/4)) ++#define CALIBRATE_CHARGING(a) (a) ++//#define CHARGING_LED_LEVEL 50 ++ ++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// type small battery ++#define CHARGING_LED_LEVEL 12 ++#define CHARGING_MAX_LEVEL 120 ++#define BATT_FULL 8100 ++#define BATT_LOW 7300 ++#define BATT_CRITICAL 6700 ++#define BATT_EMPTY 6400 ++ ++#else // CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// type large battery ++// because of ADC error CHARGING_LED_LEVEL can changed ++// from 27 to 28 ++#define CHARGING_LED_LEVEL 27 ++#define CHARGING_MAX_LEVEL 265 ++// BATT_FULL with SIMPAD_AC_STATUS_AC_OFFLINE ++#define BATT_FULL 8100 ++#define BATT_LOW 7400 ++#define BATT_CRITICAL 6800 ++#define BATT_EMPTY 6500 ++ ++#endif // CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// int simpad_get_battery(struct simpad_battery_apm *bstat); ++ ++#endif +diff -Nur linux-2.6.24.vanilla/include/asm-arm/arch-sa1100/simpad_pm.h linux-2.6.24/include/asm-arm/arch-sa1100/simpad_pm.h +--- linux-2.6.24.vanilla/include/asm-arm/arch-sa1100/simpad_pm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/include/asm-arm/arch-sa1100/simpad_pm.h 2008-02-20 21:27:39.000000000 +0100 +@@ -0,0 +1,236 @@ ++/* ++* Abstraction interface for microcontroller connection to rest of system ++* ++* Copyright 2003 Peter Pregler ++* Copyright 2000,1 Compaq Computer Corporation. ++* ++* Use consistent with the GNU GPL is permitted, ++* provided that this copyright notice is ++* preserved in its entirety in all copies and derived works. ++* ++* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, ++* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS ++* FITNESS FOR ANY PARTICULAR PURPOSE. ++* ++* Author: Peter Pregler (based on work for ipaq by Andrew Christian) ++* ++*/ ++ ++#ifndef __SIMPAD_HAL_H ++#define __SIMPAD_HAL_H ++ ++#include <linux/apm-emulation.h> ++ ++struct simpad_battery_apm { ++ unsigned char ac_status; /* line connected yes/no */ ++ unsigned char status; /* battery loading yes/no */ ++ unsigned char percentage; /* percentage loaded */ ++ unsigned short life; /* life till empty */ ++}; ++ ++extern void simpad_apm_get_power_status(struct apm_power_info *); ++ ++// extern int simpad_get_battery(struct simpad_battery_apm *bstat); ++ ++/* These should match the apm_bios.h definitions */ ++#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00 ++#define SIMPAD_AC_STATUS_AC_ONLINE 0x01 ++#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */ ++#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff ++ ++ ++/* These bitfields are rarely "or'd" together */ ++#define SIMPAD_BATT_STATUS_HIGH 0x01 ++#define SIMPAD_BATT_STATUS_LOW 0x02 ++#define SIMPAD_BATT_STATUS_CRITICAL 0x04 ++#define SIMPAD_BATT_STATUS_CHARGING 0x08 ++#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10 ++#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */ ++#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */ ++#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */ ++#define SIMPAD_BATT_STATUS_NOBATT 0x80 ++#define SIMPAD_BATT_STATUS_UNKNOWN 0xff ++ ++#if 0 // FIXME ++#include <linux/simpad_ts.h> ++ ++enum simpad_asset_type { ++ ASSET_TCHAR = 0, ++ ASSET_SHORT, ++ ASSET_LONG ++}; ++ ++#define TTYPE(_type) (((unsigned int)_type) << 8) ++#define TCHAR(_len) (TTYPE(ASSET_TCHAR) | (_len)) ++#define TSHORT TTYPE(ASSET_SHORT) ++#define TLONG TTYPE(ASSET_LONG) ++#define ASSET(_type,_num) ((((unsigned int)_type)<<16) | (_num)) ++ ++#define ASSET_HM_VERSION ASSET( TCHAR(10), 0 ) /* 1.1, 1.2 */ ++#define ASSET_SERIAL_NUMBER ASSET( TCHAR(40), 1 ) /* Unique iPAQ serial number */ ++#define ASSET_MODULE_ID ASSET( TCHAR(20), 2 ) /* E.g., "iPAQ 3700" */ ++#define ASSET_PRODUCT_REVISION ASSET( TCHAR(10), 3 ) /* 1.0, 2.0 */ ++#define ASSET_PRODUCT_ID ASSET( TSHORT, 4 ) /* 2 = Palm-sized computer */ ++#define ASSET_FRAME_RATE ASSET( TSHORT, 5 ) ++#define ASSET_PAGE_MODE ASSET( TSHORT, 6 ) /* 0 = Flash memory */ ++#define ASSET_COUNTRY_ID ASSET( TSHORT, 7 ) /* 0 = USA */ ++#define ASSET_IS_COLOR_DISPLAY ASSET( TSHORT, 8 ) /* Boolean, 1 = yes */ ++#define ASSET_ROM_SIZE ASSET( TSHORT, 9 ) /* 16, 32 */ ++#define ASSET_RAM_SIZE ASSET( TSHORT, 10 ) /* 32768 */ ++#define ASSET_HORIZONTAL_PIXELS ASSET( TSHORT, 11 ) /* 240 */ ++#define ASSET_VERTICAL_PIXELS ASSET( TSHORT, 12 ) /* 320 */ ++ ++#define ASSET_TYPE(_asset) (((_asset)&0xff000000)>>24) ++#define ASSET_TCHAR_LEN(_asset) (((_asset)&0x00ff0000)>>16) ++#define ASSET_NUMBER(_asset) ((_asset)&0x0000ffff) ++ ++#define MAX_TCHAR_LEN 40 ++ ++struct simpad_asset { ++ unsigned int type; ++ union { ++ unsigned char tchar[ MAX_TCHAR_LEN ]; ++ unsigned short vshort; ++ unsigned long vlong; ++ } a; ++}; ++ ++/******************************************************************** ++ * Interface to the hardware-type specific functions ++ * ++ * get_version Read the version number of the microcontroller on the option pack SPI bus ++ * spi_read Reads from the serial EEPROM memory on the option pack SPI bus ++ * spi_write Write to the serial EEPROM memory on the option pack SPI bus ++ * get_option_detect Returns whether or not an option pack is present ++ * ++ * get_thermal_sensor Return measured temperature of the unit, in units of 0.125 deg C ++ * set_notify_led Turns on, off, or blinks the Green LED ++ * read_light_sensor Returns the value of the front light sensor ++ * get_battery Returns the current voltage and charging state of all batteries ++ * audio_clock Sets the audio CODEC to run at a particular rate ++ * audio_power Turns on/off audio CODEC (internally calls audio_clock) ++ * audio_mute Mutes the audio CODEC ++ * asset_read Extracts PocketPC-style asset information from persistent memory ++ * backlight_control Adjusts the backlight level (only on/off for 3100) ++ * ++ * ++ * iPAQ 3100 only ++ * ============== ++ * codec_control Reset/mute/control level of 3100 audio codec ++ * contrast_control Adjusts the contrast level (only for 3100) ++ * ++ * iPAQ 3600, 3700 only ++ * ==================== ++ * eeprom_read Reads from the asset information on the eeprom of a 3600 (deprecated) ++ * eeprom_write Writes to the asset information on the eeprom (deprecated) ++ * ++ * The interfaces to the EEPROM functions are maintained only because the simpad_ts driver has ++ * a deprecated ioctl call for them. They are used internally by the "asset_read" function. ++ * ++ * iPAQ 3800, 3900 only ++ * ==================== ++ * set_ebat Tells enhanced PCMCIA sleeves that this iPAQ can handle ++ * a wider voltage range (applies to 3800, 3900) ++ * ++ *********************************************************************/ ++ ++struct simpad_hal_ops { ++ /* Functions provided by the underlying hardware */ ++ int (*get_version)( struct simpad_ts_version * ); ++ int (*eeprom_read)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*eeprom_write)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*get_thermal_sensor)( unsigned short * ); ++ int (*set_notify_led)( unsigned char mode, unsigned char duration, ++ unsigned char ontime, unsigned char offtime ); ++ int (*read_light_sensor)( unsigned char *result ); ++ int (*get_battery)( struct simpad_battery * ); ++ int (*spi_read)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*spi_write)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*codec_control)( unsigned char, unsigned char ); ++ int (*get_option_detect)( int *result ); ++ int (*audio_clock)( long samplerate ); ++ int (*audio_power)( long samplerate ); ++ int (*audio_mute)( int mute ); ++ int (*asset_read)( struct simpad_asset *asset ); ++ int (*set_ebat)( void ); ++ ++ /* Functions indirectly provided by the underlying hardware */ ++ int (*backlight_control)( enum flite_pwr power, unsigned char level ); ++ int (*contrast_control)( unsigned char level ); ++ ++ /* for module use counting */ ++ struct module *owner; ++}; ++ ++/* Used by the device-specific hardware module to register itself */ ++extern int simpad_hal_register_interface( struct simpad_hal_ops *ops ); ++extern void simpad_hal_unregister_interface( struct simpad_hal_ops *ops ); ++ ++/* ++ * Calls into HAL from the device-specific hardware module ++ * These run at interrupt time ++ */ ++extern void simpad_hal_keypress( unsigned char key ); ++extern void simpad_hal_touchpanel( unsigned short x, unsigned short y, int down ); ++extern void simpad_hal_option_detect( int present ); ++ ++/* Callbacks registered by device drivers */ ++struct simpad_driver_ops { ++ void (*keypress)( unsigned char key ); ++ void (*touchpanel)( unsigned short x, unsigned short y, int down ); ++ void (*option_detect)( int present ); ++}; ++ ++extern int simpad_hal_register_driver( struct simpad_driver_ops * ); ++extern void simpad_hal_unregister_driver( struct simpad_driver_ops * ); ++ ++ ++/* Calls into HAL from device drivers and other kernel modules */ ++extern void simpad_get_flite( struct simpad_ts_backlight *bl ); ++extern void simpad_get_contrast( unsigned char *contrast ); ++extern int simpad_set_flite( enum flite_pwr pwr, unsigned char brightness ); ++extern int simpad_set_contrast( unsigned char contrast ); ++extern int simpad_toggle_frontlight( void ); ++ ++extern int simpad_apm_get_power_status(unsigned char *ac_line_status, unsigned char *battery_status, ++ unsigned char *battery_flag, unsigned char *battery_percentage, ++ unsigned short *battery_life); ++ ++extern struct simpad_hal_ops *simpad_hal_ops; ++ ++/* Do not use this macro in driver files - instead, use the inline functions defined below */ ++#define CALL_HAL( f, args... ) \ ++ { int __result = -EIO; \ ++ if ( simpad_hal_ops && simpad_hal_ops->f ) { \ ++ __MOD_INC_USE_COUNT(simpad_hal_ops->owner); \ ++ __result = simpad_hal_ops->f(args); \ ++ __MOD_DEC_USE_COUNT(simpad_hal_ops->owner); \ ++ } \ ++ return __result; } ++ ++#define HFUNC static __inline__ int ++ ++/* The eeprom_read/write address + len has a maximum value of 512. Both must be even numbers */ ++HFUNC simpad_eeprom_read( u16 addr, u8 *data, u16 len ) CALL_HAL(eeprom_read,addr,data,len) ++HFUNC simpad_eeprom_write( u16 addr, u8 *data, u16 len) CALL_HAL(eeprom_write,addr,data,len) ++HFUNC simpad_spi_read( u8 addr, u8 *data, u16 len) CALL_HAL(spi_read,addr,data,len) ++HFUNC simpad_spi_write( u8 addr, u8 *data, u16 len) CALL_HAL(spi_write,addr,data,len) ++HFUNC simpad_get_version( struct simpad_ts_version *v ) CALL_HAL(get_version,v) ++HFUNC simpad_get_thermal_sensor( u16 *thermal ) CALL_HAL(get_thermal_sensor,thermal) ++HFUNC simpad_set_led( u8 mode, u8 dur, u8 ont, u8 offt ) CALL_HAL(set_notify_led, mode, dur, ont, offt) ++HFUNC simpad_get_light_sensor( u8 *result ) CALL_HAL(read_light_sensor,result) ++HFUNC simpad_get_battery( struct simpad_battery *bat ) CALL_HAL(get_battery,bat) ++HFUNC simpad_get_option_detect( int *result) CALL_HAL(get_option_detect,result) ++HFUNC simpad_audio_clock( long samplerate ) CALL_HAL(audio_clock,samplerate) ++HFUNC simpad_audio_power( long samplerate ) CALL_HAL(audio_power,samplerate) ++HFUNC simpad_audio_mute( int mute ) CALL_HAL(audio_mute,mute) ++HFUNC simpad_asset_read( struct simpad_asset *asset ) CALL_HAL(asset_read,asset) ++HFUNC simpad_set_ebat( void ) CALL_HAL(set_ebat) ++ ++/* Don't use these functions directly - rather, call {get,set}_{flite,contrast} */ ++ /* Functions indirectly provided by the underlying hardware */ ++HFUNC simpad_backlight_control( enum flite_pwr p, u8 v ) CALL_HAL(backlight_control,p,v) ++HFUNC simpad_contrast_control( u8 level ) CALL_HAL(contrast_control,level) ++ ++#endif ++#endif diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-cs3-simpad.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-cs3-simpad.patch new file mode 100644 index 0000000000..51232d55cd --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-cs3-simpad.patch @@ -0,0 +1,184 @@ +diff -uNr linux-2.6.24.vanilla/arch/arm/mach-sa1100/Makefile linux-2.6.24/arch/arm/mach-sa1100/Makefile +--- linux-2.6.24.vanilla/arch/arm/mach-sa1100/Makefile 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/arch/arm/mach-sa1100/Makefile 2008-02-20 20:49:57.000000000 +0100 +@@ -41,6 +41,7 @@ + obj-$(CONFIG_SA1100_SHANNON) += shannon.o + + obj-$(CONFIG_SA1100_SIMPAD) += simpad.o ++obj-$(CONFIG_SA1100_SIMPAD) += cs3-simpad.o + led-$(CONFIG_SA1100_SIMPAD) += leds-simpad.o + + # LEDs support +diff -uNr linux-2.6.24.vanilla/arch/arm/mach-sa1100/cs3-simpad.c linux-2.6.24/arch/arm/mach-sa1100/cs3-simpad.c +--- linux-2.6.24.vanilla/arch/arm/mach-sa1100/cs3-simpad.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/arch/arm/mach-sa1100/cs3-simpad.c 2008-02-20 20:49:57.000000000 +0100 +@@ -0,0 +1,169 @@ ++/* ++ * simpad-cs3.c ++ * ++ * This driver shows the GPIO states of the cs3 latch. You can also ++ * switch some GPIOS. ++ * ++ * (c) 2007 Bernhard Guillon <Bernhard.Guillon@OpenSIMpad.org> ++ * ++ * You may use this code as per GPL version 2 ++ * ++ * Some parts are based on battery.c ++ * ++ * mrdata: -added cs3_ro support ++ * -added preprocessor stuff ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/device.h> ++ ++#include <asm/arch/simpad.h> ++ ++extern long get_cs3_ro(void); ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int value); ++extern void clear_cs3_bit(int value); ++ ++struct cs3 { ++ struct class_device class_dev; ++ const char *name; ++ char *id; ++ int type; ++}; ++ ++struct cs3 cs3 ={ ++ .name = "latch_cs3", ++}; ++ ++ ++#define CS3_STORE_ATTR(namek,nameg) \ ++static ssize_t namek##_store (struct class_device *cdev, const char * buf, size_t count) \ ++{ \ ++ char val; \ ++ if (sscanf(buf, "%c",&val) != 1) \ ++ return -EINVAL; \ ++ if (val == '1') \ ++ set_cs3_bit(nameg); \ ++ else if (val == '0') \ ++ clear_cs3_bit(nameg); \ ++ return strlen(buf); \ ++} ++ ++CS3_STORE_ATTR(display_on, DISPLAY_ON); ++CS3_STORE_ATTR(dect_power_on, DECT_POWER_ON); ++CS3_STORE_ATTR(irda_sd, IRDA_SD); ++CS3_STORE_ATTR(sd_mediaq, SD_MEDIAQ); ++CS3_STORE_ATTR(led2_on, LED2_ON); ++CS3_STORE_ATTR(irda_mode, IRDA_MODE); ++CS3_STORE_ATTR(reset_simcard, RESET_SIMCARD); ++ ++ ++#define CS3_ATTR(shadro,namek,nameg,mode,store) \ ++static ssize_t namek##_show(struct class_device *class_dev, char *buf) \ ++{ \ ++ if (get_cs3_##shadro() & nameg ) \ ++ return sprintf(buf, "1\n"); \ ++ else \ ++ return sprintf(buf, "0\n"); \ ++} \ ++static CLASS_DEVICE_ATTR(namek, mode, namek##_show, store) ++ ++CS3_ATTR(shadow, vcc_5v_en, VCC_5V_EN, 0444, NULL); ++CS3_ATTR(shadow, vcc_3v_en, VCC_3V_EN, 0444, NULL); ++CS3_ATTR(shadow, en1, EN1, 0444, NULL); ++CS3_ATTR(shadow, en0, EN0, 0444, NULL); ++CS3_ATTR(shadow, display_on, DISPLAY_ON, 0664, display_on_store); ++CS3_ATTR(shadow, pcmcia_buff_dis, PCMCIA_BUFF_DIS, 0444, NULL); ++CS3_ATTR(shadow, mq_reset, MQ_RESET, 0444, NULL); ++CS3_ATTR(shadow, pcmcia_reset, PCMCIA_RESET, 0444, NULL); ++CS3_ATTR(shadow, dect_power_on, DECT_POWER_ON, 0664, dect_power_on_store); ++CS3_ATTR(shadow, irda_sd, IRDA_SD, 0664, irda_sd_store); ++CS3_ATTR(shadow, rs232_on, RS232_ON, 0444, NULL); ++CS3_ATTR(shadow, sd_mediaq, SD_MEDIAQ, 0664, sd_mediaq_store); ++CS3_ATTR(shadow, led2_on, LED2_ON, 0664, led2_on_store); ++CS3_ATTR(shadow, irda_mode, IRDA_MODE, 0664, irda_mode_store); ++CS3_ATTR(shadow, enable_5v, ENABLE_5V, 0444, NULL); ++CS3_ATTR(shadow, reset_simcard, RESET_SIMCARD, 0664, reset_simcard_store); ++CS3_ATTR(ro, pcmcia_bvd1, PCMCIA_BVD1, 0444, NULL); ++CS3_ATTR(ro, pcmcia_bvd2, PCMCIA_BVD2, 0444, NULL); ++CS3_ATTR(ro, pcmcia_vs1, PCMCIA_VS1, 0444, NULL); ++CS3_ATTR(ro, pcmcia_vs2, PCMCIA_VS2, 0444, NULL); ++CS3_ATTR(ro, lock_ind, LOCK_IND, 0444, NULL); ++CS3_ATTR(ro, charging_state, CHARGING_STATE, 0444, NULL); ++CS3_ATTR(ro, pcmcia_short, PCMCIA_SHORT, 0444, NULL); ++ ++static struct class simpad_gpios_class = { ++ .name = "simpad", ++}; ++ ++#define create_entry_conditional(namek) \ ++ rc = class_device_create_file(&cs3->class_dev, &class_device_attr_##namek); \ ++ if (rc) goto out; \ ++ ++static int register_cs3_latch(struct cs3 *cs3) ++{ ++ int rc = 0; ++ cs3->class_dev.class = &simpad_gpios_class; ++ strcpy(cs3->class_dev.class_id, cs3->name); ++ rc = class_device_register(&cs3->class_dev); ++ if(rc) ++ goto out; ++ ++ create_entry_conditional(vcc_5v_en); ++ create_entry_conditional(vcc_3v_en); ++ create_entry_conditional(en1); ++ create_entry_conditional(en0); ++ create_entry_conditional(display_on); ++ create_entry_conditional(pcmcia_buff_dis); ++ create_entry_conditional(mq_reset); ++ create_entry_conditional(pcmcia_reset); ++ create_entry_conditional(dect_power_on); ++ create_entry_conditional(irda_sd); ++ create_entry_conditional(rs232_on); ++ create_entry_conditional(sd_mediaq); ++ create_entry_conditional(led2_on); ++ create_entry_conditional(irda_mode); ++ create_entry_conditional(enable_5v); ++ create_entry_conditional(reset_simcard); ++ create_entry_conditional(pcmcia_bvd1); ++ create_entry_conditional(pcmcia_bvd2); ++ create_entry_conditional(pcmcia_vs1); ++ create_entry_conditional(pcmcia_vs2); ++ create_entry_conditional(lock_ind); ++ create_entry_conditional(charging_state); ++ create_entry_conditional(pcmcia_short); ++ ++out: ++ return rc; ++} ++ ++static int __init simpad_gpios_class_init(void) ++{ ++ int rc = 0; ++ ++ rc = class_register(&simpad_gpios_class); ++ ++ if(rc != 0) ++ { ++ printk(KERN_ERR "cs3 latch class failed to register properly\n"); ++ return rc; ++ } ++ ++ rc = register_cs3_latch(&cs3); ++ return rc; ++} ++ ++static void __exit simpad_gpios_class_exit(void) ++{ ++ class_unregister(&simpad_gpios_class); ++} ++ ++module_init(simpad_gpios_class_init); ++module_exit(simpad_gpios_class_exit); ++ ++MODULE_AUTHOR("Bernhard Guillon"); ++MODULE_DESCRIPTION("CS3_latch driver"); ++MODULE_LICENSE("GPL"); diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-hostap_cs-shared-irq.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-hostap_cs-shared-irq.patch new file mode 100644 index 0000000000..758ddae7f7 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-hostap_cs-shared-irq.patch @@ -0,0 +1,106 @@ +diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.24/drivers/net/wireless/hostap/hostap_cs.c +--- linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_cs.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_cs.c 2008-02-28 12:00:27.000000000 +0100 +@@ -1,3 +1,8 @@ ++/* ++ * ++ * mrdata: -added shared irq ++ */ ++ + #define PRISM2_PCCARD + + #include <linux/module.h> +@@ -35,7 +40,7 @@ + module_param(ignore_cis_vcc, int, 0444); + MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); + +- ++int activar=0; + /* struct local_info::hw_priv */ + struct hostap_cs_priv { + dev_node_t node; +@@ -499,11 +504,13 @@ + + PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info); + p_dev->conf.IntType = INT_MEMORY_AND_IO; +- ++ ++ activar=0; + ret = prism2_config(p_dev); + if (ret) { + PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n"); + } ++ activar=1; + + return ret; + } +@@ -693,7 +700,7 @@ + * irq structure is initialized. + */ + if (link->conf.Attributes & CONF_ENABLE_IRQ) { +- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; ++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->irq.Handler = prism2_interrupt; + link->irq.Instance = dev; +diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_hw.c linux-2.6.24/drivers/net/wireless/hostap/hostap_hw.c +--- linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_hw.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_hw.c 2008-02-28 12:00:27.000000000 +0100 +@@ -54,6 +54,7 @@ + #include "hostap.h" + #include "hostap_ap.h" + ++extern int activar; + + /* #define final_version */ + +@@ -1497,6 +1498,8 @@ + if (local->hw_downloading) + return 1; + ++ activar=1; ++ + if (prism2_hw_init(dev, initial)) { + return local->no_pri ? 0 : 1; + } +@@ -2630,8 +2633,15 @@ + int events = 0; + u16 ev; + +- iface = netdev_priv(dev); +- local = iface->local; ++ ++ // Todos los parametros de entrada son correctos (no son nulos). De momento esta es la unica forma que conozco de detectar el problema. ++ if (!activar) { ++ printk("hostap_hw.c: INTERRUPT BEFORE DEVICE INIT!\n"); ++ return IRQ_HANDLED; ++ } ++ ++ iface = netdev_priv(dev); ++ local = iface->local; + + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); + +diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_pci.c linux-2.6.24/drivers/net/wireless/hostap/hostap_pci.c +--- linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_pci.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_pci.c 2008-02-28 12:00:27.000000000 +0100 +@@ -19,6 +19,7 @@ + + #include "hostap_wlan.h" + ++int activar=1; + + static char *dev_info = "hostap_pci"; + +diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_plx.c linux-2.6.24/drivers/net/wireless/hostap/hostap_plx.c +--- linux-2.6.24.vanilla/drivers/net/wireless/hostap/hostap_plx.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_plx.c 2008-02-28 12:00:27.000000000 +0100 +@@ -21,7 +21,7 @@ + #include <asm/io.h> + + #include "hostap_wlan.h" +- ++int activar=1; + + static char *dev_info = "hostap_plx"; + diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-mq200.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-mq200.patch new file mode 100644 index 0000000000..ac9fdfbfcf --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-mq200.patch @@ -0,0 +1,2513 @@ +diff -Nur linux-2.6.24.vanilla/drivers/video/Kconfig linux-2.6.24/drivers/video/Kconfig +--- linux-2.6.24.vanilla/drivers/video/Kconfig 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/video/Kconfig 2008-02-20 21:51:22.000000000 +0100 +@@ -198,7 +198,7 @@ + This is particularly important to one driver, matroxfb. If + unsure, say N. + +-comment "Frame buffer hardware drivers" ++comment "Frambuffer hardware drivers" + depends on FB + + config FB_CIRRUS +@@ -1389,6 +1389,15 @@ + ---help--- + Driver for graphics boards with S3 Trio / S3 Virge chip. + ++config FB_MQ200 ++ bool "MQ200 Driver" ++ depends on (FB = y) && ARM && ARCH_SA1100 ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ help ++ This is a MQ200 driver tested only on Siemens SIMpads. ++ + config FB_SAVAGE + tristate "S3 Savage support" + depends on FB && PCI && EXPERIMENTAL +diff -Nur linux-2.6.24.vanilla/drivers/video/Makefile linux-2.6.24/drivers/video/Makefile +--- linux-2.6.24.vanilla/drivers/video/Makefile 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/video/Makefile 2008-02-20 21:51:22.000000000 +0100 +@@ -35,6 +35,7 @@ + obj-$(CONFIG_FB_PM2) += pm2fb.o + obj-$(CONFIG_FB_PM3) += pm3fb.o + ++obj-$(CONFIG_FB_MQ200) += mq200/ + obj-$(CONFIG_FB_MATROX) += matrox/ + obj-$(CONFIG_FB_RIVA) += riva/ + obj-$(CONFIG_FB_NVIDIA) += nvidia/ +diff -Nur linux-2.6.24.vanilla/drivers/video/backlight/Kconfig linux-2.6.24/drivers/video/backlight/Kconfig +--- linux-2.6.24.vanilla/drivers/video/backlight/Kconfig 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/video/backlight/Kconfig 2008-02-20 21:51:22.000000000 +0100 +@@ -90,3 +90,20 @@ + help + If you have a Intel LE80578 (Carillo Ranch) say Y to enable the + backlight driver. ++ ++config BACKLIGHT_SIMPAD ++ tristate "SIMpad MQ200 Backlight driver" ++ depends on SA1100_SIMPAD && BACKLIGHT_CLASS_DEVICE ++ default y ++ help ++ If you have a Siemens SIMpad say Y to enable the ++ backlight driver. ++ ++config LCD_SIMPAD ++ tristate "SIMpad MQ200 LCD driver" ++ depends on SA1100_SIMPAD && LCD_CLASS_DEVICE ++ default y ++ help ++ If you have a Siemens SIMpad say Y to enable the ++ LCD driver. ++ +diff -Nur linux-2.6.24.vanilla/drivers/video/backlight/Makefile linux-2.6.24/drivers/video/backlight/Makefile +--- linux-2.6.24.vanilla/drivers/video/backlight/Makefile 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/video/backlight/Makefile 2008-02-20 21:51:22.000000000 +0100 +@@ -9,3 +9,5 @@ + obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o + obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o + obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o ++obj-$(CONFIG_BACKLIGHT_SIMPAD) += simpad_bl.o ++obj-$(CONFIG_LCD_SIMPAD) += simpad_lcd.o +diff -Nur linux-2.6.24.vanilla/drivers/video/backlight/simpad_bl.c linux-2.6.24/drivers/video/backlight/simpad_bl.c +--- linux-2.6.24.vanilla/drivers/video/backlight/simpad_bl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/video/backlight/simpad_bl.c 2008-02-20 21:51:22.000000000 +0100 +@@ -0,0 +1,208 @@ ++/* ++ * GPLv2 <zecke@handhelds.org ++ * ++ * Implementation of the backlight_driver for ++ * the mq200 framebuffer ++ * ++ * 2007/03/17 mrdata: ++ * - small changes simpad_bl_get_brightness() ++ * simpad_bl_set_brightness() ++ * - new function simpad_bl_update_status() ++ * - changed struct backlight_properties simpad_bl_props() ++ * to new one ++ * - changed __init simpad_bl_init() -> backlight_device_register ++ * ++ * 2007/03/24 mrdata ++ * - added .brightness=127 in ++ * struct backlight_properties simpad_bl_props() ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/fb.h> ++#include <linux/backlight.h> ++ ++#include <asm/types.h> ++#include <asm/hardware.h> ++#include <asm/io.h> ++ ++#include "../mq200/mq200_data.h" ++ ++#define SIMPAD_BACKLIGHT_MASK 0x00a10044 ++#define SIMPAD_DEFAULT_INTENSITY 127 ++#define SIMPAD_MAX_INTENSITY 254 ++#define REGISTER_BASE 0xf2e00000 ++ ++static int simpad_bl_suspended; ++static int current_intensity = 0; ++ ++static void simpad_bl_send_intensity(struct backlight_device *bd) ++{ ++ int intensity = bd->props.brightness; ++ ++ union fp0fr fp0fr; ++ unsigned long dutyCycle, pwmcontrol; ++ ++ if (intensity > SIMPAD_MAX_INTENSITY) ++ intensity = SIMPAD_MAX_INTENSITY; ++ ++ if (bd->props.power != FB_BLANK_UNBLANK) ++ intensity = 0; ++ ++ if (bd->props.fb_blank != FB_BLANK_UNBLANK) ++ intensity = 0; ++ ++ if (simpad_bl_suspended) ++ intensity = 0; ++ ++ if (intensity != current_intensity) ++ { ++ /* ++ * Determine dutyCycle. ++ * Note: the lower the value, the brighter the display! ++ */ ++ ++ dutyCycle = SIMPAD_MAX_INTENSITY - intensity; ++ ++ /* ++ * Configure PWM0 (source clock = oscillator clock, pwm always enabled, ++ * zero, clock pre-divider = 4) pwm frequency = 12.0kHz ++ */ ++ ++ fp0fr.whole = readl(FP0FR(REGISTER_BASE)); ++ pwmcontrol = fp0fr.whole & 0xffff00ff; ++ fp0fr.whole &= 0xffffff00; ++ fp0fr.whole |= 0x00000044; ++ writel(fp0fr.whole, FP0FR(REGISTER_BASE)); ++ ++ /* Write to pwm duty cycle register. */ ++ fp0fr.whole = dutyCycle << 8; ++ fp0fr.whole &= 0x0000ff00; ++ fp0fr.whole |= pwmcontrol; ++ writel(fp0fr.whole, FP0FR(REGISTER_BASE)); ++ ++ current_intensity = intensity; ++ } ++} ++ ++ ++#ifdef CONFIG_PM ++static int simpad_bl_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ simpad_bl_suspended = 1; ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++ ++static int simpad_bl_resume(struct platform_device *pdev) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ simpad_bl_suspended = 0; ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++#else ++#define simpad_bl_suspend NULL ++#define simpad_bl_resume NULL ++#endif ++ ++ ++static int simpad_bl_set_intensity(struct backlight_device *bd) ++{ ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++ ++ ++static int simpad_bl_get_intensity(struct backlight_device *bd) ++{ ++ return current_intensity; ++} ++ ++ ++static struct backlight_ops simpad_bl_ops = { ++ .get_brightness = simpad_bl_get_intensity, ++ .update_status = simpad_bl_set_intensity, ++}; ++ ++ ++static int __init simpad_bl_probe(struct platform_device *pdev) ++{ ++ struct backlight_device *bd; ++ ++ bd = backlight_device_register("simpad-mq200-bl", &pdev->dev, NULL, &simpad_bl_ops); ++ ++ if (IS_ERR (bd)) ++ return PTR_ERR (bd); ++ ++ platform_set_drvdata(pdev, bd); ++ ++ bd->props.max_brightness = SIMPAD_MAX_INTENSITY; ++ bd->props.brightness = SIMPAD_DEFAULT_INTENSITY; ++ simpad_bl_send_intensity(bd); ++ ++ return 0; ++} ++ ++ ++static int simpad_bl_remove(struct platform_device *pdev) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ bd->props.brightness = 0; ++ bd->props.power = 0; ++ simpad_bl_send_intensity(bd); ++ ++ backlight_device_unregister(bd); ++ ++ return 0; ++} ++ ++static struct platform_driver simpad_bl_driver = { ++ .probe = simpad_bl_probe, ++ .remove = simpad_bl_remove, ++ .suspend = simpad_bl_suspend, ++ .resume = simpad_bl_resume, ++ .driver = { ++ .name = "simpad-mq200-bl", ++ }, ++}; ++ ++static struct platform_device *simpad_bl_device = NULL; ++ ++static int __init simpad_bl_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&simpad_bl_driver); ++ if (!ret) { ++ simpad_bl_device = platform_device_alloc("simpad-mq200-bl", -1); ++ if (!simpad_bl_device) ++ return -ENOMEM; ++ ++ ret = platform_device_add(simpad_bl_device); ++ ++ if (ret) { ++ platform_device_put(simpad_bl_device); ++ platform_driver_unregister(&simpad_bl_driver); ++ } ++ } ++ return ret; ++} ++ ++static void __exit simpad_bl_exit(void) ++{ ++ platform_device_unregister(simpad_bl_device); ++ platform_driver_unregister(&simpad_bl_driver); ++} ++ ++ ++module_init(simpad_bl_init); ++module_exit(simpad_bl_exit); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-2.6.24.vanilla/drivers/video/backlight/simpad_lcd.c linux-2.6.24/drivers/video/backlight/simpad_lcd.c +--- linux-2.6.24.vanilla/drivers/video/backlight/simpad_lcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/video/backlight/simpad_lcd.c 2008-02-20 21:51:22.000000000 +0100 +@@ -0,0 +1,172 @@ ++/* ++ * GPLv2 <zecke@handhelds.org ++ * ++ * Implementation of the lcd_driver for the mq200 framebuffer ++ * ++ * 2007/03/24 mrdata: ++ * - added simpad_lcd_get_contrast() ++ * - added simpad_lcd_set_contrast() ++ * - modify struct lcd_properties simpad_lcd_props ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/fb.h> ++#include <linux/lcd.h> ++ ++#include <asm/arch/simpad.h> ++#include <asm/hardware.h> ++ ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int); ++extern void clear_cs3_bit(int); ++ ++#define UNUSED(x) x=x ++ ++static int simpad_lcd_get_power(struct lcd_device* dev) ++{ ++ UNUSED(dev); ++ ++ return (get_cs3_shadow() & DISPLAY_ON) ? 0 : 4; ++} ++ ++static int simpad_lcd_set_power(struct lcd_device* dev, int power) ++{ ++ UNUSED(dev); ++ ++ if( power == 4 ) ++ clear_cs3_bit(DISPLAY_ON); ++ else ++ set_cs3_bit(DISPLAY_ON); ++ ++ return 0; ++} ++ ++static int simpad_lcd_get_contrast(struct lcd_device* dev) ++{ ++ UNUSED(dev); ++ ++ return 0; ++} ++ ++static int simpad_lcd_set_contrast(struct lcd_device* dev, int contrast) ++{ ++ UNUSED(dev); ++ ++ UNUSED(contrast); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int simpad_lcd_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ static int ret; ++ struct lcd_device* ld; ++ ++ UNUSED(state); ++ ++ ld = platform_get_drvdata(pdev); ++ ++ ret = simpad_lcd_set_power(ld, 4); ++ ++ return ret; ++} ++ ++static int simpad_lcd_resume(struct platform_device *pdev) ++{ ++ struct lcd_device *ld; ++ static int ret; ++ ++ ld = platform_get_drvdata(pdev); ++ ++ ret = simpad_lcd_set_power(ld, 0); ++ ++ return ret; ++} ++#else ++#define simpad_lcd_suspend NULL ++#define simpad_lcd_resume NULL ++#endif ++ ++ ++/*FIXME ++static struct lcd_properties simpad_lcd_props = { ++ .max_contrast = 0, ++}; ++*/ ++ ++static struct lcd_ops simpad_lcd_ops = { ++ .get_power = simpad_lcd_get_power, ++ .set_power = simpad_lcd_set_power, ++ .get_contrast = simpad_lcd_get_contrast, ++ .set_contrast = simpad_lcd_set_contrast, ++}; ++ ++static int __init simpad_lcd_probe(struct platform_device *pdev) ++{ ++ struct lcd_device *ld; ++ ++ ld = lcd_device_register ("simpad-mq200-lcd", &pdev->dev, NULL, &simpad_lcd_ops); ++ ++ if (IS_ERR(ld)) ++ return PTR_ERR(ld); ++ ++ platform_set_drvdata(pdev, ld); ++ ++ ld->props.max_contrast = 0; ++ ++ return 0; ++} ++ ++static int simpad_lcd_remove(struct platform_device *pdev) ++{ ++ struct lcd_device *ld = platform_get_drvdata(pdev); ++ ++ lcd_device_unregister(ld); ++ ++ return 0; ++} ++ ++static struct platform_driver simpad_lcd_driver = { ++ .probe = simpad_lcd_probe, ++ .remove = simpad_lcd_remove, ++ .suspend = simpad_lcd_suspend, ++ .resume = simpad_lcd_resume, ++ .driver = { ++ .name = "simpad-mq200-lcd", ++ }, ++}; ++ ++static struct platform_device *simpad_lcd_device = NULL; ++ ++static int __init simpad_lcd_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&simpad_lcd_driver); ++ if (!ret) { ++ simpad_lcd_device = platform_device_alloc("simpad-mq200-lcd", -1); ++ if (!simpad_lcd_device) ++ return -ENOMEM; ++ ++ ret = platform_device_add(simpad_lcd_device); ++ ++ if (ret) { ++ platform_device_put(simpad_lcd_device); ++ platform_driver_unregister(&simpad_lcd_driver); ++ } ++ } ++ return ret; ++} ++ ++static void __exit simpad_lcd_exit(void) { ++ platform_driver_unregister(&simpad_lcd_driver); ++ platform_device_unregister(simpad_lcd_device); ++} ++ ++module_init(simpad_lcd_init); ++module_exit(simpad_lcd_exit); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-2.6.24.vanilla/drivers/video/mq200/Makefile linux-2.6.24/drivers/video/mq200/Makefile +--- linux-2.6.24.vanilla/drivers/video/mq200/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/video/mq200/Makefile 2008-02-20 21:51:22.000000000 +0100 +@@ -0,0 +1,6 @@ ++# Makefile for mq200 video driver ++# 4 Aug 2003, Holger Hans Peter Freyther ++# ++ ++obj-$(CONFIG_FB_MQ200) += mq_skeleton.o mq_external.o ++ +diff -Nur linux-2.6.24.vanilla/drivers/video/mq200/mq200_data.h linux-2.6.24/drivers/video/mq200/mq200_data.h +--- linux-2.6.24.vanilla/drivers/video/mq200/mq200_data.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/video/mq200/mq200_data.h 2008-02-20 21:51:22.000000000 +0100 +@@ -0,0 +1,1120 @@ ++/* ++ * From ucLinux mq200fb.c and mq200fb.h ++ * ++ * 2007/03/11 mrdata: ++ * insert registers for graphics controller 2 module ++ */ ++ ++#ifndef __MQ200_FB_H__ ++#define __MQ200_FB_H__ ++ ++struct mq200_io_regions { ++ u32 fb_size; /* framebuffer size */ ++ unsigned long phys_mmio_base; /* physical register memory base */ ++ unsigned long virt_mmio_base; /* virtual start of registers */ ++ unsigned long phys_fb_base; /* physical address of frame buffer */ ++ unsigned long virt_fb_base; /* virtual start of the framebuffer */ ++}; ++ ++#define MQ200_MONITOR_HORI_RES(info) info->monitor_info.horizontal_res ++#define MQ200_MONITOR_VERT_RES(info) info->monitor_info.vertical_res ++#define MQ200_MONITOR_DEPTH(info) info->monitor_info.depth ++#define MQ200_MONITOR_LINE_LENGTH(info) info->monitor_info.line_length ++ ++struct mq200_monitor_info { ++ unsigned int horizontal_res; ++ unsigned int vertical_res; ++ unsigned int depth; ++ unsigned int refresh; ++ unsigned int line_length; ++ unsigned long flags; ++}; ++ ++ ++/** ++ * Addresses of Module ++ */ ++#define MQ200_FB_BASE (x) (x + 0x1800000) /* framebuffer */ ++#define MQ200_FB_SIZE 0x200000 /* framebuffer size in bytes */ ++#define MQ200_REGS_BASE(x) (x + 0x1e00000) /* start of registers area */ ++#define MQ200_REGS_SIZE 0x200000 /* registers area size */ ++ ++#define PMU_OFFSET 0x00000 /* power management */ ++#define CPU_OFFSET 0x02000 /* CPU interface */ ++#define MIU_OFFSET 0x04000 /* memory controller */ ++#define IN_OFFSET 0x08000 /* interrupt controller */ ++#define GC_OFFSET 0x0a000 /* graphics controller 1&2 */ ++#define GE_OFFSET 0x0c000 /* graphics engine */ ++#define FPI_OFFSET 0x0e000 /* flat panel controller */ ++#define CP1_OFFSET 0x10000 /* color palette 1 */ ++#define DC_OFFSET 0x14000 /* device configuration */ ++#define PCI_OFFSET 0x16000 /* PCI configuration */ ++#define PSF_OFFSET 0x18000 /* ??? */ ++ ++ ++/**** ++ * Registers ++ */ ++ ++/* power management unit */ ++#define PMR(addr) (addr + PCI_OFFSET + 0x40)/* power management ++ register */ ++#define PMR_VALUE 0x06210001 /* expected read value of PMR register */ ++#define PM00R(addr) (addr + PMU_OFFSET + 0x00) /* power management unit ++ configuration ++ register */ ++#define PM01R(addr) (addr + PMU_OFFSET + 0x04) /* D1 state control */ ++#define PM02R(addr) (addr + PMU_OFFSET + 0x08) /* d2 state control */ ++#define PM06R(addr) (addr + PMU_OFFSET + 0x18) /* PLL 2 programming */ ++#define PM07R(addr) (addr + PMU_OFFSET + 0x1c) /* PLL 3 programming */ ++ ++#define PMCSR(addr) (addr + PCI_OFFSET + 0x44) /* power management ++ control/status ++ register */ ++ ++/* memory interface unit */ ++#define MM00R(addr) (addr + MIU_OFFSET + 0x00)/* MIU interface control ++ 0 */ ++#define MM01R(addr) (addr + MIU_OFFSET + 0x04) /* MIU interface control ++ 1 */ ++#define MM02R(addr) (addr + MIU_OFFSET + 0x08) /* memory interface ++ control 2 */ ++#define MM03R(addr) (addr + MIU_OFFSET + 0x0c) /* memory interface ++ control 3 */ ++#define MM04R(addr) (addr + MIU_OFFSET + 0x10) /* memory interface ++ control 4 */ ++/* graphics controller 1 module */ ++#define GC00R(addr) (addr + GC_OFFSET + 0x00) /* graphics controller 1 ++ control */ ++#define GC01R(addr) (addr + GC_OFFSET + 0x04) /* graphics controller ++ CRT control */ ++#define GC02R(addr) (addr + GC_OFFSET + 0x08) /* horizontal display 1 ++ control */ ++#define GC03R(addr) (addr + GC_OFFSET + 0x0c) /* vertical display 1 ++ control */ ++#define GC04R(addr) (addr + GC_OFFSET + 0x10) /* horizontal sync 1 ++ control */ ++#define GC05R(addr) (addr + GC_OFFSET + 0x14) /* vertical sync 1 ++ control */ ++#define GC07R(addr) (addr + GC_OFFSET + 0x1c) /* vertical display 1 ++ count */ ++#define GC08R(addr) (addr + GC_OFFSET + 0x20) /* horizontal window 1 ++ control */ ++#define GC09R(addr) (addr + GC_OFFSET + 0x24) /* vertical window 1 ++ control */ ++#define GC0AR(addr) (addr + GC_OFFSET + 0x28) /* alternate horizontal ++ window 1 control */ ++#define GC0BR(addr) (addr + GC_OFFSET + 0x2c) /* alternate vertical ++ window 1 control */ ++#define GC0CR(addr) (addr + GC_OFFSET + 0x30) /* window 1 ++ start address */ ++#define GC0DR(addr) (addr + GC_OFFSET + 0x34) /* alternate window 1 ++ start address */ ++#define GC0ER(addr) (addr + GC_OFFSET + 0x38) /* alternate window 1 ++ stride */ ++#define GC0FR(addr) (addr + GC_OFFSET + 0x3c) /* alternate window 1 ++ line size */ ++#define GC10R(addr) (addr + GC_OFFSET + 0x40) /* hardware cursor 1 ++ position */ ++#define GC11R(addr) (addr + GC_OFFSET + 0x44) /* hardware cursor 1 ++ start address and ++ offset */ ++#define GC12R(addr) (addr + GC_OFFSET + 0x48) /* hardware cursor 1 ++ foreground color */ ++#define GC13R(addr) (addr + GC_OFFSET + 0x4c) /* hardware cursor 1 ++ background color */ ++ ++/* graphics controller 2 module */ ++#define GC20R(addr) (addr + GC_OFFSET + 0x80) /* graphics controller 2 ++ control */ ++#define GC21R(addr) (addr + GC_OFFSET + 0x84) /* graphics controller ++ CRC control */ ++#define GC22R(addr) (addr + GC_OFFSET + 0x88) /* horizontal display 2 ++ control */ ++#define GC23R(addr) (addr + GC_OFFSET + 0x8c) /* vertical display 2 ++ control */ ++#define GC24R(addr) (addr + GC_OFFSET + 0x90) /* horizontal sync 2 ++ control */ ++#define GC25R(addr) (addr + GC_OFFSET + 0x94) /* vertical sync 2 ++ control */ ++#define GC27R(addr) (addr + GC_OFFSET + 0x9c) /* vertical display 2 ++ count */ ++#define GC28R(addr) (addr + GC_OFFSET + 0xa0) /* horizontal window 2 ++ control */ ++#define GC29R(addr) (addr + GC_OFFSET + 0xa4) /* vertical window 2 ++ control */ ++#define GC2AR(addr) (addr + GC_OFFSET + 0xa8) /* alternate horizontal ++ window 2 control */ ++#define GC2BR(addr) (addr + GC_OFFSET + 0xac) /* alternate vertical ++ window 2 control */ ++#define GC2CR(addr) (addr + GC_OFFSET + 0xb0) /* window 2 ++ start address */ ++#define GC2DR(addr) (addr + GC_OFFSET + 0xb4) /* alternate window 2 ++ start address */ ++#define GC2ER(addr) (addr + GC_OFFSET + 0xb8) /* alternate window 2 ++ stride */ ++#define GC2FR(addr) (addr + GC_OFFSET + 0xbc) /* alternate window 2 ++ line size */ ++#define GC30R(addr) (addr + GC_OFFSET + 0xc0) /* hardware cursor 2 ++ position */ ++#define GC31R(addr) (addr + GC_OFFSET + 0xc4) /* hardware cursor 2 ++ start address and ++ offset */ ++#define GC32R(addr) (addr + GC_OFFSET + 0xc8) /* hardware cursor 2 ++ foreground color */ ++#define GC33R(addr) (addr + GC_OFFSET + 0xcc) /* hardware cursor 2 ++ background color */ ++ ++/* graphics engine */ ++#define ROP_SRCCOPY 0xCC /* dest = source */ ++#define ROP_SRCPAINT 0xEE /* dest = source OR dest */ ++#define ROP_SRCAND 0x88 /* dest = source AND dest */ ++#define ROP_SRCINVERT 0x66 /* dest = source XOR dest */ ++#define ROP_SRCERASE 0x44 /* dest = source AND (NOT dest) */ ++#define ROP_NOTSRCCOPY 0x33 /* dest = NOT source */ ++#define ROP_NOTSRCERASE 0x11 /* dest = (NOT source) AND (NOT dest) */ ++#define ROP_MERGECOPY 0xC0 /* dest = source AND pattern */ ++#define ROP_MERGEPAINT 0xBB /* dest = (NOT source) OR dest */ ++#define ROP_PATCOPY 0xF0 /* dest = pattern */ ++#define ROP_PATPAINT 0xFB /* dest = DPSnoo */ ++#define ROP_PATINVERT 0x5A /* dest = pattern XOR dest */ ++#define ROP_DSTINVERT 0x55 /* dest = NOT dest */ ++#define ROP_BLACKNESS 0x00 /* dest = BLACK */ ++#define ROP_WHITENESS 0xFF /* dest = WHITE */ ++ ++#define GE00R(addr) (addr + GE_OFFSET + 0x00) /* primary drawing command ++ register */ ++#define GE01R(addr) (addr + GE_OFFSET + 0x04) /* primary width and ++ height register */ ++#define GE02R(addr) (addr + GE_OFFSET + 0x08) /* primary destination ++ address register */ ++#define GE03R(addr) (addr + GE_OFFSET + 0x0c) /* primary source XY ++ register */ ++#define GE04R(addr) (addr + GE_OFFSET + 0x10) /* primary color compare ++ register */ ++#define GE05R(addr) (addr + GE_OFFSET + 0x14) /* primary clip left/top ++ register */ ++#define GE06R(addr) (addr + GE_OFFSET + 0x18) /* primary clip ++ right/bottom register ++ */ ++#define GE07R(addr) (addr + GE_OFFSET + 0x1c) /* primary source and ++ pattern offset ++ register */ ++#define GE08R(addr) (addr + GE_OFFSET + 0x20) /* primary foreground ++ color ++ register/rectangle ++ fill register */ ++#define GE09R(addr) (addr + GE_OFFSET + 0x24) /* source stride/offset ++ register */ ++#define GE0AR(addr) (addr + GE_OFFSET + 0x28) /* destination stride ++ register and color ++ depth */ ++#define GE0BR(addr) (addr + GE_OFFSET + 0x2c) /* image base address ++ register */ ++#define GE40R(addr) (addr + GE_OFFSET + 0x100) /* mono pattern register ++ 0 */ ++#define GE41R(addr) (addr + GE_OFFSET + 0x104) /* mono pattern register ++ 1 */ ++#define GE42R(addr) (addr + GE_OFFSET + 0x108) /* foreground color ++ register */ ++#define GE43R(addr) (addr + GE_OFFSET + 0x10c) /* background color ++ register */ ++/* color palette */ ++#define C1xxR(addr, regno) \ ++ (addr + CP1_OFFSET + (regno) * 4) /* graphics controller color ++ palette 1 */ ++/* device configuration */ ++#define DC00R(addr) (addr + DC_OFFSET + 0x00) /* device configuration ++ register 0 */ ++#define DC_RESET 0x4000 ++/* PCI configuration space */ ++#define PC00R(addr) (addr + PCI_OFFSET + 0x00)/* device ID/vendor ID ++ register */ ++/* Flatpanel Control */ ++#define FP00R(addr) (addr + FPI_OFFSET + 0x00) /* Flat Panel Control 0 */ ++#define FP01R(addr) (addr + FPI_OFFSET + 0x04) /* Flat Panel Output Pin */ ++#define FP02R(addr) (addr + FPI_OFFSET + 0x08) /* Flat Panel Gener Purpose ++ Outout Control Register */ ++#define FP03R(addr) (addr + FPI_OFFSET + 0x0c) /* General Purpose I/O Port ++ Control Register */ ++#define FP04R(addr) (addr + FPI_OFFSET + 0x10) /* STN Panel Control Register */ ++#define FP05R(addr) (addr + FPI_OFFSET + 0x14) /* D-STN Half Frame Buffer ++ Control Register -By Guess */ ++#define FP0FR(addr) (addr + FPI_OFFSET + 0x3c) /* Pulse Width Modulation ++ Control Register */ ++#define FRCTL_PATTERN_COUNT 32 ++#define FP10R(addr) (addr + FPI_OFFSET + 0x40) /* Frame-Rate Control Pattern ++ Register */ ++#define FP11R(addr) (addr + FPI_OFFSET + 0x44) ++#define FP2FR(addr) (addr + FPI_OFFSET + 0xc0) /* Frame-Rate Control Weight ++ Registers */ ++ ++ ++ ++ ++/* power management miscellaneous control */ ++union pm00r { ++ struct { ++ u32 pll1_n_b5 :1; /* PLL 1 N parameter bit 5 is 0 */ ++ u32 reserved_1 :1; ++ u32 pll2_enbl :1; /* PLL 2 enable */ ++ u32 pll3_enbl :1; /* PLL 3 enable */ ++ u32 reserved_2 :1; ++ u32 pwr_st_ctrl :1; /* power state status control */ ++ u32 reserved_3 :2; ++ ++ u32 ge_enbl :1; /* graphics engine enable */ ++ u32 ge_bsy_gl :1; /* graphics engine force busy (global) */ ++ u32 ge_bsy_lcl :1; /* graphics engine force busy (local) */ ++ u32 ge_clock :2; /* graphics engine clock select */ ++ u32 ge_cmd_fifo :1; /* graphics engine command FIFO reset */ ++ u32 ge_src_fifo :1; /* graphics engine CPU source FIFO reset */ ++ u32 miu_pwr_seq :1; /* memory interface unit power sequencing ++ enable */ ++ ++ u32 d3_mem_rfsh :1; /* D3 memory refresh */ ++ u32 d4_mem_rfsh :1; /* D4 memory refresh */ ++ u32 gpwr_intrvl :2; /* general power sequencing interval */ ++ u32 fppwr_intrvl:2; /* flat panel power sequencing interval */ ++ u32 gpwr_seq_ctr:1; /* general power sequencing interval control */ ++ u32 pmu_tm :1; /* PMU test mode */ ++ ++ u32 pwr_state :2; /* power state (read only) */ ++ u32 pwr_seq_st :1; /* power sequencing active status (read ++ only) */ ++ u32 reserved_4 :5; ++ } part; ++ u32 whole; ++}; ++ ++/* D1 state control */ ++union pm01r { ++ struct { ++ u32 osc_enbl :1; /* D1 oscillator enable */ ++ u32 pll1_enbl :1; /* D1 PLL 1 enable */ ++ u32 pll2_enbl :1; /* D1 PLL 2 enable */ ++ u32 pll3_enbl :1; /* D1 PLL 3 enable */ ++ u32 miu_enbl :1; /* D1 Memory Interface Unit (MIU) enable */ ++ u32 mem_rfsh :1; /* D1 memory refresh enable */ ++ u32 ge_enbl :1; /* D1 Graphics Engine (GE) enable */ ++ u32 reserved_1 :1; ++ ++ u32 crt_enbl :1; /* D1 CRT enable */ ++ u32 fpd_enbl :1; /* D1 Flat Panel enable */ ++ u32 reserved_2 :6; ++ ++ u32 ctl1_enbl :1; /* D1 controller 1 enable */ ++ u32 win1_enbl :1; /* D1 window 1 enable */ ++ u32 awin1_enbl :1; /* D1 alternate window 1 enable */ ++ u32 cur1_enbl :1; /* D1 cursor 1 enable */ ++ u32 reserved_3 :4; ++ ++ u32 ctl2_enbl :1; /* D1 controller 2 enable */ ++ u32 win2_enbl :1; /* D1 window 2 enable */ ++ u32 awin2_enbl :1; /* D1 alternate window 2 enable */ ++ u32 cur2_enbl :1; /* D1 cursor 2 enable */ ++ u32 reserved_4 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* D2 state control */ ++union pm02r { ++ struct { ++ u32 osc_enbl :1; /* D2 oscillator enable */ ++ u32 pll1_enbl :1; /* D2 PLL 1 enable */ ++ u32 pll2_enbl :1; /* D2 PLL 2 enable */ ++ u32 pll3_enbl :1; /* D2 PLL 3 enable */ ++ u32 miu_enbl :1; /* D2 Memory Interface Unit (MIU) enable */ ++ u32 mem_rfsh :1; /* D2 memory refresh enable */ ++ u32 ge_enbl :1; /* D2 Graphics Engine (GE) enable */ ++ u32 reserved_1 :1; ++ ++ u32 crt_enbl :1; /* D2 CRT enable */ ++ u32 fpd_enbl :1; /* D2 Flat Panel enable */ ++ u32 reserved_2 :6; ++ ++ u32 ctl1_enbl :1; /* D2 controller 1 enable */ ++ u32 win1_enbl :1; /* D2 window 1 enable */ ++ u32 awin1_enbl :1; /* D2 alternate window 1 enable */ ++ u32 cur1_enbl :1; /* D2 cursor 1 enable */ ++ u32 reserved_3 :4; ++ ++ u32 ctl2_enbl :1; /* D2 controller 2 enable */ ++ u32 win2_enbl :1; /* D2 window 2 enable */ ++ u32 awin2_enbl :1; /* D2 alternate window 2 enable */ ++ u32 cur2_enbl :1; /* D2 cursor 2 enable */ ++ u32 reserved_4 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* PLL 2 programming */ ++union pm06r { ++ struct { ++ u32 clk_src :1; /* PLL 2 reference clock source */ ++ u32 bypass :1; /* PLL 2 bypass */ ++ u32 reserved_1 :2; ++ u32 p_par :3; /* PLL 2 P parameter */ ++ u32 reserved_2 :1; ++ ++ u32 n_par :5; /* PLL 2 N parameter */ ++ u32 reserved_3 :3; ++ ++ u32 m_par :8; /* PLL 2 M parameter */ ++ ++ u32 reserved_4 :4; ++ u32 trim :4; /* PLL 2 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++/* PLL 3 programming */ ++union pm07r { ++ struct { ++ u32 clk_src :1; /* PLL 3 reference clock source */ ++ u32 bypass :1; /* PLL 3 bypass */ ++ u32 reserved_1 :2; ++ u32 p_par :3; /* PLL 3 P parameter */ ++ u32 reserved_2 :1; ++ ++ u32 n_par :5; /* PLL 3 N parameter */ ++ u32 reserved_3 :3; ++ ++ u32 m_par :8; /* PLL 3 M parameter */ ++ ++ u32 reserved_4 :4; ++ u32 trim :4; /* PLL 3 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++ ++ ++/* MIU interface control 1 */ ++union mm00r { ++ struct { ++ u32 miu_enbl :1; /* MIU enable bit */ ++ u32 mr_dsbl :1; /* MIU reset disable bit */ ++ u32 edr_dsbl :1; /* embedded DRAM reset disable bit */ ++ u32 reserved_1 :29; ++ } part; ++ u32 whole; ++}; ++ ++/* MIU interface control 2 */ ++union mm01r { ++ struct { ++ u32 mc_src :1; /* memory clock source */ ++ u32 msr_enbl :1; /* memory slow refresh enable bit */ ++ u32 pb_cpu :1; /* page break enable for CPU */ ++ u32 pb_gc1 :1; /* page break enable for GC1 */ ++ u32 pb_gc2 :1; /* page break enable for GC2 */ ++ u32 pb_stn_r :1; /* page break enable for STN read */ ++ u32 pb_stn_w :1; /* page break enable for STN write */ ++ u32 pb_ge :1; /* page break enable for GE */ ++ u32 reserved_1 :4; ++ u32 mr_interval :14; /* normal memory refresh time interval */ ++ u32 reserved_2 :4; ++ u32 edarm_enbl :1; /* embedded DRAM auto-refresh mode enable */ ++ u32 eds_enbl :1; /* EDRAM standby enable for EDRAM normal ++ mode operation */ ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 3 */ ++union mm02r { ++ struct { ++ u32 bs_ :2; ++ u32 bs_stnr :2; /* burst count for STN read memory cycles */ ++ u32 bs_stnw :2; /* burst count for STN write memroy cycles */ ++ u32 bs_ge :2; /* burst count for graphics engine ++ read/write memroy cycles */ ++ u32 bs_cpuw :2; /* burst count for CPU write memory cycles */ ++ u32 fifo_gc1 :4; /* GC1 display refresh FIFO threshold */ ++ u32 fifo_gc2 :4; /* GC2 display refresh FIFO threshold */ ++ u32 fifo_stnr :4; /* STN read FIFO threshold */ ++ u32 fifo_stnw :4; /* STN write FIFO threshold */ ++ u32 fifo_ge_src :3; /* GE source read FIFO threshold */ ++ u32 fifo_ge_dst :3; /* GE destination read FIFO threshold */ ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 4 */ ++union mm03r { ++ struct { ++ u32 rd_late_req :1; /* read latency request */ ++ u32 reserved_1 :31; ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 5 */ ++union mm04r { ++ struct { ++ u32 latency :3; /* EDRAM latency */ ++ u32 dmm_cyc :1; /* enable for the dummy cycle insertion ++ between read and write cycles */ ++ u32 pre_dmm_cyc :1; /* enable for the dummy cycle insertion ++ between read/write and precharge cycles ++ for the same bank */ ++ u32 reserved_1 :3; ++ u32 bnk_act_cls :2; /* bank activate command to bank close ++ command timing interval control */ ++ u32 bnk_act_rw :1; /* bank activate command to read/wirte ++ command timing interval control */ ++ u32 bnk_cls_act :1; /* bank close command to bank activate ++ command timing interval control */ ++ u32 trc :1; /* row cycle time */ ++ u32 reserved_2 :3; ++ u32 delay_r :2; /* programmable delay for read clock */ ++ u32 delay_m :2; /* programmable delay for internal memory ++ clock */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller 1 register */ ++union gc00r { ++ struct { ++ u32 ctl_enbl :1; /* Controller 1 Enable */ ++ u32 hc_reset :1; /* Horizontal Counter 1 Reset */ ++ u32 vc_reset :1; /* Vertical Counter 1 Reset */ ++ u32 iwin_enbl :1; /* Image Window 1 Enable */ ++ u32 gcd :4; /* Graphics Color Depth (GCD) */ ++ ++ u32 hc_enbl :1; /* Hardware Cursor 1 Enable */ ++ u32 reserved_1 :2; ++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */ ++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */ ++ ++ u32 g1rclk_src :2; /* G1RCLK Source */ ++ u32 tm0 :1; /* Test Mode 0 */ ++ u32 tm1 :1; /* Test Mode 1 */ ++ u32 fd :3; /* G1MCLK First Clock Divisor (FD1) */ ++ u32 reserved_2 :1; ++ ++ u32 sd :8; /* G1MCLK Second Clock Divisor (SD1) */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller CRT control */ ++union gc01r { ++ struct { ++ u32 dac_enbl :2; /* CRT DAC enable */ ++ u32 hsync_out :1; /* CRT HSYNC output during power down mode */ ++ u32 vsync_out :1; /* CRT VSYNC output during power down mode */ ++ u32 hsync_ctl :2; /* CRT HSYNC control */ ++ u32 vsync_ctl :2; /* CRT VSYNC control */ ++ /**/ ++ u32 hsync_pol :1; /* CRT HSYNC polarity */ ++ u32 vsync_pol :1; /* CRT VSYNC polarity */ ++ u32 sync_p_enbl :1; /* sync pedestal enable */ ++ u32 blnk_p_enbl :1; /* blank pedestal enable */ ++ u32 c_sync_enbl :1; /* composite sync enable */ ++ u32 vref_sel :1; /* VREF select */ ++ u32 mn_sns_enbl :1; /* monitor sense enable */ ++ u32 ct_out_enbl :1; /* constant output enable */ ++ /**/ ++ u32 dac_out_lvl :8; /* monitor sense DAC output level */ ++ /**/ ++ u32 blue_dac_r :1; /* blue DAC sense result */ ++ u32 green_dac_r :1; /* green DAC sense result */ ++ u32 red_dac_r :1; /* red DAC sense result */ ++ u32 reserved_1 :1; ++ u32 mon_col_sel :1; /* mono/color monitor select */ ++ u32 reserved_2 :3; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal display 1 control */ ++union gc02r { ++ struct { ++ u32 hd1t :12; /* horizontal display 1 total */ ++ u32 reserved_1 :4; ++ ++ u32 hd1e :12; /* horizontal display 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 1 control */ ++union gc03r { ++ struct { ++ u32 vd1t :12; /* vertical display 1 total */ ++ u32 reserved_1 :4; ++ ++ u32 vd1e :12; /* vertical display 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal sync 1 control */ ++union gc04r { ++ struct { ++ u32 hs1s :12; /* horizontal sync 1 start */ ++ u32 reserved_1 :4; ++ ++ u32 hs1e :12; /* horizontal sync 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical sync 1 control */ ++union gc05r { ++ struct { ++ u32 vs1s :12; /* vertical sync 1 start */ ++ u32 reserved_1 :4; ++ ++ u32 vs1e :12; /* vertical sync 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 1 count */ ++union gc07r { ++ struct { ++ u32 vd_cnt :12; /* vertical display 1 count */ ++ u32 reverved_1 :20; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal window 1 control */ ++union gc08r { ++ struct { ++ u32 hw1s :12; /* horizontal window 1 start (HW1S) */ ++ u32 reserved_1 :4; ++ ++ u32 hw1w :12; /* horizontal window 1 width (HW1W) */ ++ u32 w1ald :4; /* window 1 additional line data */ ++ } part; ++ u32 whole; ++}; ++ ++/* vertical window 1 control */ ++union gc09r { ++ struct { ++ u32 vw1s :12; /* vertical window 1 start */ ++ u32 reserved_1 :4; ++ u32 vw1h :12; /* vertical window 1 height */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* window 1 start address */ ++union gc0cr { ++ struct { ++ u32 w1sa :21; /* window 1 start address */ ++ u32 reserved_1 :11; ++ } part; ++ u32 whole; ++}; ++ ++/* window 1 stride */ ++union gc0er { ++ struct { ++ s16 w1st; /* window 1 stride */ ++ s16 aw1st; /* alternate window 1 stride */ ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 position */ ++union gc10r { ++ struct { ++ u32 hc1s :12; /* horizontal cursor 1 start */ ++ u32 reserved_1 :4; ++ u32 vc1s :12; /* vertical cursor 1 start */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 start address and offset */ ++union gc11r { ++ struct { ++ u32 hc1sa :11; /* hardware cursor 1 start address */ ++ u32 reserved_1 :5; ++ u32 hc1o :6; /* horizontal cursor 1 offset */ ++ u32 reserved_2 :2; ++ u32 vc1o :6; /* vertical cursor 1 offset */ ++ u32 reserved_3 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 foreground color */ ++union gc12r { ++ struct { ++ u32 hc1fc :24; /* hardware cursor 1 foreground color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 background color */ ++union gc13r { ++ struct { ++ u32 hc1bc :24; /* hardware cursor 1 background color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++ ++/* graphics controller 2 register */ ++union gc20r { ++ struct { ++ u32 ctl_enbl :1; /* Controller 2 Enable */ ++ u32 hc_reset :1; /* Horizontal Counter 2 Reset */ ++ u32 vc_reset :1; /* Vertical Counter 2 Reset */ ++ u32 iwin_enbl :1; /* Image Window 2 Enable */ ++ u32 gcd :4; /* Graphics Color Depth (GCD) */ ++ ++ u32 hc_enbl :1; /* Hardware Cursor 2 Enable */ ++ u32 reserved_1 :2; ++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */ ++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */ ++ ++ u32 g2rclk_src :2; /* G2RCLK Source */ ++ u32 tm0 :1; /* Test Mode 0 */ ++ u32 tm1 :1; /* Test Mode 1 */ ++ u32 fd :3; /* G2MCLK First Clock Divisor (FD1) */ ++ u32 reserved_2 :1; ++ ++ u32 sd :8; /* G2MCLK Second Clock Divisor (SD1) */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller CRC control */ ++union gc21r { ++ struct { ++ u32 crc_enbl :1; /* CRC enable */ ++ u32 vsync_wait :1; /* CRC input data control waitime of VSYNC */ ++ u32 crc_o_sel :2; /* CRC output select */ ++ u32 reserved_1 :4; ++ u32 crc_result :22; /* CRC result (read only) */ ++ u32 reserved_2 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal display 2 control */ ++union gc22r { ++ struct { ++ u32 hd2t :12; /* horizontal display 2 total */ ++ u32 reserved_1 :4; ++ ++ u32 hd2e :12; /* horizontal display 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 2 control */ ++union gc23r { ++ struct { ++ u32 vd2t :12; /* vertical display 2 total */ ++ u32 reserved_1 :4; ++ ++ u32 vd2e :12; /* vertical display 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal sync 2 control */ ++union gc24r { ++ struct { ++ u32 hs2s :12; /* horizontal sync 2 start */ ++ u32 reserved_1 :4; ++ ++ u32 hs2e :12; /* horizontal sync 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical sync 2 control */ ++union gc25r { ++ struct { ++ u32 vs2s :12; /* vertical sync 2 start */ ++ u32 reserved_1 :4; ++ ++ u32 vs2e :12; /* vertical sync 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 2 count */ ++union gc27r { ++ struct { ++ u32 vd_cnt :12; /* vertical display 2 count */ ++ u32 reverved_1 :20; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal window 2 control */ ++union gc28r { ++ struct { ++ u32 hw2s :12; /* horizontal window 2 start (HW2S) */ ++ u32 reserved_1 :4; ++ ++ u32 hw2w :12; /* horizontal window 2 width (HW2W) */ ++ u32 w2ald :4; /* window 2 additional line data */ ++ } part; ++ u32 whole; ++}; ++ ++/* vertical window 2 control */ ++union gc29r { ++ struct { ++ u32 vw2s :12; /* vertical window 2 start */ ++ u32 reserved_1 :4; ++ u32 vw2h :12; /* vertical window 2 height */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* window 2 start address */ ++union gc2cr { ++ struct { ++ u32 w2sa :21; /* window 2 start address */ ++ u32 reserved_1 :11; ++ } part; ++ u32 whole; ++}; ++ ++/* window 2 stride */ ++union gc2er { ++ struct { ++ s16 w2st; /* window 2 stride */ ++ s16 aw2st; /* alternate window 2 stride */ ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 position */ ++union gc30r { ++ struct { ++ u32 hc2s :12; /* horizontal cursor 2 start */ ++ u32 reserved_1 :4; ++ u32 vc2s :12; /* vertical cursor 2 start */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 start address and offset */ ++union gc31r { ++ struct { ++ u32 hc2sa :11; /* hardware cursor 2 start address */ ++ u32 reserved_1 :5; ++ u32 hc2o :6; /* horizontal cursor 2 offset */ ++ u32 reserved_2 :2; ++ u32 vc2o :6; /* vertical cursor 2 offset */ ++ u32 reserved_3 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 foreground color */ ++union gc32r { ++ struct { ++ u32 hc2fc :24; /* hardware cursor 2 foreground color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 background color */ ++union gc33r { ++ struct { ++ u32 hc2bc :24; /* hardware cursor 2 background color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++ ++/* primary drawing command register */ ++union ge00r { ++ struct { ++ u32 rop :8; /* raster operation */ ++ /**/ ++ u32 cmd_typ :3; /* command type */ ++ u32 x_dir :1; /* x direction */ ++ u32 y_dir :1; /* y direction */ ++ u32 src_mem :1; /* source memory */ ++ u32 mon_src :1; /* mono source */ ++ u32 mon_ptn :1; /* mono pattern */ ++ /**/ ++ u32 dst_trns_e :1; /* destination transparency enable */ ++ u32 dst_trns_p :1; /* destination transparency polarity */ ++ u32 mon_trns_e :1; /* mono source or mono pattern transparency ++ enable */ ++ u32 mon_trns_p :1; /* mono transparency polarity */ ++ u32 mod_sel :1; /* memory to screen or off screen to screen ++ mode select */ ++ u32 alpha_sel :2; /* Alpha byte mask selection */ ++ u32 sol_col :1; /* solid color */ ++ /**/ ++ u32 stride_eq :1; /* source stride is equal to destination ++ stride */ ++ u32 rop2_sel :1; /* ROP2 code selection */ ++ u32 clipping :1; /* enable clipping */ ++ u32 auto_exec :1; /* auto execute */ ++ u32 reserved_1 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* primary width and height register */ ++union ge01r { ++ struct { ++ u32 width :12; /* source/destination window width */ ++ u32 reserved_1 :4; ++ ++ u32 height :12; /* source/destination window height */ ++ u32 reserved_2 :1; ++ u32 reserved_3 :3; ++ } bitblt; ++ struct { ++ u32 dm :17; ++ u32 axis_major :12; ++ u32 x_y :1; /* x-major or y-major */ ++ u32 last_pix :1; /* decision to draw or not to draw the last ++ pixel of the line */ ++ u32 reserved_1 :1; ++ } bresenham; ++ u32 whole; ++}; ++ ++/* primary destination address register */ ++union ge02r { ++ struct { ++ u32 dst_x :12; /* destination x position */ ++ u32 reserved_1 :1; ++ u32 h_offset :3; /* mono/color pattern horizontal offset */ ++ ++ u32 dst_y :12; /* destination y position */ ++ u32 reserved_2 :1; ++ u32 v_offset :3; /* mono/color pattern vertical offset */ ++ } window; ++ struct { ++ u32 x :12; /* starting x coordinate */ ++ u32 dm :17; /* 17 bits major-axis delta */ ++ u32 reserved_1 :3; ++ } line; ++ u32 whole; ++}; ++ ++/* source XY register/line draw starting Y coordinate and mintor axis delta */ ++union ge03r { ++ struct { ++ u32 src_x :12; /* source X position */ ++ u32 reserved_1 :4; ++ ++ u32 src_y :12; /* source Y position */ ++ u32 reserved_2 :4; ++ } window; ++ struct { ++ u32 start_y :12; /* starting Y coordinate */ ++ u32 dn :17; /* 17 bits minor-axis delta */ ++ u32 reserved_1 :3; ++ } line; ++ u32 whole; ++}; ++ ++/* clip left/top register */ ++union ge05r { ++ struct { ++ u32 left :12; /* left edge of clipping rectangle */ ++ u32 reserved_1 :4; ++ ++ u32 top :12; /* top edge of clipping rectangle */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* source stride/offset register */ ++union ge09r { ++ struct { ++ u32 src_strid :12; /* source line stride */ ++ u32 reserved_1 :13; ++ u32 strt_bit :3; /* initial mono source bit offset */ ++ u32 strt_byte :3; /* initial mono/color source byte offset */ ++ u32 reserved_2 :1; ++ } line; ++ struct { ++ u32 strt_bit :5; /* initial mono source bit offset */ ++ u32 reserved_1 :1; ++ u32 amount :10; /* number of 16 bytes amount that MIU need ++ to fetch from frame buffer */ ++ ++ u32 reserved_2 :9; ++ u32 bit_spc :7; /* bit space between lines */ ++ } pack_mono; ++ struct { ++ u32 strt_bit :3; /* initial mono source bit offset */ ++ u32 strt_byte :3; /* initial mono/color source byte offset */ ++ u32 amount :10; /* number of 16 bytes amount that MIU need ++ to fetch from frame buffer */ ++ ++ u32 reserved_1 :9; ++ u32 bit_spc :3; /* bit space between lines */ ++ u32 byt_spc :4; /* byte space between lines */ ++ } pack_color; ++ u32 whole; ++}; ++ ++/* destination stride register and color depth */ ++union ge0ar { ++ struct { ++ u32 dst_strid :12; /* destination line stride and color depth */ ++ u32 reserved_1 :18; ++ u32 col_dpth :2; /* color depth */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller color pallete */ ++union c1xxr { ++ struct { ++ u8 red; /* red color pallete */ ++ u8 green; /* green/gray color pallete */ ++ u8 blue; /* blue color palette */ ++ u8 reserved_1; ++ } part; ++ u32 whole; ++}; ++ ++/* devicee configuration register 0 */ ++union dc00r { ++ struct { ++ u32 osc_bypass :1; /* oscillator bypass */ ++ u32 osc_enbl :1; /* oscillator enable */ ++ u32 pll1_bypass :1; /* PLL1 bypass */ ++ u32 pll1_enbl :1; /* PLL1 enable */ ++ u32 pll1_p_par :3; /* PLL1 P parameter */ ++ u32 cpu_div :1; /* CPU interface clock divisor */ ++ u32 pll1_n_par :5; /* PLL1 N parameter */ ++ u32 saisc :1; /* StrongARM interface synchronizer control */ ++ u32 s_chp_reset :1; /* software chip reset */ ++ u32 mem_enbl :1; /* memory standby enable */ ++ u32 pll1_m_par :8; /* PLL 1 M parameter */ ++ u32 osc_shaper :1; /* oscillator shaper disable */ ++ u32 fast_pwr :1; /* fast power sequencing */ ++ u32 osc_frq :2; /* oscillator frequency select */ ++ u32 pll1_trim :4; /* PLL 1 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++/* device ID/vendor ID register */ ++union pc00r { ++ struct { ++ u16 device; /* device ID */ ++ u16 vendor; /* vendor ID */ ++ } part; ++ u32 whole; ++}; ++ ++/* Flat Panel Control Register */ ++union fp00r { ++ struct { ++ u32 flatp_enbl : 2; /* Flat Panel Interface Enable */ ++ u32 flatp_type : 2; /* Flat Panel Type */ ++ u32 mono : 1; /* Mono/Color Panel Select */ ++ u32 flatp_intf : 3; /* Flat Panel Interface */ ++ u32 dither_pat : 2; /* Dither Pattern */ ++ u32 reserved : 2; /* Reserved Must Be 0*/ ++ u32 dither_col : 3; /* Dither Base Color */ ++ u32 alt_win_ctl: 1; /* Alternate Window Control */ ++ u32 frc_ctl : 2; /* FRC Control */ ++ u32 dither_adj1: 6; /* Dither Pattern Adjust 1 */ ++ u32 dither_adj2: 3; /* Dither Pattern Adjust 2 */ ++ u32 dither_adj3: 1; /* Dither Pattern Adjust 3 */ ++ u32 test_mode0 : 1; /* Test Mode 0 */ ++ u32 test_mode1 : 1; /* Test Mode 1 */ ++ u32 test_mode2 : 1; /* Test Mode 2 */ ++ u32 test_mode3 : 1; /* Test Mode 3 */ ++ } part; ++ u32 whole; ++}; ++ ++union fp01r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp02r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp03r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp04r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp05r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp0fr { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++ ++ ++ ++/**** ++ * Others ++ */ ++ ++#define CHIPNAME "MQ-200" ++ ++extern void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr); ++extern void mq200_external_setqmode(struct mq200_monitor_info*, unsigned long, spinlock_t *); ++extern void mq200_external_offdisplay(unsigned long); ++extern void mq200_external_ondisplay (unsigned long); ++extern int mq200_external_probe(unsigned long); ++ ++ ++ ++#endif +diff -Nur linux-2.6.24.vanilla/drivers/video/mq200/mq_external.c linux-2.6.24/drivers/video/mq200/mq_external.c +--- linux-2.6.24.vanilla/drivers/video/mq200/mq_external.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/video/mq200/mq_external.c 2008-02-20 21:51:22.000000000 +0100 +@@ -0,0 +1,513 @@ ++/* ++ * Copyright (C) 2005 Holger Hans Peter Freyther ++ * ++ * Based ON: ++ * ++ * linux/drivers/video/mq200fb.c -- MQ-200 for a frame buffer device ++ * based on linux/driver/video/pm2fb.c ++ * ++ * 2007/03/11 mrdata: ++ * bug found in gc1_reset(), renaming to gc1_gc2_reset() ++ * extend mq200_external_ondisplay() -> LCD for GC2 and CRT for GC1 ++ * ++ * Copyright (C) 2000 Lineo, Japan ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ */ ++ ++#include <asm/types.h> ++#include <asm/io.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++ ++#include <asm/hardware.h> ++ ++#include "mq200_data.h" ++ ++ ++#if 1 ++#define PRINTK(args...) printk(args) ++#else ++#define PRINTK(args...) ++#endif ++ ++ ++/**** ++ * power state transition to "state". ++ */ ++static void ++power_state_transition(unsigned long register_base, int state) ++{ ++ int i; ++ writel(state, PMCSR(register_base)); ++ mdelay(300); ++ for (i = 1; ; i++) { ++ udelay(100); ++ if ((readl(PMCSR(register_base)) & 0x3) == state) { ++ break; ++ } ++ } ++} ++ ++ ++/**** ++ * device configuration initialization. ++ */ ++static void ++dc_reset(unsigned long register_base) ++{ ++ union dc00r dc00r; ++ ++ /* Reset First */ ++ dc00r.whole = DC_RESET; ++ writel(dc00r.whole, DC00R(register_base)); ++ mdelay(100); ++ ++ dc00r.whole = 0xEF2082A; ++ writel(dc00r.whole, DC00R(register_base)); ++ mdelay(300); ++ PRINTK(CHIPNAME ": DC00R = 0x%08X\n", readl(DC00R(register_base))); ++} ++ ++ ++/**** ++ * initialize memory interface unit. ++ */ ++static void ++miu_reset(unsigned long register_base) ++{ ++ union mm00r mm00r; ++ union mm01r mm01r; ++ union mm02r mm02r; ++ union mm03r mm03r; ++ union mm04r mm04r; ++ ++ /* MIU interface control 1 */ ++ mm00r.whole = 0x4; ++ writel(mm00r.whole, MM00R(register_base)); ++ mdelay(50); ++ writel(0, MM00R(register_base)); ++ mdelay(50); ++ ++ /* MIU interface control 2 ++ * o PLL 1 output is used as memory clock source. ++ */ ++ mm01r.whole = 0x4143e086; ++ writel(mm01r.whole, MM01R(register_base)); ++ ++ /* memory interface control 3 */ ++ mm02r.whole = 0x6d6aabff; ++ writel(mm02r.whole, MM02R(register_base)); ++ ++ /* memory interface control 5 */ ++ mm04r.whole = 0x10d; ++ writel(mm04r.whole, MM04R(register_base)); ++ ++ /* memory interface control 4 */ ++ mm03r.whole = 0x1; ++ writel(mm03r.whole, MM03R(register_base)); ++ mdelay(50); ++ ++ /* MIU interface control 1 */ ++ mm00r.whole = 0x3; ++ writel(mm00r.whole, MM00R(register_base)); ++ mdelay(50); ++} ++ ++/**** ++ * ++ */ ++static ++void fpctrl_reset(unsigned long addr) ++{ ++ /* ++ * We're in D0 State, let us set the FPCTRL ++ */ ++ union fp00r fp00r; ++ union fp01r fp01r; ++ union fp02r fp02r; ++ union fp03r fp03r; ++ union fp04r fp04r; ++ union fp0fr fp0fr; ++ ++ fp00r.whole = 0x6320; ++ writel(fp00r.whole, FP00R(addr)); ++ ++ fp01r.whole = 0x20; ++ writel(fp01r.whole, FP01R(addr)); ++ ++ fp04r.whole = 0xBD0001; ++ writel(fp04r.whole, FP04R(addr)); ++ ++ /* Set Flat Panel General Purpose register first */ ++ fp02r.whole = 0x0; ++ writel(fp02r.whole, FP02R(addr)); ++ ++ fp03r.whole = 0x0; ++ writel(fp03r.whole, FP03R(addr)); ++ ++ fp0fr.whole = 0xA16c44; ++ writel(fp0fr.whole, FP0FR(addr)); ++ ++ /* Set them again */ ++ fp02r.whole = 0x0; ++ writel(fp02r.whole, FP02R(addr)); ++ ++ fp03r.whole = 0x0; ++ writel(fp03r.whole, FP03R(addr)); ++} ++ ++ ++/**** ++ * initialize power management unit. ++ */ ++static void ++pmu_reset(unsigned long register_base) ++{ ++ union pm00r pm00r; ++ union pm01r pm01r; ++ union pm02r pm02r; ++ ++ /* power management miscellaneous control ++ * o GE is driven by PLL 1 clock. ++ */ ++ pm00r.whole = 0xc0900; ++ writel(pm00r.whole, PM00R(register_base)); ++ ++ /* D1 state control */ ++ pm01r.whole = 0x5000271; ++ writel(pm01r.whole, PM01R(register_base)); ++ ++ /* D2 state control */ ++ pm02r.whole = 0x271; ++ writel(pm02r.whole, PM02R(register_base)); ++} ++ ++/**** ++ * initialize graphics controller 1 ++ * and graphics controller 2 ++ */ ++static void ++gc1_gc2_reset(unsigned long register_base, spinlock_t *lock ) ++{ ++ unsigned long flags; ++ union gc00r gc00r; ++ union gc01r gc01r; ++ union gc02r gc02r; ++ union gc03r gc03r; ++ union gc04r gc04r; ++ union gc05r gc05r; ++ union gc08r gc08r; ++ union gc09r gc09r; ++ union gc0cr gc0cr; ++ union gc0er gc0er; ++ union gc20r gc20r; ++ union gc22r gc22r; ++ union gc23r gc23r; ++ union gc24r gc24r; ++ union gc25r gc25r; ++ union gc28r gc28r; ++ union gc29r gc29r; ++ union gc2cr gc2cr; ++ union gc2er gc2er; ++ ++ union pm00r pm00r; ++ union pm06r pm06r; ++ union pm06r pm07r; ++ ++ spin_lock_irqsave(lock, flags); ++ ++ /* alternate window 1 stride */ ++ gc0er.whole = 0x640; ++ writel(gc0er.whole, GC0ER(register_base)); ++ ++ /* image window 1 start address */ ++ gc0cr.whole = 0x0; ++ writel(gc0cr.whole, GC0CR(register_base)); ++ ++ /* alternate window 2 stride */ ++ gc2er.whole = 0x640; ++ writel(gc0er.whole, GC2ER(register_base)); ++ ++ /* image window 2 start address */ ++ gc2cr.whole = 0x0; ++ writel(gc2cr.whole, GC2CR(register_base)); ++ ++ /* read PM Register */ ++ pm00r.whole = readl(PM00R(register_base)); ++ ++ /* horizontal window 1 control */ ++ gc08r.whole = 0x131f0000; ++ writel(gc08r.whole, GC08R(register_base)); ++ ++ /* vertical window 1 control */ ++ gc09r.whole = 0x12570000; ++ writel(gc09r.whole, GC09R(register_base)); ++ ++ /* horizontal display 1 control */ ++ gc02r.whole = 0x320041e; ++ writel(gc02r.whole, GC02R(register_base)); ++ ++ /* vertical display 1 control */ ++ gc03r.whole = 0x2570273; ++ writel(gc03r.whole, GC03R(register_base)); ++ ++ /* horizontal sync 1 control */ ++ gc04r.whole = 0x3c70347; ++ writel(gc04r.whole, GC04R(register_base)); ++ ++ /* vertical sync 1 control */ ++ gc05r.whole = 0x25d0259; ++ writel(gc05r.whole, GC05R(register_base)); ++ ++ /* graphics controller CRT control */ ++ gc01r.whole = 0x800; ++ writel(gc01r.whole, GC01R(register_base)); ++ ++ /* PLL 2 programming */ ++ pm06r.whole = 0xE90830; ++ writel(pm06r.whole, PM06R(register_base)); ++ ++ /* graphics controller 1 register ++ * o GC1 clock source is PLL 2. ++ * o hardware cursor is disabled. ++ */ ++ gc00r.whole = 0x10000C8 | 0x20000; ++ writel(gc00r.whole, GC00R(register_base)); ++ ++#if 0 ++ /* alternate horizontal window 1 control */ ++ writel(0, GC0AR(register_base)); ++ ++ /* alternate vertical window 1 control */ ++ writel(0, GC0BR(register_base)); ++ ++ /* window 1 start address */ ++ writel(0x2004100, GC0CR(register_base)); ++ ++ /* alternate window 1 start address */ ++ writel(0, GC0DR(register_base)); ++ ++ /* window 1 stride */ ++ gc0er.whole = 0x5100048; ++ writel(gc0er.whole, GC0ER(register_base)); ++ ++ /* reserved register - ??? - */ ++ writel(0x31f, GC0FR(register_base)); ++#endif ++ ++#if 0 ++ /* hardware cursor 1 position */ ++ writel(0, GC10R(register_base)); ++ ++ /* hardware cursor 1 start address and offset */ ++ gc11r.whole = 0x5100048; ++ writel(gc11r.whole, GC11R(register_base)); ++ ++ /* hardware cursor 1 foreground color */ ++ writel(0x00ffffff, GC12R(register_base)); ++ ++ /* hardware cursor 1 background color */ ++ writel(0x00000000, GC13R(register_base)); ++#endif ++ ++ /* horizontal window 2 control */ ++ gc28r.whole = 0x31f0000; ++ writel(gc28r.whole, GC28R(register_base)); ++ ++ /* vertical window 2 control */ ++ gc29r.whole = 0x2570000; ++ writel(gc29r.whole, GC29R(register_base)); ++ ++ /* horizontal display 2 control */ ++ gc22r.whole = 0x320041e; ++ writel(gc22r.whole, GC22R(register_base)); ++ ++ /* vertical display 2 control */ ++ gc23r.whole = 0x2570273; ++ writel(gc23r.whole, GC23R(register_base)); ++ ++ /* horizontal sync 2 control */ ++ gc24r.whole = 0x3c70347; ++ writel(gc24r.whole, GC24R(register_base)); ++ ++ /* vertical sync 2 control */ ++ gc25r.whole = 0x25d0259; ++ writel(gc25r.whole, GC25R(register_base)); ++ ++ /* graphics controller CRT control */ ++ gc01r.whole = 0x800; ++ writel(gc01r.whole, GC01R(register_base)); ++ ++ /* PLL 3 programming */ ++ pm07r.whole = 0xE90830; ++ writel(pm07r.whole, PM07R(register_base)); ++ ++ /* graphics controller 2 register ++ * o GC2 clock source is PLL 3. ++ * o hardware cursor is disabled. ++ */ ++ gc20r.whole = 0x10000C8 | 0x30000; ++ writel(gc20r.whole, GC20R(register_base)); ++ ++ /* ++ * Enable PLL2 and PLL3 in the PM Register ++ */ ++ pm00r.part.pll2_enbl = 0x1; ++ pm00r.part.pll3_enbl = 0x1; ++ writel(pm00r.whole, PM00R(register_base)); ++ ++ spin_unlock_irqrestore(lock, flags); ++} ++ ++ ++/**** ++ * initialize graphics engine. ++ */ ++static void ++ge_reset(unsigned long register_base) ++{ ++ /* drawing command register */ ++ writel(0, GE00R(register_base)); ++ ++ /* promary width and height register */ ++ writel(0, GE01R(register_base)); ++ ++ /* primary destination address register */ ++ writel(0, GE02R(register_base)); ++ ++ /* primary source XY register */ ++ writel(0, GE03R(register_base)); ++ ++ /* primary color compare register */ ++ writel(0, GE04R(register_base)); ++ ++ /* primary clip left/top register */ ++ writel(0, GE05R(register_base)); ++ ++ /* primary clip right/bottom register */ ++ writel(0, GE06R(register_base)); ++ ++ /* primary source and pattern offset register */ ++ writel(0, GE07R(register_base)); ++ ++ /* primary foreground color register/rectangle fill color depth */ ++ writel(0, GE08R(register_base)); ++ ++ /* source stride/offset register */ ++ writel(0, GE09R(register_base)); ++ ++ /* destination stride register and color depth */ ++ writel(0, GE0AR(register_base)); ++ ++ /* image base address register */ ++ writel(0, GE0BR(register_base)); ++} ++ ++/**** ++ * initialize Color Palette 1. ++ */ ++static void ++cp1_reset(unsigned long addr_info) ++{ ++ int i; ++ ++ for (i = 0; i < 256; i++) ++ writel(0, C1xxR(addr_info, i)); ++} ++ ++ ++/* ++ * Below functions are called from the skeleton ++ */ ++void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr) ++{ ++ writel(color,C1xxR(addr,regno)); ++} ++ ++void mq200_external_setqmode(struct mq200_monitor_info* info, ++ unsigned long addr, spinlock_t *lock) ++{ ++ dc_reset(addr); /* device configuration */ ++ ++ power_state_transition(addr, 0); /* transition to D0 state */ ++ ++ pmu_reset(addr); /* power management unit */ ++ ++ miu_reset(addr); /* memory interface unit */ ++ ++ ge_reset(addr); /* graphics engine */ ++ ++ fpctrl_reset(addr); /* reset the panel settings */ ++ ++ gc1_gc2_reset(addr, lock); /* graphics controller 1 and 2 */ ++ ++ cp1_reset(addr); /* color palette 1 */ ++ ++ mq200_external_ondisplay(addr); /* LCD and CRT */ ++} ++ ++void mq200_external_offdisplay(unsigned long addr) ++{ ++ /* ++ * Move the MQ200 to D3 mode ++ */ ++ power_state_transition(addr, 3); ++} ++ ++/** ++ * to be called after mq200_external_setqmode ++ */ ++void mq200_external_ondisplay (unsigned long addr) ++{ ++ /* ++ * Set the framebuffer details ++ */ ++ union gc00r gc00r; ++ union gc01r gc01r; ++ union gc20r gc20r; ++ union fp00r fp00r; ++ ++ /* enable LCD for GC2 */ ++ fp00r.whole = readl(FP00R(addr)); ++ fp00r.whole &= 0xfffffffc; ++ ++ gc20r.whole = readl(GC20R(addr)); ++ ++ if(!(gc20r.whole & 0x1)) { ++ gc20r.whole |= 0x1; ++ writel(gc20r.whole, GC20R(addr)); ++ } ++ ++ fp00r.whole |= 0x3; ++ writel(fp00r.whole, FP00R(addr)); ++ ++ /* enable CRT for GC1 */ ++ gc00r.whole = readl(GC00R(addr)); ++ ++ if(!(gc00r.whole & 0x1)) { ++ gc00r.whole |= 0x1; ++ writel(gc00r.whole, GC00R(addr)); ++ } ++ ++ gc01r.whole = readl(GC01R(addr)); ++ gc01r.whole &= 0xfffffffc; ++ ++ gc01r.whole |= 0x1; ++ writel(gc01r.whole, GC01R(addr)); ++ ++} ++ ++int mq200_external_probe(unsigned long addr) ++{ ++ union pc00r pc00r; ++ if(readl(PMR(addr)) != PMR_VALUE) ++ return 0; ++ ++ pc00r.whole = readl(PC00R(addr)); ++ printk(KERN_INFO "mq200 video driver found Vendor: 0x%X Device: 0x%X\n", ++ pc00r.part.device, pc00r.part.vendor); ++ return 1; ++} +diff -Nur linux-2.6.24.vanilla/drivers/video/mq200/mq_skeleton.c linux-2.6.24/drivers/video/mq200/mq_skeleton.c +--- linux-2.6.24.vanilla/drivers/video/mq200/mq_skeleton.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/video/mq200/mq_skeleton.c 2008-02-20 21:51:22.000000000 +0100 +@@ -0,0 +1,398 @@ ++/* ++ * Author: Holger Hans Peter Freyther ++ * ++ * ++ * This implements the frame buffer driver interface to communicate ++ * with the kernel. ++ * It uses the mq200 routines from the ucLinux driver from Lineo ++ * ++ * 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/autoconf.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/fb.h> ++#include <linux/types.h> ++#include <linux/spinlock.h> ++ ++#include "mq200_data.h" ++ ++#if CONFIG_SA1100_SIMPAD ++/* ++ * Siemens SIMpad specefic data ++ */ ++#include <asm/arch/simpad.h> ++#include <asm/arch/hardware.h> ++ ++#define MQ200_REGIONS simpad_mq200_regions ++#define MQ200_MONITOR simpad_mq200_panel ++ ++static struct mq200_io_regions simpad_mq200_regions = { ++ .fb_size = MQ200_FB_SIZE, ++ .phys_mmio_base = 0x4be00000, ++ .virt_mmio_base = 0xf2e00000, ++ .phys_fb_base = 0x4b800000, ++ .virt_fb_base = 0xf2800000, ++}; ++ ++static struct mq200_monitor_info simpad_mq200_panel = { ++ .horizontal_res = 800, ++ .vertical_res = 600, ++ .depth = 16, ++ .refresh = 60, ++ .line_length = 1600, ++ .flags = 0x00130004, ++}; ++ ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int value); ++extern void clear_cs3_bit(int value); ++#endif ++ ++ ++ ++struct mq200_info { ++ struct fb_info fb_info; ++ struct mq200_io_regions io_regions; ++ struct mq200_monitor_info monitor_info; ++ ++ /* palette */ ++ u32 pseudo_palette[17]; /* 16 colors + 1 in reserve not that well documented... */ ++ spinlock_t lock; ++}; ++ ++ ++ ++static int mq200_blank( int blank_mode, struct fb_info *info ) ++{ ++#ifdef CONFIG_SA1100_SIMPAD ++ if(blank_mode ){ ++ clear_cs3_bit(DISPLAY_ON); ++ }else { ++ set_cs3_bit(DISPLAY_ON); ++ } ++#endif ++ return 0; ++} ++ ++ ++static int mq200_check_var(struct fb_var_screeninfo *var, ++ struct fb_info *info ) ++{ /* TODO do we need sanity checks here */ ++ return 0; ++} ++ ++ ++static int mq200_set_par( struct fb_info *info ) ++{ ++ /* TODO set paraemeter */ ++ return 0; ++} ++ ++static int mq200_setcolreg(unsigned regno, unsigned red, unsigned green, ++ unsigned blue, unsigned transp, ++ struct fb_info *info ) ++{ ++ struct mq200_info *p; ++ unsigned long color; ++ u32* pal = info->pseudo_palette; ++ ++ p = info->par; ++ ++ if(regno > 255 ) ++ return 1; ++ ++ switch( info->var.bits_per_pixel ){ ++ case 16: ++ pal[regno] = ++ ((red & 0xf800) >> 0) | ++ ((green & 0xf800) >> 5) | ((blue & 0xf800) >> 11); ++ break; ++ case 24: ++ pal[regno] = ++ ((red & 0xff00) << 8) | ++ ((green & 0xff00)) | ((blue & 0xff00) >> 8); ++ break; ++ case 32: ++ pal[regno] = ++ ((red & 0xff00) >> 8) | ++ ((green & 0xff00)) | ((blue & 0xff00) << 8); ++ break; ++ default: ++ break; ++ } ++ ++ red &= 0xFF; ++ green &= 0xFF; ++ blue &= 0xFF; ++ ++ color = red | (green << 8) | (blue << 16); ++ mq200_external_setpal(regno, color, p->io_regions.virt_mmio_base); ++ ++ return 0; ++} ++ ++ ++static struct fb_ops mq200_ops = { ++ .owner = THIS_MODULE, ++ .fb_check_var = mq200_check_var, ++ .fb_set_par = mq200_set_par, ++ .fb_setcolreg = mq200_setcolreg, ++#ifdef FB_SOFT_CURSOR ++ .fb_cursor = soft_cursor, /* FIXME use hardware cursor */ ++#endif ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++ .fb_blank = mq200_blank, ++}; ++ ++ ++/********************************************************************* ++ * ++ * Device driver and module init code ++ * this will register to the fb layer later ++ * ++ *********************************************************************/ ++static void mq200_internal_init_color( struct fb_bitfield* red, ++ struct fb_bitfield* green, ++ struct fb_bitfield* blue, ++ int bpp ) ++{ ++ switch ( bpp ) ++ { ++ case 16: ++ red->offset = 11; ++ green->offset = 5; ++ blue->offset = 0; ++ ++ red->length = 5; ++ green->length = 6; ++ blue->length = 5; ++ break; ++ case 24: ++ red->offset = 16; ++ green->offset = 8; ++ blue->offset = 0; ++ ++ red->length = 8; ++ green->length = 8; ++ blue->length = 8; ++ break; ++ case 32: ++ red->offset = 0; ++ green->offset = 8; ++ blue->offset = 16; ++ ++ red->length = 8; ++ green->length = 8; ++ blue->length = 8; ++ case 8: /* fall through */ ++ default: ++ red->offset = green->offset = blue->offset = 0; ++ red->length = green->length = blue->length = bpp; ++ break; ++ } ++ ++} ++ ++ ++static struct mq200_info* __init mq200_internal_init_fbinfo(void) ++{ ++ struct mq200_info *info = NULL; ++ ++ info = (struct mq200_info*)kmalloc(sizeof(*info), GFP_KERNEL); ++ if(!info) ++ return NULL; ++ ++ /* ++ * Initialize memory ++ */ ++ memset(info, 0, sizeof(struct mq200_info) ); ++ spin_lock_init(&info->lock); ++ ++ /* set the base IO addresses */ ++ info->io_regions = MQ200_REGIONS; ++ info->monitor_info = MQ200_MONITOR; ++ ++ info->fb_info.screen_base = (char *)info->io_regions.virt_fb_base; ++ ++ /* fb_fix_screeninfo filling */ ++ strcpy(info->fb_info.fix.id, "MQ200_FB" ); ++ info->fb_info.fix.smem_start = info->io_regions.phys_fb_base; ++ info->fb_info.fix.smem_len = info->io_regions.fb_size; /* - CURSOR_IMAGE */ ++ info->fb_info.fix.mmio_start = info->io_regions.phys_mmio_base; ++ info->fb_info.fix.mmio_len = MQ200_REGS_SIZE; ++ info->fb_info.fix.type = FB_TYPE_PACKED_PIXELS; ++ info->fb_info.fix.accel = FB_ACCEL_NONE; ++ info->fb_info.fix.line_length = MQ200_MONITOR_LINE_LENGTH(info); ++ ++ if(MQ200_MONITOR_DEPTH(info) <= 8 ) ++ info->fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ else if( MQ200_MONITOR_DEPTH(info) >= 16 ) ++ info->fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR; ++ else ++ panic("Calling mq200 with wrong display data\n"); ++ ++ /* set the variable screen info */ ++ info->fb_info.var.xres = MQ200_MONITOR_HORI_RES(info); ++ info->fb_info.var.yres = MQ200_MONITOR_VERT_RES(info); ++ info->fb_info.var.xres_virtual = MQ200_MONITOR_HORI_RES(info); ++ info->fb_info.var.yres_virtual = MQ200_MONITOR_VERT_RES(info); ++ info->fb_info.var.bits_per_pixel = MQ200_MONITOR_DEPTH(info); ++ ++ mq200_internal_init_color(&info->fb_info.var.red, ++ &info->fb_info.var.green, ++ &info->fb_info.var.blue, ++ MQ200_MONITOR_DEPTH(info) ); ++ ++ info->fb_info.var.transp.length = info->fb_info.var.transp.offset = 0; ++ info->fb_info.var.height = info->fb_info.var.width = -1; ++ ++ info->fb_info.var.vmode = FB_VMODE_NONINTERLACED; ++ info->fb_info.var.pixclock = 10000; ++ info->fb_info.var.left_margin = info->fb_info.var.right_margin = 16; ++ info->fb_info.var.upper_margin = info->fb_info.var.lower_margin = 16; ++ info->fb_info.var.hsync_len = info->fb_info.var.vsync_len = 8; ++ ++ info->fb_info.var.nonstd = 0; ++ info->fb_info.var.activate = FB_ACTIVATE_NOW; ++ info->fb_info.var.accel_flags = 0; ++ ++ return info; ++} ++ ++ ++extern void mq200_register_attributes(struct device* ); ++/* ++ * gets called from the bus ++ * we will register our framebuffer from here ++ */ ++static int __init mq200_probe(struct device *dev) ++{ ++ struct mq200_info *info = NULL; ++ int retv= 0; ++ ++ info = mq200_internal_init_fbinfo(); ++ if(!mq200_external_probe(info->io_regions.virt_mmio_base)) ++ goto error_out; ++ ++ GPDR |= (1<<3); ++ GAFR &= ~(1<<3); ++ GPSR |= (1<<3); ++ ++ mq200_external_setqmode(&info->monitor_info, ++ info->io_regions.virt_mmio_base, ++ &info->lock); ++ ++ info->fb_info.fbops = &mq200_ops; ++ info->fb_info.flags = FBINFO_FLAG_DEFAULT; ++ ++ mq200_check_var(&info->fb_info.var, &info->fb_info ); ++ ++ fb_alloc_cmap(&info->fb_info.cmap, 1 << MQ200_MONITOR_DEPTH(info), 0 ); ++ ++ info->fb_info.pseudo_palette = (void*)info->pseudo_palette; ++ ++ /* save the pointer to the mq200 struct in var */ ++ info->fb_info.par = info; ++ ++ retv = register_framebuffer(&info->fb_info ); ++ if(retv < 0) ++ goto error_out; ++ ++ ++ /* will get unset if retv != 0 */ ++ dev_set_drvdata(dev, info ); ++ return retv; ++ ++/* ++ * Free the info and exit ++ */ ++error_out: ++ kfree(info); ++ return -EINVAL; ++} ++ ++#ifdef CONFIG_PM ++static struct mq200_info* get_mq200_info( struct device *dev) ++{ ++ return dev_get_drvdata(dev); ++} ++ ++static unsigned long get_mmio_base( struct device *dev ) ++{ ++ struct mq200_info *info = get_mq200_info(dev); ++ return info->io_regions.virt_mmio_base; ++} ++ ++static struct mq200_monitor_info* get_monitor_info( struct device *dev) ++{ ++ struct mq200_info *info = get_mq200_info(dev); ++ return &info->monitor_info; ++} ++ ++static spinlock_t* get_spinlock( struct device *dev) ++{ ++ return &get_mq200_info(dev)->lock; ++} ++ ++/* ++ * FIXME: make sure we only call mq200_external_offdisplay only once ++ * a 2nd time will hang the kernel -zecke ++ * ++ * FIXME: save the content of the framebuffer inside dev->saved_state ++ * so on resume we can memcpy it back into the buffer and userspace ++ * does not need to redraw ++ * ++ * functions for suspending and resuming ++ */ ++static int mq200_suspend(struct device *dev, pm_message_t state) ++{ ++ ++ mq200_external_offdisplay( get_mmio_base(dev) ); ++ clear_cs3_bit(DISPLAY_ON); ++ ++ ++ return 0; ++} ++ ++static int mq200_resume(struct device *dev) ++{ ++ unsigned long mem = get_mmio_base(dev); ++ struct mq200_monitor_info *monitor = get_monitor_info(dev); ++ mq200_external_setqmode(monitor, mem, get_spinlock(dev) ); ++ ++ ++ /* ++ * Set display on if it was on ++ */ ++ set_cs3_bit(DISPLAY_ON); ++ ++ return 0; ++} ++ ++ ++#endif ++ ++ ++static struct device_driver mq200fb_driver = { ++ .name = "simpad-mq200", ++ .bus = &platform_bus_type, ++ .probe = mq200_probe, /* will be called after we've registered the driver */ ++ .suspend = mq200_suspend, ++ .resume = mq200_resume ++}; ++ ++int __devinit mq200_init(void) ++{ ++ return driver_register(&mq200fb_driver); ++} ++ ++module_init(mq200_init); ++MODULE_DESCRIPTION("MQ200 framebuffer driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-orinoco_cs-shared-irq.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-orinoco_cs-shared-irq.patch new file mode 100644 index 0000000000..2568093020 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-orinoco_cs-shared-irq.patch @@ -0,0 +1,21 @@ +diff -Nur linux-2.6.24.vanilla/drivers/net/wireless/orinoco_cs.c linux-2.6.24/drivers/net/wireless/orinoco_cs.c +--- linux-2.6.24.vanilla/drivers/net/wireless/orinoco_cs.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/net/wireless/orinoco_cs.c 2008-02-22 21:23:51.000000000 +0100 +@@ -8,6 +8,8 @@ + * cards such as the 3Com AirConnect and Ericsson WLAN. + * + * Copyright notice & release notes in file orinoco.c ++ * ++ * mrdata: added shared irq + */ + + #define DRIVER_NAME "orinoco_cs" +@@ -120,7 +122,7 @@ + link->priv = dev; + + /* Interrupt setup */ +- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; ++ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->irq.Handler = orinoco_interrupt; + link->irq.Instance = dev; diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-pcmcia.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-pcmcia.patch new file mode 100644 index 0000000000..5560d67494 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-pcmcia.patch @@ -0,0 +1,209 @@ +diff -Nur linux-2.6.24.vanilla/drivers/pcmcia/cs.c linux-2.6.24/drivers/pcmcia/cs.c +--- linux-2.6.24.vanilla/drivers/pcmcia/cs.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/pcmcia/cs.c 2008-02-22 20:53:06.000000000 +0100 +@@ -10,6 +10,8 @@ + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds ++ * ++ * mrdata: -added suspend fix + */ + + #include <linux/module.h> +@@ -59,6 +61,10 @@ + INT_MODULE_PARM(unreset_delay, 10); /* centiseconds */ + INT_MODULE_PARM(unreset_check, 10); /* centiseconds */ + INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */ ++// INT_MODULE_PARM(unreset_delay, 20); /* centiseconds */ ++// INT_MODULE_PARM(unreset_check, 100); /* centiseconds */ ++// INT_MODULE_PARM(unreset_limit, 300); /* unreset_check's */ ++ + + /* Access speed for attribute memory windows */ + INT_MODULE_PARM(cis_speed, 300); /* ns */ +@@ -366,6 +372,7 @@ + skt->ops->set_socket(skt, &skt->socket); + + msleep(unreset_delay * 10); ++ + for (i = 0; i < unreset_limit; i++) { + skt->ops->get_status(skt, &status); + +@@ -434,7 +441,7 @@ + + msleep(initial_delay * 10); + +- for (i = 0; i < 100; i++) { ++ for (i = 0; i < 100; i++) { + skt->ops->get_status(skt, &status); + if (!(status & SS_DETECT)) + return CS_NO_CARD; +diff -Nur linux-2.6.24.vanilla/drivers/pcmcia/sa1100_generic.c linux-2.6.24/drivers/pcmcia/sa1100_generic.c +--- linux-2.6.24.vanilla/drivers/pcmcia/sa1100_generic.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/pcmcia/sa1100_generic.c 2008-02-22 20:53:06.000000000 +0100 +@@ -28,6 +28,9 @@ + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + ++ 2007 mrnice: added thesings changes from device_driver ++ to platform_driver - many thx to thesing ++ + ======================================================================*/ + + #include <linux/module.h> +@@ -81,13 +84,15 @@ + return ret; + } + +-static struct device_driver sa11x0_pcmcia_driver = { +- .probe = sa11x0_drv_pcmcia_probe, +- .remove = soc_common_drv_pcmcia_remove, +- .name = "sa11x0-pcmcia", +- .bus = &platform_bus_type, +- .suspend = pcmcia_socket_dev_suspend, +- .resume = pcmcia_socket_dev_resume, ++static struct platform_driver sa11x0_pcmcia_driver = { ++ .driver = { ++ .name = "sa11x0-pcmcia", ++ .probe = sa11x0_drv_pcmcia_probe, ++ .remove = soc_common_drv_pcmcia_remove, ++ .suspend= pcmcia_socket_dev_suspend, ++ .resume = pcmcia_socket_dev_resume, ++ //.bus = &platform_bus_type, ++ }, + }; + + /* sa11x0_pcmcia_init() +@@ -100,7 +105,7 @@ + */ + static int __init sa11x0_pcmcia_init(void) + { +- return driver_register(&sa11x0_pcmcia_driver); ++ return platform_driver_register(&sa11x0_pcmcia_driver); + } + + /* sa11x0_pcmcia_exit() +@@ -110,7 +115,7 @@ + */ + static void __exit sa11x0_pcmcia_exit(void) + { +- driver_unregister(&sa11x0_pcmcia_driver); ++ platform_driver_unregister(&sa11x0_pcmcia_driver); + } + + MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>"); +diff -Nur linux-2.6.24.vanilla/drivers/pcmcia/sa1100_simpad.c linux-2.6.24/drivers/pcmcia/sa1100_simpad.c +--- linux-2.6.24.vanilla/drivers/pcmcia/sa1100_simpad.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/pcmcia/sa1100_simpad.c 2008-02-22 20:53:32.000000000 +0100 +@@ -8,13 +8,15 @@ + #include <linux/kernel.h> + #include <linux/device.h> + #include <linux/init.h> ++#include <linux/delay.h> + + #include <asm/hardware.h> + #include <asm/mach-types.h> + #include <asm/irq.h> + #include <asm/arch/simpad.h> + #include "sa1100_generic.h" +- ++ ++extern long get_cs3_ro(void); + extern long get_cs3_shadow(void); + extern void set_cs3_bit(int value); + extern void clear_cs3_bit(int value); +@@ -25,8 +27,15 @@ + + static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt) + { +- +- clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); ++ clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1|PCMCIA_RESET); ++ ++ set_cs3_bit(PCMCIA_BUFF_DIS); ++ ++ msleep(10); ++ ++ clear_cs3_bit(PCMCIA_BUFF_DIS); ++ ++ msleep(5); + + skt->irq = IRQ_GPIO_CF_IRQ; + +@@ -38,7 +47,7 @@ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + + /* Disable CF bus: */ +- //set_cs3_bit(PCMCIA_BUFF_DIS); ++ set_cs3_bit(PCMCIA_BUFF_DIS); + clear_cs3_bit(PCMCIA_RESET); + } + +@@ -47,21 +56,17 @@ + struct pcmcia_state *state) + { + unsigned long levels = GPLR; +- long cs3reg = get_cs3_shadow(); +- +- state->detect=((levels & GPIO_CF_CD)==0)?1:0; +- state->ready=(levels & GPIO_CF_IRQ)?1:0; +- state->bvd1=1; /* Not available on Simpad. */ +- state->bvd2=1; /* Not available on Simpad. */ +- state->wrprot=0; /* Not available on Simpad. */ +- +- if((cs3reg & 0x0c) == 0x0c) { +- state->vs_3v=0; +- state->vs_Xv=0; +- } else { +- state->vs_3v=1; +- state->vs_Xv=0; +- } ++ ++ state->detect = ((levels & GPIO_CF_CD) == 0) ? 1 : 0 ; ++ state->ready = (levels & GPIO_CF_IRQ) ? 1 : 0 ; ++ ++ long cs3_ro_reg = get_cs3_ro(); ++ ++ state->bvd1 = (cs3_ro_reg & PCMCIA_BVD1) ? 1 : 0 ; /* old: =1 Not available on Simpad. */ ++ state->bvd2 = (cs3_ro_reg & PCMCIA_BVD2) ? 1 : 0 ; /* old: =1 Not available on Simpad. */ ++ state->wrprot = 0 ; /* Not available on Simpad. */ ++ state->vs_3v = (cs3_ro_reg & PCMCIA_VS1) ? 0 : 1 ; ++ state->vs_Xv = (cs3_ro_reg & PCMCIA_VS2) ? 0 : 1 ; + } + + static int +@@ -95,7 +100,11 @@ + local_irq_restore(flags); + return -1; + } +- ++ ++ if (state->flags & SS_RESET) ++ set_cs3_bit(PCMCIA_RESET); ++ else ++ clear_cs3_bit(PCMCIA_RESET); + + local_irq_restore(flags); + +@@ -104,6 +113,7 @@ + + static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt) + { ++ clear_cs3_bit(PCMCIA_RESET); + soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); + } + +diff -Nur linux-2.6.24.vanilla/drivers/pcmcia/soc_common.c linux-2.6.24/drivers/pcmcia/soc_common.c +--- linux-2.6.24.vanilla/drivers/pcmcia/soc_common.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/pcmcia/soc_common.c 2008-02-22 20:53:06.000000000 +0100 +@@ -747,7 +747,9 @@ + + add_timer(&skt->poll_timer); + +- device_create_file(&skt->socket.dev, &dev_attr_status); ++ ret = device_create_file(&skt->socket.dev, &dev_attr_status); ++ if (ret) ++ goto out_err_7; + } + + dev_set_drvdata(dev, sinfo); diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-rtc-sa1100.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-rtc-sa1100.patch new file mode 100644 index 0000000000..407fd89a26 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-rtc-sa1100.patch @@ -0,0 +1,68 @@ +diff -Nur linux-2.6.24.vanilla/drivers/rtc/rtc-sa1100.c linux-2.6.24_rtc/drivers/rtc/rtc-sa1100.c +--- linux-2.6.24.vanilla/drivers/rtc/rtc-sa1100.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24_rtc/drivers/rtc/rtc-sa1100.c 2008-03-24 13:49:40.000000000 +0100 +@@ -79,7 +79,10 @@ + + rtsr = RTSR; + /* clear interrupt sources */ +- RTSR = 0; ++ RTSR &= ~RTSR_HZE; //RTSR = 0; is not possible and does not work ++ RTSR &= ~RTSR_HZ; ++ RTSR &= ~RTSR_ALE; ++ RTSR &= ~RTSR_AL; + RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); + + /* clear alarm interrupt if it has occurred */ +@@ -155,6 +158,20 @@ + { + int ret; + ++ /* ++ * On some devices RTSR is set to some value but it must be set to 0. ++ * We have to set RTSR to 0 and OIER/OSSR to default. This should not be ++ * necessary here but it is. ++ */ ++ spin_lock_irq(&sa1100_rtc_lock); ++ RTSR &= ~RTSR_HZE; ++ RTSR &= ~RTSR_HZ; ++ RTSR &= ~RTSR_ALE; ++ RTSR &= ~RTSR_AL; ++ OIER &= ~OIER_E1; ++ OSSR = OSSR_M1; ++ spin_unlock_irq(&sa1100_rtc_lock); ++ + ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED, + "rtc 1Hz", dev); + if (ret) { +@@ -186,7 +203,10 @@ + static void sa1100_rtc_release(struct device *dev) + { + spin_lock_irq(&sa1100_rtc_lock); +- RTSR = 0; ++ RTSR &= ~RTSR_HZE; //RTSR = 0; is not possible and does not work ++ RTSR &= ~RTSR_HZ; ++ RTSR &= ~RTSR_ALE; ++ RTSR &= ~RTSR_AL; + OIER &= ~OIER_E1; + OSSR = OSSR_M1; + spin_unlock_irq(&sa1100_rtc_lock); +@@ -339,6 +359,19 @@ + + platform_set_drvdata(pdev, rtc); + ++ /* ++ * On some devices RTSR is set to some value but it must be set to 0. ++ * We have to set RTSR to 0 and OIER/OSSR to default. ++ */ ++ spin_lock_irq(&sa1100_rtc_lock); ++ RTSR &= ~RTSR_HZE; ++ RTSR &= ~RTSR_HZ; ++ RTSR &= ~RTSR_ALE; ++ RTSR &= ~RTSR_AL; ++ OIER &= ~OIER_E1; ++ OSSR = OSSR_M1; ++ spin_unlock_irq(&sa1100_rtc_lock); ++ + return 0; + } + diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2 b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2 new file mode 100644 index 0000000000..e47d99288f --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2 @@ -0,0 +1,358 @@ +diff -Nur linux-2.6.24.vanilla/arch/arm/mach-sa1100/simpad.c linux-2.6.24/arch/arm/mach-sa1100/simpad.c +--- linux-2.6.24.vanilla/arch/arm/mach-sa1100/simpad.c 2008-10-04 21:47:24.000000000 +0200 ++++ linux-2.6.24/arch/arm/mach-sa1100/simpad.c 2008-10-04 22:01:20.000000000 +0200 +@@ -1,5 +1,15 @@ + /* + * linux/arch/arm/mach-sa1100/simpad.c ++ * ++ * 2007/04/11 mrdata: ++ * - insert simpad_uart_set_mctrl() ++ * simpad_uart_get_mctrl() ++ * - internal RS232/DECT/Bluetooth ++ * works again (based on 2.4 simpad-serial.patch) ++ * - added cs3_ro ++ * ++ * 2007/04/12 Bernhard Guillon: ++ * -added gpio_keys (based on h3000.c from hh.org) + */ + + #include <linux/module.h> +@@ -9,6 +19,9 @@ + #include <linux/proc_fs.h> + #include <linux/string.h> + #include <linux/pm.h> ++ ++#include <linux/apm-emulation.h> ++ + #include <linux/platform_device.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> +@@ -27,12 +40,21 @@ + + #include <linux/serial_core.h> + #include <linux/ioport.h> ++#include <linux/input.h> ++#include <linux/gpio_keys.h> + #include <asm/io.h> + + #include "generic.h" + ++long cs3_ro; + long cs3_shadow; + ++long get_cs3_ro(void) ++{ ++ cs3_ro = *(CS3BUSTYPE *)(CS3_BASE); ++ return cs3_ro; ++} ++ + long get_cs3_shadow(void) + { + return cs3_shadow; +@@ -55,9 +77,12 @@ + *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow; + } + ++EXPORT_SYMBOL(get_cs3_ro); ++EXPORT_SYMBOL(get_cs3_shadow); + EXPORT_SYMBOL(set_cs3_bit); + EXPORT_SYMBOL(clear_cs3_bit); + ++ + static struct map_desc simpad_io_desc[] __initdata = { + { /* MQ200 */ + .virtual = 0xf2800000, +@@ -73,23 +98,71 @@ + }; + + ++static void simpad_uart_set_mctrl(struct uart_port *port, u_int mctrl) ++{ ++ if (port->mapbase == _Ser1UTCR0) { ++ /* internal serial port (ttySA1, DECT/Bluetooth) */ ++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART1_RTS; ++ else GPSR = GPIO_UART1_RTS; ++ ++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART1_DTR; ++ else GPSR = GPIO_UART1_DTR; ++ } ++ ++ else if (port->mapbase == _Ser3UTCR0) { ++ /* external serial port (ttySA0, RS232) */ ++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART3_RTS; ++ else GPSR = GPIO_UART3_RTS; ++ ++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART3_DTR; ++ else GPSR = GPIO_UART3_DTR; ++ } ++} ++ ++ ++static u_int simpad_uart_get_mctrl(struct uart_port *port) ++{ ++ u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; ++ ++ if (port->mapbase == _Ser1UTCR0) { ++ /* internal serial port (ttySA1, DECT/Bluetooth) */ ++ int gplr = GPLR; ++ if (gplr & GPIO_UART1_DCD) ret &= ~TIOCM_CD; ++ if (gplr & GPIO_UART1_CTS) ret &= ~TIOCM_CTS; ++ if (gplr & GPIO_UART1_DSR) ret &= ~TIOCM_DSR; ++ } ++ ++ else if (port->mapbase == _Ser3UTCR0) { ++ /* external serial port (ttySA0, RS232) */ ++ int gplr = GPLR; ++ if (gplr & GPIO_UART3_DCD) ret &= ~TIOCM_CD; ++ if (gplr & GPIO_UART3_CTS) ret &= ~TIOCM_CTS; ++ if (gplr & GPIO_UART3_DSR) ret &= ~TIOCM_DSR; ++ } ++ return ret; ++} ++ ++ + static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate) + { +- if (port->mapbase == (u_int)&Ser1UTCR0) { +- if (state) +- { +- clear_cs3_bit(RS232_ON); +- clear_cs3_bit(DECT_POWER_ON); +- }else +- { +- set_cs3_bit(RS232_ON); +- set_cs3_bit(DECT_POWER_ON); +- } +- } ++ if (port->mapbase == (u_int)&Ser3UTCR0) { ++ if (state) ++ { ++ clear_cs3_bit(RS232_ON); ++ /* clear_cs3_bit(DECT_POWER_ON); */ ++ }else ++ { ++ set_cs3_bit(RS232_ON); ++ /* set_cs3_bit(DECT_POWER_ON); */ ++ } ++ } + } + ++ + static struct sa1100_port_fns simpad_port_fns __initdata = { +- .pm = simpad_uart_pm, ++ .set_mctrl = simpad_uart_set_mctrl, ++ .get_mctrl = simpad_uart_get_mctrl, ++ .pm = simpad_uart_pm, + }; + + +@@ -135,7 +208,6 @@ + }; + + +- + static void __init simpad_map_io(void) + { + sa1100_map_io(); +@@ -144,23 +216,45 @@ + + set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON | + ENABLE_5V | RESET_SIMCARD | DECT_POWER_ON); +- +- ++ + sa1100_register_uart_fns(&simpad_port_fns); + sa1100_register_uart(0, 3); /* serial interface */ + sa1100_register_uart(1, 1); /* DECT */ + +- // Reassign UART 1 pins ++ /* Reassign UART 1 pins */ ++ /* TEST SOME OLD KERNEL STUFF INSTEAD + GAFR |= GPIO_UART_TXD | GPIO_UART_RXD; + GPDR |= GPIO_UART_TXD | GPIO_LDD13 | GPIO_LDD15; + GPDR &= ~GPIO_UART_RXD; + PPAR |= PPAR_UPR; ++ */ ++ ++ // txd and rxd use their alternate function ++ GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD); ++ ++ // the control lines are gpio ++ GAFR &= ~(GPIO_UART1_RTS | GPIO_UART1_CTS | GPIO_UART1_DCD); ++ GAFR &= ~(GPIO_UART1_DSR | GPIO_UART1_DTR); ++ GAFR &= ~(GPIO_UART3_RTS | GPIO_UART3_CTS | GPIO_UART3_DCD); ++ GAFR &= ~(GPIO_UART3_DSR | GPIO_UART3_DTR); ++ ++ // txd, rts and dtr are outputs ++ GPDR |= GPIO_UART_TXD; ++ GPDR |= GPIO_UART1_RTS | GPIO_UART3_RTS; ++ GPDR |= GPIO_UART1_DTR | GPIO_UART3_DTR; ++ ++ // cts, dcd, dsr and rxd are inputs ++ GPDR &= ~(GPIO_UART1_CTS | GPIO_UART3_CTS); ++ GPDR &= ~(GPIO_UART1_DCD | GPIO_UART3_DCD); ++ GPDR &= ~(GPIO_UART1_DSR | GPIO_UART3_DSR); ++ GPDR &= ~GPIO_UART_RXD; ++ ++ PPAR |= PPAR_UPR; + + /* + * Set up registers for sleep mode. + */ + +- + PWER = PWER_GPIO0| PWER_RTC; + PGSR = 0x818; + PCFR = 0; +@@ -171,9 +265,10 @@ + sa11x0_set_mcp_data(&simpad_mcp_data); + } + ++ + static void simpad_power_off(void) + { +- local_irq_disable(); // was cli ++ local_irq_disable(); /* was cli */ + set_cs3(0x800); /* only SD_MEDIAQ */ + + /* disable internal oscillator, float CS lines */ +@@ -191,31 +286,52 @@ + while(1); + + local_irq_enable(); /* we won't ever call it */ ++} + + +-} ++/* ++ * gpio_keys ++*/ ++ ++static struct gpio_keys_button simpad_button_table[] = { ++ { KEY_POWER, IRQ_GPIO_POWER_BUTTON, 1, "power button" }, ++}; ++ ++static struct gpio_keys_platform_data simpad_keys_data = { ++ .buttons = simpad_button_table, ++ .nbuttons = ARRAY_SIZE(simpad_button_table), ++}; ++ ++static struct platform_device simpad_keys = { ++ .name = "gpio-keys", ++ .dev = { ++ .platform_data = &simpad_keys_data, ++ }, ++}; + + + /* + * MediaQ Video Device + */ ++ + static struct platform_device simpad_mq200fb = { + .name = "simpad-mq200", + .id = 0, + }; + ++ + static struct platform_device *devices[] __initdata = { +- &simpad_mq200fb ++ &simpad_keys, ++ &simpad_mq200fb, + }; + + +- + static int __init simpad_init(void) + { + int ret; + + pm_power_off = simpad_power_off; +- ++ + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); + if(ret) + printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device"); +diff -Nur linux-2.6.24.vanilla/include/asm-arm/arch-sa1100/simpad.h linux-2.6.24/include/asm-arm/arch-sa1100/simpad.h +--- linux-2.6.24.vanilla/include/asm-arm/arch-sa1100/simpad.h 2008-10-04 21:47:17.000000000 +0200 ++++ linux-2.6.24/include/asm-arm/arch-sa1100/simpad.h 2008-10-04 22:00:57.000000000 +0200 +@@ -12,11 +12,12 @@ + #define __ASM_ARCH_SIMPAD_H + + +-#define GPIO_UART1_RTS GPIO_GPIO14 ++#define GPIO_UART1_RTS GPIO_GPIO9 + #define GPIO_UART1_DTR GPIO_GPIO7 + #define GPIO_UART1_CTS GPIO_GPIO8 + #define GPIO_UART1_DCD GPIO_GPIO23 + #define GPIO_UART1_DSR GPIO_GPIO6 ++#define GPIO_UART1_RI GPIO_GPIO19 + + #define GPIO_UART3_RTS GPIO_GPIO12 + #define GPIO_UART3_DTR GPIO_GPIO16 +@@ -48,9 +49,9 @@ + #define GPIO_SMART_CARD GPIO_GPIO10 + #define IRQ_GPIO_SMARD_CARD IRQ_GPIO10 + +-// CS3 Latch is write only, a shadow is necessary ++// CS3 Latch is write only 16-bit , a shadow is necessary + +-#define CS3BUSTYPE unsigned volatile long ++#define CS3BUSTYPE unsigned volatile long + #define CS3_BASE 0xf1000000 + + #define VCC_5V_EN 0x0001 // For 5V PCMCIA +@@ -70,43 +71,17 @@ + #define ENABLE_5V 0x4000 // Enable 5V circuit + #define RESET_SIMCARD 0x8000 + +-#define RS232_ENABLE 0x0440 +-#define PCMCIAMASK 0x402f ++// CS3 Latch is readable only 8-bit interest + ++#define PCMCIA_BVD1 0x0001 ++#define PCMCIA_BVD2 0x0002 ++#define PCMCIA_VS1 0x0004 // PCMCIA card voltage select ++#define PCMCIA_VS2 0x0008 // PCMCIA card voltage select, if both are in high state -> 5V PCMCIA card ++#define LOCK_IND 0x0010 ++#define CHARGING_STATE 0x0020 // Ladestatus ++#define PCMCIA_SHORT 0x0040 // low active + +-struct simpad_battery { +- unsigned char ac_status; /* line connected yes/no */ +- unsigned char status; /* battery loading yes/no */ +- unsigned char percentage; /* percentage loaded */ +- unsigned short life; /* life till empty */ +-}; +- +-/* These should match the apm_bios.h definitions */ +-#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00 +-#define SIMPAD_AC_STATUS_AC_ONLINE 0x01 +-#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */ +-#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff +- +-/* These bitfields are rarely "or'd" together */ +-#define SIMPAD_BATT_STATUS_HIGH 0x01 +-#define SIMPAD_BATT_STATUS_LOW 0x02 +-#define SIMPAD_BATT_STATUS_CRITICAL 0x04 +-#define SIMPAD_BATT_STATUS_CHARGING 0x08 +-#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10 +-#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */ +-#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */ +-#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */ +-#define SIMPAD_BATT_STATUS_NOBATT 0x80 +-#define SIMPAD_BATT_STATUS_UNKNOWN 0xff +- +-extern int simpad_get_battery(struct simpad_battery* ); ++#define RS232_ENABLE 0x0440 ++#define PCMCIAMASK 0x402f + + #endif // __ASM_ARCH_SIMPAD_H +- +- +- +- +- +- +- +- diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch new file mode 100644 index 0000000000..4f471b5fb4 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-audio.patch @@ -0,0 +1,1622 @@ +Index: linux-2.6.24/sound/arm/sa11xx-ucb1x00.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/sound/arm/sa11xx-ucb1x00.c 2008-12-25 23:23:38.000000000 +0100 +@@ -0,0 +1,829 @@ ++/* ++ * Driver for Philips UCB1300 on Siemens Simpad ++ * Copyright (C) 2008 Thomas Schätzlein <thomas@pnxs.de> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License. ++ * ++ */ ++ ++#include <sound/driver.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/errno.h> ++#include <linux/ioctl.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++ ++#ifdef CONFIG_PM ++#include <linux/pm.h> ++#endif ++ ++#include <asm/dma.h> ++ ++#include <sound/core.h> ++#include <sound/control.h> ++#include <sound/pcm.h> ++#include <sound/initval.h> ++#include <sound/info.h> ++ ++#include <linux/mfd/ucb1x00.h> ++ ++#undef DEBUG_MODE ++#undef DEBUG_FUNCTION_NAMES ++ ++#define AUDIO_RATE_DEFAULT 44100 ++ ++MODULE_AUTHOR("Thomas Schätzlein <thomas@pnxs.de>"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("SA1100/SA1111 + UCB1300 driver for ALSA"); ++MODULE_SUPPORTED_DEVICE("{{UCB1x00,Simpad}}"); ++ ++static char *id; /* ID for this card */ ++ ++module_param(id, charp, 0444); ++MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UCB1x00 soundcard."); ++ ++struct audio_stream { ++ char *id; /* identification string */ ++ int stream_id; /* numeric identification */ ++ dma_device_t dma_dev; /* device identifier for DMA */ ++ dma_regs_t *dma_regs; /* points to our DMA registers */ ++ unsigned int active:1; /* we are using this stream for transfer now */ ++ int period; /* current transfer period */ ++ int periods; /* current count of periods registerd in the DMA engine */ ++ unsigned int old_offset; ++ spinlock_t dma_lock; /* for locking in DMA operations (see dma-sa1100.c in the kernel) */ ++ struct snd_pcm_substream *stream; ++}; ++ ++struct sa11xx_ucb1x00 { ++ struct snd_card* card; ++ struct snd_pcm* pcm; ++ struct ucb1x00_dev* ucb1x00; ++ struct device* pdev; ++ long samplerate; ++ struct audio_stream s[2]; /* playback & capture */ ++}; ++ ++#if 0 ++static unsigned int rates[] = { ++ 8000, 10666, 10985, 14647, ++ 16000, 21970, 22050, 24000, ++ 29400, 32000, 44100, 48000, ++}; ++ ++static struct snd_pcm_hw_constraint_list hw_constraints_rates = { ++ .count = ARRAY_SIZE(rates), ++ .list = rates, ++ .mask = 0, ++}; ++#endif ++ ++#define UCB1x00_SINGLE(xname, where, reg, shift, mask, invert) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ucb1x00_info_single, \ ++ .get = snd_ucb1x00_get_single, .put = snd_ucb1x00_put_single, \ ++ .private_value = where | (reg << 5) | (shift << 9) | (mask << 13) | (invert << 19) \ ++} ++ ++#define UCB1x00_SINGLE_WHERE(value) (value & 31) ++#define UCB1x00_SINGLE_REG(value) ((value >> 5) & 15) ++#define UCB1x00_SINGLE_SHIFT(value) ((value >> 9) & 15) ++#define UCB1x00_SINGLE_MASK(value) ((value >> 13) & 63) ++#define UCB1x00_SINGLE_INV(value) ((value >> 19) & 1) ++ ++static int snd_ucb1x00_info_single(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value); ++ ++// printk(KERN_INFO "snd_ucb1x00_info_single called\n"); ++ ++ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = mask; ++ return 0; ++} ++ ++static int snd_ucb1x00_get_single(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct sa11xx_ucb1x00* chip = snd_kcontrol_chip(kcontrol); ++ struct ucb1x00 *ucb = chip->ucb1x00->ucb; ++ unsigned int retval; ++ ++ int reg = UCB1x00_SINGLE_REG(kcontrol->private_value); ++ int shift = UCB1x00_SINGLE_SHIFT(kcontrol->private_value); ++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value); ++ int invert= UCB1x00_SINGLE_INV(kcontrol->private_value); ++ ++// printk(KERN_INFO "snd_ucb1x00_get_single called\n"); ++ ++ retval = (ucb1x00_reg_read(ucb, reg) >> shift) & mask; ++ ++// printk(KERN_INFO "ucb1x00_reg_read reg=%02X value=%08X\n", reg, retval); ++ ++ ucontrol->value.integer.value[0] = retval; ++ if (invert) { ++ ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; ++ } ++ ++ return 0; ++} ++ ++static int snd_ucb1x00_put_single(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct sa11xx_ucb1x00* chip = snd_kcontrol_chip(kcontrol); ++ struct ucb1x00 *ucb = chip->ucb1x00->ucb; ++ int reg = UCB1x00_SINGLE_REG(kcontrol->private_value); ++ int shift = UCB1x00_SINGLE_SHIFT(kcontrol->private_value); ++ int mask = UCB1x00_SINGLE_MASK(kcontrol->private_value); ++ int invert= UCB1x00_SINGLE_INV(kcontrol->private_value); ++ unsigned short val; ++ unsigned int regval; ++ ++ val = (ucontrol->value.integer.value[0] & mask); ++ if (invert) ++ val = mask - val; ++ ++// printk(KERN_INFO "snd_ucb1x00_put_single called val=%d\n", val); ++ ++ regval = ucb1x00_reg_read(ucb, reg); ++ regval &= ~(mask << shift); ++ regval |= val << shift; ++// printk(KERN_INFO "snd_ucb1x00_put_single write regval=%08X to reg %d\n", regval, reg); ++ ucb1x00_reg_write(ucb, reg, regval); ++ return 0; ++} ++ ++enum ucb1x00_config { ++ CMD_OMUTE = 0, ++ CMD_VOLUME, ++ CMD_IGAIN, ++ CMD_IMUTE, ++ CMD_LOOPBACK, ++ CMD_CLIP, ++ CMD_OENA, ++}; ++ ++static struct snd_kcontrol_new snd_ucb1x00_controls[] = { ++ UCB1x00_SINGLE("Master Playback Switch", CMD_OMUTE, UCB_AC_B, 13, 0x01, 1), ++ UCB1x00_SINGLE("Master Playback Volume", CMD_VOLUME, UCB_AC_B, 0, 0x1F, 1), ++ ++ UCB1x00_SINGLE("Input Gain", CMD_IGAIN, UCB_AC_A, 7, 0x1F, 1), ++// UCB1x00_SINGLE("Input Mute", CMD_IMUTE, UCB_AC_B, 14, 1, 1), ++// UCB1x00_SINGLE("Output Enabled", CMD_OENA, UCB_AC_B, 15, 1, 1), ++ ++ UCB1x00_SINGLE("Audio Loopback", CMD_LOOPBACK, UCB_AC_B, 8, 1, 0), ++}; ++ ++static void sa11xx_ucb1x00_set_samplerate(struct sa11xx_ucb1x00 *sa11xx_ucb1x00, long rate) ++{ ++ struct ucb1x00 *ucb = sa11xx_ucb1x00->ucb1x00->ucb; ++ unsigned int div_rate = ucb1x00_clkrate(ucb) / 32; ++ unsigned int div; ++ unsigned int reg; ++ ++// printk(KERN_INFO "sa11xx_ucb1x00_set_samplerate called rate=%d div_rate=%d\n", rate, div_rate); ++ ++ div = (div_rate + (rate / 2)) / rate; ++ if (div < 6) { ++ div = 6; ++ } ++ if (div > 127) { ++ div = 127; ++ } ++/* ++ printk(KERN_INFO "ucb=%08X\n", ucb); ++ printk(KERN_INFO "ucb1x00_set_audio_divisor(%d)\n", div*32); ++ printk(KERN_INFO "ucb1x00_reg_write(reg=%d, div=%d)\n", UCB_AC_A, div); ++*/ ++ reg = ucb1x00_reg_read(ucb, UCB_AC_B); ++ reg &= ~(UCB_AC_B_IN_ENA | UCB_AC_B_OUT_ENA); ++ ucb1x00_reg_write(ucb, UCB_AC_B, reg); ++ ++ // ucb1x00_reg_write(ucb, UCB_AC_B, 0); ++ ucb1x00_set_audio_divisor(ucb, div*32); /* set divisor in MCP */ ++ ++ reg = ucb1x00_reg_read(ucb, UCB_AC_A); ++ reg &= ~(0x1F); ++ reg |= div; ++ ucb1x00_reg_write(ucb, UCB_AC_A, reg); /* set divisor in UCB1x00 */ ++ ++ reg = ucb1x00_reg_read(ucb, UCB_AC_B); ++ reg |= UCB_AC_B_IN_ENA | UCB_AC_B_OUT_ENA; ++ ucb1x00_reg_write(ucb, UCB_AC_B, reg); ++ ++ sa11xx_ucb1x00->samplerate = div_rate / div; ++ ++// printk(KERN_INFO "sa11xx_ucb1x00_set_samplerate done\n"); ++} ++ ++/* HW init and shutdown */ ++static void sa11xx_ucb1x00_audio_init(struct sa11xx_ucb1x00 *sa11xx_ucb1x00) ++{ ++ //unsigned long flags; ++ ++ /* Setup DMA stuff */ ++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UCB1x00 out"; ++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK; ++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = sa11xx_ucb1x00->ucb1x00->ucb->mcp->dma_audio_wr; ++ ++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].id = "UCB1x00 in"; ++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE; ++ sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = sa11xx_ucb1x00->ucb1x00->ucb->mcp->dma_audio_rd; ++} ++ ++#if 0 ++static void sa11xx_ucb1x00_audio_shutdown(struct sa11xx_ucb1x00 *sa11xx_ucb1x00) ++{ ++ /* mute on */ ++// set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); ++ ++ /* disable the audio power and all signals leading to the audio chip */ ++// l3_close(sa11xx_uda1341->uda1341); ++// Ser4SSCR0 = 0; ++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET); ++ ++ /* power off and mute off */ ++ /* FIXME - is muting off necesary??? */ ++ ++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON); ++// clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); ++} ++#endif ++ ++/* DMA stuff */ ++ ++/* ++ * these are the address and sizes used to fill the xmit buffer ++ * so we can get a clock in record only mode ++ */ ++//#define FORCE_CLOCK_ADDR (dma_addr_t)FLUSH_BASE_PHYS ++//#define FORCE_CLOCK_SIZE 4096 // was 2048 ++ ++// FIXME Why this value exactly - wrote comment ++#define DMA_BUF_SIZE 8176 /* <= MAX_DMA_SIZE from asm/arch-sa1100/dma.h */ ++ ++static int audio_dma_request(struct audio_stream *s, void (*callback)(void *)) ++{ ++ int ret; ++ ++// printk(KERN_INFO "audio_dma_request stream=%08X\n", s); ++ ++ ret = sa1100_request_dma(s->dma_dev, s->id, callback, s, &s->dma_regs); ++ if (ret < 0) ++ printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev); ++ return ret; ++} ++ ++static void audio_dma_free(struct audio_stream *s) ++{ ++// printk(KERN_INFO "audio_dma_free stream=%08X\n", s); ++ sa1100_free_dma(s->dma_regs); ++ s->dma_regs = 0; ++} ++ ++static u_int audio_get_dma_pos(struct audio_stream *s) ++{ ++ struct snd_pcm_substream *substream = s->stream; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ unsigned int offset; ++ unsigned long flags; ++ dma_addr_t addr; ++ ++ // this must be called w/ interrupts locked out see dma-sa1100.c in the kernel ++ spin_lock_irqsave(&s->dma_lock, flags); ++ addr = sa1100_get_dma_pos((s)->dma_regs); ++ offset = addr - runtime->dma_addr; ++ spin_unlock_irqrestore(&s->dma_lock, flags); ++ ++// printk(KERN_INFO "audio_get_dma_pos real_offset=%d\n", offset); ++ ++ offset = bytes_to_frames(runtime,offset); ++ if (offset >= runtime->buffer_size) ++ offset = 0; ++ ++// printk(KERN_INFO "audio_get_dma_pos stream=%08X pos=%d\n", s, offset); ++ ++ return offset; ++} ++ ++/* ++ * this stops the dma and clears the dma ptrs ++ */ ++static void audio_stop_dma(struct audio_stream *s) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&s->dma_lock, flags); ++ s->active = 0; ++ s->period = 0; ++ /* this stops the dma channel and clears the buffer ptrs */ ++ sa1100_clear_dma(s->dma_regs); ++ spin_unlock_irqrestore(&s->dma_lock, flags); ++} ++ ++static void audio_process_dma(struct audio_stream *s) ++{ ++ struct snd_pcm_substream *substream = s->stream; ++ struct snd_pcm_runtime *runtime; ++ unsigned int dma_size; ++ unsigned int offset; ++ int ret; ++ ++// printk(KERN_ERR "audio_process_dma called periods=%d period=%d\n", s->periods, s->period); ++ ++ /* must be set here - only valid for running streams, not for forced_clock dma fills */ ++ runtime = substream->runtime; ++ while (s->active && s->periods < runtime->periods) { ++ dma_size = frames_to_bytes(runtime, runtime->period_size); ++ if (s->old_offset) { ++ /* a little trick, we need resume from old position */ ++ offset = frames_to_bytes(runtime, s->old_offset - 1); ++ s->old_offset = 0; ++ s->periods = 0; ++ s->period = offset / dma_size; ++ offset %= dma_size; ++ dma_size = dma_size - offset; ++ if (!dma_size) ++ continue; /* special case */ ++ } else { ++ offset = dma_size * s->period; ++ snd_assert(dma_size <= DMA_BUF_SIZE, ); ++ } ++// printk(KERN_INFO "start addr=%08X size=%d\n", runtime->dma_addr + offset, dma_size); ++ ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size); ++ if (ret) { ++// printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret); ++ return; ++ } ++ ++ s->period++; ++ s->period %= runtime->periods; ++ s->periods++; ++ } ++} ++ ++static void audio_dma_callback(void *data) ++{ ++ struct audio_stream *s = data; ++ ++// printk(KERN_INFO "audio_dma_callback called stream=%08X\n", s); ++ ++ /* ++ * If we are getting a callback for an active stream then we inform ++ * the PCM middle layer we've finished a period ++ */ ++ if (s->active) { ++// printk(KERN_INFO "audio_dma_callback period elapsed\n"); ++ snd_pcm_period_elapsed(s->stream); ++ } ++ ++ spin_lock(&s->dma_lock); ++ if (s->periods > 0) { ++ s->periods--; ++ } ++ audio_process_dma(s); ++ spin_unlock(&s->dma_lock); ++} ++ ++static void sa11xx_ucb1x00_enable_audio(struct ucb1x00* ucb) ++{ ++ uint32_t reg; ++ reg = ucb1x00_reg_read(ucb, UCB_AC_B); ++ reg |= UCB_AC_B_OUT_ENA; ++ ucb1x00_reg_write(ucb, UCB_AC_B, reg); ++} ++ ++static void sa11xx_ucb1x00_disable_audio(struct ucb1x00* ucb) ++{ ++ uint32_t reg; ++ reg = ucb1x00_reg_read(ucb, UCB_AC_B); ++ reg &= ~(UCB_AC_B_OUT_ENA); ++ ucb1x00_reg_write(ucb, UCB_AC_B, reg); ++} ++ ++/* PCM setting */ ++ ++/* trigger & timer */ ++static int snd_sa11xx_ucb1x00_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream); ++ int stream_id = substream->pstr->stream; ++ struct audio_stream *s = &chip->s[stream_id]; ++ int err = 0; ++ ++// printk(KERN_INFO "snd_sa11xx_ucb1x00_trigger called\n"); ++ ++ /* note local interrupts are already disabled in the midlevel code */ ++ spin_lock(&s->dma_lock); ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ /* requested stream startup */ ++ s->active = 1; ++ sa11xx_ucb1x00_enable_audio(chip->ucb1x00->ucb); ++ audio_process_dma(s); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ /* requested stream shutdown */ ++ sa11xx_ucb1x00_disable_audio(chip->ucb1x00->ucb); ++ audio_stop_dma(s); ++ break; ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ s->active = 0; ++ sa1100_stop_dma(s->dma_regs); ++ s->old_offset = audio_get_dma_pos(s) + 1; ++#ifdef HH_VERSION ++ sa1100_dma_flush_all(s->dma_regs); ++#else ++ //FIXME - DMA API ++#endif ++ s->periods = 0; ++ break; ++ case SNDRV_PCM_TRIGGER_RESUME: ++ s->active = 1; ++ audio_process_dma(s); ++ break; ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ sa1100_stop_dma(s->dma_regs); ++ s->active = 0; ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ s->active = 1; ++ if (s->old_offset) { ++ audio_process_dma(s); ++ break; ++ } ++ sa1100_resume_dma(s->dma_regs); ++ break; ++ default: ++ err = -EINVAL; ++ break; ++ } ++ spin_unlock(&s->dma_lock); ++ return err; ++} ++ ++static int snd_sa11xx_ucb1x00_prepare(struct snd_pcm_substream *substream) ++{ ++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct audio_stream *s = &chip->s[substream->pstr->stream]; ++ ++// printk(KERN_INFO "snd_sa11xx_ucb1x00_prepare called\n"); ++ ++ /* set requested samplerate */ ++ sa11xx_ucb1x00_set_samplerate(chip, runtime->rate); ++ ++ s->period = 0; ++ s->periods = 0; ++ ++ return 0; ++} ++ ++static snd_pcm_uframes_t snd_sa11xx_ucb1x00_pointer(struct snd_pcm_substream *substream) ++{ ++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream); ++// printk(KERN_INFO "snd_sa11xx_ucb1x00_pointer called\n"); ++ return audio_get_dma_pos(&chip->s[substream->pstr->stream]); ++} ++ ++static struct snd_pcm_hardware snd_sa11xx_ucb1x00_hw = ++{ ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++/* .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ ++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\ ++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ ++ SNDRV_PCM_RATE_KNOT),*/ ++ .rate_min = 5000, ++ .rate_max = 48000, ++ .channels_min = 1, ++ .channels_max = 1, ++ .buffer_bytes_max = 64*1024, ++ .period_bytes_min = 4096, ++ .period_bytes_max = DMA_BUF_SIZE, ++ .periods_min = 2, ++ .periods_max = 255, ++ .fifo_size = 0, ++}; ++ ++#if 0 ++static struct snd_pcm_hardware snd_sa11xx_ucb1x00_playback = ++{ ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ ++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\ ++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ ++ SNDRV_PCM_RATE_KNOT), ++ .rate_min = 8000, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ .buffer_bytes_max = 64*1024, ++ .period_bytes_min = 128, ++ .period_bytes_max = DMA_BUF_SIZE, ++ .periods_min = 2, ++ .periods_max = 8, ++ .fifo_size = 0, ++}; ++#endif ++ ++static int snd_card_sa11xx_ucb1x00_open(struct snd_pcm_substream *substream) ++{ ++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int stream_id = substream->pstr->stream; ++ //int err; ++ ++ chip->s[stream_id].stream = substream; ++ ++// printk(KERN_INFO "snd_card_sa11xx_ucb1x00_open called substream=%08X\n", substream); ++ ++ runtime->hw = snd_sa11xx_ucb1x00_hw; ++ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) { ++ } ++ else { ++ } ++/* ++ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { ++ return err; ++ } ++*/ ++/* if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0) { ++ return err; ++ } ++*/ ++ return 0; ++} ++ ++static int snd_card_sa11xx_ucb1x00_close(struct snd_pcm_substream *substream) ++{ ++ struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream); ++ ++ chip->s[substream->pstr->stream].stream = NULL; ++// printk(KERN_INFO "snd_card_sa11xx_ucb1x00_close called substream=%08X\n", substream); ++ return 0; ++} ++ ++/* HW params & free */ ++ ++static int snd_sa11xx_ucb1x00_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++// struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream); ++// struct ucb1x00* ucb = chip->ucb1x00->ucb; ++// printk(KERN_INFO "snd_sa11xx_ucb1x00_hw_params called substream=%08X\n", substream); ++ ++ ++ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); ++} ++ ++static int snd_sa11xx_ucb1x00_hw_free(struct snd_pcm_substream *substream) ++{ ++// struct sa11xx_ucb1x00 *chip = snd_pcm_substream_chip(substream); ++// struct ucb1x00* ucb = chip->ucb1x00->ucb; ++// printk(KERN_INFO "snd_sa11xx_ucb1x00_hw_free called substream=%08X\n", substream); ++ ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++static struct snd_pcm_ops snd_card_sa11xx_ucb1x00_playback_ops = { ++ .open = snd_card_sa11xx_ucb1x00_open, ++ .close = snd_card_sa11xx_ucb1x00_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_sa11xx_ucb1x00_hw_params, ++ .hw_free = snd_sa11xx_ucb1x00_hw_free, ++ .prepare = snd_sa11xx_ucb1x00_prepare, ++ .trigger = snd_sa11xx_ucb1x00_trigger, ++ .pointer = snd_sa11xx_ucb1x00_pointer, ++}; ++ ++static struct snd_pcm_ops snd_card_sa11xx_ucb1x00_capture_ops = { ++ .open = snd_card_sa11xx_ucb1x00_open, ++ .close = snd_card_sa11xx_ucb1x00_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_sa11xx_ucb1x00_hw_params, ++ .hw_free = snd_sa11xx_ucb1x00_hw_free, ++ .prepare = snd_sa11xx_ucb1x00_prepare, ++ .trigger = snd_sa11xx_ucb1x00_trigger, ++ .pointer = snd_sa11xx_ucb1x00_pointer, ++}; ++ ++static int __init snd_card_sa11xx_ucb1x00_pcm(struct sa11xx_ucb1x00 *sa11xx_ucb1x00, int device) ++{ ++ struct snd_pcm *pcm; ++ int err; ++ ++ if ((err = snd_pcm_new(sa11xx_ucb1x00->card, "UCB1x00 PCM", device, 1, 1, &pcm)) < 0) { ++ return err; ++ } ++ ++ sa11xx_ucb1x00_audio_init(sa11xx_ucb1x00); ++ ++ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_ucb1x00_playback_ops); ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_ucb1x00_capture_ops); ++ pcm->private_data = sa11xx_ucb1x00; ++ pcm->info_flags = 0; ++ strcpy(pcm->name, "UCB1x00 PCM"); ++ ++ err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, ++ NULL, 64*1024, 64*1024); ++// printk(KERN_INFO "snd_pcm_lib_preallocate_pages_for_all returns %d\n", err); ++ ++ ++ /* setup DMA controller */ ++ audio_dma_request(&sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback); ++ audio_dma_request(&sa11xx_ucb1x00->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback); ++ ++ sa11xx_ucb1x00->pcm = pcm; ++ ++ return 0; ++} ++ ++#if 0 ++#ifdef CONFIG_PM ++ ++static int snd_sa11xx_ucb1x00_suspend(struct platform_device *devptr, ++ pm_message_t state) ++{ ++ struct snd_card *card = platform_get_drvdata(devptr); ++ struct sa11xx_ucb1x00 *chip = card->private_data; ++ ++ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); ++ snd_pcm_suspend_all(chip->pcm); ++#ifdef HH_VERSION ++ sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach); ++ sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach); ++#else ++ //FIXME ++#endif ++ l3_command(chip->ucb1x00, CMD_SUSPEND, NULL); ++ sa11xx_uda1341_audio_shutdown(chip); ++ ++ return 0; ++} ++ ++static int snd_sa11xx_ucb1x00_resume(struct platform_device *devptr) ++{ ++ struct snd_card *card = platform_get_drvdata(devptr); ++ struct sa11xx_ucb1x00 *chip = card->private_data; ++ ++ sa11xx_ucb1x00_audio_init(chip); ++ l3_command(chip->ucb1x00, CMD_RESUME, NULL); ++#ifdef HH_VERSION ++ sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach); ++ sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach); ++#else ++ //FIXME ++#endif ++ snd_power_change_state(card, SNDRV_CTL_POWER_D0); ++ return 0; ++} ++#endif /* COMFIG_PM */ ++#endif ++ ++void snd_sa11xx_ucb1x00_free(struct snd_card *card) ++{ ++ struct sa11xx_ucb1x00 *chip = card->private_data; ++ ++// printk(KERN_INFO "snd_sa11xx_ucb1x00_free called\n"); ++ ++ if (&chip->s[SNDRV_PCM_STREAM_PLAYBACK]) { ++ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]); ++ } ++ if (&chip->s[SNDRV_PCM_STREAM_CAPTURE]) { ++ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]); ++ } ++} ++ ++static int ucb1x00_dev_free(struct snd_device *device) ++{ ++ return 0; ++} ++ ++static int snd_chip_ucb1x00_mixer_new(struct snd_card *card) ++{ ++ static struct snd_device_ops ops = { ++ .dev_free = ucb1x00_dev_free, ++ }; ++ ++ struct sa11xx_ucb1x00 *chip = card->private_data; ++ int idx; ++ int err; ++ ++ snd_assert(card != NULL, return -EINVAL); ++ ++ for (idx = 0; idx < ARRAY_SIZE(snd_ucb1x00_controls); idx++) { ++ if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ucb1x00_controls[idx], chip))) < 0) { ++ return err; ++ } ++ } ++ ++ if ((err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops)) < 0) { ++ return err; ++ } ++ ++ strcpy(card->mixername, "UCB1x00 Mixer"); ++ // ((struct ucb1x00_dev *)clnt->driver_data)->card = card; ++ ++ return 0; ++} ++ ++static int __init sa11xx_ucb1x00_audio_add(struct ucb1x00_dev* dev) ++{ ++ int err; ++ struct snd_card *card; ++ struct sa11xx_ucb1x00 *chip; ++ ++ /* register the soundcard */ ++ card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct sa11xx_ucb1x00)); ++ if (card == NULL) { ++ return -ENOMEM; ++ } ++ ++ ucb1x00_enable(dev->ucb); ++ ++ chip = card->private_data; ++ spin_lock_init(&chip->s[0].dma_lock); ++ spin_lock_init(&chip->s[1].dma_lock); ++ ++ card->private_free = snd_sa11xx_ucb1x00_free; ++ chip->card = card; ++ chip->samplerate = AUDIO_RATE_DEFAULT; ++ chip->ucb1x00 = dev; ++ chip->pdev = &dev->ucb->mcp->attached_device; ++ dev->priv = chip; ++ ++ // mixer ++ if ((err = snd_chip_ucb1x00_mixer_new(card))) ++ goto nodev; ++ ++ // PCM ++ if ((err = snd_card_sa11xx_ucb1x00_pcm(chip, 0)) < 0) ++ goto nodev; ++ ++ strcpy(card->driver, "UCB1x00"); ++ strcpy(card->shortname, "Simpad UCB1x00"); ++ sprintf(card->longname, "Siemens Simpad with Philips UCB1x00"); ++ ++ snd_card_set_dev(card, chip->pdev); ++ ++ if ((err = snd_card_register(card)) == 0) { ++ printk( KERN_INFO "Simpad audio support initialized 2\n" ); ++ return 0; ++ } ++ ++ nodev: ++ snd_card_free(card); ++ return err; ++} ++ ++static void sa11xx_ucb1x00_audio_remove(struct ucb1x00_dev* dev) ++{ ++ struct sa11xx_ucb1x00* chip = dev->priv; ++ printk(KERN_INFO "sa11xx_ucb1x00_audio_remove called\n"); ++ ucb1x00_disable(dev->ucb); ++ snd_card_free(chip->card); ++} ++ ++static int sa11xx_ucb1x00_audio_resume(struct ucb1x00_dev* dev) ++{ ++ printk(KERN_INFO "sa11xx_ucb1x00_audio_resume called\n"); ++ return 0; ++} ++ ++/* ---- GOOD ----------------------------------------------------- */ ++ ++static struct ucb1x00_driver sa11xx_ucb1x00_audio_driver = { ++ .add = sa11xx_ucb1x00_audio_add, ++ .remove = sa11xx_ucb1x00_audio_remove, ++ .resume = sa11xx_ucb1x00_audio_resume, ++}; ++ ++static int __init sa11xx_ucb1x00_init(void) ++{ ++ return ucb1x00_register_driver(&sa11xx_ucb1x00_audio_driver); ++} ++ ++static void __exit sa11xx_ucb1x00_exit(void) ++{ ++ ucb1x00_unregister_driver(&sa11xx_ucb1x00_audio_driver); ++} ++ ++module_init(sa11xx_ucb1x00_init); ++module_exit(sa11xx_ucb1x00_exit); ++ +Index: linux-2.6.24/drivers/mfd/ucb1x00.h +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/ucb1x00.h 2008-12-23 22:00:03.000000000 +0100 ++++ /dev/null 1970-01-01 00:00:00.000000000 +0000 +@@ -1,255 +0,0 @@ +-/* +- * linux/drivers/mfd/ucb1x00.h +- * +- * Copyright (C) 2001 Russell King, All Rights Reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License. +- */ +-#ifndef UCB1200_H +-#define UCB1200_H +- +-#define UCB_IO_DATA 0x00 +-#define UCB_IO_DIR 0x01 +- +-#define UCB_IO_0 (1 << 0) +-#define UCB_IO_1 (1 << 1) +-#define UCB_IO_2 (1 << 2) +-#define UCB_IO_3 (1 << 3) +-#define UCB_IO_4 (1 << 4) +-#define UCB_IO_5 (1 << 5) +-#define UCB_IO_6 (1 << 6) +-#define UCB_IO_7 (1 << 7) +-#define UCB_IO_8 (1 << 8) +-#define UCB_IO_9 (1 << 9) +- +-#define UCB_IE_RIS 0x02 +-#define UCB_IE_FAL 0x03 +-#define UCB_IE_STATUS 0x04 +-#define UCB_IE_CLEAR 0x04 +-#define UCB_IE_ADC (1 << 11) +-#define UCB_IE_TSPX (1 << 12) +-#define UCB_IE_TSMX (1 << 13) +-#define UCB_IE_TCLIP (1 << 14) +-#define UCB_IE_ACLIP (1 << 15) +- +-#define UCB_IRQ_TSPX 12 +- +-#define UCB_TC_A 0x05 +-#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +-#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ +- +-#define UCB_TC_B 0x06 +-#define UCB_TC_B_VOICE_ENA (1 << 3) +-#define UCB_TC_B_CLIP (1 << 4) +-#define UCB_TC_B_ATT (1 << 6) +-#define UCB_TC_B_SIDE_ENA (1 << 11) +-#define UCB_TC_B_MUTE (1 << 13) +-#define UCB_TC_B_IN_ENA (1 << 14) +-#define UCB_TC_B_OUT_ENA (1 << 15) +- +-#define UCB_AC_A 0x07 +-#define UCB_AC_B 0x08 +-#define UCB_AC_B_LOOP (1 << 8) +-#define UCB_AC_B_MUTE (1 << 13) +-#define UCB_AC_B_IN_ENA (1 << 14) +-#define UCB_AC_B_OUT_ENA (1 << 15) +- +-#define UCB_TS_CR 0x09 +-#define UCB_TS_CR_TSMX_POW (1 << 0) +-#define UCB_TS_CR_TSPX_POW (1 << 1) +-#define UCB_TS_CR_TSMY_POW (1 << 2) +-#define UCB_TS_CR_TSPY_POW (1 << 3) +-#define UCB_TS_CR_TSMX_GND (1 << 4) +-#define UCB_TS_CR_TSPX_GND (1 << 5) +-#define UCB_TS_CR_TSMY_GND (1 << 6) +-#define UCB_TS_CR_TSPY_GND (1 << 7) +-#define UCB_TS_CR_MODE_INT (0 << 8) +-#define UCB_TS_CR_MODE_PRES (1 << 8) +-#define UCB_TS_CR_MODE_POS (2 << 8) +-#define UCB_TS_CR_BIAS_ENA (1 << 11) +-#define UCB_TS_CR_TSPX_LOW (1 << 12) +-#define UCB_TS_CR_TSMX_LOW (1 << 13) +- +-#define UCB_ADC_CR 0x0a +-#define UCB_ADC_SYNC_ENA (1 << 0) +-#define UCB_ADC_VREFBYP_CON (1 << 1) +-#define UCB_ADC_INP_TSPX (0 << 2) +-#define UCB_ADC_INP_TSMX (1 << 2) +-#define UCB_ADC_INP_TSPY (2 << 2) +-#define UCB_ADC_INP_TSMY (3 << 2) +-#define UCB_ADC_INP_AD0 (4 << 2) +-#define UCB_ADC_INP_AD1 (5 << 2) +-#define UCB_ADC_INP_AD2 (6 << 2) +-#define UCB_ADC_INP_AD3 (7 << 2) +-#define UCB_ADC_EXT_REF (1 << 5) +-#define UCB_ADC_START (1 << 7) +-#define UCB_ADC_ENA (1 << 15) +- +-#define UCB_ADC_DATA 0x0b +-#define UCB_ADC_DAT_VAL (1 << 15) +-#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) +- +-#define UCB_ID 0x0c +-#define UCB_ID_1200 0x1004 +-#define UCB_ID_1300 0x1005 +-#define UCB_ID_TC35143 0x9712 +- +-#define UCB_MODE 0x0d +-#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +-#define UCB_MODE_AUD_OFF_CAN (1 << 13) +- +-#include "mcp.h" +- +-struct ucb1x00_irq { +- void *devid; +- void (*fn)(int, void *); +-}; +- +-struct ucb1x00 { +- spinlock_t lock; +- struct mcp *mcp; +- unsigned int irq; +- struct semaphore adc_sem; +- spinlock_t io_lock; +- u16 id; +- u16 io_dir; +- u16 io_out; +- u16 adc_cr; +- u16 irq_fal_enbl; +- u16 irq_ris_enbl; +- struct ucb1x00_irq irq_handler[16]; +- struct class_device cdev; +- struct list_head node; +- struct list_head devs; +-}; +- +-struct ucb1x00_driver; +- +-struct ucb1x00_dev { +- struct list_head dev_node; +- struct list_head drv_node; +- struct ucb1x00 *ucb; +- struct ucb1x00_driver *drv; +- void *priv; +-}; +- +-struct ucb1x00_driver { +- struct list_head node; +- struct list_head devs; +- int (*add)(struct ucb1x00_dev *dev); +- void (*remove)(struct ucb1x00_dev *dev); +- int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state); +- int (*resume)(struct ucb1x00_dev *dev); +-}; +- +-#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, cdev) +- +-int ucb1x00_register_driver(struct ucb1x00_driver *); +-void ucb1x00_unregister_driver(struct ucb1x00_driver *); +- +-/** +- * ucb1x00_clkrate - return the UCB1x00 SIB clock rate +- * @ucb: UCB1x00 structure describing chip +- * +- * Return the SIB clock rate in Hz. +- */ +-static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) +-{ +- return mcp_get_sclk_rate(ucb->mcp); +-} +- +-/** +- * ucb1x00_enable - enable the UCB1x00 SIB clock +- * @ucb: UCB1x00 structure describing chip +- * +- * Enable the SIB clock. This can be called multiple times. +- */ +-static inline void ucb1x00_enable(struct ucb1x00 *ucb) +-{ +- mcp_enable(ucb->mcp); +-} +- +-/** +- * ucb1x00_disable - disable the UCB1x00 SIB clock +- * @ucb: UCB1x00 structure describing chip +- * +- * Disable the SIB clock. The SIB clock will only be disabled +- * when the number of ucb1x00_enable calls match the number of +- * ucb1x00_disable calls. +- */ +-static inline void ucb1x00_disable(struct ucb1x00 *ucb) +-{ +- mcp_disable(ucb->mcp); +-} +- +-/** +- * ucb1x00_reg_write - write a UCB1x00 register +- * @ucb: UCB1x00 structure describing chip +- * @reg: UCB1x00 4-bit register index to write +- * @val: UCB1x00 16-bit value to write +- * +- * Write the UCB1x00 register @reg with value @val. The SIB +- * clock must be running for this function to return. +- */ +-static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) +-{ +- mcp_reg_write(ucb->mcp, reg, val); +-} +- +-/** +- * ucb1x00_reg_read - read a UCB1x00 register +- * @ucb: UCB1x00 structure describing chip +- * @reg: UCB1x00 4-bit register index to write +- * +- * Read the UCB1x00 register @reg and return its value. The SIB +- * clock must be running for this function to return. +- */ +-static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) +-{ +- return mcp_reg_read(ucb->mcp, reg); +-} +-/** +- * ucb1x00_set_audio_divisor - +- * @ucb: UCB1x00 structure describing chip +- * @div: SIB clock divisor +- */ +-static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) +-{ +- mcp_set_audio_divisor(ucb->mcp, div); +-} +- +-/** +- * ucb1x00_set_telecom_divisor - +- * @ucb: UCB1x00 structure describing chip +- * @div: SIB clock divisor +- */ +-static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) +-{ +- mcp_set_telecom_divisor(ucb->mcp, div); +-} +- +-void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); +-void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); +-unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); +- +-#define UCB_NOSYNC (0) +-#define UCB_SYNC (1) +- +-unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); +-void ucb1x00_adc_enable(struct ucb1x00 *ucb); +-void ucb1x00_adc_disable(struct ucb1x00 *ucb); +- +-/* +- * Which edges of the IRQ do you want to control today? +- */ +-#define UCB_RISING (1 << 0) +-#define UCB_FALLING (1 << 1) +- +-int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); +-void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +-void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +-int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); +- +-#endif +Index: linux-2.6.24/sound/arm/Kconfig +=================================================================== +--- linux-2.6.24.orig/sound/arm/Kconfig 2008-12-23 21:48:54.000000000 +0100 ++++ linux-2.6.24/sound/arm/Kconfig 2008-12-23 21:50:13.000000000 +0100 +@@ -14,6 +14,16 @@ + To compile this driver as a module, choose M here: the module + will be called snd-sa11xx-uda1341. + ++config SND_SA11XX_UCB1X00 ++ tristate "SA11xx UCB1x00 driver (Simpad)" ++ depends on ARCH_SA1100 && SND ++ select SND_PCM ++ help ++ Say Y here if you have a Simpad handheld computer ++ ++ To compile this driver as a module, choose M here: the module ++ will be called snd-sa11xx-ucb1x00. ++ + config SND_ARMAACI + tristate "ARM PrimeCell PL041 AC Link support" + depends on SND && ARM_AMBA +Index: linux-2.6.24/sound/arm/Makefile +=================================================================== +--- linux-2.6.24.orig/sound/arm/Makefile 2008-12-23 21:48:56.000000000 +0100 ++++ linux-2.6.24/sound/arm/Makefile 2008-12-23 21:57:32.000000000 +0100 +@@ -5,6 +5,9 @@ + obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o + snd-sa11xx-uda1341-objs := sa11xx-uda1341.o + ++obj-$(CONFIG_SND_SA11XX_UCB1X00) += snd-sa11xx-ucb1x00.o ++snd-sa11xx-ucb1x00-objs := sa11xx-ucb1x00.o ++ + obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o + snd-aaci-objs := aaci.o devdma.o + +Index: linux-2.6.24/drivers/mfd/ucb1x00-assabet.c +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/ucb1x00-assabet.c 2008-12-23 22:03:30.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-assabet.c 2008-12-23 22:03:50.000000000 +0100 +@@ -17,7 +17,7 @@ + + #include <asm/dma.h> + +-#include "ucb1x00.h" ++#include <linux/ucb1x00.h> + + #define UCB1X00_ATTR(name,input)\ + static ssize_t name##_show(struct class_device *dev, char *buf) \ +Index: linux-2.6.24/drivers/mfd/ucb1x00-core.c +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/ucb1x00-core.c 2008-12-23 22:03:02.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-core.c 2008-12-23 22:09:00.000000000 +0100 +@@ -28,7 +28,7 @@ + #include <asm/dma.h> + #include <asm/hardware.h> + +-#include "ucb1x00.h" ++#include <linux/mfd/ucb1x00.h> + + static DEFINE_MUTEX(ucb1x00_mutex); + static LIST_HEAD(ucb1x00_drivers); +Index: linux-2.6.24/drivers/mfd/ucb1x00-switches.c +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/ucb1x00-switches.c 2008-12-23 22:03:36.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-switches.c 2008-12-23 22:09:19.000000000 +0100 +@@ -35,7 +35,7 @@ + + #include <asm/dma.h> + +-#include "ucb1x00.h" ++#include <linux/mfd/ucb1x00.h> + + #define KEY_PRESS 1 + #define KEY_RELEASE 0 +Index: linux-2.6.24/drivers/mfd/ucb1x00-ts.c +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/ucb1x00-ts.c 2008-12-23 22:03:40.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-ts.c 2008-12-23 22:09:11.000000000 +0100 +@@ -40,7 +40,7 @@ + #include <asm/arch/collie.h> + #include <asm/mach-types.h> + +-#include "ucb1x00.h" ++#include <linux/mfd/ucb1x00.h> + + + struct ucb1x00_ts { +Index: linux-2.6.24/include/linux/mfd/mcp.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/linux/mfd/mcp.h 2008-12-23 22:06:04.000000000 +0100 +@@ -0,0 +1,66 @@ ++/* ++ * linux/drivers/mfd/mcp.h ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ */ ++#ifndef MCP_H ++#define MCP_H ++ ++struct mcp_ops; ++ ++struct mcp { ++ struct module *owner; ++ struct mcp_ops *ops; ++ spinlock_t lock; ++ int use_count; ++ unsigned int sclk_rate; ++ unsigned int rw_timeout; ++ dma_device_t dma_audio_rd; ++ dma_device_t dma_audio_wr; ++ dma_device_t dma_telco_rd; ++ dma_device_t dma_telco_wr; ++ struct device attached_device; ++}; ++ ++struct mcp_ops { ++ void (*set_telecom_divisor)(struct mcp *, unsigned int); ++ void (*set_audio_divisor)(struct mcp *, unsigned int); ++ void (*reg_write)(struct mcp *, unsigned int, unsigned int); ++ unsigned int (*reg_read)(struct mcp *, unsigned int); ++ void (*enable)(struct mcp *); ++ void (*disable)(struct mcp *); ++}; ++ ++void mcp_set_telecom_divisor(struct mcp *, unsigned int); ++void mcp_set_audio_divisor(struct mcp *, unsigned int); ++void mcp_reg_write(struct mcp *, unsigned int, unsigned int); ++unsigned int mcp_reg_read(struct mcp *, unsigned int); ++void mcp_enable(struct mcp *); ++void mcp_disable(struct mcp *); ++#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) ++ ++struct mcp *mcp_host_alloc(struct device *, size_t); ++int mcp_host_register(struct mcp *); ++void mcp_host_unregister(struct mcp *); ++ ++struct mcp_driver { ++ struct device_driver drv; ++ int (*probe)(struct mcp *); ++ void (*remove)(struct mcp *); ++ int (*suspend)(struct mcp *, pm_message_t); ++ int (*resume)(struct mcp *); ++}; ++ ++int mcp_driver_register(struct mcp_driver *); ++void mcp_driver_unregister(struct mcp_driver *); ++ ++#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device) ++#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d) ++ ++#define mcp_priv(mcp) ((void *)((mcp)+1)) ++ ++#endif +Index: linux-2.6.24/drivers/mfd/mcp.h +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/mcp.h 2008-12-23 22:06:30.000000000 +0100 ++++ /dev/null 1970-01-01 00:00:00.000000000 +0000 +@@ -1,66 +0,0 @@ +-/* +- * linux/drivers/mfd/mcp.h +- * +- * Copyright (C) 2001 Russell King, All Rights Reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License. +- */ +-#ifndef MCP_H +-#define MCP_H +- +-struct mcp_ops; +- +-struct mcp { +- struct module *owner; +- struct mcp_ops *ops; +- spinlock_t lock; +- int use_count; +- unsigned int sclk_rate; +- unsigned int rw_timeout; +- dma_device_t dma_audio_rd; +- dma_device_t dma_audio_wr; +- dma_device_t dma_telco_rd; +- dma_device_t dma_telco_wr; +- struct device attached_device; +-}; +- +-struct mcp_ops { +- void (*set_telecom_divisor)(struct mcp *, unsigned int); +- void (*set_audio_divisor)(struct mcp *, unsigned int); +- void (*reg_write)(struct mcp *, unsigned int, unsigned int); +- unsigned int (*reg_read)(struct mcp *, unsigned int); +- void (*enable)(struct mcp *); +- void (*disable)(struct mcp *); +-}; +- +-void mcp_set_telecom_divisor(struct mcp *, unsigned int); +-void mcp_set_audio_divisor(struct mcp *, unsigned int); +-void mcp_reg_write(struct mcp *, unsigned int, unsigned int); +-unsigned int mcp_reg_read(struct mcp *, unsigned int); +-void mcp_enable(struct mcp *); +-void mcp_disable(struct mcp *); +-#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) +- +-struct mcp *mcp_host_alloc(struct device *, size_t); +-int mcp_host_register(struct mcp *); +-void mcp_host_unregister(struct mcp *); +- +-struct mcp_driver { +- struct device_driver drv; +- int (*probe)(struct mcp *); +- void (*remove)(struct mcp *); +- int (*suspend)(struct mcp *, pm_message_t); +- int (*resume)(struct mcp *); +-}; +- +-int mcp_driver_register(struct mcp_driver *); +-void mcp_driver_unregister(struct mcp_driver *); +- +-#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device) +-#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d) +- +-#define mcp_priv(mcp) ((void *)((mcp)+1)) +- +-#endif +Index: linux-2.6.24/drivers/mfd/mcp-core.c +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/mcp-core.c 2008-12-23 22:07:01.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/mcp-core.c 2008-12-23 22:07:23.000000000 +0100 +@@ -21,7 +21,7 @@ + #include <asm/dma.h> + #include <asm/system.h> + +-#include "mcp.h" ++#include <linux/mfd/mcp.h> + + #define to_mcp(d) container_of(d, struct mcp, attached_device) + #define to_mcp_driver(d) container_of(d, struct mcp_driver, drv) +Index: linux-2.6.24/drivers/mfd/mcp-sa11x0.c +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/mcp-sa11x0.c 2008-12-23 22:07:06.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/mcp-sa11x0.c 2008-12-23 22:07:33.000000000 +0100 +@@ -28,7 +28,7 @@ + + #include <asm/arch/assabet.h> + +-#include "mcp.h" ++#include <linux/mfd/mcp.h> + + struct mcp_sa11x0 { + u32 mccr0; +Index: linux-2.6.24/include/linux/mfd/ucb1x00.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/linux/mfd/ucb1x00.h 2008-12-23 22:08:46.000000000 +0100 +@@ -0,0 +1,255 @@ ++/* ++ * linux/drivers/mfd/ucb1x00.h ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ */ ++#ifndef UCB1200_H ++#define UCB1200_H ++ ++#define UCB_IO_DATA 0x00 ++#define UCB_IO_DIR 0x01 ++ ++#define UCB_IO_0 (1 << 0) ++#define UCB_IO_1 (1 << 1) ++#define UCB_IO_2 (1 << 2) ++#define UCB_IO_3 (1 << 3) ++#define UCB_IO_4 (1 << 4) ++#define UCB_IO_5 (1 << 5) ++#define UCB_IO_6 (1 << 6) ++#define UCB_IO_7 (1 << 7) ++#define UCB_IO_8 (1 << 8) ++#define UCB_IO_9 (1 << 9) ++ ++#define UCB_IE_RIS 0x02 ++#define UCB_IE_FAL 0x03 ++#define UCB_IE_STATUS 0x04 ++#define UCB_IE_CLEAR 0x04 ++#define UCB_IE_ADC (1 << 11) ++#define UCB_IE_TSPX (1 << 12) ++#define UCB_IE_TSMX (1 << 13) ++#define UCB_IE_TCLIP (1 << 14) ++#define UCB_IE_ACLIP (1 << 15) ++ ++#define UCB_IRQ_TSPX 12 ++ ++#define UCB_TC_A 0x05 ++#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ ++#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ ++ ++#define UCB_TC_B 0x06 ++#define UCB_TC_B_VOICE_ENA (1 << 3) ++#define UCB_TC_B_CLIP (1 << 4) ++#define UCB_TC_B_ATT (1 << 6) ++#define UCB_TC_B_SIDE_ENA (1 << 11) ++#define UCB_TC_B_MUTE (1 << 13) ++#define UCB_TC_B_IN_ENA (1 << 14) ++#define UCB_TC_B_OUT_ENA (1 << 15) ++ ++#define UCB_AC_A 0x07 ++#define UCB_AC_B 0x08 ++#define UCB_AC_B_LOOP (1 << 8) ++#define UCB_AC_B_MUTE (1 << 13) ++#define UCB_AC_B_IN_ENA (1 << 14) ++#define UCB_AC_B_OUT_ENA (1 << 15) ++ ++#define UCB_TS_CR 0x09 ++#define UCB_TS_CR_TSMX_POW (1 << 0) ++#define UCB_TS_CR_TSPX_POW (1 << 1) ++#define UCB_TS_CR_TSMY_POW (1 << 2) ++#define UCB_TS_CR_TSPY_POW (1 << 3) ++#define UCB_TS_CR_TSMX_GND (1 << 4) ++#define UCB_TS_CR_TSPX_GND (1 << 5) ++#define UCB_TS_CR_TSMY_GND (1 << 6) ++#define UCB_TS_CR_TSPY_GND (1 << 7) ++#define UCB_TS_CR_MODE_INT (0 << 8) ++#define UCB_TS_CR_MODE_PRES (1 << 8) ++#define UCB_TS_CR_MODE_POS (2 << 8) ++#define UCB_TS_CR_BIAS_ENA (1 << 11) ++#define UCB_TS_CR_TSPX_LOW (1 << 12) ++#define UCB_TS_CR_TSMX_LOW (1 << 13) ++ ++#define UCB_ADC_CR 0x0a ++#define UCB_ADC_SYNC_ENA (1 << 0) ++#define UCB_ADC_VREFBYP_CON (1 << 1) ++#define UCB_ADC_INP_TSPX (0 << 2) ++#define UCB_ADC_INP_TSMX (1 << 2) ++#define UCB_ADC_INP_TSPY (2 << 2) ++#define UCB_ADC_INP_TSMY (3 << 2) ++#define UCB_ADC_INP_AD0 (4 << 2) ++#define UCB_ADC_INP_AD1 (5 << 2) ++#define UCB_ADC_INP_AD2 (6 << 2) ++#define UCB_ADC_INP_AD3 (7 << 2) ++#define UCB_ADC_EXT_REF (1 << 5) ++#define UCB_ADC_START (1 << 7) ++#define UCB_ADC_ENA (1 << 15) ++ ++#define UCB_ADC_DATA 0x0b ++#define UCB_ADC_DAT_VAL (1 << 15) ++#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) ++ ++#define UCB_ID 0x0c ++#define UCB_ID_1200 0x1004 ++#define UCB_ID_1300 0x1005 ++#define UCB_ID_TC35143 0x9712 ++ ++#define UCB_MODE 0x0d ++#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) ++#define UCB_MODE_AUD_OFF_CAN (1 << 13) ++ ++#include "mcp.h" ++ ++struct ucb1x00_irq { ++ void *devid; ++ void (*fn)(int, void *); ++}; ++ ++struct ucb1x00 { ++ spinlock_t lock; ++ struct mcp *mcp; ++ unsigned int irq; ++ struct semaphore adc_sem; ++ spinlock_t io_lock; ++ u16 id; ++ u16 io_dir; ++ u16 io_out; ++ u16 adc_cr; ++ u16 irq_fal_enbl; ++ u16 irq_ris_enbl; ++ struct ucb1x00_irq irq_handler[16]; ++ struct class_device cdev; ++ struct list_head node; ++ struct list_head devs; ++}; ++ ++struct ucb1x00_driver; ++ ++struct ucb1x00_dev { ++ struct list_head dev_node; ++ struct list_head drv_node; ++ struct ucb1x00 *ucb; ++ struct ucb1x00_driver *drv; ++ void *priv; ++}; ++ ++struct ucb1x00_driver { ++ struct list_head node; ++ struct list_head devs; ++ int (*add)(struct ucb1x00_dev *dev); ++ void (*remove)(struct ucb1x00_dev *dev); ++ int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state); ++ int (*resume)(struct ucb1x00_dev *dev); ++}; ++ ++#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, cdev) ++ ++int ucb1x00_register_driver(struct ucb1x00_driver *); ++void ucb1x00_unregister_driver(struct ucb1x00_driver *); ++ ++/** ++ * ucb1x00_clkrate - return the UCB1x00 SIB clock rate ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Return the SIB clock rate in Hz. ++ */ ++static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) ++{ ++ return mcp_get_sclk_rate(ucb->mcp); ++} ++ ++/** ++ * ucb1x00_enable - enable the UCB1x00 SIB clock ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Enable the SIB clock. This can be called multiple times. ++ */ ++static inline void ucb1x00_enable(struct ucb1x00 *ucb) ++{ ++ mcp_enable(ucb->mcp); ++} ++ ++/** ++ * ucb1x00_disable - disable the UCB1x00 SIB clock ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Disable the SIB clock. The SIB clock will only be disabled ++ * when the number of ucb1x00_enable calls match the number of ++ * ucb1x00_disable calls. ++ */ ++static inline void ucb1x00_disable(struct ucb1x00 *ucb) ++{ ++ mcp_disable(ucb->mcp); ++} ++ ++/** ++ * ucb1x00_reg_write - write a UCB1x00 register ++ * @ucb: UCB1x00 structure describing chip ++ * @reg: UCB1x00 4-bit register index to write ++ * @val: UCB1x00 16-bit value to write ++ * ++ * Write the UCB1x00 register @reg with value @val. The SIB ++ * clock must be running for this function to return. ++ */ ++static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) ++{ ++ mcp_reg_write(ucb->mcp, reg, val); ++} ++ ++/** ++ * ucb1x00_reg_read - read a UCB1x00 register ++ * @ucb: UCB1x00 structure describing chip ++ * @reg: UCB1x00 4-bit register index to write ++ * ++ * Read the UCB1x00 register @reg and return its value. The SIB ++ * clock must be running for this function to return. ++ */ ++static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) ++{ ++ return mcp_reg_read(ucb->mcp, reg); ++} ++/** ++ * ucb1x00_set_audio_divisor - ++ * @ucb: UCB1x00 structure describing chip ++ * @div: SIB clock divisor ++ */ ++static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) ++{ ++ mcp_set_audio_divisor(ucb->mcp, div); ++} ++ ++/** ++ * ucb1x00_set_telecom_divisor - ++ * @ucb: UCB1x00 structure describing chip ++ * @div: SIB clock divisor ++ */ ++static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) ++{ ++ mcp_set_telecom_divisor(ucb->mcp, div); ++} ++ ++void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); ++void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); ++unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); ++ ++#define UCB_NOSYNC (0) ++#define UCB_SYNC (1) ++ ++unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); ++void ucb1x00_adc_enable(struct ucb1x00 *ucb); ++void ucb1x00_adc_disable(struct ucb1x00 *ucb); ++ ++/* ++ * Which edges of the IRQ do you want to control today? ++ */ ++#define UCB_RISING (1 << 0) ++#define UCB_FALLING (1 << 1) ++ ++int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); ++void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); ++void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); ++int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); ++ ++#endif +Index: linux-2.6.24/drivers/mfd/ucb1x00-simpad.c +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/ucb1x00-simpad.c 2008-12-23 22:09:59.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-simpad.c 2008-12-23 22:10:12.000000000 +0100 +@@ -26,7 +26,7 @@ + #include <asm/arch/simpad.h> + #include <asm/arch-sa1100/simpad_pm.h> + +-#include "ucb1x00.h" ++#include <linux/mfd/ucb1x00.h> + #include "ucb1x00-simpad.h" + + #define UCB1X00_ATTR(name,input,designation) \
\ No newline at end of file diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch new file mode 100644 index 0000000000..20cb56d11b --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch @@ -0,0 +1,359 @@ +diff -Nur linux-2.6.24.vanilla/drivers/mfd/Kconfig linux-2.6.24/drivers/mfd/Kconfig +--- linux-2.6.24.vanilla/drivers/mfd/Kconfig 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/Kconfig 2008-02-20 21:17:07.000000000 +0100 +@@ -38,4 +38,7 @@ + tristate "Touchscreen interface support" + depends on MCP_UCB1200 && INPUT + ++config MCP_UCB1200_SWITCHES ++ tristate "SIMpad Switches support" ++ depends on MCP_UCB1200 && INPUT + endmenu +diff -Nur linux-2.6.24.vanilla/drivers/mfd/Makefile linux-2.6.24/drivers/mfd/Makefile +--- linux-2.6.24.vanilla/drivers/mfd/Makefile 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/Makefile 2008-02-20 21:17:07.000000000 +0100 +@@ -8,7 +8,7 @@ + obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o + obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o +- ++obj-$(CONFIG_MCP_UCB1200_SWITCHES) += ucb1x00-switches.o + ifeq ($(CONFIG_SA1100_ASSABET),y) + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o + endif +diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-switches.c linux-2.6.24/drivers/mfd/ucb1x00-switches.c +--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-switches.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-switches.c 2008-02-20 21:17:07.000000000 +0100 +@@ -0,0 +1,332 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-switches.c ++ * ++ * Copyright (C) 2007 Bernhard Guillon. ++ * ++ * 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. ++ * ++ * This driver is for the Switches of Siemens SIMpad (CL4,SL4,SLC), T-Sinus-Pad and ++ * Swisscom WP50 devices. ++ * ++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8. ++ * ++ * This driver is based on the 2.4 ucb1x00-switches, the 2.6 ucb1x00-assabet ++ * and the ucb1x00-ts driver. ++ * ++ * 2007/06/21 mrdata: ++ * - create new thread kswd() to handle irq_events for ucb1300-gpio's ++ * - found out, that not every key-press or key-release ++ * generate a irq_event ++ * -> establish key_state handling ++ * key_state, key_state_last <-> KEY_PRESS, KEY_RELEASE ++ * -> after irq_event polling the ucb1300-gpio's till all keys ++ * in key_state = KEY_RELEASE ++ * ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/device.h> ++#include <linux/sched.h> ++#include <linux/freezer.h> ++#include <linux/kthread.h> ++ ++#include <asm/dma.h> ++ ++#include "ucb1x00.h" ++ ++#define KEY_PRESS 1 ++#define KEY_RELEASE 0 ++ ++static int key [6] = { KEY_PROG1,KEY_PROG2,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT }; ++ ++static unsigned short int key_state [6] = { 0, 0, 0, 0, 0, 0}; ++static unsigned short int key_state_last [6] = { 1, 1, 1, 1, 1, 1}; ++ ++struct ucb1x00_switches { ++ struct input_dev *idev; ++ struct ucb1x00 *ucb; ++ ++ wait_queue_head_t irq_wait; ++ struct task_struct *rtask; ++ ++ int idx; ++ ++ unsigned int valid:1; ++}; ++ ++static int ucb1x00_thread(void *_switches_id) ++{ ++ unsigned short int this; ++ int idx_tmp; ++ int i; ++ struct ucb1x00_switches *switches = _switches_id; ++ struct input_dev *idev = switches->idev; ++ struct task_struct *tsk = current; ++ DECLARE_WAITQUEUE(wait, tsk); ++ ++ add_wait_queue(&switches->irq_wait, &wait); ++ ++ while (!kthread_should_stop()) ++ { ++ signed long timeout; ++ ++ if ((switches->idx >= 0) && (switches->idx <= 5) && (switches->valid == 1)) ++ { ++ switches->valid = 0; ++ ++ idx_tmp = switches->idx; ++ ++ ucb1x00_enable(switches->ucb); ++ ++ this = ~ucb1x00_io_read(switches->ucb); ++ ++ ucb1x00_disable(switches->ucb); ++ ++ if (key_state[idx_tmp] == KEY_RELEASE) ++ { ++ key_state_last[idx_tmp] = KEY_RELEASE; ++ key_state[idx_tmp] = KEY_PRESS; ++ ++ input_report_key(idev, key[idx_tmp], KEY_PRESS); ++ input_sync(idev); ++ } ++ ++ for (i = 0; i < 6; i++) ++ { ++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS)) ++ { ++ key_state_last[i] = KEY_RELEASE; ++ key_state[i] = KEY_PRESS; ++ ++ input_report_key(idev, key[i], KEY_PRESS); ++ input_sync(idev); ++ } ++ } ++ ++ for(;;) ++ { ++ ucb1x00_enable(switches->ucb); ++ this = ~ucb1x00_io_read(switches->ucb); ++ ucb1x00_disable(switches->ucb); ++ ++ for (i = 0; i < 6; i++) ++ { ++ if ((key_state[i] == KEY_PRESS) && (((this & (1 << i)) ? 1 : 0) == KEY_RELEASE)) ++ { ++ key_state_last[i] = KEY_PRESS; ++ key_state[i] = KEY_RELEASE; ++ ++ input_report_key(idev, key[i], KEY_RELEASE); ++ input_sync(idev); ++ } ++ ++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS)) ++ { ++ key_state_last[i] = KEY_RELEASE; ++ key_state[i] = KEY_PRESS; ++ ++ input_report_key(idev, key[i], KEY_PRESS); ++ input_sync(idev); ++ } ++ ++ } ++ ++ // left loop, if no key press detect ++ if ((this | 0xff80) == 0xff80) ++ { ++ break; ++ } ++ ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ ++ try_to_freeze(); ++ ++ timeout = HZ / 100; ++ ++ schedule_timeout(timeout); ++ } ++ } ++ ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ ++ try_to_freeze(); ++ ++ timeout = MAX_SCHEDULE_TIMEOUT; ++ ++ schedule_timeout(timeout); ++ } ++ ++ remove_wait_queue(&switches->irq_wait, &wait); ++ ++ switches->rtask = NULL; ++ ++ return 0; ++} ++ ++ ++static void ucb1x00_dev_irq(int idx, void *id) ++{ ++ struct ucb1x00_switches *switches = id; ++ ++ switches->idx = idx; ++ switches->valid = 1; ++ ++ wake_up(&switches->irq_wait); ++} ++ ++static int ucb1x00_switches_add(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_switches *switches; ++ struct input_dev *idev; ++ int err,i; ++ ++ switches = kzalloc(sizeof(struct ucb1x00_switches), GFP_KERNEL); ++ idev = input_allocate_device(); ++ ++ if (!switches || !idev) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ switches->ucb = dev->ucb; ++ ++ idev->private = switches; ++ idev->name = "SIMpad Switches"; ++ idev->id.product = switches->ucb->id; ++ ++ __set_bit(EV_KEY, idev->evbit); ++ __set_bit(EV_REP, idev->evbit); ++ __set_bit(KEY_PROG1, idev->keybit); ++ __set_bit(KEY_PROG2, idev->keybit); ++ __set_bit(KEY_UP, idev->keybit); ++ __set_bit(KEY_DOWN, idev->keybit); ++ __set_bit(KEY_LEFT, idev->keybit); ++ __set_bit(KEY_RIGHT, idev->keybit); ++ ++ err = input_register_device(idev); ++ if (err) ++ goto fail; ++ switches->idev = idev; ++ dev->priv = switches; ++ ++ BUG_ON(switches->rtask); ++ ++ init_waitqueue_head(&switches->irq_wait); ++ ++ ucb1x00_enable(switches->ucb); ++ ++ ucb1x00_io_set_dir(switches->ucb, ++ UCB_IO_0 | UCB_IO_1 | UCB_IO_2 | ++ UCB_IO_3 | UCB_IO_4 | UCB_IO_5, ++ UCB_IO_8 | UCB_IO_9); ++ ++ ucb1x00_disable(switches->ucb); ++ ++ for (i = 0; i < 6; ++i) { ++ ucb1x00_enable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ if (ucb1x00_hook_irq(switches->ucb, i, ucb1x00_dev_irq, switches) < 0) { ++ printk(KERN_ERR "unable to hook IRQ for " ++ "UCB1300 SWITCH_%d\n", i); ++ return -EBUSY; ++ } ++ } ++ ++ switches->rtask = kthread_run(ucb1x00_thread, switches, "kswd"); ++ if (!IS_ERR(switches->rtask)) ++ { ++ return 0; ++ } ++ else ++ { ++ input_unregister_device(switches->idev); ++ ++ for (i = 5; i >= 0; --i) { ++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ /* Only error conditions are ENOENT and EINVAL; silently ++ * ignore: ++ */ ++ ucb1x00_free_irq(switches->ucb, i, NULL); ++ } ++ switches->rtask = NULL; ++ ucb1x00_disable(switches->ucb); ++ kfree(switches); ++ ++ return -EFAULT; ++ } ++ ++fail: ++ input_free_device(idev); ++ kfree(switches); ++ return err; ++ ++} ++ ++static void ucb1x00_switches_remove(struct ucb1x00_dev *dev) ++{ ++ int i; ++ struct ucb1x00_switches *switches = dev->priv; ++ ++ if (switches->rtask) ++ kthread_stop(switches->rtask); ++ ++ switches->rtask = NULL; ++ ++ input_unregister_device(switches->idev); ++ ++ for (i = 5; i >= 0; --i) { ++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ /* Only error conditions are ENOENT and EINVAL; silently ++ * ignore: ++ */ ++ ucb1x00_free_irq(switches->ucb, i, NULL); ++ } ++ ucb1x00_disable(switches->ucb); ++ kfree(switches); ++} ++ ++#ifdef CONFIG_PM ++static int ucb1x00_switches_resume(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_switches *switches = dev->priv; ++ ++ if (switches->rtask != NULL) ++ { ++ switches->valid = 0; ++ wake_up(&switches->irq_wait); ++ ++ printk(KERN_DEBUG "ucb1x00-switches.c -> _switches_resume() kswd - restart *DONE*\n"); ++ } ++ return 0; ++} ++#else ++#define ucb1x00_switches_resume NULL ++#endif ++ ++static struct ucb1x00_driver ucb1x00_switches_driver = { ++ .add = ucb1x00_switches_add, ++ .remove = ucb1x00_switches_remove, ++ .resume = ucb1x00_switches_resume, ++}; ++ ++static int __init ucb1x00_switches_init(void) ++{ ++ return ucb1x00_register_driver(&ucb1x00_switches_driver); ++} ++ ++static void __exit ucb1x00_switches_exit(void) ++{ ++ ucb1x00_unregister_driver(&ucb1x00_switches_driver); ++} ++ ++module_init(ucb1x00_switches_init); ++module_exit(ucb1x00_switches_exit); ++ ++MODULE_AUTHOR("Bernhard Guillon <Bernhard.Guillon@opensimpad.org>"); ++MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad"); ++MODULE_LICENSE("GPL"); diff --git a/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-ts-supend-and-accuracy.patch b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-ts-supend-and-accuracy.patch new file mode 100644 index 0000000000..702bbfbfba --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-ts-supend-and-accuracy.patch @@ -0,0 +1,121 @@ +diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-ts.c linux-2.6.24/drivers/mfd/ucb1x00-ts.c +--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-ts.c 2008-01-24 23:58:37.000000000 +0100 ++++ linux-2.6.24/drivers/mfd/ucb1x00-ts.c 2008-02-20 21:21:51.000000000 +0100 +@@ -16,6 +16,10 @@ + * It is important to note that the signal connected to the ADCSYNC + * pin should provide pulses even when the LCD is blanked, otherwise + * a pen touch needed to unblank the LCD will never be read. ++ * ++ * mrdata: -added some accuracy improvement based on thesings collie patch ++ * -added suspend fix ++ * + */ + #include <linux/module.h> + #include <linux/moduleparam.h> +@@ -104,6 +108,8 @@ + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ++ udelay(55); ++ + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); + } + } +@@ -130,7 +136,7 @@ + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + +- udelay(55); ++ udelay(165); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); + } +@@ -158,7 +164,7 @@ + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + +- udelay(55); ++ udelay(165); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); + } +@@ -216,13 +222,17 @@ + signed long timeout; + + ts->restart = 0; +- ++ + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ucb1x00_adc_enable(ts->ucb); + y = ucb1x00_ts_read_ypos(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ucb1x00_adc_enable(ts->ucb); + p = ucb1x00_ts_read_pressure(ts); +- ++ + /* + * Switch back to interrupt mode. + */ +@@ -231,15 +241,19 @@ + + msleep(10); + ++ if ((x < 60) || (y < 60)) { ++ p = 0; ++ } ++ + ucb1x00_enable(ts->ucb); + +- + if (ucb1x00_ts_pen_down(ts)) { ++ + set_task_state(tsk, TASK_INTERRUPTIBLE); + + ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING); + ucb1x00_disable(ts->ucb); +- ++ + /* + * If we spat out a valid sample set last time, + * spit out a "pen off" sample here. +@@ -250,7 +264,9 @@ + } + + timeout = MAX_SCHEDULE_TIMEOUT; ++ + } else { ++ + ucb1x00_disable(ts->ucb); + + /* +@@ -269,6 +285,14 @@ + + try_to_freeze(); + ++ /* ++ * While suspend the ktsd-thread goes sleep -> try_to_freeze() ++ * While resume the ktsd-thread do wakup and must rune one time ++ * again to do a clean re-setup -> enable_irq: UCB_IRQ_TSPX ++ */ ++ if(ts->restart) ++ timeout = HZ / 100; ++ + schedule_timeout(timeout); + } + +@@ -351,8 +375,12 @@ + * TS interrupt mode is set up again + * after sleep. + */ ++ + ts->restart = 1; + wake_up(&ts->irq_wait); ++ ++ printk(KERN_INFO "ucb1x00-ts.c -> ucb1x00_ts_resume() ktsd - restart *DONE*\n"); ++ + } + return 0; + } diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-GPIO-MMC-mod.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-GPIO-MMC-mod.patch new file mode 100644 index 0000000000..deacad37b0 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-GPIO-MMC-mod.patch @@ -0,0 +1,1701 @@ +Index: linux-2.6.27/drivers/mmc/card/Makefile +=================================================================== +--- linux-2.6.27.orig/drivers/mmc/card/Makefile 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/mmc/card/Makefile 2008-12-04 00:49:46.888896871 +0100 +@@ -6,6 +6,9 @@ + EXTRA_CFLAGS += -DDEBUG + endif + ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++# nothing to do ++else + obj-$(CONFIG_MMC_BLOCK) += mmc_block.o + mmc_block-objs := block.o queue.o + obj-$(CONFIG_MMC_TEST) += mmc_test.o +Index: linux-2.6.27/drivers/mmc/core/Makefile +=================================================================== +--- linux-2.6.27.orig/drivers/mmc/core/Makefile 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/mmc/core/Makefile 2008-12-04 00:50:42.755561658 +0100 +@@ -6,6 +6,9 @@ + EXTRA_CFLAGS += -DDEBUG + endif + ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++# nothing to do ++else + obj-$(CONFIG_MMC) += mmc_core.o + mmc_core-y := core.o bus.o host.o \ + mmc.o mmc_ops.o sd.o sd_ops.o \ +Index: linux-2.6.27/drivers/mmc/host/Kconfig +=================================================================== +--- linux-2.6.27.orig/drivers/mmc/host/Kconfig 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/mmc/host/Kconfig 2008-12-04 00:53:18.968875446 +0100 +@@ -4,6 +4,7 @@ + + comment "MMC/SD Host Controller Drivers" + ++ + config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA +@@ -153,6 +154,13 @@ + + If unsure, or if your system has no SPI master driver, say N. + ++config MMC_SPI_BLOCK ++ tristate "MMC/SD over GPIO (Software SPI) for SIMpad (EXPERIMENTAL)" ++ depends on SA1100_SIMPAD && EXPERIMENTAL ++ help ++ Say Y here to enable MMC block device over GPIO ++ if you have done the MMC-Mod. For Module say M. ++ + config MMC_S3C + tristate "Samsung S3C SD/MMC Card Interface support" + depends on ARCH_S3C2410 && MMC +Index: linux-2.6.27/drivers/mmc/host/Makefile +=================================================================== +--- linux-2.6.27.orig/drivers/mmc/host/Makefile 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/mmc/host/Makefile 2008-12-04 00:54:44.972204850 +0100 +@@ -6,6 +6,9 @@ + EXTRA_CFLAGS += -DDEBUG + endif + ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++obj-$(CONFIG_MMC_SPI_BLOCK) += mmc_spi_block.o ++else + obj-$(CONFIG_MMC_ARMMMCI) += mmci.o + obj-$(CONFIG_MMC_PXA) += pxamci.o + obj-$(CONFIG_MMC_IMX) += imxmmc.o +@@ -22,4 +25,5 @@ + obj-$(CONFIG_MMC_S3C) += s3cmci.o + obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o + obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o ++endif + +Index: linux-2.6.27/drivers/mmc/host/mmc_spi_block.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/mmc/host/mmc_spi_block.c 2008-12-04 00:55:17.229964087 +0100 +@@ -0,0 +1,1622 @@ ++/* ++ * Copyright (c) Cl�ent Ballabriga, 2005 - GPL ++ * Copyright (c) Guylhem Aznar, 2005 - GPL ++ * ++ * Please check http://externe.net/zaurus/simpad-bluetooth reference design first. ++ * ++ * Based on Madsuk/Rohde work on a MMC driver for the WRT54G. ++ * ++ * This is an ugly hack of a driver. I am surprised if it ever works! ++ * So please use a real driver or contribute one to the 2.4/2.6 mmc framework ++ * ++ * mrdata: ported to 2.6 ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#include <linux/sched.h> ++#include <linux/kernel.h> ++#include <linux/fs.h> ++#include <linux/errno.h> ++#include <linux/hdreg.h> ++#include <linux/kdev_t.h> ++#include <linux/blkdev.h> ++#include <linux/spinlock.h> ++#include <linux/time.h> ++#include <linux/delay.h> ++#include <linux/timer.h> ++ ++#include <linux/platform_device.h> ++ ++#include <asm/hardware.h> ++#include <asm/arch/simpad.h> ++#include <asm/arch/gpio.h> ++ ++static int major = 121; ++ ++#define DEVICE_NAME "mmc_spi" ++ ++static int hd_sizes[1<<6]; ++static int hd_blocksizes[1<<6]; ++static int hd_hardsectsizes[1<<6]; ++static int hd_maxsect[1<<6]; ++static struct hd_struct hd[1<<6]; ++ ++static struct gendisk *mmc_disk; ++ ++static struct platform_device *mmc_dev; /* the one and only instance */ ++ ++static spinlock_t mmc_spi_lock; ++ ++/* ++ * ******************************************************************* ++ * ++ * This is the only configurable part. ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++// #define DEBUG 1 ++// #define DEBUG_HD 1 ++// #define CHECK_MEDIA_CHANGE // for developement ONLY, not working yet ++ ++/* Let that include where it is or compilation fails on INIT_REQUEST/CURRENT */ ++ ++ ++/* ++ * If you are using different GPIOs in your hardware hack, you must ++ * first make sure they are unused for other functions and then ++ * configure them here. ++ * ++ * On the simpad I use spare pins from the UART1 (internal serial port -> DECT 20-polig): ++ * ++ * Funktion PIN ## Original direction GPIO ## SPI function New direction SD/MMC ++ * - DCD PIN 08 (in) GPIO 23 DO - new name: DI -> MISO (in) PIN 7 Data Out ++ * - DTR PIN 11 (out) GPIO 07 CS (out) PIN 1 Chip Select ++ * - RI PIN 14 (in) GPIO 19 CLK (out) PIN 5 Clock ++ * - DSR PIN 16 (in) GPIO 06 DI - new name: DO -> MOSI (out) PIN 2 Data In ++ * ++ * ++ * SPI: MISO = Master In / Slave OUT MOSI = Master Out / Slave In ++ * ++ * Don't worry about in/out original function - the GPIOs will be ++ * reprogrammed. ++ */ ++ ++#define GPIO_SD_DI 23 ++#define GPIO_SD_CS 7 ++#define GPIO_SD_CLK 19 ++#define GPIO_SD_DO 6 ++ ++// #define FAST_GPIO_SD_DI GPIO_GPIO23 ++// #define FAST_GPIO_SD_CS GPIO_GPIO7 ++// #define FAST_GPIO_SD_CLK GPIO_GPIO19 ++// #define FAST_GPIO_SD_DO GPIO_GPIO6 ++ ++#define FAST_GPIO_SD_DI GPIO_UART1_DCD ++#define FAST_GPIO_SD_CS GPIO_UART1_DTR ++#define FAST_GPIO_SD_CLK GPIO_UART1_RI ++#define FAST_GPIO_SD_DO GPIO_UART1_DSR ++ ++/* ++ * ******************************************************************* ++ * ++ * Do not change anything below ! ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++/* GPIO states */ ++#define LOW 0 ++#define HIGH 1 ++ ++#define INPUT 0 ++#define OUTPUT 1 ++ ++#define PRESENT 1 ++#define ABSENT 0 ++ ++typedef unsigned int uint32; ++typedef unsigned long u32_t; ++typedef unsigned short u16_t; ++typedef unsigned char u8_t; ++ ++// static struct timer_list mmc_timer; ++ ++// static struct timeval s_zeit, e_zeit; ++ ++/* start with no card */ ++static int mmc_media_detect = 0; ++// static int mmc_media_changed = 1; ++ ++ ++///////////////////// ++// prototypes ++static int mmc_open(struct inode *inode, struct file *filp); ++static int mmc_release(struct inode *inode, struct file *filp); ++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); ++static void mmc_spi_request(struct request_queue *q); ++ ++ ++/* ++ * ******************************************************************* ++ * ++ * Begin GPIO hardware access functions. ++ * ++ * ******************************************************************* ++ * ++ */ ++ ++#define gpio_read(a) ((GPLR & a) ? 1 : 0) ++#define gpio_write_high(a) GPSR = a ++#define gpio_write_low(a) GPCR = a ++ ++/* set MMC_Chip_Select to HIGH (MMC/SD-Card inactiv) */ ++#define MMC_Disable() gpio_write_high( FAST_GPIO_SD_CS) ++ ++/* set MMC_Chip_Select to LOW (MMC/SD-Card activ) */ ++#define MMC_Enable() gpio_write_low( FAST_GPIO_SD_CS) ++ ++/* ++ * ******************************************************************* ++ * ++ * Begin SPI hardware access functions. ++ * ++ * ******************************************************************* ++ * ++ */ ++static int mmc_spi_media_detect(void) ++{ ++// FIXME: add card detection/test by SPI ++ ++ return 1; ++} ++ ++static int mmc_spi_hardware_init(void) ++{ ++ printk("\nmmc: GPIO init\n"); ++ ++ /* cut existing functions */ ++ gpio_set_alternative_function(GPIO_SD_CLK, 0); ++ gpio_set_alternative_function(GPIO_SD_DI, 0); ++ gpio_set_alternative_function(GPIO_SD_DO, 0); ++ gpio_set_alternative_function(GPIO_SD_CS, 0); ++ ++ /* remap directions and set state of spi pins */ ++ gpio_direction_output(GPIO_SD_CLK, 0); ++ gpio_direction_input(GPIO_SD_DI); ++ gpio_direction_output(GPIO_SD_DO, 0); ++ gpio_direction_output(GPIO_SD_CS, 0); ++ ++ printk("mmc: initialising MMC\n"); ++ ++ /* Start */ ++ MMC_Disable(); ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ gpio_write_high( FAST_GPIO_SD_DO); ++ return 0; ++} ++ ++/* return what has been read, write the parameter */ ++/* Clockrate round about 1,2 MHz */ ++ ++static unsigned char mmc_spi_readwrite(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ return (result); ++} ++ ++/* return what has been read, write the parameter */ ++/* Clockrate round 200 kHz */ ++ ++static unsigned char mmc_spi_readwrite_slow(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ udelay(10); ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ udelay(10); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ udelay(10); ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ udelay(10); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ udelay(10); ++ ++ // printk("Send Byte = 0x%2X Receive Byte = 0x%2X \n", data_out, result); ++ ++ return (result); ++} ++ ++/* return what has been read */ ++ ++static unsigned char mmc_spi_read_only(void) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ if (gpio_read( FAST_GPIO_SD_DI) == 1) ++ { ++ result |= i; ++ } ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ return (result); ++} ++ ++/* write the parameter */ ++/* Clockrate round about 3,6 MHz */ ++ ++static unsigned char mmc_spi_write_only(unsigned char data_out) ++{ ++ unsigned char i; ++ unsigned char result = 0; ++ ++ for(i = 0x80 ; i != 0 ; i >>= 1) ++ { ++ ++ if (data_out & i) ++ { ++ gpio_write_high( FAST_GPIO_SD_DO); ++ } ++ else ++ { ++ gpio_write_low( FAST_GPIO_SD_DO); ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_CLK); ++ ++ gpio_write_low( FAST_GPIO_SD_CLK); ++ ++ } ++ ++ gpio_write_high( FAST_GPIO_SD_DO); ++ ++ return (result); ++} ++ ++ ++/** ++ * this function was contributed by: rcichielo from openwrt forums ++ * ++ * Comments added by Marc DENTY on 2007-03-20 ++ * ++ * Sequence to read a card's "CID" bytes (name, serial number etc) ++ * ++ * Send: 4ah,00h,00h,00h,00h,00h - CMD10, no args, null CRC ++ * Read: xx - NCR Time ++ * Read: xx - Command Response (Should be 00h) ++ * Read: until FEh is received - Wait for Data token ++ * Read: yy * 16 - Get 16 bytes from CID ++ * Read: zz - Read CRC lo byte ++ * Read: zz - Read CRC hi byte ++ * ++ * Useful locations in the returned data packet: ++ * ++ * 03h-08h Manufacturers's name in ascii ++ * 0ah-0dh Card's 32 bit serial number ++ */ ++/** ++ * Comments added by Cyril CATTIAUX on 2007-03-21 ++ * ++ * CID format specification (from Sandisk SD Product Manual v1.9) ++ * ++ * cid[00 ] Manufacturer ID (unsigned byte) ++ * cid[01-02] OEM/Application ID (ASCII) ++ * cid[03-07] Product Name (ASCII) ++ * cid[08 ] Product Revistion (BCD coded number) ++ * cid[09-12] Serial Number (32-bit unsigned int) ++ * cid[13-14] Reserved(bit 12->15) - Manufacture Date(bit 0->11) ++ * cid[15 ] CRC7(bit 1->7) - Not used, allways 1 (bit 0) ++*/ ++static int mmc_read_cid(unsigned char *cid) ++{ ++ unsigned char result = 0; ++ int i; ++ ++ MMC_Enable(); ++ ++ /* wait */ ++ for (i = 0; i < 4; i++) ++ { ++ result=mmc_spi_readwrite(0xff); ++ } ++ ++ /* issue CID (card identification data) read request */ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0x40 | 10); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x00); ++ mmc_spi_readwrite(0x95); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result=mmc_spi_readwrite(0xff); ++ ++ if(result == 0x00) ++ break; ++ } ++ ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return(1); ++ } ++ ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xfe) break; ++ } ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return(2); ++ } ++ ++ for (i = 0; i < 16; i++) { ++ result = mmc_spi_readwrite(0xff); ++ cid[i] = result; ++ } ++ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0xff); ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ ++ return 0; ++} ++ ++ ++/** ++ * Comments added by Cyril CATTIAUX on 2007-03-21 ++ * ++ * CID format specification (from Sandisk SD Product Manual v1.9) ++ * ++ * cid[00 ] Manufacturer ID (unsigned byte) ++ * cid[01-02] OEM/Application ID (ASCII) ++ * cid[03-07] Product Name (ASCII) ++ * cid[08 ] Product Revision (BCD coded 2 digit number) ++ * cid[09-12] Serial Number (32-bit unsigned int) ++ * cid[13-14] Manufacture Date(bit 0->11) (BCD coded 3 digit number YYM offset from 2000) - Reserved(bit 12->15) ++ * cid[15 ] Not used, allways 1 (bit 0) - CRC7(bit 1->7) ++*/ ++static void mmc_show_cid_info(void) ++{ ++ int i, result; ++ unsigned short tmps; ++ unsigned char cid[16]; ++ ++ char manufacturer_id; ++ char oem_id[3]; ++ char product_name[6]; ++ unsigned char product_revision_h, product_revision_l; ++ unsigned int product_sn; ++ unsigned short product_date_y; ++ unsigned char product_date_m; ++ ++ result = mmc_read_cid(cid); ++ ++ if (result == 0) ++ { ++ printk("mmc_init: MMC/SD Card ID: "); ++ for (i=0; i<16; i++) { ++ printk("%02X ", cid[i]); ++ } ++ manufacturer_id=cid[0]; ++ strncpy(oem_id, &cid[1], 2); ++ oem_id[2]='\0'; ++ strncpy(product_name, &cid[3], 5); ++ product_name[5]='\0'; ++ product_revision_h=(cid[8] >> 4) & 0xf; ++ product_revision_l=cid[8] & 0xf; ++ product_sn=(cid[9]<<24) + (cid[10]<<16) + (cid[11]<<8) + cid[12]; ++ tmps=((cid[13]<<8) + cid[14]) & 0x0fff; ++ product_date_y=2000 + (((tmps >> 8) & 0xf) * 10) + ((tmps >> 4) & 0xf); ++ product_date_m=tmps & 0xf; ++ ++ printk("\nManufacturer ID : %02X\n", manufacturer_id); ++ printk("OEM/Application ID: %s\n", oem_id); ++ printk("Product name : %s\n", product_name); ++ printk("Product revision : %d.%d\n", product_revision_h, product_revision_l); ++ printk("Product SN : %08X\n", product_sn); ++ printk("Product Date : %d-%d\n", product_date_y, product_date_m); ++ ++ } else { ++ printk("mmc_init: impossible to get card indentification info for reason code: %02x", result); ++ } ++} ++ ++ ++/* ++static int mmc_spi_hw_test(void) ++{ ++ unsigned char result, k; ++ ++ unsigned int i, j, t; ++ ++ printk("mmc_spi_hw_test -> \n\n"); ++ k = 0x55; ++ for ( i = 0 ; i < 5; i++) { ++ ++ printk("\n0x%2X - ", k); ++ for ( j = 0 ; j < 8; j++ ) { ++ do_gettimeofday( &s_zeit ); ++ result = mmc_spi_readwrite_slow(k); ++ do_gettimeofday( &e_zeit ); ++ ++ if ( result != k ) { ++ printk("!>ERROR<! Transfer = 0x%2X Receive = 0x%2X Trail = %d \n", k, result, j); ++ // i = 255; j = 1000; ++ } ++ ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("Durchlauf: %i Versuch: %i von 8 -> Laufzeit: 0x%X s\n", i , j, t); ++ udelay(200); ++ } ++ printk("ready "); ++ ++ // k++; ++ } ++ printk("ready "); ++ printk("\n\n"); ++ return (0); ++} ++*/ ++ ++/* ++static int mmc_spi_speed_test(void) ++{ ++ unsigned int i, j, k, l, t; ++ ++ MMC_Disable(); ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_readwrite(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_readwrite: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 1; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 1; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_readwrite_slow(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_readwrite_slow: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_read_only(); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_read_only: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ for (k = 1; k < 6; k++) ++ { ++ l = 10000 * k; ++ for (j = 0; j < 5; j++) ++ { ++ do_gettimeofday( &s_zeit ); ++ for (i = 0; i < l; i++) ++ mmc_spi_write_only(0xff); ++ do_gettimeofday( &e_zeit ); ++ t = (e_zeit.tv_sec-s_zeit.tv_sec)*1000000+(e_zeit.tv_usec-s_zeit.tv_usec); ++ printk("mmc_spi_write_only: Laufzeit %u x : 0x%X \n", l, t); ++ } ++ } ++ ++ return (1); ++ ++} ++*/ ++ ++ ++static int mmc_spi_card_init(void) ++{ ++ unsigned char result = 0; ++ short i, j; ++ ++// unsigned long flags; ++ ++ // save_flags(flags); ++ // cli(); ++ ++/* ++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CS), gpio_getalt(&gp, GPIO_SD_CS)); ++ printk("GPIO_SD_DI dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DI), gpio_getalt(&gp, GPIO_SD_DI)); ++ printk("GPIO_SD_DO dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DO), gpio_getalt(&gp, GPIO_SD_DO)); ++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CLK), gpio_getalt(&gp, GPIO_SD_CLK)); ++*/ ++ ++ // printk("\nmmc: mmc_spi_hw_test() *START*\n"); ++ ++ // mmc_spi_hw_test(); ++ ++ printk("\nmmc: card init 1/2 (CMD0)\n"); ++ ++ for (j = 0; j < 10; j++) ++ { ++ MMC_Disable(); ++ ++ for (i = 0; i < 10; i++) ++ mmc_spi_readwrite_slow(0xff); ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite_slow(0xff); ++ ++ mmc_spi_readwrite_slow(0x40); ++ ++ for (i = 0; i < 4; i++) { ++ ++ mmc_spi_readwrite_slow(0x00); ++ ++ } ++ ++ mmc_spi_readwrite_slow(0x95); ++ ++ for (i = 0; i < 8; i++) { ++ ++ result = mmc_spi_readwrite_slow(0xff); ++ ++#ifdef DEBUG_HD ++ if (result != 0xff) { ++ if (result > 0x1F && result < 0x80) ++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ else ++ printk("mmc: resp. (CMD0) Versuch(%d) BYTE: %d result = 0x%X\n", j, i, result); ++ } ++#endif ++ if (result == 0x01) ++ break; ++ } ++ ++ if (result == 0x01) ++ break; ++ ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ ++ mdelay(60); ++ } ++ ++ if (result != 0x01) { ++ ++ printk("mmc: card init 1/2 error: %d (CMD0) failed\n", result); ++ printk(" -> Hint: MMC/SD-Card realy (fully) inserted ?\n"); ++ ++ return (1); ++ } ++ ++ printk("mmc: card init 1/2 (CMD0) success\n\n"); ++ ++ mdelay(1); ++ ++ printk("mmc: card init 2/2 (CMD1)\n"); ++ for (j = 0; j < 10; j++) { ++ ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x41); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // if (result >= 32 && result <= 127) ++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ // else ++ // printk("mmc: response (CMD1) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ ++ mmc_spi_readwrite_slow(0xff); ++ ++ if (result == 0x00) { ++ printk("mmc: card init 2/2 (CMD1) success\n\n"); ++ ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x4d); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 6; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // if (result > 31 && result < 128) ++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X Zeichen = %c\n", j, i, result, result); ++ // else ++ // printk("mmc: response (CMD13) Versuch(%d) start token BYTE: %d result = 0x%X\n", j, i, result); ++#endif ++ // if (result == 0x00) ++ // break; ++ } ++ // mdelay(60); ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ // mdelay(10); ++ ++ // restore_flags(flags); ++ ++ return (0); ++ } ++ mdelay(60); ++ } ++ return (2); ++} ++ ++ ++static int mmc_spi_card_config(void) ++{ ++ unsigned char result = 0; ++ short i, j; ++ unsigned char csd[32]; ++ unsigned int c_size; ++ unsigned int c_size_mult; ++ unsigned int mult; ++ unsigned int read_bl_len; ++ unsigned int blocknr = 0; ++ unsigned int block_len = 0; ++ unsigned int size = 0; ++ unsigned char rd_buffer[528]; ++// unsigned long flags; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite_slow(0xff); ++ result = mmc_spi_readwrite_slow(0x51); ++ // mmc_spi_readwrite_slow(0x4A); ++ // mmc_spi_readwrite_slow(0x40+0x0D); ++ // mmc_spi_readwrite_slow(0x42); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ ++ // printk("mmc: (CMD17) response von 0x51 result = 0x%X\n", result); ++ ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // printk("mmc: (CMD17) response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // restore_flags(flags); ++ // mmc_spi_readwrite_slow(0xff); ++ return (1); ++ } ++ // restore_flags(flags); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ rd_buffer[i] = result; ++#ifdef DEBUG_HD ++ /* ++ if (result >= 32 && result <= 127) ++ printk("mmc: CMD17 response (start token) result = 0x%X Zeichen = %c\n", result, result); ++ else ++ printk("mmc: CMD17 response (start token) result = 0x%X\n", result); ++ */ ++#endif ++ // if (result == 0xfe) ++ // break; ++ } ++ /* ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0xff); ++ return(1); ++ } ++ */ ++ ++ for (i = 8; i < 520; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ rd_buffer[i] = result; ++ } ++ for (i = 0; i < 2; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ } ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ ++ printk("Buffer - Start\n"); ++ ++ for ( i = 0 ; i < 33 ; i++) { ++ printk("\r\n%4X - ", i*16); ++ for ( j = 0 ; j < 16 ; j++) { ++ if ( rd_buffer[i*16+j] < 16) ++ printk("0%X ", rd_buffer[i*16+j]); ++ else ++ printk("%2X ", rd_buffer[i*16+j]); ++ } ++ for ( j = 0 ; j < 16 ; j++) { ++ if ( rd_buffer[i*16+j] < ' ') ++ printk("."); ++ else ++ printk("%c", rd_buffer[i*16+j]); ++ } ++ } ++ ++ printk("\nBuffer - Ende\n"); ++ ++ mmc_show_cid_info(); ++ ++ for(j = 0 ; j < 1; j++) { ++ MMC_Enable(); ++ ++ // mdelay(1); ++ ++ // save_flags(flags); ++ // cli(); ++ mmc_spi_readwrite_slow(0xff); ++ mmc_spi_readwrite_slow(0x49); ++ // mmc_spi_readwrite_slow(0x4A); ++ // mmc_spi_readwrite_slow(0x40+0x0D); ++ // mmc_spi_readwrite_slow(0x42); ++ for (i = 0; i < 4; i++) ++ mmc_spi_readwrite_slow(0x00); ++ mmc_spi_readwrite_slow(0x95); ++ for (i = 0; i < 8; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ // printk("mmc: (CMD9) response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0x00) ++ break; ++ } ++ // restore_flags(flags); ++ if (result != 0x00) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ return (1); ++ } ++ for (i = 0; i < 22; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++#ifdef DEBUG_HD ++ if (result >= 32 && result <= 127) ++ printk("mmc: response (start token) result = 0x%X Zeichen = %c\n", result, result); ++ else ++ printk("mmc: response (start token) result = 0x%X\n", result); ++#endif ++ if (result == 0xfe) ++ break; ++ } ++ if (result == 0xfe) ++ break; ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ } ++ mdelay(60); ++ } ++ ++ if (result != 0xfe) { ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ printk("mmc: mmc card config (CMD9) failed result = 0x%X\n\n", result); ++ return (2); ++ } ++ for (i = 0; i < 16; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ csd[i] = result; ++ } ++ for (i = 0; i < 2; i++) { ++ result = mmc_spi_readwrite_slow(0xff); ++ } ++ MMC_Disable(); ++ mmc_spi_readwrite_slow(0xff); ++ // mmc_spi_readwrite_slow(0xff); ++ ++ if (result == 0x00) ++ return (3); ++ ++ c_size = (csd[8] & 0xC0) + (csd[7] << 8) + ((csd[6] & 0x03) << 16); ++ c_size >>= 6; ++ c_size_mult = (csd[10] & 0x80) + ((csd[9] & 0x03) << 8); ++ c_size_mult >>= 7; ++ read_bl_len = csd[5] & 0x0f; ++ mult = 1; ++ mult <<= c_size_mult + 2; ++ blocknr = (c_size + 1) * mult; ++ block_len = 1; ++ block_len <<= read_bl_len; ++ size = block_len * blocknr; ++ size >>= 10; ++ ++ for (i = 0; i < (1 << 6); i++) { ++ hd_blocksizes[i] = 1024; ++ hd_hardsectsizes[i] = block_len; ++ hd_maxsect[i] = 256; ++ } ++ hd_sizes[0] = size; ++ hd[0].nr_sects = blocknr; ++ ++ printk("Size = %d, hardsectsize = %d, sectors = %d\n", ++ size, block_len, blocknr); ++ ++ return 0; ++} ++ ++ ++/* ++ * ******************************************************************* ++ * ++ * End of SPI hardware access functions. ++ * ++ * ******************************************************************* ++ */ ++ ++ ++static int mmc_spi_write_block(unsigned int dest_addr, unsigned char *data) ++{ ++ unsigned int address; ++ unsigned char result = 0; ++ unsigned char ab0, ab1, ab2, ab3; ++ int i; ++ ++ address = dest_addr; ++ ++ ab3 = 0xff & (address >> 24); ++ ab2 = 0xff & (address >> 16); ++ ab1 = 0xff & (address >> 8); ++ ab0 = 0xff & address; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite(0xff); ++ ++ mmc_spi_readwrite(0x58); ++ mmc_spi_readwrite(ab3); /* msb */ ++ mmc_spi_readwrite(ab2); ++ mmc_spi_readwrite(ab1); ++ mmc_spi_readwrite(ab0); /* lsb */ ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0x00) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0x00) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (1); ++ } ++ ++ mmc_spi_readwrite(0xfe); ++ ++ for (i = 0; i < 512; i += 32) ++ { ++ mmc_spi_write_only(data[i]); ++ mmc_spi_write_only(data[i+1]); ++ mmc_spi_write_only(data[i+2]); ++ mmc_spi_write_only(data[i+3]); ++ mmc_spi_write_only(data[i+4]); ++ mmc_spi_write_only(data[i+5]); ++ mmc_spi_write_only(data[i+6]); ++ mmc_spi_write_only(data[i+7]); ++ mmc_spi_write_only(data[i+8]); ++ mmc_spi_write_only(data[i+9]); ++ mmc_spi_write_only(data[i+10]); ++ mmc_spi_write_only(data[i+11]); ++ mmc_spi_write_only(data[i+12]); ++ mmc_spi_write_only(data[i+13]); ++ mmc_spi_write_only(data[i+14]); ++ mmc_spi_write_only(data[i+15]); ++ mmc_spi_write_only(data[i+16]); ++ mmc_spi_write_only(data[i+17]); ++ mmc_spi_write_only(data[i+18]); ++ mmc_spi_write_only(data[i+19]); ++ mmc_spi_write_only(data[i+20]); ++ mmc_spi_write_only(data[i+21]); ++ mmc_spi_write_only(data[i+22]); ++ mmc_spi_write_only(data[i+23]); ++ mmc_spi_write_only(data[i+24]); ++ mmc_spi_write_only(data[i+25]); ++ mmc_spi_write_only(data[i+26]); ++ mmc_spi_write_only(data[i+27]); ++ mmc_spi_write_only(data[i+28]); ++ mmc_spi_write_only(data[i+29]); ++ mmc_spi_write_only(data[i+30]); ++ mmc_spi_write_only(data[i+31]); ++ } ++ ++ mmc_spi_readwrite(0xff); ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 1000000; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xff) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0xff) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (3); ++ } ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (0); ++} ++ ++static int mmc_spi_read_block(unsigned char *data, unsigned int src_addr) ++{ ++ unsigned int address; ++ unsigned char result = 0; ++ unsigned char ab0, ab1, ab2, ab3; ++ // unsigned long flags; ++ // int i, j; ++ int i; ++ ++ address = src_addr; ++ ++ ab3 = 0xff & (address >> 24); ++ ab2 = 0xff & (address >> 16); ++ ab1 = 0xff & (address >> 8); ++ ab0 = 0xff & address; ++ ++ MMC_Enable(); ++ ++ mmc_spi_readwrite(0xff); ++ ++ mmc_spi_readwrite(0x51); ++ mmc_spi_readwrite(ab3); /* msb */ ++ mmc_spi_readwrite(ab2); ++ mmc_spi_readwrite(ab1); ++ mmc_spi_readwrite(ab0); /* lsb */ ++ mmc_spi_readwrite(0xff); ++ ++ for (i = 0; i < 8; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0x00) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0x00) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (1); ++ } ++ ++ for (i = 0; i < 100000; i++) ++ { ++ result = mmc_spi_readwrite(0xff); ++ if (result == 0xfe) ++ { ++ break; ++ } ++ } ++ ++ if (result != 0xfe) ++ { ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ return (2); ++ } ++ ++ for (i = 0; i < 512; i += 32 ) ++ { ++ data[i] = mmc_spi_read_only(); ++ data[i+1] = mmc_spi_read_only(); ++ data[i+2] = mmc_spi_read_only(); ++ data[i+3] = mmc_spi_read_only(); ++ data[i+4] = mmc_spi_read_only(); ++ data[i+5] = mmc_spi_read_only(); ++ data[i+6] = mmc_spi_read_only(); ++ data[i+7] = mmc_spi_read_only(); ++ data[i+8] = mmc_spi_read_only(); ++ data[i+9] = mmc_spi_read_only(); ++ data[i+10] = mmc_spi_read_only(); ++ data[i+11] = mmc_spi_read_only(); ++ data[i+12] = mmc_spi_read_only(); ++ data[i+13] = mmc_spi_read_only(); ++ data[i+14] = mmc_spi_read_only(); ++ data[i+15] = mmc_spi_read_only(); ++ data[i+16] = mmc_spi_read_only(); ++ data[i+17] = mmc_spi_read_only(); ++ data[i+18] = mmc_spi_read_only(); ++ data[i+19] = mmc_spi_read_only(); ++ data[i+20] = mmc_spi_read_only(); ++ data[i+21] = mmc_spi_read_only(); ++ data[i+22] = mmc_spi_read_only(); ++ data[i+23] = mmc_spi_read_only(); ++ data[i+24] = mmc_spi_read_only(); ++ data[i+25] = mmc_spi_read_only(); ++ data[i+26] = mmc_spi_read_only(); ++ data[i+27] = mmc_spi_read_only(); ++ data[i+28] = mmc_spi_read_only(); ++ data[i+29] = mmc_spi_read_only(); ++ data[i+30] = mmc_spi_read_only(); ++ data[i+31] = mmc_spi_read_only(); ++ } ++ ++ result = mmc_spi_readwrite(0xff); ++ result = mmc_spi_readwrite(0xff); ++ ++ MMC_Disable(); ++ mmc_spi_readwrite(0xff); ++ ++ return (0); ++} ++ ++/* ++static int mmc_revalidate(kdev_t dev) ++{ ++ int target, max_p, start, i; ++ ++ mmc_media_detect = mmc_spi_media_detect(); ++ ++ if (mmc_media_detect == 0) ++ { ++ return -ENODEV; ++ } ++ ++ target = DEVICE_NR(dev); ++ ++ max_p = hd_gendisk.max_p; ++ start = target << 6; ++ for (i = max_p - 1; i >= 0; i--) ++ { ++ int minor = start + i; ++ invalidate_device(MKDEV(MAJOR_NR, minor), 1); ++ hd_gendisk.part[minor].start_sect = 0; ++ hd_gendisk.part[minor].nr_sects = 0; ++ } ++ ++ grok_partitions(&hd_gendisk, target, 1 << 6, hd_sizes[0] * 2); ++ ++ return 0; ++} ++*/ ++ ++ ++static int mmc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ if (!inode || !inode->i_rdev) ++ return -EINVAL; ++ ++ switch (cmd) { ++#if 0 ++ case BLKGETSIZE: ++ return put_user(hd[MINOR(inode->i_rdev)].nr_sects, ++ (unsigned long *)arg); ++ case BLKGETSIZE64: ++ return put_user((u64) hd[MINOR(inode->i_rdev)]. ++ nr_sects, (u64 *) arg); ++ case BLKRRPART: ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ return mmc_revalidate(inode->i_rdev); ++#endif ++ case HDIO_GETGEO: ++ { ++ struct block_device *bdev = inode->i_bdev; ++ struct hd_geometry *loc, g; ++ loc = (struct hd_geometry *)arg; ++ if (!loc) ++ return -EINVAL; ++ memset(loc, 0, sizeof(struct hd_geometry)); ++ g.heads = 4; ++ g.sectors = 16; ++ g.cylinders = get_capacity(bdev->bd_disk) / (4*16); ++ g.start = get_start_sect(bdev); ++ return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0; ++ } ++ default: ++ return -ENOTTY; ++ } ++} ++ ++ ++/* ++static int mmc_check_media_change(kdev_t dev) ++{ ++ (void) dev; ++ if (mmc_media_changed == 1) ++ { ++ mmc_media_changed = 0; ++ return 1; ++ } ++ else ++ { ++ return 0; ++ } ++} ++*/ ++ ++ ++/* ++static int mmc_init(void) ++{ ++ int result; ++ ++ result = mmc_spi_hardware_init(); ++ ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_spi_hardware_init\n", result); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_hw_test(); ++ ++ if (result != 0) ++ { ++ printk("\n mmc: mmc_spi_hw_test i.O. \n\n"); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_speed_test(); ++ ++ if (result != 0) ++ { ++ printk("\n mmc: mmc_spi_speed_test i.O. \n\n"); ++ return -1; ++ } ++ ++ ++ result = mmc_spi_card_init(); ++ mdelay(50); ++ if (result != 0) ++ { ++ // Give it an extra shot ++ // result = mmc_spi_card_init(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_card_init\n", result); ++ return -1; ++ } ++ } ++ ++ result = mmc_spi_card_config(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_card_config\n", result); ++ return -1; ++ } ++ ++ ++ return 0; ++} ++*/ ++ ++/* ++static void mmc_exit(void) ++{ ++} ++*/ ++ ++ ++/* ++static void mmc_check_media(void) ++{ ++ int old_state, new_state; ++ int result; ++ ++ old_state = mmc_media_detect; ++ new_state = mmc_spi_media_detect(); ++ ++ if (old_state != new_state) ++ { ++ mmc_media_changed = 1; ++ if (new_state == PRESENT) ++ { ++ result = mmc_init(); ++ if (result != 0) ++ { ++ printk("mmc: error %d in mmc_init\n", result); ++ } ++ else ++ { ++ mmc_exit(); ++ } ++ } ++ } ++ ++#ifdef CHECK_MEDIA_CHANGE ++ del_timer(&mmc_timer); ++ mmc_timer.expires = jiffies + 10*HZ; ++ add_timer(&mmc_timer); ++#endif ++ ++} ++*/ ++ ++ ++/* NB: There might be several requests in the queue, simply dequeuing only one ++ and not checking for more will cause a stall because the block subsystem ++ will not call this function again unless the queue is "plugged" which can ++ only happen if it runs empty... */ ++static void mmc_spi_request(struct request_queue *q) ++{ ++ struct request *req; ++ int ret; ++ ++ unsigned int mmc_address; ++ unsigned char *buffer_address; ++ int nr_sectors; ++ int i; ++ int result, success; ++ ++ if (blk_queue_plugged(q)) { ++ return; ++ } ++ ++ spin_lock(&mmc_spi_lock); ++ for(;;) { ++ req = elv_next_request(q); ++ if (!req) ++ break; ++ ++ if (!blk_fs_request(req)) { ++ printk("not a blk_fs_request\n"); ++ spin_unlock(&mmc_spi_lock); ++ continue; ++ } ++ ++ mmc_address = req->sector * hd_hardsectsizes[0]; ++ buffer_address = req->buffer; ++ nr_sectors = req->current_nr_sectors; ++ success = 1; ++ if (rq_data_dir(req) == READ) { ++ spin_unlock_irq(q->queue_lock); ++ for (i = 0; i < nr_sectors; i++) { ++ result = mmc_spi_read_block(buffer_address, mmc_address); ++ if (unlikely(result < 0)) { ++ printk(KERN_ERR "mmi_spi_block: error reading block (%d)\n", result); ++ success = 0; ++ break; ++ } ++ mmc_address += hd_hardsectsizes[0]; ++ buffer_address += hd_hardsectsizes[0]; ++ } ++ spin_lock_irq(q->queue_lock); ++ } else { ++ spin_unlock_irq(q->queue_lock); ++ for (i = 0; i < nr_sectors; i++) { ++ result = mmc_spi_write_block(mmc_address, buffer_address); ++ if (unlikely(result < 0)) { ++ printk(KERN_ERR "mmi_spi_block: error writing block (%d)\n", result); ++ success = 0; ++ break; ++ } ++ mmc_address += hd_hardsectsizes[0]; ++ buffer_address += hd_hardsectsizes[0]; ++ } ++ spin_lock_irq(q->queue_lock); ++ } ++ ret = end_that_request_chunk(req, success, nr_sectors * hd_hardsectsizes[0]); ++ if (!ret) { ++ blkdev_dequeue_request(req); ++ end_that_request_last(req, 0); ++ } ++ } ++ spin_unlock(&mmc_spi_lock); ++} ++ ++ ++static int mmc_open(struct inode *inode, struct file *filp) ++{ ++ // int device; ++ (void) filp; ++ mmc_media_detect = mmc_spi_media_detect(); ++ ++ if (mmc_media_detect == 0) ++ return -ENODEV; ++ ++ return 0; ++} ++ ++static int mmc_release(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++ ++static struct block_device_operations mmc_spi_bdops = { ++ .open = mmc_open, ++ .release = mmc_release, ++ .ioctl = mmc_ioctl, ++ .owner = THIS_MODULE, ++#if 0 ++ .check_media_change = mmc_check_media_change, ++ .revalidate = mmc_revalidate, ++#endif ++}; ++ ++static int detect_card(void) ++{ ++ int result; ++ ++ result = mmc_spi_card_init(); ++ if (result != 0) { ++ // Give it an extra shot ++ result = mmc_spi_card_init(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_init (%d)\n", result); ++ return -ENODEV; ++ } ++ } ++ ++ result = mmc_spi_card_config(); ++ // result = mmc_spi_speed_test(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_card_config (%d)\n", result); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/* Fills in the gendisk structure from the received card ++ data. */ ++static int gendisk_init(struct device *dev, struct gendisk *gd) ++{ ++ if (!gd) ++ return -EINVAL; ++ ++ gd->major = major; ++ gd->first_minor = 0; /* only one device supported */ ++ gd->fops = &mmc_spi_bdops; ++ gd->driverfs_dev = dev; ++ ++ gd->queue = blk_init_queue(mmc_spi_request,NULL); ++ ++ if (!gd->queue) ++ return -ENOMEM; ++ ++ sprintf(gd->disk_name, "mmcblk"); ++ ++ blk_queue_hardsect_size(gd->queue, hd_hardsectsizes[0]); ++ ++ set_capacity(gd, hd_sizes[0]<<1); ++ ++ return 0; ++} ++ ++static int gendisk_fini(struct gendisk *gd) ++{ ++ BUG_ON(!gd); ++ ++ if (gd->queue) ++ blk_cleanup_queue(gd->queue); ++ ++ del_gendisk(gd); ++ ++ return 0; ++} ++ ++/* platform driver device instance routines */ ++static int mmc_spi_probe(struct platform_device *pdev) ++{ ++ int result; ++ printk("$Id: mmc_spi_block.c,v 1.05 2008/01/04 01:02:10 mrdata Exp $\n"); ++ ++ result = mmc_spi_hardware_init(); ++ if (result != 0) { ++ printk(KERN_ERR "mmc_spi_block: error in mmc_spi_hardware_init (%d)\n", result); ++ result = -ENODEV; ++ return result; ++ } ++ ++ result = detect_card(); ++ if (result < 0) ++ return result; ++ ++ mmc_media_detect = 1; ++ ++ result = register_blkdev(major, DEVICE_NAME); ++ if (result < 0) ++ return result; ++ ++ if (!major) ++ major = result; ++ ++ /* allow 8 partitions per device */ ++ BUG_ON(mmc_disk!=NULL); ++ mmc_disk = alloc_disk(1 << 3); ++ if (!mmc_disk) { ++ result = -ENOMEM; ++ goto out; ++ } ++ ++ result = gendisk_init(&pdev->dev, mmc_disk); ++ if (result < 0) ++ goto out; ++ ++ add_disk(mmc_disk); ++ ++ /*init_timer(&mmc_timer); ++ mmc_timer.expires = jiffies + HZ; ++ mmc_timer.function = (void *)mmc_check_media; ++ add_timer(&mmc_timer); */ ++ return 0; ++ ++out: ++ if (mmc_disk) ++ put_disk(mmc_disk); ++ ++ unregister_blkdev(major, DEVICE_NAME); ++ return result; ++} ++ ++static int mmc_spi_remove(struct platform_device *dev) ++{ ++ // int ret; ++ ++ if (mmc_disk) { ++ gendisk_fini(mmc_disk); ++ put_disk(mmc_disk); ++ } ++ ++ // ret = unregister_blkdev(major, DEVICE_NAME); ++ unregister_blkdev(major, DEVICE_NAME); ++ return 0; ++} ++ ++struct platform_driver mmc_spi_driver = { ++ .probe = mmc_spi_probe, ++ .remove = mmc_spi_remove, ++ .driver = { ++ .name = "mmc_spi_block", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++ ++/* module init/exit */ ++static int __init mmc_spi_block_init(void) ++{ ++ int ret; ++ spin_lock_init(&mmc_spi_lock); ++ ++ ret = platform_driver_register(&mmc_spi_driver); ++ if (ret < 0) ++ return ret; ++ ++ /* we just support one device */ ++ mmc_dev = platform_device_register_simple("mmc_spi_block", -1, NULL, 0); ++ if (IS_ERR(mmc_dev)) ++ return PTR_ERR(mmc_dev); ++ ++ return 0; ++} ++ ++ ++static void __exit mmc_spi_block_exit(void) ++{ ++ platform_driver_unregister(&mmc_spi_driver); ++ if (mmc_dev) ++ platform_device_unregister(mmc_dev); ++} ++ ++ ++module_init(mmc_spi_block_init); ++module_exit(mmc_spi_block_exit); ++ ++MODULE_AUTHOR("Madsuk,Rohde,TaGana,Carsten Juttner <carjay@gmx.net>,Guylhem Aznar <mmc-driver@externe.net>,mrdata"); ++MODULE_DESCRIPTION("Driver for MMC/SD-Cards in SPI mode over GPIO (Software SPI)"); ++MODULE_SUPPORTED_DEVICE("SIMpad"); ++MODULE_LICENSE("GPL"); ++ ++module_param(major, int, 0444); ++MODULE_PARM_DESC(major, "specify the major device number for the MMC/SD SPI driver"); diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-battery-old-way-but-also-with-sysfs.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-battery-old-way-but-also-with-sysfs.patch new file mode 100644 index 0000000000..ce23e42806 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-battery-old-way-but-also-with-sysfs.patch @@ -0,0 +1,577 @@ +Index: linux-2.6.27/drivers/mfd/Makefile +=================================================================== +--- linux-2.6.27.orig/drivers/mfd/Makefile 2008-12-07 01:02:17.739605256 +0100 ++++ linux-2.6.27/drivers/mfd/Makefile 2008-12-07 01:18:53.732838088 +0100 +@@ -22,3 +22,7 @@ + ifeq ($(CONFIG_SA1100_ASSABET),y) + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o + endif ++ ++ifeq ($(CONFIG_SA1100_SIMPAD),y) ++obj-$(CONFIG_MCP_UCB1200) += ucb1x00-simpad.o ++endif +Index: linux-2.6.27/drivers/mfd/ucb1x00-simpad.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/mfd/ucb1x00-simpad.c 2008-12-07 01:17:54.760583677 +0100 +@@ -0,0 +1,228 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-simpad.c ++ * ++ * Copyright (C) 2001-2003 Russell King, All Rights Reserved. ++ * 2007/03/18 mrdata: ++ * - adapted ucb1x00-assabet.c ++ * - transfer ucb1x00-simpad.c from kernel24 ++ * to new structur of kernel26 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ * ++ * We handle the machine-specific bits of the UCB1x00 driver here. ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/proc_fs.h> ++#include <linux/device.h> ++ ++#include <linux/apm-emulation.h> ++ ++#include <asm/dma.h> ++ ++#include <mach/simpad.h> ++#include <mach/simpad_pm.h> ++ ++#include "ucb1x00.h" ++#include "ucb1x00-simpad.h" ++ ++#define UCB1X00_ATTR(name,input,designation) \ ++static ssize_t name##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); \ ++ int val; \ ++ ucb1x00_adc_enable(ucb); \ ++ val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC); \ ++ ucb1x00_adc_disable(ucb); \ ++ return sprintf(buf, "%d\n", CALIBRATE_##designation(val)); \ ++} \ ++static DEVICE_ATTR(name,0444,name##_show,NULL) ++ ++UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1, BATTERY); ++UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD2, SUPPLY); ++UCB1X00_ATTR(icharger, UCB_ADC_INP_AD3, CHARGING); ++ ++static struct ucb1x00 *ucb_alt; ++ ++#define UCB1X00_WERT(name,input,designation) \ ++static int ucb1x00_simpad_read_##name(struct ucb1x00 *ucb_alt) \ ++{ \ ++ int val; \ ++ ucb1x00_adc_enable(ucb_alt); \ ++ val = ucb1x00_adc_read(ucb_alt, input, UCB_NOSYNC); \ ++ ucb1x00_adc_disable(ucb_alt); \ ++ return CALIBRATE_##designation(val); \ ++} ++ ++UCB1X00_WERT(vbatt, UCB_ADC_INP_AD1, BATTERY); ++UCB1X00_WERT(vcharger, UCB_ADC_INP_AD2, SUPPLY); ++UCB1X00_WERT(icharger, UCB_ADC_INP_AD3, CHARGING); ++ ++static int ucb1x00_simpad_add(struct ucb1x00_dev *dev) ++{ ++ device_create_file(&dev->ucb->dev, &dev_attr_vbatt); ++ device_create_file(&dev->ucb->dev, &dev_attr_vcharger); ++ device_create_file(&dev->ucb->dev, &dev_attr_icharger); ++ ucb_alt = dev->ucb; ++ return 0; ++} ++ ++static void ucb1x00_simpad_remove(struct ucb1x00_dev *dev) ++{ ++ device_remove_file(&dev->ucb->dev, &dev_attr_icharger); ++ device_remove_file(&dev->ucb->dev, &dev_attr_vcharger); ++ device_remove_file(&dev->ucb->dev, &dev_attr_vbatt); ++} ++ ++static struct ucb1x00_driver ucb1x00_simpad_driver = { ++ .add = ucb1x00_simpad_add, ++ .remove = ucb1x00_simpad_remove, ++}; ++ ++static int __init ucb1x00_simpad_init(void) ++{ ++ apm_get_power_status = simpad_apm_get_power_status; ++ return ucb1x00_register_driver(&ucb1x00_simpad_driver); ++} ++ ++static void __exit ucb1x00_simpad_exit(void) ++{ ++ apm_get_power_status = NULL; ++ ucb1x00_unregister_driver(&ucb1x00_simpad_driver); ++} ++ ++/****************************************************************************/ ++/* Functions exported for use by the kernel and kernel modules */ ++/****************************************************************************/ ++ ++int simpad_get_battery(struct simpad_battery_apm *bstat) ++{ ++ int icharger, vcharger, vbatt; ++ ++ if ( ucb_alt ) { ++ icharger = ucb1x00_simpad_read_icharger( ucb_alt ); ++ vcharger = ucb1x00_simpad_read_vcharger( ucb_alt ); ++ vbatt = ucb1x00_simpad_read_vbatt( ucb_alt ); ++ } else { ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_UNKNOWN; ++ bstat->status = SIMPAD_BATT_STATUS_UNKNOWN; ++ bstat->percentage = 0x64; // lets say 100% ++ bstat->life = 330; // lets say a long time ++ return 0; ++ } ++ ++ /* AC status */ ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_OFFLINE; ++ if ( vcharger>MIN_SUPPLY ) { ++ bstat->ac_status = SIMPAD_AC_STATUS_AC_ONLINE; ++ } ++ ++ /* charging */ ++ bstat->status = 0x0; ++ if ( icharger >= CHARGING_LED_LEVEL ) ++ bstat->status = SIMPAD_BATT_STATUS_CHARGING; ++ ++ if ( vbatt > BATT_LOW ) ++ bstat->status |= SIMPAD_BATT_STATUS_HIGH; ++ else if ( vbatt < BATT_CRITICAL ) ++ bstat->status |= SIMPAD_BATT_STATUS_CRITICAL; ++ else ++ bstat->status |= SIMPAD_BATT_STATUS_LOW; ++ ++ if (bstat->status & SIMPAD_BATT_STATUS_CHARGING) { ++ if (icharger > CHARGING_MAX_LEVEL) ++ icharger = CHARGING_MAX_LEVEL; ++ if (icharger < CHARGING_LED_LEVEL) ++ icharger = CHARGING_LED_LEVEL; ++ ++ bstat->percentage = 100 - 100 * (icharger - CHARGING_LED_LEVEL) / ++ (CHARGING_MAX_LEVEL - CHARGING_LED_LEVEL); ++ } else { ++ if (vbatt > BATT_FULL) ++ vbatt = BATT_FULL; ++ if (vbatt < BATT_EMPTY) ++ vbatt = BATT_EMPTY; ++ ++ bstat->percentage = 100 * (vbatt - BATT_EMPTY) / (BATT_FULL - BATT_EMPTY); ++ } ++ ++ /* let's assume: full load is 7h */ ++ /* bstat->life = 420*bstat->percentage/100; */ ++ /* mrdata: think, 4h is more realistic */ ++ bstat->life = 240*(bstat->percentage)/100; ++ ++#if 0 ++ printk("get_battery: ac: %02x / ch: %02x / perc: %02x / life: %d \n", ++ bstat->ac_status, bstat->status, ++ bstat->percentage, bstat->life ); ++#endif ++ ++ return 0; ++} ++ ++void simpad_apm_get_power_status(struct apm_power_info *info) ++{ ++ struct simpad_battery_apm bstat; ++ unsigned char ac = APM_AC_UNKNOWN; ++ unsigned char level = APM_BATTERY_STATUS_UNKNOWN; ++ int status, result; ++ ++ result = simpad_get_battery(&bstat); ++ if (result) { ++ printk("%s: unable to access battery information: result=%d\n", __FUNCTION__, result); ++ return; ++ } ++ ++ switch (bstat.ac_status) { ++ case SIMPAD_AC_STATUS_AC_OFFLINE: ++ ac = APM_AC_OFFLINE; ++ break; ++ case SIMPAD_AC_STATUS_AC_ONLINE: ++ ac = APM_AC_ONLINE; ++ break; ++ case SIMPAD_AC_STATUS_AC_BACKUP: ++ ac = APM_AC_BACKUP; ++ break; ++ } ++ ++ info->ac_line_status = ac; ++ ++ status = bstat.status; ++ if (status & (SIMPAD_BATT_STATUS_CHARGING | SIMPAD_BATT_STATUS_CHARGE_MAIN)) ++ level = APM_BATTERY_STATUS_CHARGING; ++ else if (status & (SIMPAD_BATT_STATUS_HIGH | SIMPAD_BATT_STATUS_FULL)) ++ level = APM_BATTERY_STATUS_HIGH; ++ else if (status & SIMPAD_BATT_STATUS_LOW) ++ level = APM_BATTERY_STATUS_LOW; ++ else if (status & SIMPAD_BATT_STATUS_CRITICAL) ++ level = APM_BATTERY_STATUS_CRITICAL; ++ ++ info->battery_status = level; ++ info->battery_flag = info->battery_status; ++ ++ info->battery_life = bstat.percentage; ++ ++ /* we have a dumb battery - so we know nothing */ ++ info->time = bstat.life; ++ info->units = APM_UNITS_MINS; ++ ++#if 0 ++ printk("apm_get_power: ac: %02x / bs: %02x / bf: %02x / perc: %02x / life: %d\n", ++ info->ac_line_status, info->battery_status, info->battery_flag, ++ info->battery_life, info->time ); ++#endif ++ return; ++} ++ ++module_init(ucb1x00_simpad_init); ++module_exit(ucb1x00_simpad_exit); ++ ++ ++MODULE_AUTHOR("Juergen Messerer <juergen.messerer@freesurf.ch>"); ++MODULE_DESCRIPTION("SIMpad noddy testing only example ADC driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.27/drivers/mfd/ucb1x00-simpad.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/mfd/ucb1x00-simpad.h 2008-12-07 01:02:19.936271486 +0100 +@@ -0,0 +1,86 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-simpad.h ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ */ ++#ifndef UCB1300_SIMPAD_H ++#define UCB1300_SIMPAD_H ++ ++/* ++ * Conversion from AD -> mV ++ * 7.5V = 1023 7.33137mV/Digit ++ * ++ * 400 Units == 9.7V ++ * a = ADC value ++ * 2007/03/24 mrdata: ++ * according UCB1300 datasheet ADC error max. 3 LSB ++ * 5-95% of full scale -> 3*7.33137mV = 21.99mV ++ * // old ++ * 21 = ADC error ++ * 12600 = Divident to get 2*7.3242 ++ * 860 = Divider to get 2*7.3242 ++ * 170 = Voltagedrop over ++ * ++ * // new ++ * 3 = ADC error ++ * 12610 = Divident to get 2*7.33139 ++ * 860 = Divider to get 2*7.33139 ++ * 170 = Voltagedrop over ++ */ ++// #define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170) ++#define CALIBRATE_BATTERY(a) ((((a + 3)*12610)/860) + 170) ++ ++/* ++ * We have two types of batteries a small and a large one ++ * To get the right value we to distinguish between those two ++ * 450 Units == 15 V ++ */ ++#ifdef SMALL_BATTERY ++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 51) ++#define MIN_SUPPLY 8500 /* Less then 8.5V means no powersupply */ ++#else ++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45) ++//#define MIN_SUPPLY 14000 /* Less then 14V means no powersupply */ ++#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */ ++#endif ++ ++/* ++ * Charging Current ++ * if value is >= 50 then charging is on ++ */ ++// #define CALIBRATE_CHARGING(a) (((a) * 1000) / (152/4)) ++#define CALIBRATE_CHARGING(a) (a) ++//#define CHARGING_LED_LEVEL 50 ++ ++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// type small battery ++#define CHARGING_LED_LEVEL 12 ++#define CHARGING_MAX_LEVEL 120 ++#define BATT_FULL 8100 ++#define BATT_LOW 7300 ++#define BATT_CRITICAL 6700 ++#define BATT_EMPTY 6400 ++ ++#else // CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// type large battery ++// because of ADC error CHARGING_LED_LEVEL can changed ++// from 27 to 28 ++#define CHARGING_LED_LEVEL 27 ++#define CHARGING_MAX_LEVEL 265 ++// BATT_FULL with SIMPAD_AC_STATUS_AC_OFFLINE ++#define BATT_FULL 8100 ++#define BATT_LOW 7400 ++#define BATT_CRITICAL 6800 ++#define BATT_EMPTY 6500 ++ ++#endif // CONFIG_SA1100_SIMPAD_SINUSPAD ++ ++// int simpad_get_battery(struct simpad_battery_apm *bstat); ++ ++#endif +Index: linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad_pm.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad_pm.h 2008-12-07 01:02:19.936271486 +0100 +@@ -0,0 +1,236 @@ ++/* ++* Abstraction interface for microcontroller connection to rest of system ++* ++* Copyright 2003 Peter Pregler ++* Copyright 2000,1 Compaq Computer Corporation. ++* ++* Use consistent with the GNU GPL is permitted, ++* provided that this copyright notice is ++* preserved in its entirety in all copies and derived works. ++* ++* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, ++* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS ++* FITNESS FOR ANY PARTICULAR PURPOSE. ++* ++* Author: Peter Pregler (based on work for ipaq by Andrew Christian) ++* ++*/ ++ ++#ifndef __SIMPAD_HAL_H ++#define __SIMPAD_HAL_H ++ ++#include <linux/apm-emulation.h> ++ ++struct simpad_battery_apm { ++ unsigned char ac_status; /* line connected yes/no */ ++ unsigned char status; /* battery loading yes/no */ ++ unsigned char percentage; /* percentage loaded */ ++ unsigned short life; /* life till empty */ ++}; ++ ++extern void simpad_apm_get_power_status(struct apm_power_info *); ++ ++// extern int simpad_get_battery(struct simpad_battery_apm *bstat); ++ ++/* These should match the apm_bios.h definitions */ ++#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00 ++#define SIMPAD_AC_STATUS_AC_ONLINE 0x01 ++#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */ ++#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff ++ ++ ++/* These bitfields are rarely "or'd" together */ ++#define SIMPAD_BATT_STATUS_HIGH 0x01 ++#define SIMPAD_BATT_STATUS_LOW 0x02 ++#define SIMPAD_BATT_STATUS_CRITICAL 0x04 ++#define SIMPAD_BATT_STATUS_CHARGING 0x08 ++#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10 ++#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */ ++#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */ ++#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */ ++#define SIMPAD_BATT_STATUS_NOBATT 0x80 ++#define SIMPAD_BATT_STATUS_UNKNOWN 0xff ++ ++#if 0 // FIXME ++#include <linux/simpad_ts.h> ++ ++enum simpad_asset_type { ++ ASSET_TCHAR = 0, ++ ASSET_SHORT, ++ ASSET_LONG ++}; ++ ++#define TTYPE(_type) (((unsigned int)_type) << 8) ++#define TCHAR(_len) (TTYPE(ASSET_TCHAR) | (_len)) ++#define TSHORT TTYPE(ASSET_SHORT) ++#define TLONG TTYPE(ASSET_LONG) ++#define ASSET(_type,_num) ((((unsigned int)_type)<<16) | (_num)) ++ ++#define ASSET_HM_VERSION ASSET( TCHAR(10), 0 ) /* 1.1, 1.2 */ ++#define ASSET_SERIAL_NUMBER ASSET( TCHAR(40), 1 ) /* Unique iPAQ serial number */ ++#define ASSET_MODULE_ID ASSET( TCHAR(20), 2 ) /* E.g., "iPAQ 3700" */ ++#define ASSET_PRODUCT_REVISION ASSET( TCHAR(10), 3 ) /* 1.0, 2.0 */ ++#define ASSET_PRODUCT_ID ASSET( TSHORT, 4 ) /* 2 = Palm-sized computer */ ++#define ASSET_FRAME_RATE ASSET( TSHORT, 5 ) ++#define ASSET_PAGE_MODE ASSET( TSHORT, 6 ) /* 0 = Flash memory */ ++#define ASSET_COUNTRY_ID ASSET( TSHORT, 7 ) /* 0 = USA */ ++#define ASSET_IS_COLOR_DISPLAY ASSET( TSHORT, 8 ) /* Boolean, 1 = yes */ ++#define ASSET_ROM_SIZE ASSET( TSHORT, 9 ) /* 16, 32 */ ++#define ASSET_RAM_SIZE ASSET( TSHORT, 10 ) /* 32768 */ ++#define ASSET_HORIZONTAL_PIXELS ASSET( TSHORT, 11 ) /* 240 */ ++#define ASSET_VERTICAL_PIXELS ASSET( TSHORT, 12 ) /* 320 */ ++ ++#define ASSET_TYPE(_asset) (((_asset)&0xff000000)>>24) ++#define ASSET_TCHAR_LEN(_asset) (((_asset)&0x00ff0000)>>16) ++#define ASSET_NUMBER(_asset) ((_asset)&0x0000ffff) ++ ++#define MAX_TCHAR_LEN 40 ++ ++struct simpad_asset { ++ unsigned int type; ++ union { ++ unsigned char tchar[ MAX_TCHAR_LEN ]; ++ unsigned short vshort; ++ unsigned long vlong; ++ } a; ++}; ++ ++/******************************************************************** ++ * Interface to the hardware-type specific functions ++ * ++ * get_version Read the version number of the microcontroller on the option pack SPI bus ++ * spi_read Reads from the serial EEPROM memory on the option pack SPI bus ++ * spi_write Write to the serial EEPROM memory on the option pack SPI bus ++ * get_option_detect Returns whether or not an option pack is present ++ * ++ * get_thermal_sensor Return measured temperature of the unit, in units of 0.125 deg C ++ * set_notify_led Turns on, off, or blinks the Green LED ++ * read_light_sensor Returns the value of the front light sensor ++ * get_battery Returns the current voltage and charging state of all batteries ++ * audio_clock Sets the audio CODEC to run at a particular rate ++ * audio_power Turns on/off audio CODEC (internally calls audio_clock) ++ * audio_mute Mutes the audio CODEC ++ * asset_read Extracts PocketPC-style asset information from persistent memory ++ * backlight_control Adjusts the backlight level (only on/off for 3100) ++ * ++ * ++ * iPAQ 3100 only ++ * ============== ++ * codec_control Reset/mute/control level of 3100 audio codec ++ * contrast_control Adjusts the contrast level (only for 3100) ++ * ++ * iPAQ 3600, 3700 only ++ * ==================== ++ * eeprom_read Reads from the asset information on the eeprom of a 3600 (deprecated) ++ * eeprom_write Writes to the asset information on the eeprom (deprecated) ++ * ++ * The interfaces to the EEPROM functions are maintained only because the simpad_ts driver has ++ * a deprecated ioctl call for them. They are used internally by the "asset_read" function. ++ * ++ * iPAQ 3800, 3900 only ++ * ==================== ++ * set_ebat Tells enhanced PCMCIA sleeves that this iPAQ can handle ++ * a wider voltage range (applies to 3800, 3900) ++ * ++ *********************************************************************/ ++ ++struct simpad_hal_ops { ++ /* Functions provided by the underlying hardware */ ++ int (*get_version)( struct simpad_ts_version * ); ++ int (*eeprom_read)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*eeprom_write)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*get_thermal_sensor)( unsigned short * ); ++ int (*set_notify_led)( unsigned char mode, unsigned char duration, ++ unsigned char ontime, unsigned char offtime ); ++ int (*read_light_sensor)( unsigned char *result ); ++ int (*get_battery)( struct simpad_battery * ); ++ int (*spi_read)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*spi_write)( unsigned short address, unsigned char *data, unsigned short len ); ++ int (*codec_control)( unsigned char, unsigned char ); ++ int (*get_option_detect)( int *result ); ++ int (*audio_clock)( long samplerate ); ++ int (*audio_power)( long samplerate ); ++ int (*audio_mute)( int mute ); ++ int (*asset_read)( struct simpad_asset *asset ); ++ int (*set_ebat)( void ); ++ ++ /* Functions indirectly provided by the underlying hardware */ ++ int (*backlight_control)( enum flite_pwr power, unsigned char level ); ++ int (*contrast_control)( unsigned char level ); ++ ++ /* for module use counting */ ++ struct module *owner; ++}; ++ ++/* Used by the device-specific hardware module to register itself */ ++extern int simpad_hal_register_interface( struct simpad_hal_ops *ops ); ++extern void simpad_hal_unregister_interface( struct simpad_hal_ops *ops ); ++ ++/* ++ * Calls into HAL from the device-specific hardware module ++ * These run at interrupt time ++ */ ++extern void simpad_hal_keypress( unsigned char key ); ++extern void simpad_hal_touchpanel( unsigned short x, unsigned short y, int down ); ++extern void simpad_hal_option_detect( int present ); ++ ++/* Callbacks registered by device drivers */ ++struct simpad_driver_ops { ++ void (*keypress)( unsigned char key ); ++ void (*touchpanel)( unsigned short x, unsigned short y, int down ); ++ void (*option_detect)( int present ); ++}; ++ ++extern int simpad_hal_register_driver( struct simpad_driver_ops * ); ++extern void simpad_hal_unregister_driver( struct simpad_driver_ops * ); ++ ++ ++/* Calls into HAL from device drivers and other kernel modules */ ++extern void simpad_get_flite( struct simpad_ts_backlight *bl ); ++extern void simpad_get_contrast( unsigned char *contrast ); ++extern int simpad_set_flite( enum flite_pwr pwr, unsigned char brightness ); ++extern int simpad_set_contrast( unsigned char contrast ); ++extern int simpad_toggle_frontlight( void ); ++ ++extern int simpad_apm_get_power_status(unsigned char *ac_line_status, unsigned char *battery_status, ++ unsigned char *battery_flag, unsigned char *battery_percentage, ++ unsigned short *battery_life); ++ ++extern struct simpad_hal_ops *simpad_hal_ops; ++ ++/* Do not use this macro in driver files - instead, use the inline functions defined below */ ++#define CALL_HAL( f, args... ) \ ++ { int __result = -EIO; \ ++ if ( simpad_hal_ops && simpad_hal_ops->f ) { \ ++ __MOD_INC_USE_COUNT(simpad_hal_ops->owner); \ ++ __result = simpad_hal_ops->f(args); \ ++ __MOD_DEC_USE_COUNT(simpad_hal_ops->owner); \ ++ } \ ++ return __result; } ++ ++#define HFUNC static __inline__ int ++ ++/* The eeprom_read/write address + len has a maximum value of 512. Both must be even numbers */ ++HFUNC simpad_eeprom_read( u16 addr, u8 *data, u16 len ) CALL_HAL(eeprom_read,addr,data,len) ++HFUNC simpad_eeprom_write( u16 addr, u8 *data, u16 len) CALL_HAL(eeprom_write,addr,data,len) ++HFUNC simpad_spi_read( u8 addr, u8 *data, u16 len) CALL_HAL(spi_read,addr,data,len) ++HFUNC simpad_spi_write( u8 addr, u8 *data, u16 len) CALL_HAL(spi_write,addr,data,len) ++HFUNC simpad_get_version( struct simpad_ts_version *v ) CALL_HAL(get_version,v) ++HFUNC simpad_get_thermal_sensor( u16 *thermal ) CALL_HAL(get_thermal_sensor,thermal) ++HFUNC simpad_set_led( u8 mode, u8 dur, u8 ont, u8 offt ) CALL_HAL(set_notify_led, mode, dur, ont, offt) ++HFUNC simpad_get_light_sensor( u8 *result ) CALL_HAL(read_light_sensor,result) ++HFUNC simpad_get_battery( struct simpad_battery *bat ) CALL_HAL(get_battery,bat) ++HFUNC simpad_get_option_detect( int *result) CALL_HAL(get_option_detect,result) ++HFUNC simpad_audio_clock( long samplerate ) CALL_HAL(audio_clock,samplerate) ++HFUNC simpad_audio_power( long samplerate ) CALL_HAL(audio_power,samplerate) ++HFUNC simpad_audio_mute( int mute ) CALL_HAL(audio_mute,mute) ++HFUNC simpad_asset_read( struct simpad_asset *asset ) CALL_HAL(asset_read,asset) ++HFUNC simpad_set_ebat( void ) CALL_HAL(set_ebat) ++ ++/* Don't use these functions directly - rather, call {get,set}_{flite,contrast} */ ++ /* Functions indirectly provided by the underlying hardware */ ++HFUNC simpad_backlight_control( enum flite_pwr p, u8 v ) CALL_HAL(backlight_control,p,v) ++HFUNC simpad_contrast_control( u8 level ) CALL_HAL(contrast_control,level) ++ ++#endif ++#endif diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-cs3-simpad.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-cs3-simpad.patch new file mode 100644 index 0000000000..400e171208 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-cs3-simpad.patch @@ -0,0 +1,192 @@ +Index: linux-2.6.27/arch/arm/mach-sa1100/Makefile +=================================================================== +--- linux-2.6.27.orig/arch/arm/mach-sa1100/Makefile 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/arch/arm/mach-sa1100/Makefile 2008-12-04 03:12:57.098042257 +0100 +@@ -41,6 +41,7 @@ + obj-$(CONFIG_SA1100_SHANNON) += shannon.o + + obj-$(CONFIG_SA1100_SIMPAD) += simpad.o ++obj-$(CONFIG_SA1100_SIMPAD) += cs3-simpad.o + led-$(CONFIG_SA1100_SIMPAD) += leds-simpad.o + + # LEDs support +Index: linux-2.6.27/arch/arm/mach-sa1100/cs3-simpad.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/arch/arm/mach-sa1100/cs3-simpad.c 2008-12-07 00:36:17.353090743 +0100 +@@ -0,0 +1,175 @@ ++/* ++ * simpad-cs3.c ++ * ++ * This driver shows the GPIO states of the cs3 latch. You can also ++ * switch some GPIOS. ++ * ++ * (c) 2007 Bernhard Guillon <Bernhard.Guillon@OpenSIMpad.org> ++ * ++ * You may use this code as per GPL version 2 ++ * ++ * Some parts are based on battery.c ++ * ++ * mrdata: -added cs3_ro support ++ * -added preprocessor stuff ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/device.h> ++ ++#include <mach/simpad.h> ++ ++extern long get_cs3_ro(void); ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int value); ++extern void clear_cs3_bit(int value); ++ ++struct cs3 { ++ struct device dev; ++ const char *name; ++ char *id; ++ int type; ++}; ++ ++struct cs3 cs3 ={ ++ .name = "latch_cs3", ++}; ++ ++ ++#define CS3_STORE_ATTR(namek,nameg) \ ++static ssize_t namek##_store (struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ char val; \ ++ if (sscanf(buf, "%c",&val) != 1) \ ++ return -EINVAL; \ ++ if (val == '1') \ ++ set_cs3_bit(nameg); \ ++ else if (val == '0') \ ++ clear_cs3_bit(nameg); \ ++ return strlen(buf); \ ++} ++ ++CS3_STORE_ATTR(display_on, DISPLAY_ON); ++CS3_STORE_ATTR(dect_power_on, DECT_POWER_ON); ++CS3_STORE_ATTR(irda_sd, IRDA_SD); ++CS3_STORE_ATTR(sd_mediaq, SD_MEDIAQ); ++CS3_STORE_ATTR(led2_on, LED2_ON); ++CS3_STORE_ATTR(irda_mode, IRDA_MODE); ++CS3_STORE_ATTR(reset_simcard, RESET_SIMCARD); ++ ++ ++#define CS3_ATTR(shadro,namek,nameg,mode,store) \ ++static ssize_t namek##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ if (get_cs3_##shadro() & nameg ) { \ ++ return sprintf(buf, "1\n"); \ ++ } \ ++ else { \ ++ return sprintf(buf, "0\n"); \ ++ } \ ++} \ ++static DEVICE_ATTR(namek, mode, namek##_show, store) ++ ++CS3_ATTR(shadow, vcc_5v_en, VCC_5V_EN, 0444, NULL); ++CS3_ATTR(shadow, vcc_3v_en, VCC_3V_EN, 0444, NULL); ++CS3_ATTR(shadow, en1, EN1, 0444, NULL); ++CS3_ATTR(shadow, en0, EN0, 0444, NULL); ++CS3_ATTR(shadow, display_on, DISPLAY_ON, 0664, display_on_store); ++CS3_ATTR(shadow, pcmcia_buff_dis, PCMCIA_BUFF_DIS, 0444, NULL); ++CS3_ATTR(shadow, mq_reset, MQ_RESET, 0444, NULL); ++CS3_ATTR(shadow, pcmcia_reset, PCMCIA_RESET, 0444, NULL); ++CS3_ATTR(shadow, dect_power_on, DECT_POWER_ON, 0664, dect_power_on_store); ++CS3_ATTR(shadow, irda_sd, IRDA_SD, 0664, irda_sd_store); ++CS3_ATTR(shadow, rs232_on, RS232_ON, 0444, NULL); ++CS3_ATTR(shadow, sd_mediaq, SD_MEDIAQ, 0664, sd_mediaq_store); ++CS3_ATTR(shadow, led2_on, LED2_ON, 0664, led2_on_store); ++CS3_ATTR(shadow, irda_mode, IRDA_MODE, 0664, irda_mode_store); ++CS3_ATTR(shadow, enable_5v, ENABLE_5V, 0444, NULL); ++CS3_ATTR(shadow, reset_simcard, RESET_SIMCARD, 0664, reset_simcard_store); ++CS3_ATTR(ro, pcmcia_bvd1, PCMCIA_BVD1, 0444, NULL); ++CS3_ATTR(ro, pcmcia_bvd2, PCMCIA_BVD2, 0444, NULL); ++CS3_ATTR(ro, pcmcia_vs1, PCMCIA_VS1, 0444, NULL); ++CS3_ATTR(ro, pcmcia_vs2, PCMCIA_VS2, 0444, NULL); ++CS3_ATTR(ro, lock_ind, LOCK_IND, 0444, NULL); ++CS3_ATTR(ro, charging_state, CHARGING_STATE, 0444, NULL); ++CS3_ATTR(ro, pcmcia_short, PCMCIA_SHORT, 0444, NULL); ++ ++static struct device simpad_gpios_device = { ++ .init_name = "simpad", ++}; ++ ++#define create_entry_conditional(namek) \ ++ rc = device_create_file(&cs3->dev, &dev_attr_##namek); \ ++ if (rc) goto out; \ ++ ++static int register_cs3_latch(struct cs3 *cs3) ++{ ++ int rc = 0; ++ cs3->dev.parent = &simpad_gpios_device; ++ strcpy(cs3->dev.bus_id, cs3->name); ++ rc = device_register(&cs3->dev); ++ if(rc) ++ goto out; ++ ++ create_entry_conditional(vcc_5v_en); ++ create_entry_conditional(vcc_3v_en); ++ create_entry_conditional(en1); ++ create_entry_conditional(en0); ++ create_entry_conditional(display_on); ++ create_entry_conditional(pcmcia_buff_dis); ++ create_entry_conditional(mq_reset); ++ create_entry_conditional(pcmcia_reset); ++ create_entry_conditional(dect_power_on); ++ create_entry_conditional(irda_sd); ++ create_entry_conditional(rs232_on); ++ create_entry_conditional(sd_mediaq); ++ create_entry_conditional(led2_on); ++ create_entry_conditional(irda_mode); ++ create_entry_conditional(enable_5v); ++ create_entry_conditional(reset_simcard); ++ create_entry_conditional(pcmcia_bvd1); ++ create_entry_conditional(pcmcia_bvd2); ++ create_entry_conditional(pcmcia_vs1); ++ create_entry_conditional(pcmcia_vs2); ++ create_entry_conditional(lock_ind); ++ create_entry_conditional(charging_state); ++ create_entry_conditional(pcmcia_short); ++ ++out: ++ return rc; ++} ++ ++static int __init simpad_gpios_device_init(void) ++{ ++ int rc = 0; ++ ++ rc = device_register(&simpad_gpios_device); ++ ++ if(rc != 0) ++ { ++ printk(KERN_ERR "cs3 latch device failed to register properly\n"); ++ return rc; ++ } ++ ++ rc = register_cs3_latch(&cs3); ++ return rc; ++} ++ ++static void __exit simpad_gpios_device_exit(void) ++{ ++ device_unregister(&simpad_gpios_device); ++} ++ ++module_init(simpad_gpios_device_init); ++module_exit(simpad_gpios_device_exit); ++ ++MODULE_AUTHOR("Bernhard Guillon"); ++MODULE_DESCRIPTION("CS3_latch driver"); ++MODULE_LICENSE("GPL"); diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-mq200.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-mq200.patch new file mode 100644 index 0000000000..b64715116c --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-mq200.patch @@ -0,0 +1,2640 @@ +Index: linux-2.6.27/drivers/video/Kconfig +=================================================================== +--- linux-2.6.27.orig/drivers/video/Kconfig 2008-12-07 01:18:57.236171217 +0100 ++++ linux-2.6.27/drivers/video/Kconfig 2008-12-07 01:19:16.649503720 +0100 +@@ -16,7 +16,7 @@ + config VIDEO_OUTPUT_CONTROL + tristate "Lowlevel video output switch controls" + help +- This framework adds support for low-level control of the video ++ This framework adds support for low-level control of the video + output switch. + + menuconfig FB +@@ -231,7 +231,7 @@ + This is particularly important to one driver, matroxfb. If + unsure, say N. + +-comment "Frame buffer hardware drivers" ++comment "Frambuffer hardware drivers" + depends on FB + + config FB_CIRRUS +@@ -611,7 +611,7 @@ + BIOS routines contained in a ROM chip in HP PA-RISC based machines. + Enabling this option will implement the linux framebuffer device + using calls to the STI BIOS routines for initialisation. +- ++ + If you enable this option, you will get a planar framebuffer device + /dev/fb which will work on the most common HP graphic cards of the + NGLE family, including the artist chips (in the 7xx and Bxxx series), +@@ -1061,36 +1061,36 @@ + select FB_CFB_IMAGEBLIT + select VGASTATE + help +- This driver supports the on-board graphics built in to the Intel 810 ++ This driver supports the on-board graphics built in to the Intel 810 + and 815 chipsets. Say Y if you have and plan to use such a board. + + To compile this driver as a module, choose M here: the + module will be called i810fb. + +- For more information, please read ++ For more information, please read + <file:Documentation/fb/intel810.txt> + + config FB_I810_GTF + bool "use VESA Generalized Timing Formula" + depends on FB_I810 + help +- If you say Y, then the VESA standard, Generalized Timing Formula ++ If you say Y, then the VESA standard, Generalized Timing Formula + or GTF, will be used to calculate the required video timing values +- per video mode. Since the GTF allows nondiscrete timings ++ per video mode. Since the GTF allows nondiscrete timings + (nondiscrete being a range of values as opposed to discrete being a +- set of values), you'll be able to use any combination of horizontal ++ set of values), you'll be able to use any combination of horizontal + and vertical resolutions, and vertical refresh rates without having + to specify your own timing parameters. This is especially useful +- to maximize the performance of an aging display, or if you just +- have a display with nonstandard dimensions. A VESA compliant ++ to maximize the performance of an aging display, or if you just ++ have a display with nonstandard dimensions. A VESA compliant + monitor is recommended, but can still work with non-compliant ones. +- If you need or want this, then select this option. The timings may +- not be compliant with Intel's recommended values. Use at your own ++ If you need or want this, then select this option. The timings may ++ not be compliant with Intel's recommended values. Use at your own + risk. + +- If you say N, the driver will revert to discrete video timings ++ If you say N, the driver will revert to discrete video timings + using a set recommended by Intel in their documentation. +- ++ + If unsure, say N. + + config FB_I810_I2C +@@ -1214,10 +1214,10 @@ + framebuffer section. G450/G550 secondary head and digital output + are supported without additional modules. + +- The driver starts in monitor mode. You must use the matroxset tool +- (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to +- swap primary and secondary head outputs, or to change output mode. +- Secondary head driver always start in 640x480 resolution and you ++ The driver starts in monitor mode. You must use the matroxset tool ++ (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to ++ swap primary and secondary head outputs, or to change output mode. ++ Secondary head driver always start in 640x480 resolution and you + must use fbset to change it. + + Do not forget that second head supports only 16 and 32 bpp +@@ -1315,7 +1315,7 @@ + "I2C support" and "I2C bit-banging support" in the character devices + section. + +- If you say M here then "I2C support" and "I2C bit-banging support" ++ If you say M here then "I2C support" and "I2C bit-banging support" + can be build either as modules or built-in. + + There is a product page at +@@ -1327,7 +1327,7 @@ + select FB_DDC + default y + help +- Say Y here if you want DDC/I2C support for your Radeon board. ++ Say Y here if you want DDC/I2C support for your Radeon board. + + config FB_RADEON_BACKLIGHT + bool "Support for backlight control" +@@ -1431,6 +1431,15 @@ + ---help--- + Driver for graphics boards with S3 Trio / S3 Virge chip. + ++config FB_MQ200 ++ bool "MQ200 Driver" ++ depends on (FB = y) && ARM && ARCH_SA1100 ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ help ++ This is a MQ200 driver tested only on Siemens SIMpads. ++ + config FB_SAVAGE + tristate "S3 Savage support" + depends on FB && PCI && EXPERIMENTAL +@@ -1508,7 +1517,7 @@ + select VGASTATE + help + This driver supports notebooks with NeoMagic PCI chips. +- Say Y if you have such a graphics card. ++ Say Y if you have such a graphics card. + + To compile this driver as a module, choose M here: the + module will be called neofb. +@@ -1554,7 +1563,7 @@ + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- +- Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or ++ Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or + Voodoo2 (cvg) based graphics card. + + To compile this driver as a module, choose M here: the +Index: linux-2.6.27/drivers/video/Makefile +=================================================================== +--- linux-2.6.27.orig/drivers/video/Makefile 2008-12-07 01:18:57.252839170 +0100 ++++ linux-2.6.27/drivers/video/Makefile 2008-12-07 01:19:16.649503720 +0100 +@@ -36,6 +36,7 @@ + obj-$(CONFIG_FB_PM2) += pm2fb.o + obj-$(CONFIG_FB_PM3) += pm3fb.o + ++obj-$(CONFIG_FB_MQ200) += mq200/ + obj-$(CONFIG_FB_MATROX) += matrox/ + obj-$(CONFIG_FB_RIVA) += riva/ + obj-$(CONFIG_FB_NVIDIA) += nvidia/ +Index: linux-2.6.27/drivers/video/backlight/Kconfig +=================================================================== +--- linux-2.6.27.orig/drivers/video/backlight/Kconfig 2008-12-07 01:18:57.262838759 +0100 ++++ linux-2.6.27/drivers/video/backlight/Kconfig 2008-12-07 01:19:16.649503720 +0100 +@@ -164,3 +164,19 @@ + If you have an Apple Macbook Pro with Nvidia graphics hardware say Y + to enable a driver for its backlight + ++config BACKLIGHT_SIMPAD ++ tristate "SIMpad MQ200 Backlight driver" ++ depends on SA1100_SIMPAD && BACKLIGHT_CLASS_DEVICE ++ default y ++ help ++ If you have a Siemens SIMpad say Y to enable the ++ backlight driver. ++ ++config LCD_SIMPAD ++ tristate "SIMpad MQ200 LCD driver" ++ depends on SA1100_SIMPAD && LCD_CLASS_DEVICE ++ default y ++ help ++ If you have a Siemens SIMpad say Y to enable the ++ LCD driver. ++ +Index: linux-2.6.27/drivers/video/backlight/Makefile +=================================================================== +--- linux-2.6.27.orig/drivers/video/backlight/Makefile 2008-12-07 01:18:57.279504370 +0100 ++++ linux-2.6.27/drivers/video/backlight/Makefile 2008-12-07 01:19:16.652836923 +0100 +@@ -16,4 +16,5 @@ + obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o + obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o + obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o +- ++obj-$(CONFIG_BACKLIGHT_SIMPAD) += simpad_bl.o ++obj-$(CONFIG_LCD_SIMPAD) += simpad_lcd.o +Index: linux-2.6.27/drivers/video/backlight/simpad_bl.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/video/backlight/simpad_bl.c 2008-12-07 01:32:34.467302764 +0100 +@@ -0,0 +1,208 @@ ++/* ++ * GPLv2 <zecke@handhelds.org ++ * ++ * Implementation of the backlight_driver for ++ * the mq200 framebuffer ++ * ++ * 2007/03/17 mrdata: ++ * - small changes simpad_bl_get_brightness() ++ * simpad_bl_set_brightness() ++ * - new function simpad_bl_update_status() ++ * - changed struct backlight_properties simpad_bl_props() ++ * to new one ++ * - changed __init simpad_bl_init() -> backlight_device_register ++ * ++ * 2007/03/24 mrdata ++ * - added .brightness=127 in ++ * struct backlight_properties simpad_bl_props() ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/fb.h> ++#include <linux/backlight.h> ++ ++#include <asm/types.h> ++#include <mach/hardware.h> ++#include <asm/io.h> ++ ++#include "../mq200/mq200_data.h" ++ ++#define SIMPAD_BACKLIGHT_MASK 0x00a10044 ++#define SIMPAD_DEFAULT_INTENSITY 127 ++#define SIMPAD_MAX_INTENSITY 254 ++#define REGISTER_BASE 0xf2e00000 ++ ++static int simpad_bl_suspended; ++static int current_intensity = 0; ++ ++static void simpad_bl_send_intensity(struct backlight_device *bd) ++{ ++ int intensity = bd->props.brightness; ++ ++ union fp0fr fp0fr; ++ unsigned long dutyCycle, pwmcontrol; ++ ++ if (intensity > SIMPAD_MAX_INTENSITY) ++ intensity = SIMPAD_MAX_INTENSITY; ++ ++ if (bd->props.power != FB_BLANK_UNBLANK) ++ intensity = 0; ++ ++ if (bd->props.fb_blank != FB_BLANK_UNBLANK) ++ intensity = 0; ++ ++ if (simpad_bl_suspended) ++ intensity = 0; ++ ++ if (intensity != current_intensity) ++ { ++ /* ++ * Determine dutyCycle. ++ * Note: the lower the value, the brighter the display! ++ */ ++ ++ dutyCycle = SIMPAD_MAX_INTENSITY - intensity; ++ ++ /* ++ * Configure PWM0 (source clock = oscillator clock, pwm always enabled, ++ * zero, clock pre-divider = 4) pwm frequency = 12.0kHz ++ */ ++ ++ fp0fr.whole = readl(FP0FR(REGISTER_BASE)); ++ pwmcontrol = fp0fr.whole & 0xffff00ff; ++ fp0fr.whole &= 0xffffff00; ++ fp0fr.whole |= 0x00000044; ++ writel(fp0fr.whole, FP0FR(REGISTER_BASE)); ++ ++ /* Write to pwm duty cycle register. */ ++ fp0fr.whole = dutyCycle << 8; ++ fp0fr.whole &= 0x0000ff00; ++ fp0fr.whole |= pwmcontrol; ++ writel(fp0fr.whole, FP0FR(REGISTER_BASE)); ++ ++ current_intensity = intensity; ++ } ++} ++ ++ ++#ifdef CONFIG_PM ++static int simpad_bl_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ simpad_bl_suspended = 1; ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++ ++static int simpad_bl_resume(struct platform_device *pdev) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ simpad_bl_suspended = 0; ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++#else ++#define simpad_bl_suspend NULL ++#define simpad_bl_resume NULL ++#endif ++ ++ ++static int simpad_bl_set_intensity(struct backlight_device *bd) ++{ ++ simpad_bl_send_intensity(bd); ++ return 0; ++} ++ ++ ++static int simpad_bl_get_intensity(struct backlight_device *bd) ++{ ++ return current_intensity; ++} ++ ++ ++static struct backlight_ops simpad_bl_ops = { ++ .get_brightness = simpad_bl_get_intensity, ++ .update_status = simpad_bl_set_intensity, ++}; ++ ++ ++static int __init simpad_bl_probe(struct platform_device *pdev) ++{ ++ struct backlight_device *bd; ++ ++ bd = backlight_device_register("simpad-mq200-bl", &pdev->dev, NULL, &simpad_bl_ops); ++ ++ if (IS_ERR (bd)) ++ return PTR_ERR (bd); ++ ++ platform_set_drvdata(pdev, bd); ++ ++ bd->props.max_brightness = SIMPAD_MAX_INTENSITY; ++ bd->props.brightness = SIMPAD_DEFAULT_INTENSITY; ++ simpad_bl_send_intensity(bd); ++ ++ return 0; ++} ++ ++ ++static int simpad_bl_remove(struct platform_device *pdev) ++{ ++ struct backlight_device *bd = platform_get_drvdata(pdev); ++ ++ bd->props.brightness = 0; ++ bd->props.power = 0; ++ simpad_bl_send_intensity(bd); ++ ++ backlight_device_unregister(bd); ++ ++ return 0; ++} ++ ++static struct platform_driver simpad_bl_driver = { ++ .probe = simpad_bl_probe, ++ .remove = simpad_bl_remove, ++ .suspend = simpad_bl_suspend, ++ .resume = simpad_bl_resume, ++ .driver = { ++ .name = "simpad-mq200-bl", ++ }, ++}; ++ ++static struct platform_device *simpad_bl_device = NULL; ++ ++static int __init simpad_bl_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&simpad_bl_driver); ++ if (!ret) { ++ simpad_bl_device = platform_device_alloc("simpad-mq200-bl", -1); ++ if (!simpad_bl_device) ++ return -ENOMEM; ++ ++ ret = platform_device_add(simpad_bl_device); ++ ++ if (ret) { ++ platform_device_put(simpad_bl_device); ++ platform_driver_unregister(&simpad_bl_driver); ++ } ++ } ++ return ret; ++} ++ ++static void __exit simpad_bl_exit(void) ++{ ++ platform_device_unregister(simpad_bl_device); ++ platform_driver_unregister(&simpad_bl_driver); ++} ++ ++ ++module_init(simpad_bl_init); ++module_exit(simpad_bl_exit); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.27/drivers/video/backlight/simpad_lcd.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/video/backlight/simpad_lcd.c 2008-12-07 01:33:39.386863247 +0100 +@@ -0,0 +1,172 @@ ++/* ++ * GPLv2 <zecke@handhelds.org ++ * ++ * Implementation of the lcd_driver for the mq200 framebuffer ++ * ++ * 2007/03/24 mrdata: ++ * - added simpad_lcd_get_contrast() ++ * - added simpad_lcd_set_contrast() ++ * - modify struct lcd_properties simpad_lcd_props ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/fb.h> ++#include <linux/lcd.h> ++ ++#include <mach/simpad.h> ++#include <mach/hardware.h> ++ ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int); ++extern void clear_cs3_bit(int); ++ ++#define UNUSED(x) x=x ++ ++static int simpad_lcd_get_power(struct lcd_device* dev) ++{ ++ UNUSED(dev); ++ ++ return (get_cs3_shadow() & DISPLAY_ON) ? 0 : 4; ++} ++ ++static int simpad_lcd_set_power(struct lcd_device* dev, int power) ++{ ++ UNUSED(dev); ++ ++ if( power == 4 ) ++ clear_cs3_bit(DISPLAY_ON); ++ else ++ set_cs3_bit(DISPLAY_ON); ++ ++ return 0; ++} ++ ++static int simpad_lcd_get_contrast(struct lcd_device* dev) ++{ ++ UNUSED(dev); ++ ++ return 0; ++} ++ ++static int simpad_lcd_set_contrast(struct lcd_device* dev, int contrast) ++{ ++ UNUSED(dev); ++ ++ UNUSED(contrast); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int simpad_lcd_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ static int ret; ++ struct lcd_device* ld; ++ ++ UNUSED(state); ++ ++ ld = platform_get_drvdata(pdev); ++ ++ ret = simpad_lcd_set_power(ld, 4); ++ ++ return ret; ++} ++ ++static int simpad_lcd_resume(struct platform_device *pdev) ++{ ++ struct lcd_device *ld; ++ static int ret; ++ ++ ld = platform_get_drvdata(pdev); ++ ++ ret = simpad_lcd_set_power(ld, 0); ++ ++ return ret; ++} ++#else ++#define simpad_lcd_suspend NULL ++#define simpad_lcd_resume NULL ++#endif ++ ++ ++/*FIXME ++static struct lcd_properties simpad_lcd_props = { ++ .max_contrast = 0, ++}; ++*/ ++ ++static struct lcd_ops simpad_lcd_ops = { ++ .get_power = simpad_lcd_get_power, ++ .set_power = simpad_lcd_set_power, ++ .get_contrast = simpad_lcd_get_contrast, ++ .set_contrast = simpad_lcd_set_contrast, ++}; ++ ++static int __init simpad_lcd_probe(struct platform_device *pdev) ++{ ++ struct lcd_device *ld; ++ ++ ld = lcd_device_register ("simpad-mq200-lcd", &pdev->dev, NULL, &simpad_lcd_ops); ++ ++ if (IS_ERR(ld)) ++ return PTR_ERR(ld); ++ ++ platform_set_drvdata(pdev, ld); ++ ++ ld->props.max_contrast = 0; ++ ++ return 0; ++} ++ ++static int simpad_lcd_remove(struct platform_device *pdev) ++{ ++ struct lcd_device *ld = platform_get_drvdata(pdev); ++ ++ lcd_device_unregister(ld); ++ ++ return 0; ++} ++ ++static struct platform_driver simpad_lcd_driver = { ++ .probe = simpad_lcd_probe, ++ .remove = simpad_lcd_remove, ++ .suspend = simpad_lcd_suspend, ++ .resume = simpad_lcd_resume, ++ .driver = { ++ .name = "simpad-mq200-lcd", ++ }, ++}; ++ ++static struct platform_device *simpad_lcd_device = NULL; ++ ++static int __init simpad_lcd_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&simpad_lcd_driver); ++ if (!ret) { ++ simpad_lcd_device = platform_device_alloc("simpad-mq200-lcd", -1); ++ if (!simpad_lcd_device) ++ return -ENOMEM; ++ ++ ret = platform_device_add(simpad_lcd_device); ++ ++ if (ret) { ++ platform_device_put(simpad_lcd_device); ++ platform_driver_unregister(&simpad_lcd_driver); ++ } ++ } ++ return ret; ++} ++ ++static void __exit simpad_lcd_exit(void) { ++ platform_driver_unregister(&simpad_lcd_driver); ++ platform_device_unregister(simpad_lcd_device); ++} ++ ++module_init(simpad_lcd_init); ++module_exit(simpad_lcd_exit); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.27/drivers/video/mq200/Makefile +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/video/mq200/Makefile 2008-12-07 01:19:16.652836923 +0100 +@@ -0,0 +1,6 @@ ++# Makefile for mq200 video driver ++# 4 Aug 2003, Holger Hans Peter Freyther ++# ++ ++obj-$(CONFIG_FB_MQ200) += mq_skeleton.o mq_external.o ++ +Index: linux-2.6.27/drivers/video/mq200/mq200_data.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/video/mq200/mq200_data.h 2008-12-07 01:19:16.656170018 +0100 +@@ -0,0 +1,1120 @@ ++/* ++ * From ucLinux mq200fb.c and mq200fb.h ++ * ++ * 2007/03/11 mrdata: ++ * insert registers for graphics controller 2 module ++ */ ++ ++#ifndef __MQ200_FB_H__ ++#define __MQ200_FB_H__ ++ ++struct mq200_io_regions { ++ u32 fb_size; /* framebuffer size */ ++ unsigned long phys_mmio_base; /* physical register memory base */ ++ unsigned long virt_mmio_base; /* virtual start of registers */ ++ unsigned long phys_fb_base; /* physical address of frame buffer */ ++ unsigned long virt_fb_base; /* virtual start of the framebuffer */ ++}; ++ ++#define MQ200_MONITOR_HORI_RES(info) info->monitor_info.horizontal_res ++#define MQ200_MONITOR_VERT_RES(info) info->monitor_info.vertical_res ++#define MQ200_MONITOR_DEPTH(info) info->monitor_info.depth ++#define MQ200_MONITOR_LINE_LENGTH(info) info->monitor_info.line_length ++ ++struct mq200_monitor_info { ++ unsigned int horizontal_res; ++ unsigned int vertical_res; ++ unsigned int depth; ++ unsigned int refresh; ++ unsigned int line_length; ++ unsigned long flags; ++}; ++ ++ ++/** ++ * Addresses of Module ++ */ ++#define MQ200_FB_BASE (x) (x + 0x1800000) /* framebuffer */ ++#define MQ200_FB_SIZE 0x200000 /* framebuffer size in bytes */ ++#define MQ200_REGS_BASE(x) (x + 0x1e00000) /* start of registers area */ ++#define MQ200_REGS_SIZE 0x200000 /* registers area size */ ++ ++#define PMU_OFFSET 0x00000 /* power management */ ++#define CPU_OFFSET 0x02000 /* CPU interface */ ++#define MIU_OFFSET 0x04000 /* memory controller */ ++#define IN_OFFSET 0x08000 /* interrupt controller */ ++#define GC_OFFSET 0x0a000 /* graphics controller 1&2 */ ++#define GE_OFFSET 0x0c000 /* graphics engine */ ++#define FPI_OFFSET 0x0e000 /* flat panel controller */ ++#define CP1_OFFSET 0x10000 /* color palette 1 */ ++#define DC_OFFSET 0x14000 /* device configuration */ ++#define PCI_OFFSET 0x16000 /* PCI configuration */ ++#define PSF_OFFSET 0x18000 /* ??? */ ++ ++ ++/**** ++ * Registers ++ */ ++ ++/* power management unit */ ++#define PMR(addr) (addr + PCI_OFFSET + 0x40)/* power management ++ register */ ++#define PMR_VALUE 0x06210001 /* expected read value of PMR register */ ++#define PM00R(addr) (addr + PMU_OFFSET + 0x00) /* power management unit ++ configuration ++ register */ ++#define PM01R(addr) (addr + PMU_OFFSET + 0x04) /* D1 state control */ ++#define PM02R(addr) (addr + PMU_OFFSET + 0x08) /* d2 state control */ ++#define PM06R(addr) (addr + PMU_OFFSET + 0x18) /* PLL 2 programming */ ++#define PM07R(addr) (addr + PMU_OFFSET + 0x1c) /* PLL 3 programming */ ++ ++#define PMCSR(addr) (addr + PCI_OFFSET + 0x44) /* power management ++ control/status ++ register */ ++ ++/* memory interface unit */ ++#define MM00R(addr) (addr + MIU_OFFSET + 0x00)/* MIU interface control ++ 0 */ ++#define MM01R(addr) (addr + MIU_OFFSET + 0x04) /* MIU interface control ++ 1 */ ++#define MM02R(addr) (addr + MIU_OFFSET + 0x08) /* memory interface ++ control 2 */ ++#define MM03R(addr) (addr + MIU_OFFSET + 0x0c) /* memory interface ++ control 3 */ ++#define MM04R(addr) (addr + MIU_OFFSET + 0x10) /* memory interface ++ control 4 */ ++/* graphics controller 1 module */ ++#define GC00R(addr) (addr + GC_OFFSET + 0x00) /* graphics controller 1 ++ control */ ++#define GC01R(addr) (addr + GC_OFFSET + 0x04) /* graphics controller ++ CRT control */ ++#define GC02R(addr) (addr + GC_OFFSET + 0x08) /* horizontal display 1 ++ control */ ++#define GC03R(addr) (addr + GC_OFFSET + 0x0c) /* vertical display 1 ++ control */ ++#define GC04R(addr) (addr + GC_OFFSET + 0x10) /* horizontal sync 1 ++ control */ ++#define GC05R(addr) (addr + GC_OFFSET + 0x14) /* vertical sync 1 ++ control */ ++#define GC07R(addr) (addr + GC_OFFSET + 0x1c) /* vertical display 1 ++ count */ ++#define GC08R(addr) (addr + GC_OFFSET + 0x20) /* horizontal window 1 ++ control */ ++#define GC09R(addr) (addr + GC_OFFSET + 0x24) /* vertical window 1 ++ control */ ++#define GC0AR(addr) (addr + GC_OFFSET + 0x28) /* alternate horizontal ++ window 1 control */ ++#define GC0BR(addr) (addr + GC_OFFSET + 0x2c) /* alternate vertical ++ window 1 control */ ++#define GC0CR(addr) (addr + GC_OFFSET + 0x30) /* window 1 ++ start address */ ++#define GC0DR(addr) (addr + GC_OFFSET + 0x34) /* alternate window 1 ++ start address */ ++#define GC0ER(addr) (addr + GC_OFFSET + 0x38) /* alternate window 1 ++ stride */ ++#define GC0FR(addr) (addr + GC_OFFSET + 0x3c) /* alternate window 1 ++ line size */ ++#define GC10R(addr) (addr + GC_OFFSET + 0x40) /* hardware cursor 1 ++ position */ ++#define GC11R(addr) (addr + GC_OFFSET + 0x44) /* hardware cursor 1 ++ start address and ++ offset */ ++#define GC12R(addr) (addr + GC_OFFSET + 0x48) /* hardware cursor 1 ++ foreground color */ ++#define GC13R(addr) (addr + GC_OFFSET + 0x4c) /* hardware cursor 1 ++ background color */ ++ ++/* graphics controller 2 module */ ++#define GC20R(addr) (addr + GC_OFFSET + 0x80) /* graphics controller 2 ++ control */ ++#define GC21R(addr) (addr + GC_OFFSET + 0x84) /* graphics controller ++ CRC control */ ++#define GC22R(addr) (addr + GC_OFFSET + 0x88) /* horizontal display 2 ++ control */ ++#define GC23R(addr) (addr + GC_OFFSET + 0x8c) /* vertical display 2 ++ control */ ++#define GC24R(addr) (addr + GC_OFFSET + 0x90) /* horizontal sync 2 ++ control */ ++#define GC25R(addr) (addr + GC_OFFSET + 0x94) /* vertical sync 2 ++ control */ ++#define GC27R(addr) (addr + GC_OFFSET + 0x9c) /* vertical display 2 ++ count */ ++#define GC28R(addr) (addr + GC_OFFSET + 0xa0) /* horizontal window 2 ++ control */ ++#define GC29R(addr) (addr + GC_OFFSET + 0xa4) /* vertical window 2 ++ control */ ++#define GC2AR(addr) (addr + GC_OFFSET + 0xa8) /* alternate horizontal ++ window 2 control */ ++#define GC2BR(addr) (addr + GC_OFFSET + 0xac) /* alternate vertical ++ window 2 control */ ++#define GC2CR(addr) (addr + GC_OFFSET + 0xb0) /* window 2 ++ start address */ ++#define GC2DR(addr) (addr + GC_OFFSET + 0xb4) /* alternate window 2 ++ start address */ ++#define GC2ER(addr) (addr + GC_OFFSET + 0xb8) /* alternate window 2 ++ stride */ ++#define GC2FR(addr) (addr + GC_OFFSET + 0xbc) /* alternate window 2 ++ line size */ ++#define GC30R(addr) (addr + GC_OFFSET + 0xc0) /* hardware cursor 2 ++ position */ ++#define GC31R(addr) (addr + GC_OFFSET + 0xc4) /* hardware cursor 2 ++ start address and ++ offset */ ++#define GC32R(addr) (addr + GC_OFFSET + 0xc8) /* hardware cursor 2 ++ foreground color */ ++#define GC33R(addr) (addr + GC_OFFSET + 0xcc) /* hardware cursor 2 ++ background color */ ++ ++/* graphics engine */ ++#define ROP_SRCCOPY 0xCC /* dest = source */ ++#define ROP_SRCPAINT 0xEE /* dest = source OR dest */ ++#define ROP_SRCAND 0x88 /* dest = source AND dest */ ++#define ROP_SRCINVERT 0x66 /* dest = source XOR dest */ ++#define ROP_SRCERASE 0x44 /* dest = source AND (NOT dest) */ ++#define ROP_NOTSRCCOPY 0x33 /* dest = NOT source */ ++#define ROP_NOTSRCERASE 0x11 /* dest = (NOT source) AND (NOT dest) */ ++#define ROP_MERGECOPY 0xC0 /* dest = source AND pattern */ ++#define ROP_MERGEPAINT 0xBB /* dest = (NOT source) OR dest */ ++#define ROP_PATCOPY 0xF0 /* dest = pattern */ ++#define ROP_PATPAINT 0xFB /* dest = DPSnoo */ ++#define ROP_PATINVERT 0x5A /* dest = pattern XOR dest */ ++#define ROP_DSTINVERT 0x55 /* dest = NOT dest */ ++#define ROP_BLACKNESS 0x00 /* dest = BLACK */ ++#define ROP_WHITENESS 0xFF /* dest = WHITE */ ++ ++#define GE00R(addr) (addr + GE_OFFSET + 0x00) /* primary drawing command ++ register */ ++#define GE01R(addr) (addr + GE_OFFSET + 0x04) /* primary width and ++ height register */ ++#define GE02R(addr) (addr + GE_OFFSET + 0x08) /* primary destination ++ address register */ ++#define GE03R(addr) (addr + GE_OFFSET + 0x0c) /* primary source XY ++ register */ ++#define GE04R(addr) (addr + GE_OFFSET + 0x10) /* primary color compare ++ register */ ++#define GE05R(addr) (addr + GE_OFFSET + 0x14) /* primary clip left/top ++ register */ ++#define GE06R(addr) (addr + GE_OFFSET + 0x18) /* primary clip ++ right/bottom register ++ */ ++#define GE07R(addr) (addr + GE_OFFSET + 0x1c) /* primary source and ++ pattern offset ++ register */ ++#define GE08R(addr) (addr + GE_OFFSET + 0x20) /* primary foreground ++ color ++ register/rectangle ++ fill register */ ++#define GE09R(addr) (addr + GE_OFFSET + 0x24) /* source stride/offset ++ register */ ++#define GE0AR(addr) (addr + GE_OFFSET + 0x28) /* destination stride ++ register and color ++ depth */ ++#define GE0BR(addr) (addr + GE_OFFSET + 0x2c) /* image base address ++ register */ ++#define GE40R(addr) (addr + GE_OFFSET + 0x100) /* mono pattern register ++ 0 */ ++#define GE41R(addr) (addr + GE_OFFSET + 0x104) /* mono pattern register ++ 1 */ ++#define GE42R(addr) (addr + GE_OFFSET + 0x108) /* foreground color ++ register */ ++#define GE43R(addr) (addr + GE_OFFSET + 0x10c) /* background color ++ register */ ++/* color palette */ ++#define C1xxR(addr, regno) \ ++ (addr + CP1_OFFSET + (regno) * 4) /* graphics controller color ++ palette 1 */ ++/* device configuration */ ++#define DC00R(addr) (addr + DC_OFFSET + 0x00) /* device configuration ++ register 0 */ ++#define DC_RESET 0x4000 ++/* PCI configuration space */ ++#define PC00R(addr) (addr + PCI_OFFSET + 0x00)/* device ID/vendor ID ++ register */ ++/* Flatpanel Control */ ++#define FP00R(addr) (addr + FPI_OFFSET + 0x00) /* Flat Panel Control 0 */ ++#define FP01R(addr) (addr + FPI_OFFSET + 0x04) /* Flat Panel Output Pin */ ++#define FP02R(addr) (addr + FPI_OFFSET + 0x08) /* Flat Panel Gener Purpose ++ Outout Control Register */ ++#define FP03R(addr) (addr + FPI_OFFSET + 0x0c) /* General Purpose I/O Port ++ Control Register */ ++#define FP04R(addr) (addr + FPI_OFFSET + 0x10) /* STN Panel Control Register */ ++#define FP05R(addr) (addr + FPI_OFFSET + 0x14) /* D-STN Half Frame Buffer ++ Control Register -By Guess */ ++#define FP0FR(addr) (addr + FPI_OFFSET + 0x3c) /* Pulse Width Modulation ++ Control Register */ ++#define FRCTL_PATTERN_COUNT 32 ++#define FP10R(addr) (addr + FPI_OFFSET + 0x40) /* Frame-Rate Control Pattern ++ Register */ ++#define FP11R(addr) (addr + FPI_OFFSET + 0x44) ++#define FP2FR(addr) (addr + FPI_OFFSET + 0xc0) /* Frame-Rate Control Weight ++ Registers */ ++ ++ ++ ++ ++/* power management miscellaneous control */ ++union pm00r { ++ struct { ++ u32 pll1_n_b5 :1; /* PLL 1 N parameter bit 5 is 0 */ ++ u32 reserved_1 :1; ++ u32 pll2_enbl :1; /* PLL 2 enable */ ++ u32 pll3_enbl :1; /* PLL 3 enable */ ++ u32 reserved_2 :1; ++ u32 pwr_st_ctrl :1; /* power state status control */ ++ u32 reserved_3 :2; ++ ++ u32 ge_enbl :1; /* graphics engine enable */ ++ u32 ge_bsy_gl :1; /* graphics engine force busy (global) */ ++ u32 ge_bsy_lcl :1; /* graphics engine force busy (local) */ ++ u32 ge_clock :2; /* graphics engine clock select */ ++ u32 ge_cmd_fifo :1; /* graphics engine command FIFO reset */ ++ u32 ge_src_fifo :1; /* graphics engine CPU source FIFO reset */ ++ u32 miu_pwr_seq :1; /* memory interface unit power sequencing ++ enable */ ++ ++ u32 d3_mem_rfsh :1; /* D3 memory refresh */ ++ u32 d4_mem_rfsh :1; /* D4 memory refresh */ ++ u32 gpwr_intrvl :2; /* general power sequencing interval */ ++ u32 fppwr_intrvl:2; /* flat panel power sequencing interval */ ++ u32 gpwr_seq_ctr:1; /* general power sequencing interval control */ ++ u32 pmu_tm :1; /* PMU test mode */ ++ ++ u32 pwr_state :2; /* power state (read only) */ ++ u32 pwr_seq_st :1; /* power sequencing active status (read ++ only) */ ++ u32 reserved_4 :5; ++ } part; ++ u32 whole; ++}; ++ ++/* D1 state control */ ++union pm01r { ++ struct { ++ u32 osc_enbl :1; /* D1 oscillator enable */ ++ u32 pll1_enbl :1; /* D1 PLL 1 enable */ ++ u32 pll2_enbl :1; /* D1 PLL 2 enable */ ++ u32 pll3_enbl :1; /* D1 PLL 3 enable */ ++ u32 miu_enbl :1; /* D1 Memory Interface Unit (MIU) enable */ ++ u32 mem_rfsh :1; /* D1 memory refresh enable */ ++ u32 ge_enbl :1; /* D1 Graphics Engine (GE) enable */ ++ u32 reserved_1 :1; ++ ++ u32 crt_enbl :1; /* D1 CRT enable */ ++ u32 fpd_enbl :1; /* D1 Flat Panel enable */ ++ u32 reserved_2 :6; ++ ++ u32 ctl1_enbl :1; /* D1 controller 1 enable */ ++ u32 win1_enbl :1; /* D1 window 1 enable */ ++ u32 awin1_enbl :1; /* D1 alternate window 1 enable */ ++ u32 cur1_enbl :1; /* D1 cursor 1 enable */ ++ u32 reserved_3 :4; ++ ++ u32 ctl2_enbl :1; /* D1 controller 2 enable */ ++ u32 win2_enbl :1; /* D1 window 2 enable */ ++ u32 awin2_enbl :1; /* D1 alternate window 2 enable */ ++ u32 cur2_enbl :1; /* D1 cursor 2 enable */ ++ u32 reserved_4 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* D2 state control */ ++union pm02r { ++ struct { ++ u32 osc_enbl :1; /* D2 oscillator enable */ ++ u32 pll1_enbl :1; /* D2 PLL 1 enable */ ++ u32 pll2_enbl :1; /* D2 PLL 2 enable */ ++ u32 pll3_enbl :1; /* D2 PLL 3 enable */ ++ u32 miu_enbl :1; /* D2 Memory Interface Unit (MIU) enable */ ++ u32 mem_rfsh :1; /* D2 memory refresh enable */ ++ u32 ge_enbl :1; /* D2 Graphics Engine (GE) enable */ ++ u32 reserved_1 :1; ++ ++ u32 crt_enbl :1; /* D2 CRT enable */ ++ u32 fpd_enbl :1; /* D2 Flat Panel enable */ ++ u32 reserved_2 :6; ++ ++ u32 ctl1_enbl :1; /* D2 controller 1 enable */ ++ u32 win1_enbl :1; /* D2 window 1 enable */ ++ u32 awin1_enbl :1; /* D2 alternate window 1 enable */ ++ u32 cur1_enbl :1; /* D2 cursor 1 enable */ ++ u32 reserved_3 :4; ++ ++ u32 ctl2_enbl :1; /* D2 controller 2 enable */ ++ u32 win2_enbl :1; /* D2 window 2 enable */ ++ u32 awin2_enbl :1; /* D2 alternate window 2 enable */ ++ u32 cur2_enbl :1; /* D2 cursor 2 enable */ ++ u32 reserved_4 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* PLL 2 programming */ ++union pm06r { ++ struct { ++ u32 clk_src :1; /* PLL 2 reference clock source */ ++ u32 bypass :1; /* PLL 2 bypass */ ++ u32 reserved_1 :2; ++ u32 p_par :3; /* PLL 2 P parameter */ ++ u32 reserved_2 :1; ++ ++ u32 n_par :5; /* PLL 2 N parameter */ ++ u32 reserved_3 :3; ++ ++ u32 m_par :8; /* PLL 2 M parameter */ ++ ++ u32 reserved_4 :4; ++ u32 trim :4; /* PLL 2 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++/* PLL 3 programming */ ++union pm07r { ++ struct { ++ u32 clk_src :1; /* PLL 3 reference clock source */ ++ u32 bypass :1; /* PLL 3 bypass */ ++ u32 reserved_1 :2; ++ u32 p_par :3; /* PLL 3 P parameter */ ++ u32 reserved_2 :1; ++ ++ u32 n_par :5; /* PLL 3 N parameter */ ++ u32 reserved_3 :3; ++ ++ u32 m_par :8; /* PLL 3 M parameter */ ++ ++ u32 reserved_4 :4; ++ u32 trim :4; /* PLL 3 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++ ++ ++/* MIU interface control 1 */ ++union mm00r { ++ struct { ++ u32 miu_enbl :1; /* MIU enable bit */ ++ u32 mr_dsbl :1; /* MIU reset disable bit */ ++ u32 edr_dsbl :1; /* embedded DRAM reset disable bit */ ++ u32 reserved_1 :29; ++ } part; ++ u32 whole; ++}; ++ ++/* MIU interface control 2 */ ++union mm01r { ++ struct { ++ u32 mc_src :1; /* memory clock source */ ++ u32 msr_enbl :1; /* memory slow refresh enable bit */ ++ u32 pb_cpu :1; /* page break enable for CPU */ ++ u32 pb_gc1 :1; /* page break enable for GC1 */ ++ u32 pb_gc2 :1; /* page break enable for GC2 */ ++ u32 pb_stn_r :1; /* page break enable for STN read */ ++ u32 pb_stn_w :1; /* page break enable for STN write */ ++ u32 pb_ge :1; /* page break enable for GE */ ++ u32 reserved_1 :4; ++ u32 mr_interval :14; /* normal memory refresh time interval */ ++ u32 reserved_2 :4; ++ u32 edarm_enbl :1; /* embedded DRAM auto-refresh mode enable */ ++ u32 eds_enbl :1; /* EDRAM standby enable for EDRAM normal ++ mode operation */ ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 3 */ ++union mm02r { ++ struct { ++ u32 bs_ :2; ++ u32 bs_stnr :2; /* burst count for STN read memory cycles */ ++ u32 bs_stnw :2; /* burst count for STN write memroy cycles */ ++ u32 bs_ge :2; /* burst count for graphics engine ++ read/write memroy cycles */ ++ u32 bs_cpuw :2; /* burst count for CPU write memory cycles */ ++ u32 fifo_gc1 :4; /* GC1 display refresh FIFO threshold */ ++ u32 fifo_gc2 :4; /* GC2 display refresh FIFO threshold */ ++ u32 fifo_stnr :4; /* STN read FIFO threshold */ ++ u32 fifo_stnw :4; /* STN write FIFO threshold */ ++ u32 fifo_ge_src :3; /* GE source read FIFO threshold */ ++ u32 fifo_ge_dst :3; /* GE destination read FIFO threshold */ ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 4 */ ++union mm03r { ++ struct { ++ u32 rd_late_req :1; /* read latency request */ ++ u32 reserved_1 :31; ++ } part; ++ u32 whole; ++}; ++ ++/* memory interface control 5 */ ++union mm04r { ++ struct { ++ u32 latency :3; /* EDRAM latency */ ++ u32 dmm_cyc :1; /* enable for the dummy cycle insertion ++ between read and write cycles */ ++ u32 pre_dmm_cyc :1; /* enable for the dummy cycle insertion ++ between read/write and precharge cycles ++ for the same bank */ ++ u32 reserved_1 :3; ++ u32 bnk_act_cls :2; /* bank activate command to bank close ++ command timing interval control */ ++ u32 bnk_act_rw :1; /* bank activate command to read/wirte ++ command timing interval control */ ++ u32 bnk_cls_act :1; /* bank close command to bank activate ++ command timing interval control */ ++ u32 trc :1; /* row cycle time */ ++ u32 reserved_2 :3; ++ u32 delay_r :2; /* programmable delay for read clock */ ++ u32 delay_m :2; /* programmable delay for internal memory ++ clock */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller 1 register */ ++union gc00r { ++ struct { ++ u32 ctl_enbl :1; /* Controller 1 Enable */ ++ u32 hc_reset :1; /* Horizontal Counter 1 Reset */ ++ u32 vc_reset :1; /* Vertical Counter 1 Reset */ ++ u32 iwin_enbl :1; /* Image Window 1 Enable */ ++ u32 gcd :4; /* Graphics Color Depth (GCD) */ ++ ++ u32 hc_enbl :1; /* Hardware Cursor 1 Enable */ ++ u32 reserved_1 :2; ++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */ ++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */ ++ ++ u32 g1rclk_src :2; /* G1RCLK Source */ ++ u32 tm0 :1; /* Test Mode 0 */ ++ u32 tm1 :1; /* Test Mode 1 */ ++ u32 fd :3; /* G1MCLK First Clock Divisor (FD1) */ ++ u32 reserved_2 :1; ++ ++ u32 sd :8; /* G1MCLK Second Clock Divisor (SD1) */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller CRT control */ ++union gc01r { ++ struct { ++ u32 dac_enbl :2; /* CRT DAC enable */ ++ u32 hsync_out :1; /* CRT HSYNC output during power down mode */ ++ u32 vsync_out :1; /* CRT VSYNC output during power down mode */ ++ u32 hsync_ctl :2; /* CRT HSYNC control */ ++ u32 vsync_ctl :2; /* CRT VSYNC control */ ++ /**/ ++ u32 hsync_pol :1; /* CRT HSYNC polarity */ ++ u32 vsync_pol :1; /* CRT VSYNC polarity */ ++ u32 sync_p_enbl :1; /* sync pedestal enable */ ++ u32 blnk_p_enbl :1; /* blank pedestal enable */ ++ u32 c_sync_enbl :1; /* composite sync enable */ ++ u32 vref_sel :1; /* VREF select */ ++ u32 mn_sns_enbl :1; /* monitor sense enable */ ++ u32 ct_out_enbl :1; /* constant output enable */ ++ /**/ ++ u32 dac_out_lvl :8; /* monitor sense DAC output level */ ++ /**/ ++ u32 blue_dac_r :1; /* blue DAC sense result */ ++ u32 green_dac_r :1; /* green DAC sense result */ ++ u32 red_dac_r :1; /* red DAC sense result */ ++ u32 reserved_1 :1; ++ u32 mon_col_sel :1; /* mono/color monitor select */ ++ u32 reserved_2 :3; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal display 1 control */ ++union gc02r { ++ struct { ++ u32 hd1t :12; /* horizontal display 1 total */ ++ u32 reserved_1 :4; ++ ++ u32 hd1e :12; /* horizontal display 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 1 control */ ++union gc03r { ++ struct { ++ u32 vd1t :12; /* vertical display 1 total */ ++ u32 reserved_1 :4; ++ ++ u32 vd1e :12; /* vertical display 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal sync 1 control */ ++union gc04r { ++ struct { ++ u32 hs1s :12; /* horizontal sync 1 start */ ++ u32 reserved_1 :4; ++ ++ u32 hs1e :12; /* horizontal sync 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical sync 1 control */ ++union gc05r { ++ struct { ++ u32 vs1s :12; /* vertical sync 1 start */ ++ u32 reserved_1 :4; ++ ++ u32 vs1e :12; /* vertical sync 1 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 1 count */ ++union gc07r { ++ struct { ++ u32 vd_cnt :12; /* vertical display 1 count */ ++ u32 reverved_1 :20; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal window 1 control */ ++union gc08r { ++ struct { ++ u32 hw1s :12; /* horizontal window 1 start (HW1S) */ ++ u32 reserved_1 :4; ++ ++ u32 hw1w :12; /* horizontal window 1 width (HW1W) */ ++ u32 w1ald :4; /* window 1 additional line data */ ++ } part; ++ u32 whole; ++}; ++ ++/* vertical window 1 control */ ++union gc09r { ++ struct { ++ u32 vw1s :12; /* vertical window 1 start */ ++ u32 reserved_1 :4; ++ u32 vw1h :12; /* vertical window 1 height */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* window 1 start address */ ++union gc0cr { ++ struct { ++ u32 w1sa :21; /* window 1 start address */ ++ u32 reserved_1 :11; ++ } part; ++ u32 whole; ++}; ++ ++/* window 1 stride */ ++union gc0er { ++ struct { ++ s16 w1st; /* window 1 stride */ ++ s16 aw1st; /* alternate window 1 stride */ ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 position */ ++union gc10r { ++ struct { ++ u32 hc1s :12; /* horizontal cursor 1 start */ ++ u32 reserved_1 :4; ++ u32 vc1s :12; /* vertical cursor 1 start */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 start address and offset */ ++union gc11r { ++ struct { ++ u32 hc1sa :11; /* hardware cursor 1 start address */ ++ u32 reserved_1 :5; ++ u32 hc1o :6; /* horizontal cursor 1 offset */ ++ u32 reserved_2 :2; ++ u32 vc1o :6; /* vertical cursor 1 offset */ ++ u32 reserved_3 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 foreground color */ ++union gc12r { ++ struct { ++ u32 hc1fc :24; /* hardware cursor 1 foreground color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 1 background color */ ++union gc13r { ++ struct { ++ u32 hc1bc :24; /* hardware cursor 1 background color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++ ++/* graphics controller 2 register */ ++union gc20r { ++ struct { ++ u32 ctl_enbl :1; /* Controller 2 Enable */ ++ u32 hc_reset :1; /* Horizontal Counter 2 Reset */ ++ u32 vc_reset :1; /* Vertical Counter 2 Reset */ ++ u32 iwin_enbl :1; /* Image Window 2 Enable */ ++ u32 gcd :4; /* Graphics Color Depth (GCD) */ ++ ++ u32 hc_enbl :1; /* Hardware Cursor 2 Enable */ ++ u32 reserved_1 :2; ++ u32 aiwin_enbl :1; /* Alternate Image Window Enable */ ++ u32 agcd :4; /* Alternate Graphics Color Depth (AGCD) */ ++ ++ u32 g2rclk_src :2; /* G2RCLK Source */ ++ u32 tm0 :1; /* Test Mode 0 */ ++ u32 tm1 :1; /* Test Mode 1 */ ++ u32 fd :3; /* G2MCLK First Clock Divisor (FD1) */ ++ u32 reserved_2 :1; ++ ++ u32 sd :8; /* G2MCLK Second Clock Divisor (SD1) */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller CRC control */ ++union gc21r { ++ struct { ++ u32 crc_enbl :1; /* CRC enable */ ++ u32 vsync_wait :1; /* CRC input data control waitime of VSYNC */ ++ u32 crc_o_sel :2; /* CRC output select */ ++ u32 reserved_1 :4; ++ u32 crc_result :22; /* CRC result (read only) */ ++ u32 reserved_2 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal display 2 control */ ++union gc22r { ++ struct { ++ u32 hd2t :12; /* horizontal display 2 total */ ++ u32 reserved_1 :4; ++ ++ u32 hd2e :12; /* horizontal display 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 2 control */ ++union gc23r { ++ struct { ++ u32 vd2t :12; /* vertical display 2 total */ ++ u32 reserved_1 :4; ++ ++ u32 vd2e :12; /* vertical display 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal sync 2 control */ ++union gc24r { ++ struct { ++ u32 hs2s :12; /* horizontal sync 2 start */ ++ u32 reserved_1 :4; ++ ++ u32 hs2e :12; /* horizontal sync 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical sync 2 control */ ++union gc25r { ++ struct { ++ u32 vs2s :12; /* vertical sync 2 start */ ++ u32 reserved_1 :4; ++ ++ u32 vs2e :12; /* vertical sync 2 end */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* vertical display 2 count */ ++union gc27r { ++ struct { ++ u32 vd_cnt :12; /* vertical display 2 count */ ++ u32 reverved_1 :20; ++ } part; ++ u32 whole; ++}; ++ ++/* horizontal window 2 control */ ++union gc28r { ++ struct { ++ u32 hw2s :12; /* horizontal window 2 start (HW2S) */ ++ u32 reserved_1 :4; ++ ++ u32 hw2w :12; /* horizontal window 2 width (HW2W) */ ++ u32 w2ald :4; /* window 2 additional line data */ ++ } part; ++ u32 whole; ++}; ++ ++/* vertical window 2 control */ ++union gc29r { ++ struct { ++ u32 vw2s :12; /* vertical window 2 start */ ++ u32 reserved_1 :4; ++ u32 vw2h :12; /* vertical window 2 height */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* window 2 start address */ ++union gc2cr { ++ struct { ++ u32 w2sa :21; /* window 2 start address */ ++ u32 reserved_1 :11; ++ } part; ++ u32 whole; ++}; ++ ++/* window 2 stride */ ++union gc2er { ++ struct { ++ s16 w2st; /* window 2 stride */ ++ s16 aw2st; /* alternate window 2 stride */ ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 position */ ++union gc30r { ++ struct { ++ u32 hc2s :12; /* horizontal cursor 2 start */ ++ u32 reserved_1 :4; ++ u32 vc2s :12; /* vertical cursor 2 start */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 start address and offset */ ++union gc31r { ++ struct { ++ u32 hc2sa :11; /* hardware cursor 2 start address */ ++ u32 reserved_1 :5; ++ u32 hc2o :6; /* horizontal cursor 2 offset */ ++ u32 reserved_2 :2; ++ u32 vc2o :6; /* vertical cursor 2 offset */ ++ u32 reserved_3 :2; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 foreground color */ ++union gc32r { ++ struct { ++ u32 hc2fc :24; /* hardware cursor 2 foreground color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++/* hardware cursor 2 background color */ ++union gc33r { ++ struct { ++ u32 hc2bc :24; /* hardware cursor 2 background color */ ++ u32 reserved_1 :8; ++ } part; ++ u32 whole; ++}; ++ ++ ++/* primary drawing command register */ ++union ge00r { ++ struct { ++ u32 rop :8; /* raster operation */ ++ /**/ ++ u32 cmd_typ :3; /* command type */ ++ u32 x_dir :1; /* x direction */ ++ u32 y_dir :1; /* y direction */ ++ u32 src_mem :1; /* source memory */ ++ u32 mon_src :1; /* mono source */ ++ u32 mon_ptn :1; /* mono pattern */ ++ /**/ ++ u32 dst_trns_e :1; /* destination transparency enable */ ++ u32 dst_trns_p :1; /* destination transparency polarity */ ++ u32 mon_trns_e :1; /* mono source or mono pattern transparency ++ enable */ ++ u32 mon_trns_p :1; /* mono transparency polarity */ ++ u32 mod_sel :1; /* memory to screen or off screen to screen ++ mode select */ ++ u32 alpha_sel :2; /* Alpha byte mask selection */ ++ u32 sol_col :1; /* solid color */ ++ /**/ ++ u32 stride_eq :1; /* source stride is equal to destination ++ stride */ ++ u32 rop2_sel :1; /* ROP2 code selection */ ++ u32 clipping :1; /* enable clipping */ ++ u32 auto_exec :1; /* auto execute */ ++ u32 reserved_1 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* primary width and height register */ ++union ge01r { ++ struct { ++ u32 width :12; /* source/destination window width */ ++ u32 reserved_1 :4; ++ ++ u32 height :12; /* source/destination window height */ ++ u32 reserved_2 :1; ++ u32 reserved_3 :3; ++ } bitblt; ++ struct { ++ u32 dm :17; ++ u32 axis_major :12; ++ u32 x_y :1; /* x-major or y-major */ ++ u32 last_pix :1; /* decision to draw or not to draw the last ++ pixel of the line */ ++ u32 reserved_1 :1; ++ } bresenham; ++ u32 whole; ++}; ++ ++/* primary destination address register */ ++union ge02r { ++ struct { ++ u32 dst_x :12; /* destination x position */ ++ u32 reserved_1 :1; ++ u32 h_offset :3; /* mono/color pattern horizontal offset */ ++ ++ u32 dst_y :12; /* destination y position */ ++ u32 reserved_2 :1; ++ u32 v_offset :3; /* mono/color pattern vertical offset */ ++ } window; ++ struct { ++ u32 x :12; /* starting x coordinate */ ++ u32 dm :17; /* 17 bits major-axis delta */ ++ u32 reserved_1 :3; ++ } line; ++ u32 whole; ++}; ++ ++/* source XY register/line draw starting Y coordinate and mintor axis delta */ ++union ge03r { ++ struct { ++ u32 src_x :12; /* source X position */ ++ u32 reserved_1 :4; ++ ++ u32 src_y :12; /* source Y position */ ++ u32 reserved_2 :4; ++ } window; ++ struct { ++ u32 start_y :12; /* starting Y coordinate */ ++ u32 dn :17; /* 17 bits minor-axis delta */ ++ u32 reserved_1 :3; ++ } line; ++ u32 whole; ++}; ++ ++/* clip left/top register */ ++union ge05r { ++ struct { ++ u32 left :12; /* left edge of clipping rectangle */ ++ u32 reserved_1 :4; ++ ++ u32 top :12; /* top edge of clipping rectangle */ ++ u32 reserved_2 :4; ++ } part; ++ u32 whole; ++}; ++ ++/* source stride/offset register */ ++union ge09r { ++ struct { ++ u32 src_strid :12; /* source line stride */ ++ u32 reserved_1 :13; ++ u32 strt_bit :3; /* initial mono source bit offset */ ++ u32 strt_byte :3; /* initial mono/color source byte offset */ ++ u32 reserved_2 :1; ++ } line; ++ struct { ++ u32 strt_bit :5; /* initial mono source bit offset */ ++ u32 reserved_1 :1; ++ u32 amount :10; /* number of 16 bytes amount that MIU need ++ to fetch from frame buffer */ ++ ++ u32 reserved_2 :9; ++ u32 bit_spc :7; /* bit space between lines */ ++ } pack_mono; ++ struct { ++ u32 strt_bit :3; /* initial mono source bit offset */ ++ u32 strt_byte :3; /* initial mono/color source byte offset */ ++ u32 amount :10; /* number of 16 bytes amount that MIU need ++ to fetch from frame buffer */ ++ ++ u32 reserved_1 :9; ++ u32 bit_spc :3; /* bit space between lines */ ++ u32 byt_spc :4; /* byte space between lines */ ++ } pack_color; ++ u32 whole; ++}; ++ ++/* destination stride register and color depth */ ++union ge0ar { ++ struct { ++ u32 dst_strid :12; /* destination line stride and color depth */ ++ u32 reserved_1 :18; ++ u32 col_dpth :2; /* color depth */ ++ } part; ++ u32 whole; ++}; ++ ++/* graphics controller color pallete */ ++union c1xxr { ++ struct { ++ u8 red; /* red color pallete */ ++ u8 green; /* green/gray color pallete */ ++ u8 blue; /* blue color palette */ ++ u8 reserved_1; ++ } part; ++ u32 whole; ++}; ++ ++/* devicee configuration register 0 */ ++union dc00r { ++ struct { ++ u32 osc_bypass :1; /* oscillator bypass */ ++ u32 osc_enbl :1; /* oscillator enable */ ++ u32 pll1_bypass :1; /* PLL1 bypass */ ++ u32 pll1_enbl :1; /* PLL1 enable */ ++ u32 pll1_p_par :3; /* PLL1 P parameter */ ++ u32 cpu_div :1; /* CPU interface clock divisor */ ++ u32 pll1_n_par :5; /* PLL1 N parameter */ ++ u32 saisc :1; /* StrongARM interface synchronizer control */ ++ u32 s_chp_reset :1; /* software chip reset */ ++ u32 mem_enbl :1; /* memory standby enable */ ++ u32 pll1_m_par :8; /* PLL 1 M parameter */ ++ u32 osc_shaper :1; /* oscillator shaper disable */ ++ u32 fast_pwr :1; /* fast power sequencing */ ++ u32 osc_frq :2; /* oscillator frequency select */ ++ u32 pll1_trim :4; /* PLL 1 trim value */ ++ } part; ++ u32 whole; ++}; ++ ++/* device ID/vendor ID register */ ++union pc00r { ++ struct { ++ u16 device; /* device ID */ ++ u16 vendor; /* vendor ID */ ++ } part; ++ u32 whole; ++}; ++ ++/* Flat Panel Control Register */ ++union fp00r { ++ struct { ++ u32 flatp_enbl : 2; /* Flat Panel Interface Enable */ ++ u32 flatp_type : 2; /* Flat Panel Type */ ++ u32 mono : 1; /* Mono/Color Panel Select */ ++ u32 flatp_intf : 3; /* Flat Panel Interface */ ++ u32 dither_pat : 2; /* Dither Pattern */ ++ u32 reserved : 2; /* Reserved Must Be 0*/ ++ u32 dither_col : 3; /* Dither Base Color */ ++ u32 alt_win_ctl: 1; /* Alternate Window Control */ ++ u32 frc_ctl : 2; /* FRC Control */ ++ u32 dither_adj1: 6; /* Dither Pattern Adjust 1 */ ++ u32 dither_adj2: 3; /* Dither Pattern Adjust 2 */ ++ u32 dither_adj3: 1; /* Dither Pattern Adjust 3 */ ++ u32 test_mode0 : 1; /* Test Mode 0 */ ++ u32 test_mode1 : 1; /* Test Mode 1 */ ++ u32 test_mode2 : 1; /* Test Mode 2 */ ++ u32 test_mode3 : 1; /* Test Mode 3 */ ++ } part; ++ u32 whole; ++}; ++ ++union fp01r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp02r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp03r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp04r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp05r { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++union fp0fr { ++ struct { ++ u32 dummy; ++ } part; ++ u32 whole; ++}; ++ ++ ++ ++ ++/**** ++ * Others ++ */ ++ ++#define CHIPNAME "MQ-200" ++ ++extern void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr); ++extern void mq200_external_setqmode(struct mq200_monitor_info*, unsigned long, spinlock_t *); ++extern void mq200_external_offdisplay(unsigned long); ++extern void mq200_external_ondisplay (unsigned long); ++extern int mq200_external_probe(unsigned long); ++ ++ ++ ++#endif +Index: linux-2.6.27/drivers/video/mq200/mq_external.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/video/mq200/mq_external.c 2008-12-07 01:35:01.269407967 +0100 +@@ -0,0 +1,513 @@ ++/* ++ * Copyright (C) 2005 Holger Hans Peter Freyther ++ * ++ * Based ON: ++ * ++ * linux/drivers/video/mq200fb.c -- MQ-200 for a frame buffer device ++ * based on linux/driver/video/pm2fb.c ++ * ++ * 2007/03/11 mrdata: ++ * bug found in gc1_reset(), renaming to gc1_gc2_reset() ++ * extend mq200_external_ondisplay() -> LCD for GC2 and CRT for GC1 ++ * ++ * Copyright (C) 2000 Lineo, Japan ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ */ ++ ++#include <asm/types.h> ++#include <asm/io.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++ ++#include <mach/hardware.h> ++ ++#include "mq200_data.h" ++ ++ ++#if 1 ++#define PRINTK(args...) printk(args) ++#else ++#define PRINTK(args...) ++#endif ++ ++ ++/**** ++ * power state transition to "state". ++ */ ++static void ++power_state_transition(unsigned long register_base, int state) ++{ ++ int i; ++ writel(state, PMCSR(register_base)); ++ mdelay(300); ++ for (i = 1; ; i++) { ++ udelay(100); ++ if ((readl(PMCSR(register_base)) & 0x3) == state) { ++ break; ++ } ++ } ++} ++ ++ ++/**** ++ * device configuration initialization. ++ */ ++static void ++dc_reset(unsigned long register_base) ++{ ++ union dc00r dc00r; ++ ++ /* Reset First */ ++ dc00r.whole = DC_RESET; ++ writel(dc00r.whole, DC00R(register_base)); ++ mdelay(100); ++ ++ dc00r.whole = 0xEF2082A; ++ writel(dc00r.whole, DC00R(register_base)); ++ mdelay(300); ++ PRINTK(CHIPNAME ": DC00R = 0x%08X\n", readl(DC00R(register_base))); ++} ++ ++ ++/**** ++ * initialize memory interface unit. ++ */ ++static void ++miu_reset(unsigned long register_base) ++{ ++ union mm00r mm00r; ++ union mm01r mm01r; ++ union mm02r mm02r; ++ union mm03r mm03r; ++ union mm04r mm04r; ++ ++ /* MIU interface control 1 */ ++ mm00r.whole = 0x4; ++ writel(mm00r.whole, MM00R(register_base)); ++ mdelay(50); ++ writel(0, MM00R(register_base)); ++ mdelay(50); ++ ++ /* MIU interface control 2 ++ * o PLL 1 output is used as memory clock source. ++ */ ++ mm01r.whole = 0x4143e086; ++ writel(mm01r.whole, MM01R(register_base)); ++ ++ /* memory interface control 3 */ ++ mm02r.whole = 0x6d6aabff; ++ writel(mm02r.whole, MM02R(register_base)); ++ ++ /* memory interface control 5 */ ++ mm04r.whole = 0x10d; ++ writel(mm04r.whole, MM04R(register_base)); ++ ++ /* memory interface control 4 */ ++ mm03r.whole = 0x1; ++ writel(mm03r.whole, MM03R(register_base)); ++ mdelay(50); ++ ++ /* MIU interface control 1 */ ++ mm00r.whole = 0x3; ++ writel(mm00r.whole, MM00R(register_base)); ++ mdelay(50); ++} ++ ++/**** ++ * ++ */ ++static ++void fpctrl_reset(unsigned long addr) ++{ ++ /* ++ * We're in D0 State, let us set the FPCTRL ++ */ ++ union fp00r fp00r; ++ union fp01r fp01r; ++ union fp02r fp02r; ++ union fp03r fp03r; ++ union fp04r fp04r; ++ union fp0fr fp0fr; ++ ++ fp00r.whole = 0x6320; ++ writel(fp00r.whole, FP00R(addr)); ++ ++ fp01r.whole = 0x20; ++ writel(fp01r.whole, FP01R(addr)); ++ ++ fp04r.whole = 0xBD0001; ++ writel(fp04r.whole, FP04R(addr)); ++ ++ /* Set Flat Panel General Purpose register first */ ++ fp02r.whole = 0x0; ++ writel(fp02r.whole, FP02R(addr)); ++ ++ fp03r.whole = 0x0; ++ writel(fp03r.whole, FP03R(addr)); ++ ++ fp0fr.whole = 0xA16c44; ++ writel(fp0fr.whole, FP0FR(addr)); ++ ++ /* Set them again */ ++ fp02r.whole = 0x0; ++ writel(fp02r.whole, FP02R(addr)); ++ ++ fp03r.whole = 0x0; ++ writel(fp03r.whole, FP03R(addr)); ++} ++ ++ ++/**** ++ * initialize power management unit. ++ */ ++static void ++pmu_reset(unsigned long register_base) ++{ ++ union pm00r pm00r; ++ union pm01r pm01r; ++ union pm02r pm02r; ++ ++ /* power management miscellaneous control ++ * o GE is driven by PLL 1 clock. ++ */ ++ pm00r.whole = 0xc0900; ++ writel(pm00r.whole, PM00R(register_base)); ++ ++ /* D1 state control */ ++ pm01r.whole = 0x5000271; ++ writel(pm01r.whole, PM01R(register_base)); ++ ++ /* D2 state control */ ++ pm02r.whole = 0x271; ++ writel(pm02r.whole, PM02R(register_base)); ++} ++ ++/**** ++ * initialize graphics controller 1 ++ * and graphics controller 2 ++ */ ++static void ++gc1_gc2_reset(unsigned long register_base, spinlock_t *lock ) ++{ ++ unsigned long flags; ++ union gc00r gc00r; ++ union gc01r gc01r; ++ union gc02r gc02r; ++ union gc03r gc03r; ++ union gc04r gc04r; ++ union gc05r gc05r; ++ union gc08r gc08r; ++ union gc09r gc09r; ++ union gc0cr gc0cr; ++ union gc0er gc0er; ++ union gc20r gc20r; ++ union gc22r gc22r; ++ union gc23r gc23r; ++ union gc24r gc24r; ++ union gc25r gc25r; ++ union gc28r gc28r; ++ union gc29r gc29r; ++ union gc2cr gc2cr; ++ union gc2er gc2er; ++ ++ union pm00r pm00r; ++ union pm06r pm06r; ++ union pm06r pm07r; ++ ++ spin_lock_irqsave(lock, flags); ++ ++ /* alternate window 1 stride */ ++ gc0er.whole = 0x640; ++ writel(gc0er.whole, GC0ER(register_base)); ++ ++ /* image window 1 start address */ ++ gc0cr.whole = 0x0; ++ writel(gc0cr.whole, GC0CR(register_base)); ++ ++ /* alternate window 2 stride */ ++ gc2er.whole = 0x640; ++ writel(gc0er.whole, GC2ER(register_base)); ++ ++ /* image window 2 start address */ ++ gc2cr.whole = 0x0; ++ writel(gc2cr.whole, GC2CR(register_base)); ++ ++ /* read PM Register */ ++ pm00r.whole = readl(PM00R(register_base)); ++ ++ /* horizontal window 1 control */ ++ gc08r.whole = 0x131f0000; ++ writel(gc08r.whole, GC08R(register_base)); ++ ++ /* vertical window 1 control */ ++ gc09r.whole = 0x12570000; ++ writel(gc09r.whole, GC09R(register_base)); ++ ++ /* horizontal display 1 control */ ++ gc02r.whole = 0x320041e; ++ writel(gc02r.whole, GC02R(register_base)); ++ ++ /* vertical display 1 control */ ++ gc03r.whole = 0x2570273; ++ writel(gc03r.whole, GC03R(register_base)); ++ ++ /* horizontal sync 1 control */ ++ gc04r.whole = 0x3c70347; ++ writel(gc04r.whole, GC04R(register_base)); ++ ++ /* vertical sync 1 control */ ++ gc05r.whole = 0x25d0259; ++ writel(gc05r.whole, GC05R(register_base)); ++ ++ /* graphics controller CRT control */ ++ gc01r.whole = 0x800; ++ writel(gc01r.whole, GC01R(register_base)); ++ ++ /* PLL 2 programming */ ++ pm06r.whole = 0xE90830; ++ writel(pm06r.whole, PM06R(register_base)); ++ ++ /* graphics controller 1 register ++ * o GC1 clock source is PLL 2. ++ * o hardware cursor is disabled. ++ */ ++ gc00r.whole = 0x10000C8 | 0x20000; ++ writel(gc00r.whole, GC00R(register_base)); ++ ++#if 0 ++ /* alternate horizontal window 1 control */ ++ writel(0, GC0AR(register_base)); ++ ++ /* alternate vertical window 1 control */ ++ writel(0, GC0BR(register_base)); ++ ++ /* window 1 start address */ ++ writel(0x2004100, GC0CR(register_base)); ++ ++ /* alternate window 1 start address */ ++ writel(0, GC0DR(register_base)); ++ ++ /* window 1 stride */ ++ gc0er.whole = 0x5100048; ++ writel(gc0er.whole, GC0ER(register_base)); ++ ++ /* reserved register - ??? - */ ++ writel(0x31f, GC0FR(register_base)); ++#endif ++ ++#if 0 ++ /* hardware cursor 1 position */ ++ writel(0, GC10R(register_base)); ++ ++ /* hardware cursor 1 start address and offset */ ++ gc11r.whole = 0x5100048; ++ writel(gc11r.whole, GC11R(register_base)); ++ ++ /* hardware cursor 1 foreground color */ ++ writel(0x00ffffff, GC12R(register_base)); ++ ++ /* hardware cursor 1 background color */ ++ writel(0x00000000, GC13R(register_base)); ++#endif ++ ++ /* horizontal window 2 control */ ++ gc28r.whole = 0x31f0000; ++ writel(gc28r.whole, GC28R(register_base)); ++ ++ /* vertical window 2 control */ ++ gc29r.whole = 0x2570000; ++ writel(gc29r.whole, GC29R(register_base)); ++ ++ /* horizontal display 2 control */ ++ gc22r.whole = 0x320041e; ++ writel(gc22r.whole, GC22R(register_base)); ++ ++ /* vertical display 2 control */ ++ gc23r.whole = 0x2570273; ++ writel(gc23r.whole, GC23R(register_base)); ++ ++ /* horizontal sync 2 control */ ++ gc24r.whole = 0x3c70347; ++ writel(gc24r.whole, GC24R(register_base)); ++ ++ /* vertical sync 2 control */ ++ gc25r.whole = 0x25d0259; ++ writel(gc25r.whole, GC25R(register_base)); ++ ++ /* graphics controller CRT control */ ++ gc01r.whole = 0x800; ++ writel(gc01r.whole, GC01R(register_base)); ++ ++ /* PLL 3 programming */ ++ pm07r.whole = 0xE90830; ++ writel(pm07r.whole, PM07R(register_base)); ++ ++ /* graphics controller 2 register ++ * o GC2 clock source is PLL 3. ++ * o hardware cursor is disabled. ++ */ ++ gc20r.whole = 0x10000C8 | 0x30000; ++ writel(gc20r.whole, GC20R(register_base)); ++ ++ /* ++ * Enable PLL2 and PLL3 in the PM Register ++ */ ++ pm00r.part.pll2_enbl = 0x1; ++ pm00r.part.pll3_enbl = 0x1; ++ writel(pm00r.whole, PM00R(register_base)); ++ ++ spin_unlock_irqrestore(lock, flags); ++} ++ ++ ++/**** ++ * initialize graphics engine. ++ */ ++static void ++ge_reset(unsigned long register_base) ++{ ++ /* drawing command register */ ++ writel(0, GE00R(register_base)); ++ ++ /* promary width and height register */ ++ writel(0, GE01R(register_base)); ++ ++ /* primary destination address register */ ++ writel(0, GE02R(register_base)); ++ ++ /* primary source XY register */ ++ writel(0, GE03R(register_base)); ++ ++ /* primary color compare register */ ++ writel(0, GE04R(register_base)); ++ ++ /* primary clip left/top register */ ++ writel(0, GE05R(register_base)); ++ ++ /* primary clip right/bottom register */ ++ writel(0, GE06R(register_base)); ++ ++ /* primary source and pattern offset register */ ++ writel(0, GE07R(register_base)); ++ ++ /* primary foreground color register/rectangle fill color depth */ ++ writel(0, GE08R(register_base)); ++ ++ /* source stride/offset register */ ++ writel(0, GE09R(register_base)); ++ ++ /* destination stride register and color depth */ ++ writel(0, GE0AR(register_base)); ++ ++ /* image base address register */ ++ writel(0, GE0BR(register_base)); ++} ++ ++/**** ++ * initialize Color Palette 1. ++ */ ++static void ++cp1_reset(unsigned long addr_info) ++{ ++ int i; ++ ++ for (i = 0; i < 256; i++) ++ writel(0, C1xxR(addr_info, i)); ++} ++ ++ ++/* ++ * Below functions are called from the skeleton ++ */ ++void mq200_external_setpal(unsigned regno, unsigned long color, unsigned long addr) ++{ ++ writel(color,C1xxR(addr,regno)); ++} ++ ++void mq200_external_setqmode(struct mq200_monitor_info* info, ++ unsigned long addr, spinlock_t *lock) ++{ ++ dc_reset(addr); /* device configuration */ ++ ++ power_state_transition(addr, 0); /* transition to D0 state */ ++ ++ pmu_reset(addr); /* power management unit */ ++ ++ miu_reset(addr); /* memory interface unit */ ++ ++ ge_reset(addr); /* graphics engine */ ++ ++ fpctrl_reset(addr); /* reset the panel settings */ ++ ++ gc1_gc2_reset(addr, lock); /* graphics controller 1 and 2 */ ++ ++ cp1_reset(addr); /* color palette 1 */ ++ ++ mq200_external_ondisplay(addr); /* LCD and CRT */ ++} ++ ++void mq200_external_offdisplay(unsigned long addr) ++{ ++ /* ++ * Move the MQ200 to D3 mode ++ */ ++ power_state_transition(addr, 3); ++} ++ ++/** ++ * to be called after mq200_external_setqmode ++ */ ++void mq200_external_ondisplay (unsigned long addr) ++{ ++ /* ++ * Set the framebuffer details ++ */ ++ union gc00r gc00r; ++ union gc01r gc01r; ++ union gc20r gc20r; ++ union fp00r fp00r; ++ ++ /* enable LCD for GC2 */ ++ fp00r.whole = readl(FP00R(addr)); ++ fp00r.whole &= 0xfffffffc; ++ ++ gc20r.whole = readl(GC20R(addr)); ++ ++ if(!(gc20r.whole & 0x1)) { ++ gc20r.whole |= 0x1; ++ writel(gc20r.whole, GC20R(addr)); ++ } ++ ++ fp00r.whole |= 0x3; ++ writel(fp00r.whole, FP00R(addr)); ++ ++ /* enable CRT for GC1 */ ++ gc00r.whole = readl(GC00R(addr)); ++ ++ if(!(gc00r.whole & 0x1)) { ++ gc00r.whole |= 0x1; ++ writel(gc00r.whole, GC00R(addr)); ++ } ++ ++ gc01r.whole = readl(GC01R(addr)); ++ gc01r.whole &= 0xfffffffc; ++ ++ gc01r.whole |= 0x1; ++ writel(gc01r.whole, GC01R(addr)); ++ ++} ++ ++int mq200_external_probe(unsigned long addr) ++{ ++ union pc00r pc00r; ++ if(readl(PMR(addr)) != PMR_VALUE) ++ return 0; ++ ++ pc00r.whole = readl(PC00R(addr)); ++ printk(KERN_INFO "mq200 video driver found Vendor: 0x%X Device: 0x%X\n", ++ pc00r.part.device, pc00r.part.vendor); ++ return 1; ++} +Index: linux-2.6.27/drivers/video/mq200/mq_skeleton.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/video/mq200/mq_skeleton.c 2008-12-07 01:34:29.666075531 +0100 +@@ -0,0 +1,398 @@ ++/* ++ * Author: Holger Hans Peter Freyther ++ * ++ * ++ * This implements the frame buffer driver interface to communicate ++ * with the kernel. ++ * It uses the mq200 routines from the ucLinux driver from Lineo ++ * ++ * 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/autoconf.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/fb.h> ++#include <linux/types.h> ++#include <linux/spinlock.h> ++ ++#include "mq200_data.h" ++ ++#if CONFIG_SA1100_SIMPAD ++/* ++ * Siemens SIMpad specefic data ++ */ ++#include <mach/simpad.h> ++#include <mach/hardware.h> ++ ++#define MQ200_REGIONS simpad_mq200_regions ++#define MQ200_MONITOR simpad_mq200_panel ++ ++static struct mq200_io_regions simpad_mq200_regions = { ++ .fb_size = MQ200_FB_SIZE, ++ .phys_mmio_base = 0x4be00000, ++ .virt_mmio_base = 0xf2e00000, ++ .phys_fb_base = 0x4b800000, ++ .virt_fb_base = 0xf2800000, ++}; ++ ++static struct mq200_monitor_info simpad_mq200_panel = { ++ .horizontal_res = 800, ++ .vertical_res = 600, ++ .depth = 16, ++ .refresh = 60, ++ .line_length = 1600, ++ .flags = 0x00130004, ++}; ++ ++extern long get_cs3_shadow(void); ++extern void set_cs3_bit(int value); ++extern void clear_cs3_bit(int value); ++#endif ++ ++ ++ ++struct mq200_info { ++ struct fb_info fb_info; ++ struct mq200_io_regions io_regions; ++ struct mq200_monitor_info monitor_info; ++ ++ /* palette */ ++ u32 pseudo_palette[17]; /* 16 colors + 1 in reserve not that well documented... */ ++ spinlock_t lock; ++}; ++ ++ ++ ++static int mq200_blank( int blank_mode, struct fb_info *info ) ++{ ++#ifdef CONFIG_SA1100_SIMPAD ++ if(blank_mode ){ ++ clear_cs3_bit(DISPLAY_ON); ++ }else { ++ set_cs3_bit(DISPLAY_ON); ++ } ++#endif ++ return 0; ++} ++ ++ ++static int mq200_check_var(struct fb_var_screeninfo *var, ++ struct fb_info *info ) ++{ /* TODO do we need sanity checks here */ ++ return 0; ++} ++ ++ ++static int mq200_set_par( struct fb_info *info ) ++{ ++ /* TODO set paraemeter */ ++ return 0; ++} ++ ++static int mq200_setcolreg(unsigned regno, unsigned red, unsigned green, ++ unsigned blue, unsigned transp, ++ struct fb_info *info ) ++{ ++ struct mq200_info *p; ++ unsigned long color; ++ u32* pal = info->pseudo_palette; ++ ++ p = info->par; ++ ++ if(regno > 255 ) ++ return 1; ++ ++ switch( info->var.bits_per_pixel ){ ++ case 16: ++ pal[regno] = ++ ((red & 0xf800) >> 0) | ++ ((green & 0xf800) >> 5) | ((blue & 0xf800) >> 11); ++ break; ++ case 24: ++ pal[regno] = ++ ((red & 0xff00) << 8) | ++ ((green & 0xff00)) | ((blue & 0xff00) >> 8); ++ break; ++ case 32: ++ pal[regno] = ++ ((red & 0xff00) >> 8) | ++ ((green & 0xff00)) | ((blue & 0xff00) << 8); ++ break; ++ default: ++ break; ++ } ++ ++ red &= 0xFF; ++ green &= 0xFF; ++ blue &= 0xFF; ++ ++ color = red | (green << 8) | (blue << 16); ++ mq200_external_setpal(regno, color, p->io_regions.virt_mmio_base); ++ ++ return 0; ++} ++ ++ ++static struct fb_ops mq200_ops = { ++ .owner = THIS_MODULE, ++ .fb_check_var = mq200_check_var, ++ .fb_set_par = mq200_set_par, ++ .fb_setcolreg = mq200_setcolreg, ++#ifdef FB_SOFT_CURSOR ++ .fb_cursor = soft_cursor, /* FIXME use hardware cursor */ ++#endif ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++ .fb_blank = mq200_blank, ++}; ++ ++ ++/********************************************************************* ++ * ++ * Device driver and module init code ++ * this will register to the fb layer later ++ * ++ *********************************************************************/ ++static void mq200_internal_init_color( struct fb_bitfield* red, ++ struct fb_bitfield* green, ++ struct fb_bitfield* blue, ++ int bpp ) ++{ ++ switch ( bpp ) ++ { ++ case 16: ++ red->offset = 11; ++ green->offset = 5; ++ blue->offset = 0; ++ ++ red->length = 5; ++ green->length = 6; ++ blue->length = 5; ++ break; ++ case 24: ++ red->offset = 16; ++ green->offset = 8; ++ blue->offset = 0; ++ ++ red->length = 8; ++ green->length = 8; ++ blue->length = 8; ++ break; ++ case 32: ++ red->offset = 0; ++ green->offset = 8; ++ blue->offset = 16; ++ ++ red->length = 8; ++ green->length = 8; ++ blue->length = 8; ++ case 8: /* fall through */ ++ default: ++ red->offset = green->offset = blue->offset = 0; ++ red->length = green->length = blue->length = bpp; ++ break; ++ } ++ ++} ++ ++ ++static struct mq200_info* __init mq200_internal_init_fbinfo(void) ++{ ++ struct mq200_info *info = NULL; ++ ++ info = (struct mq200_info*)kmalloc(sizeof(*info), GFP_KERNEL); ++ if(!info) ++ return NULL; ++ ++ /* ++ * Initialize memory ++ */ ++ memset(info, 0, sizeof(struct mq200_info) ); ++ spin_lock_init(&info->lock); ++ ++ /* set the base IO addresses */ ++ info->io_regions = MQ200_REGIONS; ++ info->monitor_info = MQ200_MONITOR; ++ ++ info->fb_info.screen_base = (char *)info->io_regions.virt_fb_base; ++ ++ /* fb_fix_screeninfo filling */ ++ strcpy(info->fb_info.fix.id, "MQ200_FB" ); ++ info->fb_info.fix.smem_start = info->io_regions.phys_fb_base; ++ info->fb_info.fix.smem_len = info->io_regions.fb_size; /* - CURSOR_IMAGE */ ++ info->fb_info.fix.mmio_start = info->io_regions.phys_mmio_base; ++ info->fb_info.fix.mmio_len = MQ200_REGS_SIZE; ++ info->fb_info.fix.type = FB_TYPE_PACKED_PIXELS; ++ info->fb_info.fix.accel = FB_ACCEL_NONE; ++ info->fb_info.fix.line_length = MQ200_MONITOR_LINE_LENGTH(info); ++ ++ if(MQ200_MONITOR_DEPTH(info) <= 8 ) ++ info->fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ else if( MQ200_MONITOR_DEPTH(info) >= 16 ) ++ info->fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR; ++ else ++ panic("Calling mq200 with wrong display data\n"); ++ ++ /* set the variable screen info */ ++ info->fb_info.var.xres = MQ200_MONITOR_HORI_RES(info); ++ info->fb_info.var.yres = MQ200_MONITOR_VERT_RES(info); ++ info->fb_info.var.xres_virtual = MQ200_MONITOR_HORI_RES(info); ++ info->fb_info.var.yres_virtual = MQ200_MONITOR_VERT_RES(info); ++ info->fb_info.var.bits_per_pixel = MQ200_MONITOR_DEPTH(info); ++ ++ mq200_internal_init_color(&info->fb_info.var.red, ++ &info->fb_info.var.green, ++ &info->fb_info.var.blue, ++ MQ200_MONITOR_DEPTH(info) ); ++ ++ info->fb_info.var.transp.length = info->fb_info.var.transp.offset = 0; ++ info->fb_info.var.height = info->fb_info.var.width = -1; ++ ++ info->fb_info.var.vmode = FB_VMODE_NONINTERLACED; ++ info->fb_info.var.pixclock = 10000; ++ info->fb_info.var.left_margin = info->fb_info.var.right_margin = 16; ++ info->fb_info.var.upper_margin = info->fb_info.var.lower_margin = 16; ++ info->fb_info.var.hsync_len = info->fb_info.var.vsync_len = 8; ++ ++ info->fb_info.var.nonstd = 0; ++ info->fb_info.var.activate = FB_ACTIVATE_NOW; ++ info->fb_info.var.accel_flags = 0; ++ ++ return info; ++} ++ ++ ++extern void mq200_register_attributes(struct device* ); ++/* ++ * gets called from the bus ++ * we will register our framebuffer from here ++ */ ++static int __init mq200_probe(struct device *dev) ++{ ++ struct mq200_info *info = NULL; ++ int retv= 0; ++ ++ info = mq200_internal_init_fbinfo(); ++ if(!mq200_external_probe(info->io_regions.virt_mmio_base)) ++ goto error_out; ++ ++ GPDR |= (1<<3); ++ GAFR &= ~(1<<3); ++ GPSR |= (1<<3); ++ ++ mq200_external_setqmode(&info->monitor_info, ++ info->io_regions.virt_mmio_base, ++ &info->lock); ++ ++ info->fb_info.fbops = &mq200_ops; ++ info->fb_info.flags = FBINFO_FLAG_DEFAULT; ++ ++ mq200_check_var(&info->fb_info.var, &info->fb_info ); ++ ++ fb_alloc_cmap(&info->fb_info.cmap, 1 << MQ200_MONITOR_DEPTH(info), 0 ); ++ ++ info->fb_info.pseudo_palette = (void*)info->pseudo_palette; ++ ++ /* save the pointer to the mq200 struct in var */ ++ info->fb_info.par = info; ++ ++ retv = register_framebuffer(&info->fb_info ); ++ if(retv < 0) ++ goto error_out; ++ ++ ++ /* will get unset if retv != 0 */ ++ dev_set_drvdata(dev, info ); ++ return retv; ++ ++/* ++ * Free the info and exit ++ */ ++error_out: ++ kfree(info); ++ return -EINVAL; ++} ++ ++#ifdef CONFIG_PM ++static struct mq200_info* get_mq200_info( struct device *dev) ++{ ++ return dev_get_drvdata(dev); ++} ++ ++static unsigned long get_mmio_base( struct device *dev ) ++{ ++ struct mq200_info *info = get_mq200_info(dev); ++ return info->io_regions.virt_mmio_base; ++} ++ ++static struct mq200_monitor_info* get_monitor_info( struct device *dev) ++{ ++ struct mq200_info *info = get_mq200_info(dev); ++ return &info->monitor_info; ++} ++ ++static spinlock_t* get_spinlock( struct device *dev) ++{ ++ return &get_mq200_info(dev)->lock; ++} ++ ++/* ++ * FIXME: make sure we only call mq200_external_offdisplay only once ++ * a 2nd time will hang the kernel -zecke ++ * ++ * FIXME: save the content of the framebuffer inside dev->saved_state ++ * so on resume we can memcpy it back into the buffer and userspace ++ * does not need to redraw ++ * ++ * functions for suspending and resuming ++ */ ++static int mq200_suspend(struct device *dev, pm_message_t state) ++{ ++ ++ mq200_external_offdisplay( get_mmio_base(dev) ); ++ clear_cs3_bit(DISPLAY_ON); ++ ++ ++ return 0; ++} ++ ++static int mq200_resume(struct device *dev) ++{ ++ unsigned long mem = get_mmio_base(dev); ++ struct mq200_monitor_info *monitor = get_monitor_info(dev); ++ mq200_external_setqmode(monitor, mem, get_spinlock(dev) ); ++ ++ ++ /* ++ * Set display on if it was on ++ */ ++ set_cs3_bit(DISPLAY_ON); ++ ++ return 0; ++} ++ ++ ++#endif ++ ++ ++static struct device_driver mq200fb_driver = { ++ .name = "simpad-mq200", ++ .bus = &platform_bus_type, ++ .probe = mq200_probe, /* will be called after we've registered the driver */ ++ .suspend = mq200_suspend, ++ .resume = mq200_resume ++}; ++ ++int __devinit mq200_init(void) ++{ ++ return driver_register(&mq200fb_driver); ++} ++ ++module_init(mq200_init); ++MODULE_DESCRIPTION("MQ200 framebuffer driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Holger Hans Peter Freyther"); diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-pcmcia.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-pcmcia.patch new file mode 100644 index 0000000000..13abef9ba2 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-pcmcia.patch @@ -0,0 +1,226 @@ +Index: linux-2.6.27/drivers/pcmcia/cs.c +=================================================================== +--- linux-2.6.27.orig/drivers/pcmcia/cs.c 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/pcmcia/cs.c 2008-12-04 01:10:04.236701484 +0100 +@@ -10,6 +10,8 @@ + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds ++ * ++ * mrdata: -added suspend fix + */ + + #include <linux/module.h> +@@ -57,6 +59,10 @@ + INT_MODULE_PARM(unreset_delay, 10); /* centiseconds */ + INT_MODULE_PARM(unreset_check, 10); /* centiseconds */ + INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */ ++// INT_MODULE_PARM(unreset_delay, 20); /* centiseconds */ ++// INT_MODULE_PARM(unreset_check, 100); /* centiseconds */ ++// INT_MODULE_PARM(unreset_limit, 300); /* unreset_check's */ ++ + + /* Access speed for attribute memory windows */ + INT_MODULE_PARM(cis_speed, 300); /* ns */ +@@ -362,6 +368,7 @@ + skt->ops->set_socket(skt, &skt->socket); + + msleep(unreset_delay * 10); ++ + for (i = 0; i < unreset_limit; i++) { + skt->ops->get_status(skt, &status); + +@@ -826,7 +833,7 @@ + int pcmcia_resume_card(struct pcmcia_socket *skt) + { + int ret; +- ++ + cs_dbg(skt, 1, "waking up socket\n"); + + mutex_lock(&skt->skt_mutex); +@@ -854,7 +861,7 @@ + int pcmcia_eject_card(struct pcmcia_socket *skt) + { + int ret; +- ++ + cs_dbg(skt, 1, "user eject request\n"); + + mutex_lock(&skt->skt_mutex); +Index: linux-2.6.27/drivers/pcmcia/sa1100_generic.c +=================================================================== +--- linux-2.6.27.orig/drivers/pcmcia/sa1100_generic.c 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/pcmcia/sa1100_generic.c 2008-12-04 01:03:33.558818180 +0100 +@@ -28,6 +28,9 @@ + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + ++ 2007 mrnice: added thesings changes from device_driver ++ to platform_driver - many thx to thesing ++ + ======================================================================*/ + + #include <linux/module.h> +@@ -81,13 +84,15 @@ + return ret; + } + +-static struct device_driver sa11x0_pcmcia_driver = { +- .probe = sa11x0_drv_pcmcia_probe, +- .remove = soc_common_drv_pcmcia_remove, +- .name = "sa11x0-pcmcia", +- .bus = &platform_bus_type, +- .suspend = pcmcia_socket_dev_suspend, +- .resume = pcmcia_socket_dev_resume, ++static struct platform_driver sa11x0_pcmcia_driver = { ++ .driver = { ++ .name = "sa11x0-pcmcia", ++ .probe = sa11x0_drv_pcmcia_probe, ++ .remove = soc_common_drv_pcmcia_remove, ++ .suspend= pcmcia_socket_dev_suspend, ++ .resume = pcmcia_socket_dev_resume, ++ //.bus = &platform_bus_type, ++ }, + }; + + /* sa11x0_pcmcia_init() +@@ -100,7 +105,7 @@ + */ + static int __init sa11x0_pcmcia_init(void) + { +- return driver_register(&sa11x0_pcmcia_driver); ++ return platform_driver_register(&sa11x0_pcmcia_driver); + } + + /* sa11x0_pcmcia_exit() +@@ -110,7 +115,7 @@ + */ + static void __exit sa11x0_pcmcia_exit(void) + { +- driver_unregister(&sa11x0_pcmcia_driver); ++ platform_driver_unregister(&sa11x0_pcmcia_driver); + } + + MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>"); +Index: linux-2.6.27/drivers/pcmcia/sa1100_simpad.c +=================================================================== +--- linux-2.6.27.orig/drivers/pcmcia/sa1100_simpad.c 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/pcmcia/sa1100_simpad.c 2008-12-04 01:10:21.458773451 +0100 +@@ -8,15 +8,17 @@ + #include <linux/kernel.h> + #include <linux/device.h> + #include <linux/init.h> ++#include <linux/delay.h> + + #include <mach/hardware.h> + #include <asm/mach-types.h> + #include <asm/irq.h> + #include <mach/simpad.h> + #include "sa1100_generic.h" +- ++ ++extern long get_cs3_ro(void); + extern long get_cs3_shadow(void); +-extern void set_cs3_bit(int value); ++extern void set_cs3_bit(int value); + extern void clear_cs3_bit(int value); + + static struct pcmcia_irqs irqs[] = { +@@ -25,8 +27,15 @@ + + static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt) + { ++ clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1|PCMCIA_RESET); ++ ++ set_cs3_bit(PCMCIA_BUFF_DIS); ++ ++ msleep(10); ++ ++ clear_cs3_bit(PCMCIA_BUFF_DIS); + +- clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); ++ msleep(5); + + skt->irq = IRQ_GPIO_CF_IRQ; + +@@ -38,8 +47,8 @@ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + + /* Disable CF bus: */ +- //set_cs3_bit(PCMCIA_BUFF_DIS); +- clear_cs3_bit(PCMCIA_RESET); ++ set_cs3_bit(PCMCIA_BUFF_DIS); ++ clear_cs3_bit(PCMCIA_RESET); + } + + static void +@@ -47,21 +56,17 @@ + struct pcmcia_state *state) + { + unsigned long levels = GPLR; +- long cs3reg = get_cs3_shadow(); + +- state->detect=((levels & GPIO_CF_CD)==0)?1:0; +- state->ready=(levels & GPIO_CF_IRQ)?1:0; +- state->bvd1=1; /* Not available on Simpad. */ +- state->bvd2=1; /* Not available on Simpad. */ +- state->wrprot=0; /* Not available on Simpad. */ +- +- if((cs3reg & 0x0c) == 0x0c) { +- state->vs_3v=0; +- state->vs_Xv=0; +- } else { +- state->vs_3v=1; +- state->vs_Xv=0; +- } ++ state->detect = ((levels & GPIO_CF_CD) == 0) ? 1 : 0 ; ++ state->ready = (levels & GPIO_CF_IRQ) ? 1 : 0 ; ++ ++ long cs3_ro_reg = get_cs3_ro(); ++ ++ state->bvd1 = (cs3_ro_reg & PCMCIA_BVD1) ? 1 : 0 ; /* old: =1 Not available on Simpad. */ ++ state->bvd2 = (cs3_ro_reg & PCMCIA_BVD2) ? 1 : 0 ; /* old: =1 Not available on Simpad. */ ++ state->wrprot = 0 ; /* Not available on Simpad. */ ++ state->vs_3v = (cs3_ro_reg & PCMCIA_VS1) ? 0 : 1 ; ++ state->vs_Xv = (cs3_ro_reg & PCMCIA_VS2) ? 0 : 1 ; + } + + static int +@@ -78,7 +83,7 @@ + clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); + break; + +- case 33: ++ case 33: + clear_cs3_bit(VCC_3V_EN|EN1); + set_cs3_bit(VCC_5V_EN|EN0); + break; +@@ -96,6 +101,10 @@ + return -1; + } + ++ if (state->flags & SS_RESET) ++ set_cs3_bit(PCMCIA_RESET); ++ else ++ clear_cs3_bit(PCMCIA_RESET); + + local_irq_restore(flags); + +@@ -104,6 +113,7 @@ + + static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt) + { ++ clear_cs3_bit(PCMCIA_RESET); + soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); + } + +@@ -113,7 +123,7 @@ + set_cs3_bit(PCMCIA_RESET); + } + +-static struct pcmcia_low_level simpad_pcmcia_ops = { ++static struct pcmcia_low_level simpad_pcmcia_ops = { + .owner = THIS_MODULE, + .hw_init = simpad_pcmcia_hw_init, + .hw_shutdown = simpad_pcmcia_hw_shutdown, diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2 b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2 new file mode 100644 index 0000000000..b044b471c5 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-serial-gpio_keys-and-cs3-ro.patch.v2 @@ -0,0 +1,358 @@ +diff -Nur linux-2.6.24.vanilla/arch/arm/mach-sa1100/simpad.c linux-2.6.24/arch/arm/mach-sa1100/simpad.c +--- linux-2.6.24.vanilla/arch/arm/mach-sa1100/simpad.c 2008-10-04 21:47:24.000000000 +0200 ++++ linux-2.6.24/arch/arm/mach-sa1100/simpad.c 2008-10-04 22:01:20.000000000 +0200 +@@ -1,5 +1,15 @@ + /* + * linux/arch/arm/mach-sa1100/simpad.c ++ * ++ * 2007/04/11 mrdata: ++ * - insert simpad_uart_set_mctrl() ++ * simpad_uart_get_mctrl() ++ * - internal RS232/DECT/Bluetooth ++ * works again (based on 2.4 simpad-serial.patch) ++ * - added cs3_ro ++ * ++ * 2007/04/12 Bernhard Guillon: ++ * -added gpio_keys (based on h3000.c from hh.org) + */ + + #include <linux/module.h> +@@ -9,6 +19,9 @@ + #include <linux/proc_fs.h> + #include <linux/string.h> + #include <linux/pm.h> ++ ++#include <linux/apm-emulation.h> ++ + #include <linux/platform_device.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> +@@ -27,12 +40,21 @@ + + #include <linux/serial_core.h> + #include <linux/ioport.h> ++#include <linux/input.h> ++#include <linux/gpio_keys.h> + #include <asm/io.h> + + #include "generic.h" + ++long cs3_ro; + long cs3_shadow; + ++long get_cs3_ro(void) ++{ ++ cs3_ro = *(CS3BUSTYPE *)(CS3_BASE); ++ return cs3_ro; ++} ++ + long get_cs3_shadow(void) + { + return cs3_shadow; +@@ -55,9 +77,12 @@ + *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow; + } + ++EXPORT_SYMBOL(get_cs3_ro); ++EXPORT_SYMBOL(get_cs3_shadow); + EXPORT_SYMBOL(set_cs3_bit); + EXPORT_SYMBOL(clear_cs3_bit); + ++ + static struct map_desc simpad_io_desc[] __initdata = { + { /* MQ200 */ + .virtual = 0xf2800000, +@@ -73,23 +98,71 @@ + }; + + ++static void simpad_uart_set_mctrl(struct uart_port *port, u_int mctrl) ++{ ++ if (port->mapbase == _Ser1UTCR0) { ++ /* internal serial port (ttySA1, DECT/Bluetooth) */ ++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART1_RTS; ++ else GPSR = GPIO_UART1_RTS; ++ ++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART1_DTR; ++ else GPSR = GPIO_UART1_DTR; ++ } ++ ++ else if (port->mapbase == _Ser3UTCR0) { ++ /* external serial port (ttySA0, RS232) */ ++ if (mctrl & TIOCM_RTS) GPCR = GPIO_UART3_RTS; ++ else GPSR = GPIO_UART3_RTS; ++ ++ if (mctrl & TIOCM_DTR) GPCR = GPIO_UART3_DTR; ++ else GPSR = GPIO_UART3_DTR; ++ } ++} ++ ++ ++static u_int simpad_uart_get_mctrl(struct uart_port *port) ++{ ++ u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; ++ ++ if (port->mapbase == _Ser1UTCR0) { ++ /* internal serial port (ttySA1, DECT/Bluetooth) */ ++ int gplr = GPLR; ++ if (gplr & GPIO_UART1_DCD) ret &= ~TIOCM_CD; ++ if (gplr & GPIO_UART1_CTS) ret &= ~TIOCM_CTS; ++ if (gplr & GPIO_UART1_DSR) ret &= ~TIOCM_DSR; ++ } ++ ++ else if (port->mapbase == _Ser3UTCR0) { ++ /* external serial port (ttySA0, RS232) */ ++ int gplr = GPLR; ++ if (gplr & GPIO_UART3_DCD) ret &= ~TIOCM_CD; ++ if (gplr & GPIO_UART3_CTS) ret &= ~TIOCM_CTS; ++ if (gplr & GPIO_UART3_DSR) ret &= ~TIOCM_DSR; ++ } ++ return ret; ++} ++ ++ + static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate) + { +- if (port->mapbase == (u_int)&Ser1UTCR0) { +- if (state) +- { +- clear_cs3_bit(RS232_ON); +- clear_cs3_bit(DECT_POWER_ON); +- }else +- { +- set_cs3_bit(RS232_ON); +- set_cs3_bit(DECT_POWER_ON); +- } +- } ++ if (port->mapbase == (u_int)&Ser3UTCR0) { ++ if (state) ++ { ++ clear_cs3_bit(RS232_ON); ++ /* clear_cs3_bit(DECT_POWER_ON); */ ++ }else ++ { ++ set_cs3_bit(RS232_ON); ++ /* set_cs3_bit(DECT_POWER_ON); */ ++ } ++ } + } + ++ + static struct sa1100_port_fns simpad_port_fns __initdata = { +- .pm = simpad_uart_pm, ++ .set_mctrl = simpad_uart_set_mctrl, ++ .get_mctrl = simpad_uart_get_mctrl, ++ .pm = simpad_uart_pm, + }; + + +@@ -135,7 +208,6 @@ + }; + + +- + static void __init simpad_map_io(void) + { + sa1100_map_io(); +@@ -144,23 +216,45 @@ + + set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON | + ENABLE_5V | RESET_SIMCARD | DECT_POWER_ON); +- +- ++ + sa1100_register_uart_fns(&simpad_port_fns); + sa1100_register_uart(0, 3); /* serial interface */ + sa1100_register_uart(1, 1); /* DECT */ + +- // Reassign UART 1 pins ++ /* Reassign UART 1 pins */ ++ /* TEST SOME OLD KERNEL STUFF INSTEAD + GAFR |= GPIO_UART_TXD | GPIO_UART_RXD; + GPDR |= GPIO_UART_TXD | GPIO_LDD13 | GPIO_LDD15; + GPDR &= ~GPIO_UART_RXD; + PPAR |= PPAR_UPR; ++ */ ++ ++ // txd and rxd use their alternate function ++ GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD); ++ ++ // the control lines are gpio ++ GAFR &= ~(GPIO_UART1_RTS | GPIO_UART1_CTS | GPIO_UART1_DCD); ++ GAFR &= ~(GPIO_UART1_DSR | GPIO_UART1_DTR); ++ GAFR &= ~(GPIO_UART3_RTS | GPIO_UART3_CTS | GPIO_UART3_DCD); ++ GAFR &= ~(GPIO_UART3_DSR | GPIO_UART3_DTR); ++ ++ // txd, rts and dtr are outputs ++ GPDR |= GPIO_UART_TXD; ++ GPDR |= GPIO_UART1_RTS | GPIO_UART3_RTS; ++ GPDR |= GPIO_UART1_DTR | GPIO_UART3_DTR; ++ ++ // cts, dcd, dsr and rxd are inputs ++ GPDR &= ~(GPIO_UART1_CTS | GPIO_UART3_CTS); ++ GPDR &= ~(GPIO_UART1_DCD | GPIO_UART3_DCD); ++ GPDR &= ~(GPIO_UART1_DSR | GPIO_UART3_DSR); ++ GPDR &= ~GPIO_UART_RXD; ++ ++ PPAR |= PPAR_UPR; + + /* + * Set up registers for sleep mode. + */ + +- + PWER = PWER_GPIO0| PWER_RTC; + PGSR = 0x818; + PCFR = 0; +@@ -171,9 +265,10 @@ + sa11x0_set_mcp_data(&simpad_mcp_data); + } + ++ + static void simpad_power_off(void) + { +- local_irq_disable(); // was cli ++ local_irq_disable(); /* was cli */ + set_cs3(0x800); /* only SD_MEDIAQ */ + + /* disable internal oscillator, float CS lines */ +@@ -191,31 +286,52 @@ + while(1); + + local_irq_enable(); /* we won't ever call it */ ++} + + +-} ++/* ++ * gpio_keys ++*/ ++ ++static struct gpio_keys_button simpad_button_table[] = { ++ { KEY_POWER, IRQ_GPIO_POWER_BUTTON, 1, "power button" }, ++}; ++ ++static struct gpio_keys_platform_data simpad_keys_data = { ++ .buttons = simpad_button_table, ++ .nbuttons = ARRAY_SIZE(simpad_button_table), ++}; ++ ++static struct platform_device simpad_keys = { ++ .name = "gpio-keys", ++ .dev = { ++ .platform_data = &simpad_keys_data, ++ }, ++}; + + + /* + * MediaQ Video Device + */ ++ + static struct platform_device simpad_mq200fb = { + .name = "simpad-mq200", + .id = 0, + }; + ++ + static struct platform_device *devices[] __initdata = { +- &simpad_mq200fb ++ &simpad_keys, ++ &simpad_mq200fb, + }; + + +- + static int __init simpad_init(void) + { + int ret; + + pm_power_off = simpad_power_off; +- ++ + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); + if(ret) + printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device"); +diff -Nur linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad.h linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad.h +--- linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad.h 2008-10-04 21:47:17.000000000 +0200 ++++ linux-2.6.27/arch/arm/mach-sa1100/include/mach/simpad.h 2008-10-04 22:00:57.000000000 +0200 +@@ -12,11 +12,12 @@ + #define __ASM_ARCH_SIMPAD_H + + +-#define GPIO_UART1_RTS GPIO_GPIO14 ++#define GPIO_UART1_RTS GPIO_GPIO9 + #define GPIO_UART1_DTR GPIO_GPIO7 + #define GPIO_UART1_CTS GPIO_GPIO8 + #define GPIO_UART1_DCD GPIO_GPIO23 + #define GPIO_UART1_DSR GPIO_GPIO6 ++#define GPIO_UART1_RI GPIO_GPIO19 + + #define GPIO_UART3_RTS GPIO_GPIO12 + #define GPIO_UART3_DTR GPIO_GPIO16 +@@ -48,9 +49,9 @@ + #define GPIO_SMART_CARD GPIO_GPIO10 + #define IRQ_GPIO_SMARD_CARD IRQ_GPIO10 + +-// CS3 Latch is write only, a shadow is necessary ++// CS3 Latch is write only 16-bit , a shadow is necessary + +-#define CS3BUSTYPE unsigned volatile long ++#define CS3BUSTYPE unsigned volatile long + #define CS3_BASE 0xf1000000 + + #define VCC_5V_EN 0x0001 // For 5V PCMCIA +@@ -70,43 +71,17 @@ + #define ENABLE_5V 0x4000 // Enable 5V circuit + #define RESET_SIMCARD 0x8000 + +-#define RS232_ENABLE 0x0440 +-#define PCMCIAMASK 0x402f ++// CS3 Latch is readable only 8-bit interest + ++#define PCMCIA_BVD1 0x0001 ++#define PCMCIA_BVD2 0x0002 ++#define PCMCIA_VS1 0x0004 // PCMCIA card voltage select ++#define PCMCIA_VS2 0x0008 // PCMCIA card voltage select, if both are in high state -> 5V PCMCIA card ++#define LOCK_IND 0x0010 ++#define CHARGING_STATE 0x0020 // Ladestatus ++#define PCMCIA_SHORT 0x0040 // low active + +-struct simpad_battery { +- unsigned char ac_status; /* line connected yes/no */ +- unsigned char status; /* battery loading yes/no */ +- unsigned char percentage; /* percentage loaded */ +- unsigned short life; /* life till empty */ +-}; +- +-/* These should match the apm_bios.h definitions */ +-#define SIMPAD_AC_STATUS_AC_OFFLINE 0x00 +-#define SIMPAD_AC_STATUS_AC_ONLINE 0x01 +-#define SIMPAD_AC_STATUS_AC_BACKUP 0x02 /* What does this mean? */ +-#define SIMPAD_AC_STATUS_AC_UNKNOWN 0xff +- +-/* These bitfields are rarely "or'd" together */ +-#define SIMPAD_BATT_STATUS_HIGH 0x01 +-#define SIMPAD_BATT_STATUS_LOW 0x02 +-#define SIMPAD_BATT_STATUS_CRITICAL 0x04 +-#define SIMPAD_BATT_STATUS_CHARGING 0x08 +-#define SIMPAD_BATT_STATUS_CHARGE_MAIN 0x10 +-#define SIMPAD_BATT_STATUS_DEAD 0x20 /* Battery will not charge */ +-#define SIMPAD_BATT_NOT_INSTALLED 0x20 /* For expansion pack batteries */ +-#define SIMPAD_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */ +-#define SIMPAD_BATT_STATUS_NOBATT 0x80 +-#define SIMPAD_BATT_STATUS_UNKNOWN 0xff +- +-extern int simpad_get_battery(struct simpad_battery* ); ++#define RS232_ENABLE 0x0440 ++#define PCMCIAMASK 0x402f + + #endif // __ASM_ARCH_SIMPAD_H +- +- +- +- +- +- +- +- diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-switches.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-switches.patch new file mode 100644 index 0000000000..f67a9c29b6 --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-switches.patch @@ -0,0 +1,362 @@ +Index: linux-2.6.27/drivers/mfd/Kconfig +=================================================================== +--- linux-2.6.27.orig/drivers/mfd/Kconfig 2008-12-07 01:18:53.719505919 +0100 ++++ linux-2.6.27/drivers/mfd/Kconfig 2008-12-07 01:19:19.436170998 +0100 +@@ -101,4 +101,7 @@ + tristate "Touchscreen interface support" + depends on MCP_UCB1200 && INPUT + ++config MCP_UCB1200_SWITCHES ++ tristate "SIMpad Switches support" ++ depends on MCP_UCB1200 && INPUT + endmenu +Index: linux-2.6.27/drivers/mfd/Makefile +=================================================================== +--- linux-2.6.27.orig/drivers/mfd/Makefile 2008-12-07 01:18:53.732838088 +0100 ++++ linux-2.6.27/drivers/mfd/Makefile 2008-12-07 01:19:19.436170998 +0100 +@@ -18,7 +18,7 @@ + obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o + obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o +- ++obj-$(CONFIG_MCP_UCB1200_SWITCHES) += ucb1x00-switches.o + ifeq ($(CONFIG_SA1100_ASSABET),y) + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o + endif +Index: linux-2.6.27/drivers/mfd/ucb1x00-switches.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.27/drivers/mfd/ucb1x00-switches.c 2008-12-07 01:29:24.479452365 +0100 +@@ -0,0 +1,332 @@ ++/* ++ * linux/drivers/mfd/ucb1x00-switches.c ++ * ++ * Copyright (C) 2007 Bernhard Guillon. ++ * ++ * 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. ++ * ++ * This driver is for the Switches of Siemens SIMpad (CL4,SL4,SLC), T-Sinus-Pad and ++ * Swisscom WP50 devices. ++ * ++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8. ++ * ++ * This driver is based on the 2.4 ucb1x00-switches, the 2.6 ucb1x00-assabet ++ * and the ucb1x00-ts driver. ++ * ++ * 2007/06/21 mrdata: ++ * - create new thread kswd() to handle irq_events for ucb1300-gpio's ++ * - found out, that not every key-press or key-release ++ * generate a irq_event ++ * -> establish key_state handling ++ * key_state, key_state_last <-> KEY_PRESS, KEY_RELEASE ++ * -> after irq_event polling the ucb1300-gpio's till all keys ++ * in key_state = KEY_RELEASE ++ * ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/device.h> ++#include <linux/sched.h> ++#include <linux/freezer.h> ++#include <linux/kthread.h> ++ ++#include <asm/dma.h> ++ ++#include "ucb1x00.h" ++ ++#define KEY_PRESS 1 ++#define KEY_RELEASE 0 ++ ++static int key [6] = { KEY_PROG1,KEY_PROG2,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT }; ++ ++static unsigned short int key_state [6] = { 0, 0, 0, 0, 0, 0}; ++static unsigned short int key_state_last [6] = { 1, 1, 1, 1, 1, 1}; ++ ++struct ucb1x00_switches { ++ struct input_dev *idev; ++ struct ucb1x00 *ucb; ++ ++ wait_queue_head_t irq_wait; ++ struct task_struct *rtask; ++ ++ int idx; ++ ++ unsigned int valid:1; ++}; ++ ++static int ucb1x00_thread(void *_switches_id) ++{ ++ unsigned short int this; ++ int idx_tmp; ++ int i; ++ struct ucb1x00_switches *switches = _switches_id; ++ struct input_dev *idev = switches->idev; ++ struct task_struct *tsk = current; ++ DECLARE_WAITQUEUE(wait, tsk); ++ ++ add_wait_queue(&switches->irq_wait, &wait); ++ ++ while (!kthread_should_stop()) ++ { ++ signed long timeout; ++ ++ if ((switches->idx >= 0) && (switches->idx <= 5) && (switches->valid == 1)) ++ { ++ switches->valid = 0; ++ ++ idx_tmp = switches->idx; ++ ++ ucb1x00_enable(switches->ucb); ++ ++ this = ~ucb1x00_io_read(switches->ucb); ++ ++ ucb1x00_disable(switches->ucb); ++ ++ if (key_state[idx_tmp] == KEY_RELEASE) ++ { ++ key_state_last[idx_tmp] = KEY_RELEASE; ++ key_state[idx_tmp] = KEY_PRESS; ++ ++ input_report_key(idev, key[idx_tmp], KEY_PRESS); ++ input_sync(idev); ++ } ++ ++ for (i = 0; i < 6; i++) ++ { ++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS)) ++ { ++ key_state_last[i] = KEY_RELEASE; ++ key_state[i] = KEY_PRESS; ++ ++ input_report_key(idev, key[i], KEY_PRESS); ++ input_sync(idev); ++ } ++ } ++ ++ for(;;) ++ { ++ ucb1x00_enable(switches->ucb); ++ this = ~ucb1x00_io_read(switches->ucb); ++ ucb1x00_disable(switches->ucb); ++ ++ for (i = 0; i < 6; i++) ++ { ++ if ((key_state[i] == KEY_PRESS) && (((this & (1 << i)) ? 1 : 0) == KEY_RELEASE)) ++ { ++ key_state_last[i] = KEY_PRESS; ++ key_state[i] = KEY_RELEASE; ++ ++ input_report_key(idev, key[i], KEY_RELEASE); ++ input_sync(idev); ++ } ++ ++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS)) ++ { ++ key_state_last[i] = KEY_RELEASE; ++ key_state[i] = KEY_PRESS; ++ ++ input_report_key(idev, key[i], KEY_PRESS); ++ input_sync(idev); ++ } ++ ++ } ++ ++ // left loop, if no key press detect ++ if ((this | 0xff80) == 0xff80) ++ { ++ break; ++ } ++ ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ ++ try_to_freeze(); ++ ++ timeout = HZ / 100; ++ ++ schedule_timeout(timeout); ++ } ++ } ++ ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ ++ try_to_freeze(); ++ ++ timeout = MAX_SCHEDULE_TIMEOUT; ++ ++ schedule_timeout(timeout); ++ } ++ ++ remove_wait_queue(&switches->irq_wait, &wait); ++ ++ switches->rtask = NULL; ++ ++ return 0; ++} ++ ++ ++static void ucb1x00_dev_irq(int idx, void *id) ++{ ++ struct ucb1x00_switches *switches = id; ++ ++ switches->idx = idx; ++ switches->valid = 1; ++ ++ wake_up(&switches->irq_wait); ++} ++ ++static int ucb1x00_switches_add(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_switches *switches; ++ struct input_dev *idev; ++ int err,i; ++ ++ switches = kzalloc(sizeof(struct ucb1x00_switches), GFP_KERNEL); ++ idev = input_allocate_device(); ++ ++ if (!switches || !idev) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ switches->ucb = dev->ucb; ++ ++ input_set_drvdata(idev, switches); ++ idev->name = "SIMpad Switches"; ++ idev->id.product = switches->ucb->id; ++ ++ __set_bit(EV_KEY, idev->evbit); ++ __set_bit(EV_REP, idev->evbit); ++ __set_bit(KEY_PROG1, idev->keybit); ++ __set_bit(KEY_PROG2, idev->keybit); ++ __set_bit(KEY_UP, idev->keybit); ++ __set_bit(KEY_DOWN, idev->keybit); ++ __set_bit(KEY_LEFT, idev->keybit); ++ __set_bit(KEY_RIGHT, idev->keybit); ++ ++ err = input_register_device(idev); ++ if (err) ++ goto fail; ++ switches->idev = idev; ++ dev->priv = switches; ++ ++ BUG_ON(switches->rtask); ++ ++ init_waitqueue_head(&switches->irq_wait); ++ ++ ucb1x00_enable(switches->ucb); ++ ++ ucb1x00_io_set_dir(switches->ucb, ++ UCB_IO_0 | UCB_IO_1 | UCB_IO_2 | ++ UCB_IO_3 | UCB_IO_4 | UCB_IO_5, ++ UCB_IO_8 | UCB_IO_9); ++ ++ ucb1x00_disable(switches->ucb); ++ ++ for (i = 0; i < 6; ++i) { ++ ucb1x00_enable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ if (ucb1x00_hook_irq(switches->ucb, i, ucb1x00_dev_irq, switches) < 0) { ++ printk(KERN_ERR "unable to hook IRQ for " ++ "UCB1300 SWITCH_%d\n", i); ++ return -EBUSY; ++ } ++ } ++ ++ switches->rtask = kthread_run(ucb1x00_thread, switches, "kswd"); ++ if (!IS_ERR(switches->rtask)) ++ { ++ return 0; ++ } ++ else ++ { ++ input_unregister_device(switches->idev); ++ ++ for (i = 5; i >= 0; --i) { ++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ /* Only error conditions are ENOENT and EINVAL; silently ++ * ignore: ++ */ ++ ucb1x00_free_irq(switches->ucb, i, NULL); ++ } ++ switches->rtask = NULL; ++ ucb1x00_disable(switches->ucb); ++ kfree(switches); ++ ++ return -EFAULT; ++ } ++ ++fail: ++ input_free_device(idev); ++ kfree(switches); ++ return err; ++ ++} ++ ++static void ucb1x00_switches_remove(struct ucb1x00_dev *dev) ++{ ++ int i; ++ struct ucb1x00_switches *switches = dev->priv; ++ ++ if (switches->rtask) ++ kthread_stop(switches->rtask); ++ ++ switches->rtask = NULL; ++ ++ input_unregister_device(switches->idev); ++ ++ for (i = 5; i >= 0; --i) { ++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING); ++ ++ /* Only error conditions are ENOENT and EINVAL; silently ++ * ignore: ++ */ ++ ucb1x00_free_irq(switches->ucb, i, NULL); ++ } ++ ucb1x00_disable(switches->ucb); ++ kfree(switches); ++} ++ ++#ifdef CONFIG_PM ++static int ucb1x00_switches_resume(struct ucb1x00_dev *dev) ++{ ++ struct ucb1x00_switches *switches = dev->priv; ++ ++ if (switches->rtask != NULL) ++ { ++ switches->valid = 0; ++ wake_up(&switches->irq_wait); ++ ++ printk(KERN_DEBUG "ucb1x00-switches.c -> _switches_resume() kswd - restart *DONE*\n"); ++ } ++ return 0; ++} ++#else ++#define ucb1x00_switches_resume NULL ++#endif ++ ++static struct ucb1x00_driver ucb1x00_switches_driver = { ++ .add = ucb1x00_switches_add, ++ .remove = ucb1x00_switches_remove, ++ .resume = ucb1x00_switches_resume, ++}; ++ ++static int __init ucb1x00_switches_init(void) ++{ ++ return ucb1x00_register_driver(&ucb1x00_switches_driver); ++} ++ ++static void __exit ucb1x00_switches_exit(void) ++{ ++ ucb1x00_unregister_driver(&ucb1x00_switches_driver); ++} ++ ++module_init(ucb1x00_switches_init); ++module_exit(ucb1x00_switches_exit); ++ ++MODULE_AUTHOR("Bernhard Guillon <Bernhard.Guillon@opensimpad.org>"); ++MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad"); ++MODULE_LICENSE("GPL"); diff --git a/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-ts-supend-and-accuracy.patch b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-ts-supend-and-accuracy.patch new file mode 100644 index 0000000000..ef40cd05ef --- /dev/null +++ b/recipes/linux/linux/simpad/linux-2.6.27-SIMpad-ucb1x00-ts-supend-and-accuracy.patch @@ -0,0 +1,105 @@ +Index: linux-2.6.27/drivers/mfd/ucb1x00-ts.c +=================================================================== +--- linux-2.6.27.orig/drivers/mfd/ucb1x00-ts.c 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/mfd/ucb1x00-ts.c 2008-12-04 01:27:04.168672848 +0100 +@@ -16,6 +16,10 @@ + * It is important to note that the signal connected to the ADCSYNC + * pin should provide pulses even when the LCD is blanked, otherwise + * a pen touch needed to unblank the LCD will never be read. ++ * ++ * mrdata: -added some accuracy improvement based on thesings collie patch ++ * -added suspend fix ++ * + */ + #include <linux/module.h> + #include <linux/moduleparam.h> +@@ -103,6 +107,8 @@ + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ++ udelay(55); ++ + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); + } + } +@@ -129,7 +135,7 @@ + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + +- udelay(55); ++ udelay(165); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); + } +@@ -157,7 +163,7 @@ + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + +- udelay(55); ++ udelay(165); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); + } +@@ -218,7 +224,11 @@ + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ucb1x00_adc_enable(ts->ucb); + y = ucb1x00_ts_read_ypos(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ucb1x00_adc_enable(ts->ucb); + p = ucb1x00_ts_read_pressure(ts); + + /* +@@ -229,8 +239,11 @@ + + msleep(10); + +- ucb1x00_enable(ts->ucb); ++ if ((x < 60) || (y < 60)) { ++ p = 0; ++ } + ++ ucb1x00_enable(ts->ucb); + + if (ucb1x00_ts_pen_down(ts)) { + set_current_state(TASK_INTERRUPTIBLE); +@@ -248,7 +261,9 @@ + } + + timeout = MAX_SCHEDULE_TIMEOUT; ++ + } else { ++ + ucb1x00_disable(ts->ucb); + + /* +@@ -267,6 +282,14 @@ + + try_to_freeze(); + ++ /* ++ * While suspend the ktsd-thread goes sleep -> try_to_freeze() ++ * While resume the ktsd-thread do wakup and must rune one time ++ * again to do a clean re-setup -> enable_irq: UCB_IRQ_TSPX ++ */ ++ if(ts->restart) ++ timeout = HZ / 100; ++ + schedule_timeout(timeout); + } + +@@ -349,8 +372,12 @@ + * TS interrupt mode is set up again + * after sleep. + */ ++ + ts->restart = 1; + wake_up(&ts->irq_wait); ++ ++ printk(KERN_INFO "ucb1x00-ts.c -> ucb1x00_ts_resume() ktsd - restart *DONE*\n"); ++ + } + return 0; + } |