]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'sa11x0-mcp' into sa11x0
authorRussell King <rmk+kernel@arm.linux.org.uk>
Sun, 25 Mar 2012 22:56:30 +0000 (23:56 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sun, 25 Mar 2012 22:57:10 +0000 (23:57 +0100)
Conflicts:
arch/arm/mach-sa1100/assabet.c
arch/arm/mach-sa1100/collie.c
arch/arm/mach-sa1100/generic.c
arch/arm/mach-sa1100/lart.c
arch/arm/mach-sa1100/shannon.c

17 files changed:
arch/arm/mach-sa1100/assabet.c
arch/arm/mach-sa1100/cerf.c
arch/arm/mach-sa1100/collie.c
arch/arm/mach-sa1100/generic.c
arch/arm/mach-sa1100/generic.h
arch/arm/mach-sa1100/include/mach/mcp.h
arch/arm/mach-sa1100/lart.c
arch/arm/mach-sa1100/shannon.c
arch/arm/mach-sa1100/simpad.c
drivers/mfd/Kconfig
drivers/mfd/mcp-core.c
drivers/mfd/mcp-sa11x0.c
drivers/mfd/ucb1x00-assabet.c
drivers/mfd/ucb1x00-core.c
drivers/mfd/ucb1x00-ts.c
include/linux/mfd/mcp.h
include/linux/mfd/ucb1x00.h

index c3f5064df4bf9449756cb8856fe4eb1702aecc99..e708a93a7ddb3b4092928b3284a0837db39e07d7 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/serial_core.h>
+#include <linux/mfd/ucb1x00.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
@@ -71,6 +72,12 @@ void ASSABET_BCR_frob(unsigned int mask, unsigned int val)
 
 EXPORT_SYMBOL(ASSABET_BCR_frob);
 
+static void assabet_ucb1x00_reset(enum ucb1x00_reset state)
+{
+       if (state == UCB_RST_PROBE)
+               ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
+}
+
 
 /*
  * Assabet flash support code.
@@ -167,9 +174,15 @@ static struct irda_platform_data assabet_irda_data = {
        .set_speed      = assabet_irda_set_speed,
 };
 
+static struct ucb1x00_plat_data assabet_ucb1x00_data = {
+       .reset          = assabet_ucb1x00_reset,
+       .gpio_base      = -1,
+};
+
 static struct mcp_plat_data assabet_mcp_data = {
        .mccr0          = MCCR0_ADM,
        .sclk_rate      = 11981000,
+       .codec_pdata    = &assabet_ucb1x00_data,
 };
 
 static void assabet_lcd_set_visual(u32 visual)
@@ -309,6 +322,8 @@ static void __init assabet_init(void)
        PPDR |= PPC_TXD3 | PPC_TXD1;
        PPSR |= PPC_TXD3 | PPC_TXD1;
 
+       sa11x0_ppc_configure_mcp();
+
        if (machine_has_neponset()) {
                /*
                 * Angel sets this, but other bootloaders may not.
index c2f9ba3a9578356e657761662c9bd05030b0b8d6..8015604cfc22632c4a078b054955fe40523a6143 100644 (file)
@@ -121,6 +121,7 @@ static struct mcp_plat_data cerf_mcp_data = {
 
 static void __init cerf_init(void)
 {
+       sa11x0_ppc_configure_mcp();
        platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices));
        sa11x0_register_mtd(&cerf_flash_data, &cerf_flash_resource, 1);
        sa11x0_register_mcp(&cerf_mcp_data);
index 841041e118152d3dee9fe77f68fa43f8f28a7aa9..d4339d6394756637eed375d2d5a614418d132e7b 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/tty.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
+#include <linux/mfd/ucb1x00.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/timer.h>
@@ -83,10 +84,14 @@ static struct scoop_pcmcia_config collie_pcmcia_config = {
        .num_devs       = 1,
 };
 
+static struct ucb1x00_plat_data collie_ucb1x00_data = {
+       .gpio_base      = COLLIE_TC35143_GPIO_BASE,
+};
+
 static struct mcp_plat_data collie_mcp_data = {
        .mccr0          = MCCR0_ADM | MCCR0_ExtClk,
        .sclk_rate      = 9216000,
-       .gpio_base      = COLLIE_TC35143_GPIO_BASE,
+       .codec_pdata    = &collie_ucb1x00_data,
 };
 
 /*
@@ -341,6 +346,10 @@ static void __init collie_init(void)
 
        collie_power_resource[0].start = gpio_to_irq(COLLIE_GPIO_AC_IN);
        collie_power_resource[0].end = gpio_to_irq(COLLIE_GPIO_AC_IN);
+
+       sa11x0_ppc_configure_mcp();
+
+
        platform_scoop_config = &collie_pcmcia_config;
 
        ret = platform_add_devices(devices, ARRAY_SIZE(devices));
index 0296d69622ace09b6f1368221d0f31a0537926c7..97e9bdf7f2979863a64e58d9ac92fb5a7ae8b479 100644 (file)
@@ -195,7 +195,8 @@ static struct platform_device sa11x0uart3_device = {
 
 static struct resource sa11x0mcp_resources[] = {
        [0] = DEFINE_RES_MEM(__PREG(Ser4MCCR0), SZ_64K),
-       [1] = DEFINE_RES_IRQ(IRQ_Ser4MCP),
+       [1] = DEFINE_RES_MEM(__PREG(Ser4MCCR1), 4),
+       [2] = DEFINE_RES_IRQ(IRQ_Ser4MCP),
 };
 
 static u64 sa11x0mcp_dma_mask = 0xffffffffUL;
@@ -211,6 +212,16 @@ static struct platform_device sa11x0mcp_device = {
        .resource       = sa11x0mcp_resources,
 };
 
+void __init sa11x0_ppc_configure_mcp(void)
+{
+       /* Setup the PPC unit for the MCP */
+       PPDR &= ~PPC_RXD4;
+       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+       PSDR |= PPC_RXD4;
+       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+}
+
 void sa11x0_register_mcp(struct mcp_plat_data *data)
 {
        sa11x0_register_device(&sa11x0mcp_device, data);
index 5c68be858e0c8f4b3376bfdd73bb59b2cf3f264a..9eb3b3cd5a63501f18297676fc8c32dbdca21b57 100644 (file)
@@ -36,6 +36,7 @@ struct irda_platform_data;
 void sa11x0_register_irda(struct irda_platform_data *irda);
 
 struct mcp_plat_data;
+void sa11x0_ppc_configure_mcp(void);
 void sa11x0_register_mcp(struct mcp_plat_data *data);
 
 struct sa1100fb_mach_info;
index ed1a331508a754bf2e802f537689a97ee2b524c5..4b2860ae3828ef7ab6b5e934de636626a64fcb55 100644 (file)
@@ -16,7 +16,7 @@ struct mcp_plat_data {
        u32 mccr0;
        u32 mccr1;
        unsigned int sclk_rate;
-       int gpio_base;
+       void *codec_pdata;
 };
 
 #endif
index 463a322a425b0736c0b040aa4992b62562ec1570..570f75fb73a283504ee8b6b69401746536e88cdf 100644 (file)
@@ -107,6 +107,7 @@ static void __init lart_init(void)
        if (inf)
                sa11x0_register_lcd(inf);
 
+       sa11x0_ppc_configure_mcp();
        sa11x0_register_mcp(&lart_mcp_data);
 }
 
index 77b2b9b522aca5d8625e58f8ef73c1ddbfe1e174..08bb1228961f3dca8acea793c74fa992de543c4e 100644 (file)
@@ -72,6 +72,7 @@ static struct sa1100fb_mach_info shannon_lcd_info = {
 
 static void __init shannon_init(void)
 {
+       sa11x0_ppc_configure_mcp();
        sa11x0_register_lcd(&shannon_lcd_info);
        sa11x0_register_mtd(&shannon_flash_data, &shannon_flash_resource, 1);
        sa11x0_register_mcp(&shannon_mcp_data);
index cdb9d197c092429af79f4aa72a794fbe5231b37b..3da4c1f11cf59cab0a965047b99fcb632d6286dd 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/string.h> 
 #include <linux/pm.h>
 #include <linux/platform_device.h>
+#include <linux/mfd/ucb1x00.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/io.h>
@@ -180,10 +181,14 @@ static struct resource simpad_flash_resources [] = {
        DEFINE_RES_MEM(SA1100_CS1_PHYS, SZ_16M),
 };
 
+static struct ucb1x00_plat_data simpad_ucb1x00_data = {
+       .gpio_base      = SIMPAD_UCB1X00_GPIO_BASE,
+};
+
 static struct mcp_plat_data simpad_mcp_data = {
        .mccr0          = MCCR0_ADM,
        .sclk_rate      = 11981000,
-       .gpio_base      = SIMPAD_UCB1X00_GPIO_BASE,
+       .codec_pdata    = &simpad_ucb1x00_data,
 };
 
 
@@ -369,6 +374,7 @@ static int __init simpad_init(void)
 
        pm_power_off = simpad_power_off;
 
+       sa11x0_ppc_configure_mcp();
        sa11x0_register_mtd(&simpad_flash_data, simpad_flash_resources,
                              ARRAY_SIZE(simpad_flash_resources));
        sa11x0_register_mcp(&simpad_mcp_data);
index cd13e9f2f5e668d14927b698b851b6d0f99dd7bb..28a301b28579d12af842c4694d37c7d50ebd7733 100644 (file)
@@ -847,8 +847,9 @@ config MCP_SA11X0
 
 # Chip drivers
 config MCP_UCB1200
-       tristate "Support for UCB1200 / UCB1300"
-       depends on MCP
+       bool "Support for UCB1200 / UCB1300"
+       depends on MCP_SA11X0
+       select MCP
 
 config MCP_UCB1200_TS
        tristate "Touchscreen interface support"
index 86cc3f7841cdfdb2714368b1459a5fd54ac6491a..6acf2e03f2baa935039690637ff8b3c0f793ff2b 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/string.h>
 #include <linux/mfd/mcp.h>
 
-#include <mach/dma.h>
 #include <asm/system.h>
 
 
@@ -48,39 +47,11 @@ static int mcp_bus_remove(struct device *dev)
        return 0;
 }
 
-static int mcp_bus_suspend(struct device *dev, pm_message_t state)
-{
-       struct mcp *mcp = to_mcp(dev);
-       int ret = 0;
-
-       if (dev->driver) {
-               struct mcp_driver *drv = to_mcp_driver(dev->driver);
-
-               ret = drv->suspend(mcp, state);
-       }
-       return ret;
-}
-
-static int mcp_bus_resume(struct device *dev)
-{
-       struct mcp *mcp = to_mcp(dev);
-       int ret = 0;
-
-       if (dev->driver) {
-               struct mcp_driver *drv = to_mcp_driver(dev->driver);
-
-               ret = drv->resume(mcp);
-       }
-       return ret;
-}
-
 static struct bus_type mcp_bus_type = {
        .name           = "mcp",
        .match          = mcp_bus_match,
        .probe          = mcp_bus_probe,
        .remove         = mcp_bus_remove,
-       .suspend        = mcp_bus_suspend,
-       .resume         = mcp_bus_resume,
 };
 
 /**
@@ -208,6 +179,7 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size)
        mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL);
        if (mcp) {
                spin_lock_init(&mcp->lock);
+               device_initialize(&mcp->attached_device);
                mcp->attached_device.parent = parent;
                mcp->attached_device.bus = &mcp_bus_type;
                mcp->attached_device.dma_mask = parent->dma_mask;
@@ -217,18 +189,25 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size)
 }
 EXPORT_SYMBOL(mcp_host_alloc);
 
-int mcp_host_register(struct mcp *mcp)
+int mcp_host_add(struct mcp *mcp, void *pdata)
 {
+       mcp->attached_device.platform_data = pdata;
        dev_set_name(&mcp->attached_device, "mcp0");
-       return device_register(&mcp->attached_device);
+       return device_add(&mcp->attached_device);
+}
+EXPORT_SYMBOL(mcp_host_add);
+
+void mcp_host_del(struct mcp *mcp)
+{
+       device_del(&mcp->attached_device);
 }
-EXPORT_SYMBOL(mcp_host_register);
+EXPORT_SYMBOL(mcp_host_del);
 
-void mcp_host_unregister(struct mcp *mcp)
+void mcp_host_free(struct mcp *mcp)
 {
-       device_unregister(&mcp->attached_device);
+       put_device(&mcp->attached_device);
 }
-EXPORT_SYMBOL(mcp_host_unregister);
+EXPORT_SYMBOL(mcp_host_free);
 
 int mcp_driver_register(struct mcp_driver *mcpdrv)
 {
index 02c53a0766c4275d7496ece5b96cb20b0e1ddc49..1c0ceacaa1f6463db20cf2a275df68747c9cf80f 100644 (file)
  */
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/io.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/spinlock.h>
 #include <linux/platform_device.h>
+#include <linux/pm.h>
 #include <linux/mfd/mcp.h>
 
-#include <mach/dma.h>
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 #include <asm/system.h>
 #include <mach/mcp.h>
 
-#include <mach/assabet.h>
-
+#define DRIVER_NAME "sa11x0-mcp"
 
 struct mcp_sa11x0 {
-       u32     mccr0;
-       u32     mccr1;
+       void __iomem    *base0;
+       void __iomem    *base1;
+       u32             mccr0;
+       u32             mccr1;
 };
 
+/* Register offsets */
+#define MCCR0(m)       ((m)->base0 + 0x00)
+#define MCDR0(m)       ((m)->base0 + 0x08)
+#define MCDR1(m)       ((m)->base0 + 0x0c)
+#define MCDR2(m)       ((m)->base0 + 0x10)
+#define MCSR(m)                ((m)->base0 + 0x18)
+#define MCCR1(m)       ((m)->base1 + 0x00)
+
 #define priv(mcp)      ((struct mcp_sa11x0 *)mcp_priv(mcp))
 
 static void
 mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
 {
-       unsigned int mccr0;
+       struct mcp_sa11x0 *m = priv(mcp);
 
        divisor /= 32;
 
-       mccr0 = Ser4MCCR0 & ~0x00007f00;
-       mccr0 |= divisor << 8;
-       Ser4MCCR0 = mccr0;
+       m->mccr0 &= ~0x00007f00;
+       m->mccr0 |= divisor << 8;
+       writel_relaxed(m->mccr0, MCCR0(m));
 }
 
 static void
 mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
 {
-       unsigned int mccr0;
+       struct mcp_sa11x0 *m = priv(mcp);
 
        divisor /= 32;
 
-       mccr0 = Ser4MCCR0 & ~0x0000007f;
-       mccr0 |= divisor;
-       Ser4MCCR0 = mccr0;
+       m->mccr0 &= ~0x0000007f;
+       m->mccr0 |= divisor;
+       writel_relaxed(m->mccr0, MCCR0(m));
 }
 
 /*
@@ -69,14 +79,15 @@ mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
 static void
 mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
 {
+       struct mcp_sa11x0 *m = priv(mcp);
        int ret = -ETIME;
        int i;
 
-       Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
+       writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m));
 
        for (i = 0; i < 2; i++) {
                udelay(mcp->rw_timeout);
-               if (Ser4MCSR & MCSR_CWC) {
+               if (readl_relaxed(MCSR(m)) & MCSR_CWC) {
                        ret = 0;
                        break;
                }
@@ -95,15 +106,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
 static unsigned int
 mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
 {
+       struct mcp_sa11x0 *m = priv(mcp);
        int ret = -ETIME;
        int i;
 
-       Ser4MCDR2 = reg << 17 | MCDR2_Rd;
+       writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m));
 
        for (i = 0; i < 2; i++) {
                udelay(mcp->rw_timeout);
-               if (Ser4MCSR & MCSR_CRC) {
-                       ret = Ser4MCDR2 & 0xffff;
+               if (readl_relaxed(MCSR(m)) & MCSR_CRC) {
+                       ret = readl_relaxed(MCDR2(m)) & 0xffff;
                        break;
                }
        }
@@ -116,13 +128,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
 
 static void mcp_sa11x0_enable(struct mcp *mcp)
 {
-       Ser4MCSR = -1;
-       Ser4MCCR0 |= MCCR0_MCE;
+       struct mcp_sa11x0 *m = priv(mcp);
+
+       writel(-1, MCSR(m));
+       m->mccr0 |= MCCR0_MCE;
+       writel_relaxed(m->mccr0, MCCR0(m));
 }
 
 static void mcp_sa11x0_disable(struct mcp *mcp)
 {
-       Ser4MCCR0 &= ~MCCR0_MCE;
+       struct mcp_sa11x0 *m = priv(mcp);
+
+       m->mccr0 &= ~MCCR0_MCE;
+       writel_relaxed(m->mccr0, MCCR0(m));
 }
 
 /*
@@ -137,55 +155,64 @@ static struct mcp_ops mcp_sa11x0 = {
        .disable                = mcp_sa11x0_disable,
 };
 
-static int mcp_sa11x0_probe(struct platform_device *pdev)
+static int mcp_sa11x0_probe(struct platform_device *dev)
 {
-       struct mcp_plat_data *data = pdev->dev.platform_data;
+       struct mcp_plat_data *data = dev->dev.platform_data;
+       struct resource *mem0, *mem1;
+       struct mcp_sa11x0 *m;
        struct mcp *mcp;
        int ret;
 
        if (!data)
                return -ENODEV;
 
-       if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
-               return -EBUSY;
+       mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
+       if (!mem0 || !mem1)
+               return -ENXIO;
+
+       if (!request_mem_region(mem0->start, resource_size(mem0),
+                               DRIVER_NAME)) {
+               ret = -EBUSY;
+               goto err_mem0;
+       }
 
-       mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));
+       if (!request_mem_region(mem1->start, resource_size(mem1),
+                               DRIVER_NAME)) {
+               ret = -EBUSY;
+               goto err_mem1;
+       }
+
+       mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0));
        if (!mcp) {
                ret = -ENOMEM;
-               goto release;
+               goto err_alloc;
        }
 
        mcp->owner              = THIS_MODULE;
        mcp->ops                = &mcp_sa11x0;
        mcp->sclk_rate          = data->sclk_rate;
-       mcp->dma_audio_rd       = DMA_Ser4MCP0Rd;
-       mcp->dma_audio_wr       = DMA_Ser4MCP0Wr;
-       mcp->dma_telco_rd       = DMA_Ser4MCP1Rd;
-       mcp->dma_telco_wr       = DMA_Ser4MCP1Wr;
-       mcp->gpio_base          = data->gpio_base;
 
-       platform_set_drvdata(pdev, mcp);
+       m = priv(mcp);
+       m->mccr0 = data->mccr0 | 0x7f7f;
+       m->mccr1 = data->mccr1;
 
-       if (machine_is_assabet()) {
-               ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
+       m->base0 = ioremap(mem0->start, resource_size(mem0));
+       m->base1 = ioremap(mem1->start, resource_size(mem1));
+       if (!m->base0 || !m->base1) {
+               ret = -ENOMEM;
+               goto err_ioremap;
        }
 
-       /*
-        * Setup the PPC unit correctly.
-        */
-       PPDR &= ~PPC_RXD4;
-       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
-       PSDR |= PPC_RXD4;
-       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
-       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+       platform_set_drvdata(dev, mcp);
 
        /*
         * Initialise device.  Note that we initially
         * set the sampling rate to minimum.
         */
-       Ser4MCSR = -1;
-       Ser4MCCR1 = data->mccr1;
-       Ser4MCCR0 = data->mccr0 | 0x7f7f;
+       writel_relaxed(-1, MCSR(m));
+       writel_relaxed(m->mccr1, MCCR1(m));
+       writel_relaxed(m->mccr0, MCCR0(m));
 
        /*
         * Calculate the read/write timeout (us) from the bit clock
@@ -195,62 +222,90 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
        mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
                          mcp->sclk_rate;
 
-       ret = mcp_host_register(mcp);
+       ret = mcp_host_add(mcp, data->codec_pdata);
        if (ret == 0)
-               goto out;
+               return 0;
 
- release:
-       release_mem_region(0x80060000, 0x60);
-       platform_set_drvdata(pdev, NULL);
+       platform_set_drvdata(dev, NULL);
 
- out:
+ err_ioremap:
+       iounmap(m->base1);
+       iounmap(m->base0);
+       mcp_host_free(mcp);
+ err_alloc:
+       release_mem_region(mem1->start, resource_size(mem1));
+ err_mem1:
+       release_mem_region(mem0->start, resource_size(mem0));
+ err_mem0:
        return ret;
 }
 
 static int mcp_sa11x0_remove(struct platform_device *dev)
 {
        struct mcp *mcp = platform_get_drvdata(dev);
+       struct mcp_sa11x0 *m = priv(mcp);
+       struct resource *mem0, *mem1;
+
+       if (m->mccr0 & MCCR0_MCE)
+               dev_warn(&dev->dev,
+                        "device left active (missing disable call?)\n");
+
+       mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
 
        platform_set_drvdata(dev, NULL);
-       mcp_host_unregister(mcp);
-       release_mem_region(0x80060000, 0x60);
+       mcp_host_del(mcp);
+       iounmap(m->base1);
+       iounmap(m->base0);
+       mcp_host_free(mcp);
+       release_mem_region(mem1->start, resource_size(mem1));
+       release_mem_region(mem0->start, resource_size(mem0));
 
        return 0;
 }
 
-static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int mcp_sa11x0_suspend(struct device *dev)
 {
-       struct mcp *mcp = platform_get_drvdata(dev);
+       struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
+
+       if (m->mccr0 & MCCR0_MCE)
+               dev_warn(dev, "device left active (missing disable call?)\n");
 
-       priv(mcp)->mccr0 = Ser4MCCR0;
-       priv(mcp)->mccr1 = Ser4MCCR1;
-       Ser4MCCR0 &= ~MCCR0_MCE;
+       writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m));
 
        return 0;
 }
 
-static int mcp_sa11x0_resume(struct platform_device *dev)
+static int mcp_sa11x0_resume(struct device *dev)
 {
-       struct mcp *mcp = platform_get_drvdata(dev);
+       struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
 
-       Ser4MCCR1 = priv(mcp)->mccr1;
-       Ser4MCCR0 = priv(mcp)->mccr0;
+       writel_relaxed(m->mccr1, MCCR1(m));
+       writel_relaxed(m->mccr0, MCCR0(m));
 
        return 0;
 }
-
-/*
- * The driver for the SA11x0 MCP port.
- */
-MODULE_ALIAS("platform:sa11x0-mcp");
+#endif
+
+static const struct dev_pm_ops mcp_sa11x0_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+       .suspend = mcp_sa11x0_suspend,
+       .freeze = mcp_sa11x0_suspend,
+       .poweroff = mcp_sa11x0_suspend,
+       .resume_noirq = mcp_sa11x0_resume,
+       .thaw_noirq = mcp_sa11x0_resume,
+       .restore_noirq = mcp_sa11x0_resume,
+#endif
+};
 
 static struct platform_driver mcp_sa11x0_driver = {
        .probe          = mcp_sa11x0_probe,
        .remove         = mcp_sa11x0_remove,
-       .suspend        = mcp_sa11x0_suspend,
-       .resume         = mcp_sa11x0_resume,
        .driver         = {
-               .name   = "sa11x0-mcp",
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .pm     = &mcp_sa11x0_pm_ops,
        },
 };
 
@@ -259,6 +314,7 @@ static struct platform_driver mcp_sa11x0_driver = {
  */
 module_platform_driver(mcp_sa11x0_driver);
 
+MODULE_ALIAS("platform:" DRIVER_NAME);
 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
 MODULE_LICENSE("GPL");
index cea9da60850d61f87c342f062483e2798b60e436..b63c0756a66917a60a55218dec79d9fbee283b75 100644 (file)
  */
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
 #include <linux/fs.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
 #include <linux/proc_fs.h>
-#include <linux/device.h>
 #include <linux/mfd/ucb1x00.h>
 
-#include <mach/dma.h>
-
-
 #define UCB1X00_ATTR(name,input)\
 static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
                           char *buf)   \
@@ -38,14 +39,45 @@ UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2);
 
 static int ucb1x00_assabet_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_batt_temp);
+       struct ucb1x00 *ucb = dev->ucb;
+       struct platform_device *pdev;
+       struct gpio_keys_platform_data keys;
+       static struct gpio_keys_button buttons[6];
+       unsigned i;
+
+       memset(buttons, 0, sizeof(buttons));
+       memset(&keys, 0, sizeof(keys));
+
+       for (i = 0; i < ARRAY_SIZE(buttons); i++) {
+               buttons[i].code = BTN_0 + i;
+               buttons[i].gpio = ucb->gpio.base + i;
+               buttons[i].type = EV_KEY;
+               buttons[i].can_disable = true;
+       }
+
+       keys.buttons = buttons;
+       keys.nbuttons = ARRAY_SIZE(buttons);
+       keys.poll_interval = 50;
+       keys.name = "ucb1x00";
+
+       pdev = platform_device_register_data(&ucb->dev, "gpio-keys", -1,
+               &keys, sizeof(keys));
+
+       device_create_file(&ucb->dev, &dev_attr_vbatt);
+       device_create_file(&ucb->dev, &dev_attr_vcharger);
+       device_create_file(&ucb->dev, &dev_attr_batt_temp);
+
+       dev->priv = pdev;
        return 0;
 }
 
 static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev)
 {
+       struct platform_device *pdev = dev->priv;
+
+       if (!IS_ERR(pdev))
+               platform_device_unregister(pdev);
+
        device_remove_file(&dev->ucb->dev, &dev_attr_batt_temp);
        device_remove_file(&dev->ucb->dev, &dev_attr_vcharger);
        device_remove_file(&dev->ucb->dev, &dev_attr_vbatt);
index febc90cdef7ea4a97885f7cc88b36ceb543e8b7e..70f02daeb22a884da64297f26bc3f43e4e365aa8 100644 (file)
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/device.h>
 #include <linux/mutex.h>
 #include <linux/mfd/ucb1x00.h>
+#include <linux/pm.h>
 #include <linux/gpio.h>
-#include <linux/semaphore.h>
-
-#include <mach/dma.h>
-#include <mach/hardware.h>
 
 static DEFINE_MUTEX(ucb1x00_mutex);
 static LIST_HEAD(ucb1x00_drivers);
@@ -102,7 +100,7 @@ void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear)
  *     ucb1x00_enable must have been called to enable the comms
  *     before using this function.
  *
- *     This function does not take any semaphores or spinlocks.
+ *     This function does not take any mutexes or spinlocks.
  */
 unsigned int ucb1x00_io_read(struct ucb1x00 *ucb)
 {
@@ -120,14 +118,22 @@ static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
        else
                ucb->io_out &= ~(1 << offset);
 
+       ucb1x00_enable(ucb);
        ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+       ucb1x00_disable(ucb);
        spin_unlock_irqrestore(&ucb->io_lock, flags);
 }
 
 static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
-       return ucb1x00_reg_read(ucb, UCB_IO_DATA) & (1 << offset);
+       unsigned val;
+
+       ucb1x00_enable(ucb);
+       val = ucb1x00_reg_read(ucb, UCB_IO_DATA);
+       ucb1x00_disable(ucb);
+
+       return val & (1 << offset);
 }
 
 static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -137,7 +143,9 @@ static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
 
        spin_lock_irqsave(&ucb->io_lock, flags);
        ucb->io_dir &= ~(1 << offset);
+       ucb1x00_enable(ucb);
        ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+       ucb1x00_disable(ucb);
        spin_unlock_irqrestore(&ucb->io_lock, flags);
 
        return 0;
@@ -157,6 +165,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
        else
                ucb->io_out &= ~mask;
 
+       ucb1x00_enable(ucb);
        if (old != ucb->io_out)
                ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
 
@@ -164,11 +173,19 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
                ucb->io_dir |= mask;
                ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
        }
+       ucb1x00_disable(ucb);
        spin_unlock_irqrestore(&ucb->io_lock, flags);
 
        return 0;
 }
 
+static int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+       struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+
+       return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO;
+}
+
 /*
  * UCB1300 data sheet says we must:
  *  1. enable ADC      => 5us (including reference startup time)
@@ -186,7 +203,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
  *     Any code wishing to use the ADC converter must call this
  *     function prior to using it.
  *
- *     This function takes the ADC semaphore to prevent two or more
+ *     This function takes the ADC mutex to prevent two or more
  *     concurrent uses, and therefore may sleep.  As a result, it
  *     can only be called from process context, not interrupt
  *     context.
@@ -196,7 +213,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
  */
 void ucb1x00_adc_enable(struct ucb1x00 *ucb)
 {
-       down(&ucb->adc_sem);
+       mutex_lock(&ucb->adc_mutex);
 
        ucb->adc_cr |= UCB_ADC_ENA;
 
@@ -218,7 +235,7 @@ void ucb1x00_adc_enable(struct ucb1x00 *ucb)
  *     complete (2 frames max without sync).
  *
  *     If called for a synchronised ADC conversion, it may sleep
- *     with the ADC semaphore held.
+ *     with the ADC mutex held.
  */
 unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
 {
@@ -246,7 +263,7 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
  *     ucb1x00_adc_disable - disable the ADC converter
  *     @ucb: UCB1x00 structure describing chip
  *
- *     Disable the ADC converter and release the ADC semaphore.
+ *     Disable the ADC converter and release the ADC mutex.
  */
 void ucb1x00_adc_disable(struct ucb1x00 *ucb)
 {
@@ -254,7 +271,7 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb)
        ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
        ucb1x00_disable(ucb);
 
-       up(&ucb->adc_sem);
+       mutex_unlock(&ucb->adc_mutex);
 }
 
 /*
@@ -265,10 +282,9 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb)
  * SIBCLK to talk to the chip.  We leave the clock running until
  * we have finished processing all interrupts from the chip.
  */
-static irqreturn_t ucb1x00_irq(int irqnr, void *devid)
+static void ucb1x00_irq(unsigned int irq, struct irq_desc *desc)
 {
-       struct ucb1x00 *ucb = devid;
-       struct ucb1x00_irq *irq;
+       struct ucb1x00 *ucb = irq_desc_get_handler_data(desc);
        unsigned int isr, i;
 
        ucb1x00_enable(ucb);
@@ -276,157 +292,104 @@ static irqreturn_t ucb1x00_irq(int irqnr, void *devid)
        ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
        ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
 
-       for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++)
-               if (isr & 1 && irq->fn)
-                       irq->fn(i, irq->devid);
+       for (i = 0; i < 16 && isr; i++, isr >>= 1, irq++)
+               if (isr & 1)
+                       generic_handle_irq(ucb->irq_base + i);
        ucb1x00_disable(ucb);
-
-       return IRQ_HANDLED;
 }
 
-/**
- *     ucb1x00_hook_irq - hook a UCB1x00 interrupt
- *     @ucb:   UCB1x00 structure describing chip
- *     @idx:   interrupt index
- *     @fn:    function to call when interrupt is triggered
- *     @devid: device id to pass to interrupt handler
- *
- *     Hook the specified interrupt.  You can only register one handler
- *     for each interrupt source.  The interrupt source is not enabled
- *     by this function; use ucb1x00_enable_irq instead.
- *
- *     Interrupt handlers will be called with other interrupts enabled.
- *
- *     Returns zero on success, or one of the following errors:
- *      -EINVAL if the interrupt index is invalid
- *      -EBUSY if the interrupt has already been hooked
- */
-int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid)
+static void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask)
 {
-       struct ucb1x00_irq *irq;
-       int ret = -EINVAL;
-
-       if (idx < 16) {
-               irq = ucb->irq_handler + idx;
-               ret = -EBUSY;
-
-               spin_lock_irq(&ucb->lock);
-               if (irq->fn == NULL) {
-                       irq->devid = devid;
-                       irq->fn = fn;
-                       ret = 0;
-               }
-               spin_unlock_irq(&ucb->lock);
-       }
-       return ret;
+       ucb1x00_enable(ucb);
+       if (ucb->irq_ris_enbl & mask)
+               ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+                                 ucb->irq_mask);
+       if (ucb->irq_fal_enbl & mask)
+               ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+                                 ucb->irq_mask);
+       ucb1x00_disable(ucb);
 }
 
-/**
- *     ucb1x00_enable_irq - enable an UCB1x00 interrupt source
- *     @ucb: UCB1x00 structure describing chip
- *     @idx: interrupt index
- *     @edges: interrupt edges to enable
- *
- *     Enable the specified interrupt to trigger on %UCB_RISING,
- *     %UCB_FALLING or both edges.  The interrupt should have been
- *     hooked by ucb1x00_hook_irq.
- */
-void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
+static void ucb1x00_irq_noop(struct irq_data *data)
 {
-       unsigned long flags;
+}
 
-       if (idx < 16) {
-               spin_lock_irqsave(&ucb->lock, flags);
+static void ucb1x00_irq_mask(struct irq_data *data)
+{
+       struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+       unsigned mask = 1 << (data->irq - ucb->irq_base);
 
-               ucb1x00_enable(ucb);
-               if (edges & UCB_RISING) {
-                       ucb->irq_ris_enbl |= 1 << idx;
-                       ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
-               }
-               if (edges & UCB_FALLING) {
-                       ucb->irq_fal_enbl |= 1 << idx;
-                       ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
-               }
-               ucb1x00_disable(ucb);
-               spin_unlock_irqrestore(&ucb->lock, flags);
-       }
+       raw_spin_lock(&ucb->irq_lock);
+       ucb->irq_mask &= ~mask;
+       ucb1x00_irq_update(ucb, mask);
+       raw_spin_unlock(&ucb->irq_lock);
 }
 
-/**
- *     ucb1x00_disable_irq - disable an UCB1x00 interrupt source
- *     @ucb: UCB1x00 structure describing chip
- *     @edges: interrupt edges to disable
- *
- *     Disable the specified interrupt triggering on the specified
- *     (%UCB_RISING, %UCB_FALLING or both) edges.
- */
-void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
+static void ucb1x00_irq_unmask(struct irq_data *data)
 {
-       unsigned long flags;
+       struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+       unsigned mask = 1 << (data->irq - ucb->irq_base);
 
-       if (idx < 16) {
-               spin_lock_irqsave(&ucb->lock, flags);
-
-               ucb1x00_enable(ucb);
-               if (edges & UCB_RISING) {
-                       ucb->irq_ris_enbl &= ~(1 << idx);
-                       ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
-               }
-               if (edges & UCB_FALLING) {
-                       ucb->irq_fal_enbl &= ~(1 << idx);
-                       ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
-               }
-               ucb1x00_disable(ucb);
-               spin_unlock_irqrestore(&ucb->lock, flags);
-       }
+       raw_spin_lock(&ucb->irq_lock);
+       ucb->irq_mask |= mask;
+       ucb1x00_irq_update(ucb, mask);
+       raw_spin_unlock(&ucb->irq_lock);
 }
 
-/**
- *     ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt
- *     @ucb: UCB1x00 structure describing chip
- *     @idx: interrupt index
- *     @devid: device id.
- *
- *     Disable the interrupt source and remove the handler.  devid must
- *     match the devid passed when hooking the interrupt.
- *
- *     Returns zero on success, or one of the following errors:
- *      -EINVAL if the interrupt index is invalid
- *      -ENOENT if devid does not match
- */
-int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid)
+static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type)
 {
-       struct ucb1x00_irq *irq;
-       int ret;
+       struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+       unsigned mask = 1 << (data->irq - ucb->irq_base);
 
-       if (idx >= 16)
-               goto bad;
+       raw_spin_lock(&ucb->irq_lock);
+       if (type & IRQ_TYPE_EDGE_RISING)
+               ucb->irq_ris_enbl |= mask;
+       else
+               ucb->irq_ris_enbl &= ~mask;
 
-       irq = ucb->irq_handler + idx;
-       ret = -ENOENT;
+       if (type & IRQ_TYPE_EDGE_FALLING)
+               ucb->irq_fal_enbl |= mask;
+       else
+               ucb->irq_fal_enbl &= ~mask;
+       if (ucb->irq_mask & mask) {
+               ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+                                 ucb->irq_mask);
+               ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+                                 ucb->irq_mask);
+       }
+       raw_spin_unlock(&ucb->irq_lock);
 
-       spin_lock_irq(&ucb->lock);
-       if (irq->devid == devid) {
-               ucb->irq_ris_enbl &= ~(1 << idx);
-               ucb->irq_fal_enbl &= ~(1 << idx);
+       return 0;
+}
 
-               ucb1x00_enable(ucb);
-               ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
-               ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
-               ucb1x00_disable(ucb);
+static int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+       struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+       struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data;
+       unsigned mask = 1 << (data->irq - ucb->irq_base);
 
-               irq->fn = NULL;
-               irq->devid = NULL;
-               ret = 0;
-       }
-       spin_unlock_irq(&ucb->lock);
-       return ret;
+       if (!pdata || !pdata->can_wakeup)
+               return -EINVAL;
 
-bad:
-       printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx);
-       return -EINVAL;
+       raw_spin_lock(&ucb->irq_lock);
+       if (on)
+               ucb->irq_wake |= mask;
+       else
+               ucb->irq_wake &= ~mask;
+       raw_spin_unlock(&ucb->irq_lock);
+
+       return 0;
 }
 
+static struct irq_chip ucb1x00_irqchip = {
+       .name = "ucb1x00",
+       .irq_ack = ucb1x00_irq_noop,
+       .irq_mask = ucb1x00_irq_mask,
+       .irq_unmask = ucb1x00_irq_unmask,
+       .irq_set_type = ucb1x00_irq_set_type,
+       .irq_set_wake = ucb1x00_irq_set_wake,
+};
+
 static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
 {
        struct ucb1x00_dev *dev;
@@ -440,8 +403,8 @@ static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
                ret = drv->add(dev);
 
                if (ret == 0) {
-                       list_add(&dev->dev_node, &ucb->devs);
-                       list_add(&dev->drv_node, &drv->devs);
+                       list_add_tail(&dev->dev_node, &ucb->devs);
+                       list_add_tail(&dev->drv_node, &drv->devs);
                } else {
                        kfree(dev);
                }
@@ -533,98 +496,126 @@ static struct class ucb1x00_class = {
 
 static int ucb1x00_probe(struct mcp *mcp)
 {
-       struct ucb1x00 *ucb;
+       struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
        struct ucb1x00_driver *drv;
-       unsigned int id;
+       struct ucb1x00 *ucb;
+       unsigned id, i, irq_base;
        int ret = -ENODEV;
-       int temp;
+
+       /* Tell the platform to deassert the UCB1x00 reset */
+       if (pdata && pdata->reset)
+               pdata->reset(UCB_RST_PROBE);
 
        mcp_enable(mcp);
        id = mcp_reg_read(mcp, UCB_ID);
+       mcp_disable(mcp);
 
        if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) {
                printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
-               goto err_disable;
+               goto out;
        }
 
        ucb = kzalloc(sizeof(struct ucb1x00), GFP_KERNEL);
        ret = -ENOMEM;
        if (!ucb)
-               goto err_disable;
-
+               goto out;
 
+       device_initialize(&ucb->dev);
        ucb->dev.class = &ucb1x00_class;
        ucb->dev.parent = &mcp->attached_device;
        dev_set_name(&ucb->dev, "ucb1x00");
 
-       spin_lock_init(&ucb->lock);
+       raw_spin_lock_init(&ucb->irq_lock);
        spin_lock_init(&ucb->io_lock);
-       sema_init(&ucb->adc_sem, 1);
+       mutex_init(&ucb->adc_mutex);
 
        ucb->id  = id;
        ucb->mcp = mcp;
+
+       ret = device_add(&ucb->dev);
+       if (ret)
+               goto err_dev_add;
+
+       ucb1x00_enable(ucb);
        ucb->irq = ucb1x00_detect_irq(ucb);
+       ucb1x00_disable(ucb);
        if (ucb->irq == NO_IRQ) {
-               printk(KERN_ERR "UCB1x00: IRQ probe failed\n");
+               dev_err(&ucb->dev, "IRQ probe failed\n");
                ret = -ENODEV;
-               goto err_free;
+               goto err_no_irq;
        }
 
        ucb->gpio.base = -1;
-       if (mcp->gpio_base != 0) {
+       irq_base = pdata ? pdata->irq_base : 0;
+       ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1);
+       if (ucb->irq_base < 0) {
+               dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n",
+                       ucb->irq_base);
+               goto err_irq_alloc;
+       }
+
+       for (i = 0; i < 16; i++) {
+               unsigned irq = ucb->irq_base + i;
+
+               irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq);
+               irq_set_chip_data(irq, ucb);
+               set_irq_flags(irq, IRQF_VALID | IRQ_NOREQUEST);
+       }
+
+       irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING);
+       irq_set_handler_data(ucb->irq, ucb);
+       irq_set_chained_handler(ucb->irq, ucb1x00_irq);
+
+       if (pdata && pdata->gpio_base) {
                ucb->gpio.label = dev_name(&ucb->dev);
-               ucb->gpio.base = mcp->gpio_base;
+               ucb->gpio.dev = &ucb->dev;
+               ucb->gpio.owner = THIS_MODULE;
+               ucb->gpio.base = pdata->gpio_base;
                ucb->gpio.ngpio = 10;
                ucb->gpio.set = ucb1x00_gpio_set;
                ucb->gpio.get = ucb1x00_gpio_get;
                ucb->gpio.direction_input = ucb1x00_gpio_direction_input;
                ucb->gpio.direction_output = ucb1x00_gpio_direction_output;
+               ucb->gpio.to_irq = ucb1x00_to_irq;
                ret = gpiochip_add(&ucb->gpio);
                if (ret)
-                       goto err_free;
+                       goto err_gpio_add;
        } else
                dev_info(&ucb->dev, "gpio_base not set so no gpiolib support");
 
-       ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING,
-                         "UCB1x00", ucb);
-       if (ret) {
-               printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
-                       ucb->irq, ret);
-               goto err_gpio;
-       }
-
        mcp_set_drvdata(mcp, ucb);
 
-       ret = device_register(&ucb->dev);
-       if (ret)
-               goto err_irq;
-
+       if (pdata)
+               device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup);
 
        INIT_LIST_HEAD(&ucb->devs);
        mutex_lock(&ucb1x00_mutex);
-       list_add(&ucb->node, &ucb1x00_devices);
+       list_add_tail(&ucb->node, &ucb1x00_devices);
        list_for_each_entry(drv, &ucb1x00_drivers, node) {
                ucb1x00_add_dev(ucb, drv);
        }
        mutex_unlock(&ucb1x00_mutex);
 
-       goto out;
+       return ret;
 
- err_irq:
-       free_irq(ucb->irq, ucb);
- err_gpio:
-       if (ucb->gpio.base != -1)
-               temp = gpiochip_remove(&ucb->gpio);
- err_free:
-       kfree(ucb);
- err_disable:
-       mcp_disable(mcp);
+ err_gpio_add:
+       irq_set_chained_handler(ucb->irq, NULL);
+ err_irq_alloc:
+       if (ucb->irq_base > 0)
+               irq_free_descs(ucb->irq_base, 16);
+ err_no_irq:
+       device_del(&ucb->dev);
+ err_dev_add:
+       put_device(&ucb->dev);
  out:
+       if (pdata && pdata->reset)
+               pdata->reset(UCB_RST_PROBE_FAIL);
        return ret;
 }
 
 static void ucb1x00_remove(struct mcp *mcp)
 {
+       struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
        struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
        struct list_head *l, *n;
        int ret;
@@ -643,8 +634,12 @@ static void ucb1x00_remove(struct mcp *mcp)
                        dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret);
        }
 
-       free_irq(ucb->irq, ucb);
+       irq_set_chained_handler(ucb->irq, NULL);
+       irq_free_descs(ucb->irq_base, 16);
        device_unregister(&ucb->dev);
+
+       if (pdata && pdata->reset)
+               pdata->reset(UCB_RST_REMOVE);
 }
 
 int ucb1x00_register_driver(struct ucb1x00_driver *drv)
@@ -653,7 +648,7 @@ int ucb1x00_register_driver(struct ucb1x00_driver *drv)
 
        INIT_LIST_HEAD(&drv->devs);
        mutex_lock(&ucb1x00_mutex);
-       list_add(&drv->node, &ucb1x00_drivers);
+       list_add_tail(&drv->node, &ucb1x00_drivers);
        list_for_each_entry(ucb, &ucb1x00_devices, node) {
                ucb1x00_add_dev(ucb, drv);
        }
@@ -674,44 +669,86 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv)
        mutex_unlock(&ucb1x00_mutex);
 }
 
-static int ucb1x00_suspend(struct mcp *mcp, pm_message_t state)
+static int ucb1x00_suspend(struct device *dev)
 {
-       struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
-       struct ucb1x00_dev *dev;
+       struct ucb1x00_plat_data *pdata = dev->platform_data;
+       struct ucb1x00 *ucb = dev_get_drvdata(dev);
+       struct ucb1x00_dev *udev;
 
        mutex_lock(&ucb1x00_mutex);
-       list_for_each_entry(dev, &ucb->devs, dev_node) {
-               if (dev->drv->suspend)
-                       dev->drv->suspend(dev, state);
+       list_for_each_entry(udev, &ucb->devs, dev_node) {
+               if (udev->drv->suspend)
+                       udev->drv->suspend(udev);
        }
        mutex_unlock(&ucb1x00_mutex);
+
+       if (ucb->irq_wake) {
+               unsigned long flags;
+
+               raw_spin_lock_irqsave(&ucb->irq_lock, flags);
+               ucb1x00_enable(ucb);
+               ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+                                 ucb->irq_wake);
+               ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+                                 ucb->irq_wake);
+               ucb1x00_disable(ucb);
+               raw_spin_unlock_irqrestore(&ucb->irq_lock, flags);
+
+               enable_irq_wake(ucb->irq);
+       } else if (pdata && pdata->reset)
+               pdata->reset(UCB_RST_SUSPEND);
+
        return 0;
 }
 
-static int ucb1x00_resume(struct mcp *mcp)
+static int ucb1x00_resume(struct device *dev)
 {
-       struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
-       struct ucb1x00_dev *dev;
+       struct ucb1x00_plat_data *pdata = dev->platform_data;
+       struct ucb1x00 *ucb = dev_get_drvdata(dev);
+       struct ucb1x00_dev *udev;
+
+       if (!ucb->irq_wake && pdata && pdata->reset)
+               pdata->reset(UCB_RST_RESUME);
 
+       ucb1x00_enable(ucb);
        ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
        ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+
+       if (ucb->irq_wake) {
+               unsigned long flags;
+
+               raw_spin_lock_irqsave(&ucb->irq_lock, flags);
+               ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+                                 ucb->irq_mask);
+               ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+                                 ucb->irq_mask);
+               raw_spin_unlock_irqrestore(&ucb->irq_lock, flags);
+
+               disable_irq_wake(ucb->irq);
+       }
+       ucb1x00_disable(ucb);
+
        mutex_lock(&ucb1x00_mutex);
-       list_for_each_entry(dev, &ucb->devs, dev_node) {
-               if (dev->drv->resume)
-                       dev->drv->resume(dev);
+       list_for_each_entry(udev, &ucb->devs, dev_node) {
+               if (udev->drv->resume)
+                       udev->drv->resume(udev);
        }
        mutex_unlock(&ucb1x00_mutex);
        return 0;
 }
 
+static const struct dev_pm_ops ucb1x00_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume)
+};
+
 static struct mcp_driver ucb1x00_driver = {
        .drv            = {
                .name   = "ucb1x00",
+               .owner  = THIS_MODULE,
+               .pm     = &ucb1x00_pm_ops,
        },
        .probe          = ucb1x00_probe,
        .remove         = ucb1x00_remove,
-       .suspend        = ucb1x00_suspend,
-       .resume         = ucb1x00_resume,
 };
 
 static int __init ucb1x00_init(void)
@@ -742,14 +779,10 @@ EXPORT_SYMBOL(ucb1x00_adc_enable);
 EXPORT_SYMBOL(ucb1x00_adc_read);
 EXPORT_SYMBOL(ucb1x00_adc_disable);
 
-EXPORT_SYMBOL(ucb1x00_hook_irq);
-EXPORT_SYMBOL(ucb1x00_free_irq);
-EXPORT_SYMBOL(ucb1x00_enable_irq);
-EXPORT_SYMBOL(ucb1x00_disable_irq);
-
 EXPORT_SYMBOL(ucb1x00_register_driver);
 EXPORT_SYMBOL(ucb1x00_unregister_driver);
 
+MODULE_ALIAS("mcp:ucb1x00");
 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 MODULE_DESCRIPTION("UCB1x00 core driver");
 MODULE_LICENSE("GPL");
index 63a3cbdfa3f33a85a146f34fff7459cc6b46c17a..1e0e20c0e082b07836f2ede84659ef7d6c40efc1 100644 (file)
@@ -20,8 +20,9 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
-#include <linux/smp.h>
+#include <linux/interrupt.h>
 #include <linux/sched.h>
+#include <linux/spinlock.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/string.h>
@@ -32,7 +33,6 @@
 #include <linux/kthread.h>
 #include <linux/mfd/ucb1x00.h>
 
-#include <mach/dma.h>
 #include <mach/collie.h>
 #include <asm/mach-types.h>
 
@@ -42,6 +42,8 @@ struct ucb1x00_ts {
        struct input_dev        *idev;
        struct ucb1x00          *ucb;
 
+       spinlock_t              irq_lock;
+       unsigned                irq_disabled;
        wait_queue_head_t       irq_wait;
        struct task_struct      *rtask;
        u16                     x_res;
@@ -238,7 +240,12 @@ static int ucb1x00_thread(void *_ts)
                if (ucb1x00_ts_pen_down(ts)) {
                        set_current_state(TASK_INTERRUPTIBLE);
 
-                       ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING);
+                       spin_lock_irq(&ts->irq_lock);
+                       if (ts->irq_disabled) {
+                               ts->irq_disabled = 0;
+                               enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX);
+                       }
+                       spin_unlock_irq(&ts->irq_lock);
                        ucb1x00_disable(ts->ucb);
 
                        /*
@@ -281,23 +288,37 @@ static int ucb1x00_thread(void *_ts)
  * We only detect touch screen _touches_ with this interrupt
  * handler, and even then we just schedule our task.
  */
-static void ucb1x00_ts_irq(int idx, void *id)
+static irqreturn_t ucb1x00_ts_irq(int irq, void *id)
 {
        struct ucb1x00_ts *ts = id;
 
-       ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+       spin_lock(&ts->irq_lock);
+       ts->irq_disabled = 1;
+       disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX);
+       spin_unlock(&ts->irq_lock);
        wake_up(&ts->irq_wait);
+
+       return IRQ_HANDLED;
 }
 
 static int ucb1x00_ts_open(struct input_dev *idev)
 {
        struct ucb1x00_ts *ts = input_get_drvdata(idev);
+       unsigned long flags = 0;
        int ret = 0;
 
        BUG_ON(ts->rtask);
 
+       if (machine_is_collie())
+               flags = IRQF_TRIGGER_RISING;
+       else
+               flags = IRQF_TRIGGER_FALLING;
+
+       ts->irq_disabled = 0;
+
        init_waitqueue_head(&ts->irq_wait);
-       ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
+       ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq,
+                         flags, "ucb1x00-ts", ts);
        if (ret < 0)
                goto out;
 
@@ -314,7 +335,7 @@ static int ucb1x00_ts_open(struct input_dev *idev)
        if (!IS_ERR(ts->rtask)) {
                ret = 0;
        } else {
-               ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+               free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
                ts->rtask = NULL;
                ret = -EFAULT;
        }
@@ -334,7 +355,7 @@ static void ucb1x00_ts_close(struct input_dev *idev)
                kthread_stop(ts->rtask);
 
        ucb1x00_enable(ts->ucb);
-       ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+       free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
        ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
        ucb1x00_disable(ts->ucb);
 }
@@ -359,11 +380,13 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
        ts->ucb = dev->ucb;
        ts->idev = idev;
        ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
+       spin_lock_init(&ts->irq_lock);
 
        idev->name       = "Touchscreen panel";
        idev->id.product = ts->ucb->id;
        idev->open       = ucb1x00_ts_open;
        idev->close      = ucb1x00_ts_close;
+       idev->dev.parent = &ts->ucb->dev;
 
        idev->evbit[0]   = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
        idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
index f88c1cc0cb0f24bfda24e69f63cb728a923d5d45..a9e8bd157673a475ebfb5d78e81f0b8d6d621ed6 100644 (file)
@@ -10,8 +10,6 @@
 #ifndef MCP_H
 #define MCP_H
 
-#include <mach/dma.h>
-
 struct mcp_ops;
 
 struct mcp {
@@ -21,12 +19,7 @@ struct mcp {
        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;
-       int             gpio_base;
 };
 
 struct mcp_ops {
@@ -47,15 +40,14 @@ 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 *);
+int mcp_host_add(struct mcp *, void *);
+void mcp_host_del(struct mcp *);
+void mcp_host_free(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 *);
index 4321f044d1e45e1ad5cdaf20eba1440427b1fb5e..28af417563609b50bbff851bea7e0fb4692c966d 100644 (file)
@@ -12,7 +12,7 @@
 
 #include <linux/mfd/mcp.h>
 #include <linux/gpio.h>
-#include <linux/semaphore.h>
+#include <linux/mutex.h>
 
 #define UCB_IO_DATA    0x00
 #define UCB_IO_DIR     0x01
 #define UCB_MODE_DYN_VFLAG_ENA (1 << 12)
 #define UCB_MODE_AUD_OFF_CAN   (1 << 13)
 
+enum ucb1x00_reset {
+       UCB_RST_PROBE,
+       UCB_RST_RESUME,
+       UCB_RST_SUSPEND,
+       UCB_RST_REMOVE,
+       UCB_RST_PROBE_FAIL,
+};
 
-struct ucb1x00_irq {
-       void *devid;
-       void (*fn)(int, void *);
+struct ucb1x00_plat_data {
+       void                    (*reset)(enum ucb1x00_reset);
+       unsigned                irq_base;
+       int                     gpio_base;
+       unsigned                can_wakeup;
 };
 
 struct ucb1x00 {
-       spinlock_t              lock;
+       raw_spinlock_t          irq_lock;
        struct mcp              *mcp;
        unsigned int            irq;
-       struct semaphore        adc_sem;
+       int                     irq_base;
+       struct mutex            adc_mutex;
        spinlock_t              io_lock;
        u16                     id;
        u16                     io_dir;
@@ -122,7 +132,8 @@ struct ucb1x00 {
        u16                     adc_cr;
        u16                     irq_fal_enbl;
        u16                     irq_ris_enbl;
-       struct ucb1x00_irq      irq_handler[16];
+       u16                     irq_mask;
+       u16                     irq_wake;
        struct device           dev;
        struct list_head        node;
        struct list_head        devs;
@@ -144,7 +155,7 @@ struct ucb1x00_driver {
        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     (*suspend)(struct ucb1x00_dev *dev);
        int     (*resume)(struct ucb1x00_dev *dev);
 };
 
@@ -245,15 +256,4 @@ 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