diff options
| author | Martin Jansa <Martin.Jansa@gmail.com> | 2010-05-19 18:53:47 +0200 |
|---|---|---|
| committer | Martin Jansa <Martin.Jansa@gmail.com> | 2010-05-25 09:09:24 +0200 |
| commit | edc90bdfa987ddc140c72891de4b41ae0bb2ef04 (patch) | |
| tree | c0c44d7e3c7fb947a61b6e54eb8c0bab185be964 | |
| parent | a9102cac0e6f56766e7317d9ea7af357a1593af7 (diff) | |
linux-openmoko-2.6.34: add rebased gdrm and qtmoko patches, use gta02_drm_defconfig again
Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
15 files changed, 7691 insertions, 885 deletions
diff --git a/recipes/linux/linux-openmoko-2.6.34/0001-DRM-for-platform-devices.patch b/recipes/linux/linux-openmoko-2.6.34/0001-DRM-for-platform-devices.patch new file mode 100644 index 0000000000..56197a263f --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.34/0001-DRM-for-platform-devices.patch @@ -0,0 +1,458 @@ +From 69d87612a1b545e6d4cf9fc93117be86f871f7d2 Mon Sep 17 00:00:00 2001 +From: Thomas White <taw@bitwiz.org.uk> +Date: Sat, 22 May 2010 18:59:58 +0200 +Subject: [PATCH 01/13] DRM for platform devices + +This modifies the DRM core in a small number of places to allow platform +devices to be used for direct rendering, alongside PCI devices. + +Signed-off-by: Thomas White <taw@bitwiz.org.uk> +--- + drivers/gpu/drm/Kconfig | 2 +- + drivers/gpu/drm/drm_bufs.c | 2 +- + drivers/gpu/drm/drm_drv.c | 27 ++++++++++ + drivers/gpu/drm/drm_info.c | 27 ++++++++-- + drivers/gpu/drm/drm_ioctl.c | 118 ++++++++++++++++++++++++++++++------------- + drivers/gpu/drm/drm_stub.c | 76 +++++++++++++++++++++++++++- + drivers/gpu/drm/drm_sysfs.c | 6 ++- + include/drm/drmP.h | 13 +++++ + 8 files changed, 224 insertions(+), 47 deletions(-) + +diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig +index 305c590..7244cef 100644 +--- a/drivers/gpu/drm/Kconfig ++++ b/drivers/gpu/drm/Kconfig +@@ -6,7 +6,7 @@ + # + menuconfig DRM + tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" +- depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU ++ depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU + select I2C + select I2C_ALGOBIT + help +diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c +index f7ba82e..30ce982 100644 +--- a/drivers/gpu/drm/drm_bufs.c ++++ b/drivers/gpu/drm/drm_bufs.c +@@ -189,7 +189,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +-#if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__) ++#if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__arm__) + if (map->offset + (map->size-1) < map->offset || + map->offset < virt_to_phys(high_memory)) { + kfree(map); +diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c +index 4a66201..769d12b 100644 +--- a/drivers/gpu/drm/drm_drv.c ++++ b/drivers/gpu/drm/drm_drv.c +@@ -250,6 +250,7 @@ int drm_lastclose(struct drm_device * dev) + */ + int drm_init(struct drm_driver *driver) + { ++#ifdef CONFIG_PCI + struct pci_dev *pdev = NULL; + const struct pci_device_id *pid; + int i; +@@ -283,11 +284,37 @@ int drm_init(struct drm_driver *driver) + drm_get_dev(pdev, pid, driver); + } + } ++#endif + return 0; + } + + EXPORT_SYMBOL(drm_init); + ++/** ++ * Call this to associate a drm_driver with a platform_device. ++ * ++ * \return zero on success or a negative number on failure. ++ * ++ * This is a replacement for drm_init(), but for platform drivers. ++ * In this case, the caller must provide the matching platform_device ++ * ++ * since there is no physical bus to scan through. ++ * ++ * \sa drm_init ++ * ++ */ ++int drm_platform_init(struct drm_driver *driver, struct platform_device *pdev, ++ void *priv) ++{ ++ DRM_DEBUG("\n"); ++ ++ INIT_LIST_HEAD(&driver->device_list); ++ ++ return drm_get_platform_dev(pdev, driver, priv); ++} ++ ++EXPORT_SYMBOL(drm_platform_init); ++ + void drm_exit(struct drm_driver *driver) + { + struct drm_device *dev, *tmp; +diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c +index f0f6c6b..838c2ee 100644 +--- a/drivers/gpu/drm/drm_info.c ++++ b/drivers/gpu/drm/drm_info.c +@@ -52,12 +52,28 @@ int drm_name_info(struct seq_file *m, void *data) + return 0; + + if (master->unique) { +- seq_printf(m, "%s %s %s\n", +- dev->driver->pci_driver.name, +- pci_name(dev->pdev), master->unique); ++ ++ if (drm_core_is_platform(dev)) { ++ seq_printf(m, "%s %s %s\n", ++ dev->driver->name, ++ dev_name(&dev->platform_dev->dev), ++ master->unique); ++ } else { ++ seq_printf(m, "%s %s %s\n", ++ dev->driver->pci_driver.name, ++ pci_name(dev->pdev), master->unique); ++ } ++ + } else { +- seq_printf(m, "%s %s\n", dev->driver->pci_driver.name, +- pci_name(dev->pdev)); ++ ++ if (drm_core_is_platform(dev)) { ++ seq_printf(m, "%s %s\n", dev->driver->name, ++ dev_name(&dev->platform_dev->dev)); ++ } else { ++ seq_printf(m, "%s %s\n", dev->driver->pci_driver.name, ++ pci_name(dev->pdev)); ++ } ++ + } + + return 0; +@@ -325,4 +341,3 @@ int drm_vma_info(struct seq_file *m, void *data) + } + + #endif +- +diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c +index 9b9ff46..133ef29 100644 +--- a/drivers/gpu/drm/drm_ioctl.c ++++ b/drivers/gpu/drm/drm_ioctl.c +@@ -83,7 +83,6 @@ int drm_setunique(struct drm_device *dev, void *data, + { + struct drm_unique *u = data; + struct drm_master *master = file_priv->master; +- int domain, bus, slot, func, ret; + + if (master->unique_len || master->unique) + return -EBUSY; +@@ -101,28 +100,46 @@ int drm_setunique(struct drm_device *dev, void *data, + + master->unique[master->unique_len] = '\0'; + +- dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) + +- strlen(master->unique) + 2, GFP_KERNEL); +- if (!dev->devname) +- return -ENOMEM; ++ if ( !drm_core_is_platform(dev) ) { + +- sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, +- master->unique); ++ int domain, bus, slot, func, ret; + +- /* Return error if the busid submitted doesn't match the device's actual +- * busid. +- */ +- ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); +- if (ret != 3) +- return -EINVAL; +- domain = bus >> 8; +- bus &= 0xff; ++ /* PCI device */ ++ dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) + ++ strlen(master->unique) + 2, GFP_KERNEL); ++ if (!dev->devname) ++ return -ENOMEM; + +- if ((domain != drm_get_pci_domain(dev)) || +- (bus != dev->pdev->bus->number) || +- (slot != PCI_SLOT(dev->pdev->devfn)) || +- (func != PCI_FUNC(dev->pdev->devfn))) +- return -EINVAL; ++ sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, ++ master->unique); ++ ++ /* Return error if the busid submitted doesn't match the ++ * device's actual busid. ++ */ ++ ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); ++ if (ret != 3) ++ return -EINVAL; ++ domain = bus >> 8; ++ bus &= 0xff; ++ ++ if ((domain != drm_get_pci_domain(dev)) || ++ (bus != dev->pdev->bus->number) || ++ (slot != PCI_SLOT(dev->pdev->devfn)) || ++ (func != PCI_FUNC(dev->pdev->devfn))) ++ return -EINVAL; ++ ++ } else { ++ ++ /* Platform device */ ++ dev->devname = kmalloc(strlen(dev->driver->name) + ++ strlen(master->unique) + 2, GFP_KERNEL); ++ if (!dev->devname) ++ return -ENOMEM; ++ ++ sprintf(dev->devname, "%s@%s", dev->driver->name, ++ master->unique); ++ ++ } + + return 0; + } +@@ -141,23 +158,52 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) + if (master->unique == NULL) + return -ENOMEM; + +- len = snprintf(master->unique, master->unique_len, "pci:%04x:%02x:%02x.%d", +- drm_get_pci_domain(dev), +- dev->pdev->bus->number, +- PCI_SLOT(dev->pdev->devfn), +- PCI_FUNC(dev->pdev->devfn)); +- if (len >= master->unique_len) +- DRM_ERROR("buffer overflow"); +- else +- master->unique_len = len; +- +- dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) + +- master->unique_len + 2, GFP_KERNEL); +- if (dev->devname == NULL) +- return -ENOMEM; ++ if ( !drm_core_is_platform(dev) ) { ++ ++ /* PCI device */ + +- sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, +- master->unique); ++ len = snprintf(master->unique, master->unique_len, ++ "pci:%04x:%02x:%02x.%d", ++ drm_get_pci_domain(dev), ++ dev->pdev->bus->number, ++ PCI_SLOT(dev->pdev->devfn), ++ PCI_FUNC(dev->pdev->devfn)); ++ if (len >= master->unique_len) ++ DRM_ERROR("buffer overflow"); ++ else ++ master->unique_len = len; ++ ++ dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) + ++ master->unique_len + 2, GFP_KERNEL); ++ if (dev->devname == NULL) ++ return -ENOMEM; ++ ++ sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, ++ master->unique); ++ ++ } else { ++ ++ /* Platform device */ ++ ++ int len; ++ ++ len = snprintf(master->unique, master->unique_len, ++ "platform:%s", dev->platform_dev->name); ++ ++ if (len >= master->unique_len) ++ DRM_ERROR("buffer overflow"); ++ else ++ master->unique_len = len; ++ ++ dev->devname = kmalloc(strlen(dev->driver->name) ++ + master->unique_len + 2, GFP_KERNEL); ++ if (dev->devname == NULL) ++ return -ENOMEM; ++ ++ sprintf(dev->devname, "%s@%s", dev->driver->name, ++ master->unique); ++ ++ } + + return 0; + } +diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c +index a0c365f..7e7eba6 100644 +--- a/drivers/gpu/drm/drm_stub.c ++++ b/drivers/gpu/drm/drm_stub.c +@@ -246,8 +246,10 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, + idr_init(&dev->drw_idr); + + dev->pdev = pdev; +- dev->pci_device = pdev->device; +- dev->pci_vendor = pdev->vendor; ++ if (pdev) { ++ dev->pci_device = pdev->device; ++ dev->pci_vendor = pdev->vendor; ++ } + + #ifdef __alpha__ + dev->hose = pdev->sysdata; +@@ -465,6 +467,76 @@ err_g1: + EXPORT_SYMBOL(drm_get_dev); + + /** ++ * ++ * Register a platform device as a DRM device ++ * ++ * \param pdev - platform device structure ++ * \param driver - the matching drm_driver structure ++ * \return zero on success or a negative number on failure. ++ * ++ * Attempt to gets inter module "drm" information. If we are first ++ * then register the character device and inter module information. ++ * Try and register, if we fail to register, backout previous work. ++ * ++ * \sa drm_get_dev ++ */ ++int drm_get_platform_dev(struct platform_device *pdev, ++ struct drm_driver *driver, void *priv) ++{ ++ struct drm_device *dev; ++ int ret; ++ DRM_DEBUG("\n"); ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ dev->dev_private = priv; ++ ++ if ((ret = drm_fill_in_dev(dev, NULL, NULL, driver))) { ++ printk(KERN_ERR "DRM: Fill_in_dev failed.\n"); ++ goto err_g1; ++ } ++ dev->platform_dev = pdev; ++ ++ if (drm_core_check_feature(dev, DRIVER_MODESET)) { ++ ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL); ++ if (ret) ++ goto err_g2; ++ } ++ ++ if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY))) ++ goto err_g3; ++ ++ if (dev->driver->load) { ++ ret = dev->driver->load(dev, 0); ++ if (ret) ++ goto err_g3; ++ } ++ ++ /* setup the grouping for the legacy output */ ++ if (drm_core_check_feature(dev, DRIVER_MODESET)) { ++ ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); ++ if (ret) ++ goto err_g3; ++ } ++ ++ list_add_tail(&dev->driver_item, &driver->device_list); ++ ++ DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", ++ driver->name, driver->major, driver->minor, driver->patchlevel, ++ driver->date, dev->primary->index); ++ ++ return 0; ++ ++err_g3: ++ drm_put_minor(&dev->primary); ++err_g2: ++err_g1: ++ kfree(dev); ++ return ret; ++} ++ ++/** + * Put a secondary minor number. + * + * \param sec_minor - structure to be released +diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c +index 25bbd30..947e731 100644 +--- a/drivers/gpu/drm/drm_sysfs.c ++++ b/drivers/gpu/drm/drm_sysfs.c +@@ -488,7 +488,11 @@ int drm_sysfs_device_add(struct drm_minor *minor) + int err; + char *minor_str; + +- minor->kdev.parent = &minor->dev->pdev->dev; ++ if (minor->dev->pdev) { ++ minor->kdev.parent = &minor->dev->pdev->dev; ++ } else { ++ minor->kdev.parent = &minor->dev->platform_dev->dev; ++ } + minor->kdev.class = drm_class; + minor->kdev.release = drm_sysfs_device_release; + minor->kdev.devt = minor->device; +diff --git a/include/drm/drmP.h b/include/drm/drmP.h +index 2f3b3a0..43894ec 100644 +--- a/include/drm/drmP.h ++++ b/include/drm/drmP.h +@@ -56,6 +56,7 @@ + #include <linux/cdev.h> + #include <linux/mutex.h> + #include <linux/slab.h> ++#include <linux/platform_device.h> + #if defined(__alpha__) || defined(__powerpc__) + #include <asm/pgtable.h> /* For pte_wrprotect */ + #endif +@@ -144,6 +145,7 @@ extern void drm_ut_debug_printk(unsigned int request_level, + #define DRIVER_IRQ_VBL2 0x800 + #define DRIVER_GEM 0x1000 + #define DRIVER_MODESET 0x2000 ++#define DRIVER_IS_PLATFORM 0x4000 + + /***********************************************************************/ + /** \name Begin the DRM... */ +@@ -1014,6 +1016,7 @@ struct drm_device { + wait_queue_head_t buf_writers; /**< Processes waiting to ctx switch */ + + struct drm_agp_head *agp; /**< AGP data */ ++ struct platform_device *platform_dev; /**< platform device structure */ + + struct pci_dev *pdev; /**< PCI device structure */ + int pci_vendor; /**< PCI vendor id */ +@@ -1124,12 +1127,20 @@ static inline int drm_mtrr_del(int handle, unsigned long offset, + } + #endif + ++static inline int drm_core_is_platform(struct drm_device *dev) ++{ ++ return drm_core_check_feature(dev, DRIVER_IS_PLATFORM); ++} ++ + /******************************************************************/ + /** \name Internal function definitions */ + /*@{*/ + + /* Driver support (drm_drv.h) */ + extern int drm_init(struct drm_driver *driver); ++extern int drm_platform_init(struct drm_driver *driver, ++ struct platform_device *pdev, ++ void *dev_private); + extern void drm_exit(struct drm_driver *driver); + extern long drm_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg); +@@ -1350,6 +1361,8 @@ extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + struct drm_master *drm_master_create(struct drm_minor *minor); + extern struct drm_master *drm_master_get(struct drm_master *master); ++extern int drm_get_platform_dev(struct platform_device *pdev, ++ struct drm_driver *driver, void *priv); + extern void drm_master_put(struct drm_master **master); + extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, + struct drm_driver *driver); +-- +1.7.1 + diff --git a/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch b/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch new file mode 100644 index 0000000000..1b6964aee9 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch @@ -0,0 +1,1759 @@ +From b4c5d1702abcaef7d8af5d3dc8934b2184ba6be0 Mon Sep 17 00:00:00 2001 +From: Radek Polak <psonek2@seznam.cz> +Date: Fri, 9 Apr 2010 09:15:40 +0200 +Subject: [PATCH 1/4] accels.patch + +adds support for accelerometers. You will need include/linux/lis302dl.h and +drivers/input/misc/lis302dl.c from andy-tracking. The patch needs +spi_bitbang_transfer_sync() and bitbang_work() to be ported correctly (some +fixes from original 2.6.32 are missing). + +Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com> +--- + arch/arm/mach-s3c2410/include/mach/spi-gpio.h | 3 +- + arch/arm/mach-s3c2440/mach-gta02.c | 157 ++++ + drivers/input/misc/Kconfig | 9 + + drivers/input/misc/Makefile | 1 + + drivers/input/misc/lis302dl.c | 952 +++++++++++++++++++++++++ + drivers/spi/spi_bitbang.c | 231 ++++--- + drivers/spi/spi_s3c24xx_gpio.c | 7 +- + include/linux/lis302dl.h | 152 ++++ + include/linux/spi/spi.h | 30 + + include/linux/spi/spi_bitbang.h | 5 + + 10 files changed, 1433 insertions(+), 114 deletions(-) + create mode 100644 drivers/input/misc/lis302dl.c + create mode 100644 include/linux/lis302dl.h + +diff --git a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h +index dcef228..8eedc9c 100644 +--- a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h ++++ b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h +@@ -21,7 +21,8 @@ struct s3c2410_spigpio_info { + int num_chipselect; + int bus_num; + +- void (*chip_select)(struct s3c2410_spigpio_info *spi, int cs); ++ int non_blocking_transfer; ++ void (*chip_select)(struct s3c2410_spigpio_info *spi, int csid, int cs); + }; + + +diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c +index e8ac76b..795b9f4 100644 +--- a/arch/arm/mach-s3c2440/mach-gta02.c ++++ b/arch/arm/mach-s3c2440/mach-gta02.c +@@ -62,6 +62,7 @@ + + #include <linux/input.h> + #include <linux/gpio_keys.h> ++#include <linux/lis302dl.h> + + #include <linux/leds.h> + #include <linux/leds_pwm.h> +@@ -111,6 +112,22 @@ + #include <linux/glamofb.h> + #include <linux/mfd/glamo.h> + ++#define S3C2410_GPIONO(bank,offset) ((bank) + (offset)) ++ ++#define S3C2410_GPIO_BANKD (32*3) ++#define S3C2410_GPIO_BANKG (32*6) ++ ++#define S3C2410_GPG5 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 5) ++#define S3C2410_GPG6 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 6) ++#define S3C2410_GPG7 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 7) ++#define S3C2410_GPD12 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 12) ++#define S3C2410_GPD13 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 13) ++ ++#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */ ++#define BITBANG_CS_INACTIVE 0 ++ ++#define S3C_SYSTEM_REV_ATAG GTA02v6_SYSTEM_REV ++ + static struct pcf50633 *gta02_pcf; + + /* +@@ -322,6 +339,60 @@ const static struct jbt6k74_platform_data jbt6k74_pdata = { + .gpio_reset = GTA02_GPIO_GLAMO(4), + }; + ++/*----------- SPI: Accelerometers attached to SPI of s3c244x ----------------- */ ++ ++void gta02_lis302dl_suspend_io(struct lis302dl_info *lis, int resume) ++{ ++ struct lis302dl_platform_data *pdata = lis->pdata; ++ ++ if (!resume) { ++ /* ++ * we don't want to power them with a high level ++ * because GSENSOR_3V3 is not up during suspend ++ */ ++ s3c2410_gpio_setpin(pdata->pin_chip_select, 0); ++ s3c2410_gpio_setpin(pdata->pin_clk, 0); ++ s3c2410_gpio_setpin(pdata->pin_mosi, 0); ++ /* misnomer: it is a pullDOWN in 2442 */ ++ s3c2410_gpio_pullup(pdata->pin_miso, 1); ++ return; ++ } ++ ++ /* back to normal */ ++ s3c2410_gpio_setpin(pdata->pin_chip_select, 1); ++ s3c2410_gpio_setpin(pdata->pin_clk, 1); ++ /* misnomer: it is a pullDOWN in 2442 */ ++ s3c2410_gpio_pullup(pdata->pin_miso, 0); ++ ++ s3c2410_gpio_cfgpin(pdata->pin_chip_select, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_cfgpin(pdata->pin_clk, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_cfgpin(pdata->pin_mosi, S3C2410_GPIO_OUTPUT); ++ s3c2410_gpio_cfgpin(pdata->pin_miso, S3C2410_GPIO_INPUT); ++ ++} ++ ++struct lis302dl_platform_data lis302_pdata_top = { ++ .name = "lis302-1 (top)", ++ .pin_chip_select= S3C2410_GPD12, ++ .pin_clk = S3C2410_GPG7, ++ .pin_mosi = S3C2410_GPG6, ++ .pin_miso = S3C2410_GPG5, ++ .interrupt = GTA02_IRQ_GSENSOR_1, ++ .open_drain = 1, /* altered at runtime by PCB rev */ ++ .lis302dl_suspend_io = gta02_lis302dl_suspend_io, ++}; ++ ++struct lis302dl_platform_data lis302_pdata_bottom = { ++ .name = "lis302-2 (bottom)", ++ .pin_chip_select= S3C2410_GPD13, ++ .pin_clk = S3C2410_GPG7, ++ .pin_mosi = S3C2410_GPG6, ++ .pin_miso = S3C2410_GPG5, ++ .interrupt = GTA02_IRQ_GSENSOR_2, ++ .open_drain = 1, /* altered at runtime by PCB rev */ ++ .lis302dl_suspend_io = gta02_lis302dl_suspend_io, ++}; ++ + static struct spi_board_info gta02_spi_board_info[] = { + { + .modalias = "jbt6k74", +@@ -332,6 +403,81 @@ static struct spi_board_info gta02_spi_board_info[] = { + .bus_num = 2, + .chip_select = 0 + }, ++ { ++ .modalias = "lis302dl", ++ /* platform_data */ ++ .platform_data = &lis302_pdata_top, ++ /* controller_data */ ++ /* irq */ ++ .max_speed_hz = 100 * 1000, ++ .bus_num = 3, ++ .chip_select = 0, ++ }, ++ { ++ .modalias = "lis302dl", ++ /* platform_data */ ++ .platform_data = &lis302_pdata_bottom, ++ /* controller_data */ ++ /* irq */ ++ .max_speed_hz = 100 * 1000, ++ .bus_num = 3, ++ .chip_select = 1, ++ }, ++}; ++ ++static void gta02_lis302_chip_select(struct s3c2410_spigpio_info *info, int csid, int cs) ++{ ++ ++ /* ++ * Huh... "quirk"... CS on this device is not really "CS" like you can ++ * expect. ++ * ++ * When it is 0 it selects SPI interface mode. ++ * When it is 1 it selects I2C interface mode. ++ * ++ * Because we have 2 devices on one interface we have to make sure ++ * that the "disabled" device (actually in I2C mode) don't think we're ++ * talking to it. ++ * ++ * When we talk to the "enabled" device, the "disabled" device sees ++ * the clocks as I2C clocks, creating havoc. ++ * ++ * I2C sees MOSI going LOW while CLK HIGH as a START action, thus we ++ * must ensure this is never issued. ++ */ ++ ++ int cs_gpio, other_cs_gpio; ++ ++ cs_gpio = csid ? S3C2410_GPD13 : S3C2410_GPD12; ++ other_cs_gpio = (1 - csid) ? S3C2410_GPD13 : S3C2410_GPD12; ++ ++ ++ if (cs == BITBANG_CS_ACTIVE) { ++ s3c2410_gpio_setpin(other_cs_gpio, 1); ++ s3c2410_gpio_setpin(cs_gpio, 1); ++ s3c2410_gpio_setpin(info->pin_clk, 1); ++ s3c2410_gpio_setpin(cs_gpio, 0); ++ } else { ++ s3c2410_gpio_setpin(cs_gpio, 1); ++ s3c2410_gpio_setpin(other_cs_gpio, 1); ++ } ++} ++ ++static struct s3c2410_spigpio_info gta02_spigpio_cfg = { ++ .pin_clk = S3C2410_GPG7, ++ .pin_mosi = S3C2410_GPG6, ++ .pin_miso = S3C2410_GPG5, ++ .bus_num = 3, ++ .num_chipselect = 2, ++ .chip_select = gta02_lis302_chip_select, ++ .non_blocking_transfer = 1, ++}; ++ ++static struct platform_device gta02_spi_gpio_dev = { ++ .name = "spi_s3c24xx_gpio", ++ .dev = { ++ .platform_data = >a02_spigpio_cfg, ++ }, + }; + + static struct resource gta02_glamo_resources[] = { +@@ -1091,6 +1237,7 @@ static struct platform_device *gta02_devices[] __initdata = { + >a02_pm_bt_dev, + >a02_pm_wlan_dev, + >a02_glamo_dev, ++ >a02_spi_gpio_dev, + &s3c_device_adc, + &s3c_device_ts, + }; +@@ -1302,6 +1449,16 @@ static void __init gta02_machine_init(void) + /* Set the panic callback to make AUX LED blink at ~5Hz. */ + panic_blink = gta02_panic_blink; + ++ switch (S3C_SYSTEM_REV_ATAG) { ++ case GTA02v6_SYSTEM_REV: ++ /* we need push-pull interrupt from motion sensors */ ++ lis302_pdata_top.open_drain = 0; ++ lis302_pdata_bottom.open_drain = 0; ++ break; ++ default: ++ break; ++ } ++ + bus_register_notifier(&platform_bus_type, >a02_device_register_notifier); + bus_register_notifier(&spi_bus_type, >a02_device_register_notifier); + +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index 23140a3..d8d0932 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -340,4 +340,13 @@ config INPUT_PCAP + To compile this driver as a module, choose M here: the + module will be called pcap_keys. + ++config INPUT_LIS302DL ++ tristate "STmicro LIS302DL 3-axis accelerometer" ++ depends on SPI_MASTER ++ help ++ SPI driver for the STmicro LIS302DL 3-axis accelerometer. ++ ++ The userspece interface is a 3-axis (X/Y/Z) relative movement ++ Linux input device, reporting REL_[XYZ] events. ++ + endif +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index 7e95a5d..5b810db 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -32,4 +32,5 @@ obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o + obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o + obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o + obj-$(CONFIG_INPUT_YEALINK) += yealink.o ++obj-$(CONFIG_INPUT_LIS302DL) += lis302dl.o + +diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c +new file mode 100644 +index 0000000..d345bfb +--- /dev/null ++++ b/drivers/input/misc/lis302dl.c +@@ -0,0 +1,952 @@ ++/* Linux kernel driver for the ST LIS302D 3-axis accelerometer ++ * ++ * Copyright (C) 2007-2008 by Openmoko, Inc. ++ * Author: Harald Welte <laforge@openmoko.org> ++ * converted to private bitbang by: ++ * Andy Green <andy@openmoko.com> ++ * ability to set acceleration threshold added by: ++ * Simon Kagstrom <simon.kagstrom@gmail.com> ++ * 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, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ * ++ * TODO ++ * * statistics for overflow events ++ * * configuration interface (sysfs) for ++ * * enable/disable x/y/z axis data ready ++ * * enable/disable resume from freee fall / click ++ * * free fall / click parameters ++ * * high pass filter parameters ++ */ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/module.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/irq.h> ++#include <linux/interrupt.h> ++#include <linux/sysfs.h> ++#include <linux/spi/spi.h> ++ ++#include <linux/lis302dl.h> ++ ++/* Utility functions */ ++static u8 __reg_read(struct lis302dl_info *lis, u8 reg) ++{ ++ struct spi_message msg; ++ struct spi_transfer t; ++ u8 data[2] = {0xc0 | reg}; ++ int rc; ++ ++ spi_message_init(&msg); ++ memset(&t, 0, sizeof t); ++ t.len = 2; ++ spi_message_add_tail(&t, &msg); ++ t.tx_buf = &data[0]; ++ t.rx_buf = &data[0]; ++ ++ /* Should complete without blocking */ ++ rc = spi_non_blocking_transfer(lis->spi, &msg); ++ if (rc < 0) { ++ dev_err(lis->dev, "Error reading register\n"); ++ return rc; ++ } ++ ++ return data[1]; ++} ++ ++static void __reg_write(struct lis302dl_info *lis, u8 reg, u8 val) ++{ ++ struct spi_message msg; ++ struct spi_transfer t; ++ u8 data[2] = {reg, val}; ++ ++ spi_message_init(&msg); ++ memset(&t, 0, sizeof t); ++ t.len = 2; ++ spi_message_add_tail(&t, &msg); ++ t.tx_buf = &data[0]; ++ t.rx_buf = &data[0]; ++ ++ /* Completes without blocking */ ++ if (spi_non_blocking_transfer(lis->spi, &msg) < 0) ++ dev_err(lis->dev, "Error writing register\n"); ++} ++ ++static void __reg_set_bit_mask(struct lis302dl_info *lis, u8 reg, u8 mask, ++ u8 val) ++{ ++ u_int8_t tmp; ++ ++ val &= mask; ++ ++ tmp = __reg_read(lis, reg); ++ tmp &= ~mask; ++ tmp |= val; ++ __reg_write(lis, reg, tmp); ++} ++ ++static int __ms_to_duration(struct lis302dl_info *lis, int ms) ++{ ++ /* If we have 400 ms sampling rate, the stepping is 2.5 ms, ++ * on 100 ms the stepping is 10ms */ ++ if (lis->flags & LIS302DL_F_DR) ++ return min((ms * 10) / 25, 637); ++ ++ return min(ms / 10, 2550); ++} ++ ++static int __duration_to_ms(struct lis302dl_info *lis, int duration) ++{ ++ if (lis->flags & LIS302DL_F_DR) ++ return (duration * 25) / 10; ++ ++ return duration * 10; ++} ++ ++static u8 __mg_to_threshold(struct lis302dl_info *lis, int mg) ++{ ++ /* If FS is set each bit is 71mg, otherwise 18mg. The THS register ++ * has 7 bits for the threshold value */ ++ if (lis->flags & LIS302DL_F_FS) ++ return min(mg / 71, 127); ++ ++ return min(mg / 18, 127); ++} ++ ++static int __threshold_to_mg(struct lis302dl_info *lis, u8 threshold) ++{ ++ if (lis->flags & LIS302DL_F_FS) ++ return threshold * 71; ++ ++ return threshold * 18; ++} ++ ++/* interrupt handling related */ ++ ++enum lis302dl_intmode { ++ LIS302DL_INTMODE_GND = 0x00, ++ LIS302DL_INTMODE_FF_WU_1 = 0x01, ++ LIS302DL_INTMODE_FF_WU_2 = 0x02, ++ LIS302DL_INTMODE_FF_WU_12 = 0x03, ++ LIS302DL_INTMODE_DATA_READY = 0x04, ++ LIS302DL_INTMODE_CLICK = 0x07, ++}; ++ ++static void __lis302dl_int_mode(struct device *dev, int int_pin, ++ enum lis302dl_intmode mode) ++{ ++ struct lis302dl_info *lis = dev_get_drvdata(dev); ++ ++ switch (int_pin) { ++ case 1: ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x07, mode); ++ break; ++ case 2: ++ __reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x38, mode << 3); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void __enable_wakeup(struct lis302dl_info *lis) ++{ ++ __reg_write(lis, LIS302DL_REG_CTRL1, 0); ++ ++ /* First zero to get to a known state */ ++ __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, LIS302DL_FFWUCFG_XHIE | ++ LIS302DL_FFWUCFG_YHIE | LIS302DL_FFWUCFG_ZHIE | ++ LIS302DL_FFWUCFG_LIR); ++ __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, ++ __mg_to_threshold(lis, lis->wakeup.threshold)); ++ __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, ++ __ms_to_duration(lis, lis->wakeup.duration)); ++ ++ /* Route the interrupt for wakeup */ ++ __lis302dl_int_mode(lis->dev, 1, ++ LIS302DL_INTMODE_FF_WU_1); ++ ++ __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET); ++ __reg_read(lis, LIS302DL_REG_OUT_X); ++ __reg_read(lis, LIS302DL_REG_OUT_Y); ++ __reg_read(lis, LIS302DL_REG_OUT_Z); ++ __reg_read(lis, LIS302DL_REG_STATUS); ++ __reg_read(lis, LIS302DL_REG_FF_WU_SRC_1); ++ __reg_read(lis, LIS302DL_REG_FF_WU_SRC_2); ++ __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | 7); ++} ++ ++static void __enable_data_collection(struct lis302dl_info *lis) ++{ ++ u_int8_t ctrl1 = LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen | ++ LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen |
