From 30816ac0495cb4f33fc8d748f64ac3cc880cb3c1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 22:51:07 +0000 Subject: MFD: mcp-core: sanitize host creation/removal host_unregister() gives us no chance between removing the device and the mcp data structure being freed to access the data inbetween, which drivers may need to do if they need to iounmap() pointers in their private data structures. Therefore, re-jig the interfaces, which are now, on creation: mcp = mcp_host_alloc() if (mcp) { ret = mcp_host_add(mcp, data); if (!ret) mcp_host_free(mcp); } and on removal: mcp_host_del(mcp); ... access mcp ... mcp_host_free(mcp); The free does the final put_device() on the struct device as one would expect. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-core.c | 19 +++++++++++++------ drivers/mfd/mcp-sa11x0.c | 6 ++++-- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 86cc3f7841cd..cc7643177841 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -208,6 +208,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 +218,24 @@ 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) { dev_set_name(&mcp->attached_device, "mcp0"); - return device_register(&mcp->attached_device); + return device_add(&mcp->attached_device); } -EXPORT_SYMBOL(mcp_host_register); +EXPORT_SYMBOL(mcp_host_add); -void mcp_host_unregister(struct mcp *mcp) +void mcp_host_del(struct mcp *mcp) { - device_unregister(&mcp->attached_device); + device_del(&mcp->attached_device); } -EXPORT_SYMBOL(mcp_host_unregister); +EXPORT_SYMBOL(mcp_host_del); + +void mcp_host_free(struct mcp *mcp) +{ + put_device(&mcp->attached_device); +} +EXPORT_SYMBOL(mcp_host_free); int mcp_driver_register(struct mcp_driver *mcpdrv) { diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 02c53a0766c4..33cadc0ec121 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -195,10 +195,11 @@ 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); if (ret == 0) goto out; + mcp_host_free(mcp); release: release_mem_region(0x80060000, 0x60); platform_set_drvdata(pdev, NULL); @@ -212,7 +213,8 @@ static int mcp_sa11x0_remove(struct platform_device *dev) struct mcp *mcp = platform_get_drvdata(dev); platform_set_drvdata(dev, NULL); - mcp_host_unregister(mcp); + mcp_host_del(mcp); + mcp_host_free(mcp); release_mem_region(0x80060000, 0x60); return 0; -- cgit v1.2.3 From 7658e7f9a8122b0678e4b4280308560aa5444bd5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 12 Jan 2012 19:04:43 +0000 Subject: MFD: mcp-sa11x0: remove DMA initializers and variables The dma_device_t variables are only ever written to by mcp-sa11x0 and never read. As the old SA11x0 DMA support will be removed, remove these so that it no longer depends on the old SA11x0 DMA definitions. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-core.c | 1 - drivers/mfd/mcp-sa11x0.c | 5 ----- drivers/mfd/ucb1x00-assabet.c | 3 --- drivers/mfd/ucb1x00-core.c | 1 - drivers/mfd/ucb1x00-ts.c | 1 - include/linux/mfd/mcp.h | 6 ------ 6 files changed, 17 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index cc7643177841..280a4f8a7876 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -19,7 +19,6 @@ #include #include -#include #include diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 33cadc0ec121..d2ebc641b014 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -158,10 +157,6 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) 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); diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index cea9da60850d..b7be613cb503 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -16,9 +16,6 @@ #include #include -#include - - #define UCB1X00_ATTR(name,input)\ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index febc90cdef7e..f2fb4205467c 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -29,7 +29,6 @@ #include #include -#include #include static DEFINE_MUTEX(ucb1x00_mutex); diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 63a3cbdfa3f3..ec6ffb6e287d 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -32,7 +32,6 @@ #include #include -#include #include #include diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index 79a6b13ba20c..dfe7e517ad9b 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -10,8 +10,6 @@ #ifndef MCP_H #define MCP_H -#include - struct mcp_ops; struct mcp { @@ -21,10 +19,6 @@ 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; }; -- cgit v1.2.3 From e36e26a8b761d1a601e284e2b5d8aff84de3b756 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 22:24:07 +0000 Subject: MFD: mcp-sa11x0: move setup of PPC unit out of mcp-sa11x0.c Patch taken from af9081ae64 (ARM: sa1100: Refactor mcp-sa11x0 to use platform resources.) by Jochen Friedrich , and consolidated to use a common function. Move the setup of the PPC unit out of mcp-sa11x0 into the core SA11x0 code, and call it from each platforms initialization file. This centralizes the setup of the PPC unit while not polluting the mcp-sa11x0 driver with these details. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- arch/arm/mach-sa1100/assabet.c | 2 ++ arch/arm/mach-sa1100/cerf.c | 1 + arch/arm/mach-sa1100/collie.c | 2 ++ arch/arm/mach-sa1100/generic.c | 10 ++++++++++ arch/arm/mach-sa1100/generic.h | 1 + arch/arm/mach-sa1100/lart.c | 1 + arch/arm/mach-sa1100/shannon.c | 1 + arch/arm/mach-sa1100/simpad.c | 1 + drivers/mfd/mcp-sa11x0.c | 9 --------- 9 files changed, 19 insertions(+), 9 deletions(-) (limited to 'drivers/mfd') diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index 0c4b76ab4d8e..3a1914568374 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -231,6 +231,8 @@ static void __init assabet_init(void) PPDR |= PPC_TXD3 | PPC_TXD1; PPSR |= PPC_TXD3 | PPC_TXD1; + sa11x0_ppc_configure_mcp(); + sa1100fb_lcd_power = assabet_lcd_power; sa1100fb_backlight_power = assabet_backlight_power; diff --git a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c index 11bb6d0b9be3..bc11879f80c8 100644 --- a/arch/arm/mach-sa1100/cerf.c +++ b/arch/arm/mach-sa1100/cerf.c @@ -128,6 +128,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); diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index b9060e236def..efa2bc132cbf 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -341,6 +341,8 @@ static void __init collie_init(void) GPSR |= _COLLIE_GPIO_UCB1x00_RESET; + sa11x0_ppc_configure_mcp(); + platform_scoop_config = &collie_pcmcia_config; diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 480d2ea46b00..1416094cc013 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -240,6 +240,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); diff --git a/arch/arm/mach-sa1100/generic.h b/arch/arm/mach-sa1100/generic.h index 33268cf6be36..9236ff42bd1f 100644 --- a/arch/arm/mach-sa1100/generic.h +++ b/arch/arm/mach-sa1100/generic.h @@ -39,4 +39,5 @@ 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); diff --git a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c index af4e2761f3db..eba64b781500 100644 --- a/arch/arm/mach-sa1100/lart.c +++ b/arch/arm/mach-sa1100/lart.c @@ -28,6 +28,7 @@ static struct mcp_plat_data lart_mcp_data = { static void __init lart_init(void) { + sa11x0_ppc_configure_mcp(); sa11x0_register_mcp(&lart_mcp_data); } diff --git a/arch/arm/mach-sa1100/shannon.c b/arch/arm/mach-sa1100/shannon.c index 318b2b766a0b..3efb4ac62240 100644 --- a/arch/arm/mach-sa1100/shannon.c +++ b/arch/arm/mach-sa1100/shannon.c @@ -59,6 +59,7 @@ static struct mcp_plat_data shannon_mcp_data = { static void __init shannon_init(void) { + sa11x0_ppc_configure_mcp(); sa11x0_register_mtd(&shannon_flash_data, &shannon_flash_resource, 1); sa11x0_register_mcp(&shannon_mcp_data); } diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index e17c04d6e324..3aa36ec21039 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -376,6 +376,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); diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index d2ebc641b014..5373a7a835c6 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -165,15 +165,6 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); } - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - /* * Initialise device. Note that we initially * set the sampling rate to minimum. -- cgit v1.2.3 From c4592ce4e8f69d78711d53f0a42ec1b4dbf00cde Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 22:30:15 +0000 Subject: MFD: mcp-sa11x0: add .owner initializer Patch partly taken from af9081ae64 (ARM: sa1100: Refactor mcp-sa11x0 to use platform resources.) by Jochen Friedrich Move the MODULE_ALIAS() alongside the other MODULE_* definitions, and use DRIVER_NAME to ensure that the driver is consistently named. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-sa11x0.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 5373a7a835c6..703e76a77005 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -27,6 +27,7 @@ #include +#define DRIVER_NAME "sa11x0-mcp" struct mcp_sa11x0 { u32 mccr0; @@ -227,18 +228,14 @@ static int mcp_sa11x0_resume(struct platform_device *dev) return 0; } -/* - * The driver for the SA11x0 MCP port. - */ -MODULE_ALIAS("platform:sa11x0-mcp"); - 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, }, }; @@ -247,6 +244,7 @@ static struct platform_driver mcp_sa11x0_driver = { */ module_platform_driver(mcp_sa11x0_driver); +MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_AUTHOR("Russell King "); MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 45c7f75fd4b57035cd35954986a2faefb07dac9d Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 23:09:42 +0000 Subject: MFD: mcp-sa11x0: convert mcp-sa11x0 to use platform resources Patch taken from af9081ae64 (ARM: sa1100: Refactor mcp-sa11x0 to use platform resources.) by Jochen Friedrich , and fixes applied. We can safely do this now that we have sanitized host removal; the original patch had use-after-free bugs in the removal code. Not only that, but there was no checking of the ioremap() return. The final change over Jochen's patch is that we wrap the base pointer selection inside the various register indexes, which reduces the possibility of the wrong register index being used. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- arch/arm/mach-sa1100/generic.c | 5 ++ drivers/mfd/mcp-sa11x0.c | 141 +++++++++++++++++++++++++++++------------ 2 files changed, 104 insertions(+), 42 deletions(-) (limited to 'drivers/mfd') diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index 1416094cc013..9379c53d3018 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -221,6 +221,11 @@ static struct resource sa11x0mcp_resources[] = { .flags = IORESOURCE_MEM, }, [1] = { + .start = __PREG(Ser4MCCR1), + .end = __PREG(Ser4MCCR1) + 4 - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { .start = IRQ_Ser4MCP, .end = IRQ_Ser4MCP, .flags = IORESOURCE_IRQ, diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 703e76a77005..4d06db72bb9c 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -13,6 +13,7 @@ */ #include #include +#include #include #include #include @@ -30,34 +31,44 @@ #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 +80,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 +107,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 +129,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,22 +156,38 @@ 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; @@ -160,7 +195,18 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->sclk_rate = data->sclk_rate; mcp->gpio_base = data->gpio_base; - platform_set_drvdata(pdev, mcp); + m = priv(mcp); + m->mccr0 = data->mccr0 | 0x7f7f; + m->mccr1 = data->mccr1; + + 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; + } + + platform_set_drvdata(dev, mcp); if (machine_is_assabet()) { ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); @@ -170,9 +216,9 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) * 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 @@ -184,46 +230,57 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) ret = mcp_host_add(mcp); if (ret == 0) - goto out; + return 0; - mcp_host_free(mcp); - 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; + + mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); + mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); platform_set_drvdata(dev, NULL); mcp_host_del(mcp); + iounmap(m->base1); + iounmap(m->base0); mcp_host_free(mcp); - release_mem_region(0x80060000, 0x60); + 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) { - struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *m = priv(platform_get_drvdata(dev)); - 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) { - struct mcp *mcp = platform_get_drvdata(dev); + struct mcp_sa11x0 *m = priv(platform_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; } -- cgit v1.2.3 From 2796e397373a15ece69267ed1bbb9f61435a3ac7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 09:34:30 +0000 Subject: MFD: mcp-sa11x0: convert to use dev_pm_ops Convert the sa11x0 MCP driver to use dev_pm_ops rather than the legacy members in the platform driver. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-sa11x0.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 4d06db72bb9c..46167f676129 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -266,33 +267,38 @@ static int mcp_sa11x0_remove(struct platform_device *dev) 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_sa11x0 *m = priv(platform_get_drvdata(dev)); + struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); 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_sa11x0 *m = priv(platform_get_drvdata(dev)); + struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); writel_relaxed(m->mccr1, MCCR1(m)); writel_relaxed(m->mccr0, MCCR0(m)); return 0; } +#endif + +static const struct dev_pm_ops mcp_sa11x0_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mcp_sa11x0_suspend, mcp_sa11x0_resume) +}; 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 = DRIVER_NAME, .owner = THIS_MODULE, + .pm = &mcp_sa11x0_pm_ops, }, }; -- cgit v1.2.3 From a6aecae29affdd1a84198afe45ef9e13ecbf9826 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 18:03:00 +0000 Subject: MFD: mcp-sa11x0: use _noirq resume methods The genirq code requires early access to interrupt controllers. In order to allow this, we need to resume the MCP via the _noirq methods instead of the standard methods. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-sa11x0.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 46167f676129..420710b19f2d 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -289,7 +289,14 @@ static int mcp_sa11x0_resume(struct device *dev) #endif static const struct dev_pm_ops mcp_sa11x0_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(mcp_sa11x0_suspend, mcp_sa11x0_resume) +#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 = { -- cgit v1.2.3 From abe06082d07fcb0673cb93338c1d6f037fdc375b Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 22:13:52 +0000 Subject: MFD: mcp/ucb1x00: separate ucb1x00 driver data from the MCP data Patch taken from 5dd7bf59e0 (ARM: sa11x0: Implement autoloading of codec and codec pdata for mcp bus.) by Jochen Friedrich . This adds just the codec data part of the patch. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- arch/arm/mach-sa1100/collie.c | 7 ++++++- arch/arm/mach-sa1100/include/mach/mcp.h | 2 +- arch/arm/mach-sa1100/simpad.c | 7 ++++++- drivers/mfd/mcp-core.c | 3 ++- drivers/mfd/mcp-sa11x0.c | 3 +-- drivers/mfd/ucb1x00-core.c | 7 ++++--- include/linux/mfd/mcp.h | 3 +-- include/linux/mfd/ucb1x00.h | 3 +++ 8 files changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers/mfd') diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index efa2bc132cbf..0e7359785159 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -85,10 +86,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, }; /* diff --git a/arch/arm/mach-sa1100/include/mach/mcp.h b/arch/arm/mach-sa1100/include/mach/mcp.h index ed1a331508a7..4b2860ae3828 100644 --- a/arch/arm/mach-sa1100/include/mach/mcp.h +++ b/arch/arm/mach-sa1100/include/mach/mcp.h @@ -16,7 +16,7 @@ struct mcp_plat_data { u32 mccr0; u32 mccr1; unsigned int sclk_rate; - int gpio_base; + void *codec_pdata; }; #endif diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index 3aa36ec21039..81506562ee26 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -187,10 +188,14 @@ static struct resource simpad_flash_resources [] = { } }; +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, }; diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 280a4f8a7876..c409d6327140 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -217,8 +217,9 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) } EXPORT_SYMBOL(mcp_host_alloc); -int mcp_host_add(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_add(&mcp->attached_device); } diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 420710b19f2d..960ebc790389 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -194,7 +194,6 @@ static int mcp_sa11x0_probe(struct platform_device *dev) mcp->owner = THIS_MODULE; mcp->ops = &mcp_sa11x0; mcp->sclk_rate = data->sclk_rate; - mcp->gpio_base = data->gpio_base; m = priv(mcp); m->mccr0 = data->mccr0 | 0x7f7f; @@ -229,7 +228,7 @@ static int mcp_sa11x0_probe(struct platform_device *dev) mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / mcp->sclk_rate; - ret = mcp_host_add(mcp); + ret = mcp_host_add(mcp, data->codec_pdata); if (ret == 0) return 0; diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index f2fb4205467c..6825169b06fc 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -534,6 +534,7 @@ static int ucb1x00_probe(struct mcp *mcp) { struct ucb1x00 *ucb; struct ucb1x00_driver *drv; + struct ucb1x00_plat_data *pdata; unsigned int id; int ret = -ENODEV; int temp; @@ -551,7 +552,7 @@ static int ucb1x00_probe(struct mcp *mcp) if (!ucb) goto err_disable; - + pdata = mcp->attached_device.platform_data; ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; dev_set_name(&ucb->dev, "ucb1x00"); @@ -570,9 +571,9 @@ static int ucb1x00_probe(struct mcp *mcp) } ucb->gpio.base = -1; - if (mcp->gpio_base != 0) { + if (pdata && pdata->gpio_base) { ucb->gpio.label = dev_name(&ucb->dev); - ucb->gpio.base = mcp->gpio_base; + ucb->gpio.base = pdata->gpio_base; ucb->gpio.ngpio = 10; ucb->gpio.set = ucb1x00_gpio_set; ucb->gpio.get = ucb1x00_gpio_get; diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index dfe7e517ad9b..bfcdf6d3f1bf 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -20,7 +20,6 @@ struct mcp { unsigned int sclk_rate; unsigned int rw_timeout; struct device attached_device; - int gpio_base; }; struct mcp_ops { @@ -41,7 +40,7 @@ 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_add(struct mcp *); +int mcp_host_add(struct mcp *, void *); void mcp_host_del(struct mcp *); void mcp_host_free(struct mcp *); diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 4321f044d1e4..731b23a656c0 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -104,6 +104,9 @@ #define UCB_MODE_DYN_VFLAG_ENA (1 << 12) #define UCB_MODE_AUD_OFF_CAN (1 << 13) +struct ucb1x00_plat_data { + int gpio_base; +}; struct ucb1x00_irq { void *devid; -- cgit v1.2.3 From 945f6310d3cf0523560b33b9ddb3271666141bd5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 09:30:27 +0000 Subject: MFD: ucb1x00-ts: provide input layer with device parent Provide the input layer struct device with its parent device, so that the input layer's device appears in the correct place in the device tree. This also allows the input device to be visibily associated with its hardware. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-ts.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index ec6ffb6e287d..742d0c7bbbc2 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -363,6 +363,7 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) 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); -- cgit v1.2.3 From c364ff473a8d31c93da5e21ac5d2789a935c8faf Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 16:41:26 +0000 Subject: MFD: ucb1x00-core: get rid of mach/hardware.h include Nothing in this driver requires anything from the machine/platform headers, so remove this needless header file. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 6825169b06fc..cc1c0dce7bd9 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -29,8 +29,6 @@ #include #include -#include - static DEFINE_MUTEX(ucb1x00_mutex); static LIST_HEAD(ucb1x00_drivers); static LIST_HEAD(ucb1x00_devices); -- cgit v1.2.3 From 2f7510c6070932371e0b842a5470ce7190dcf162 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 22 Jan 2012 19:02:25 +0000 Subject: MFD: ucb1x00-core: add handling for ucb1x00 reset Provide a way to handle the software controlled ucb1x00 reset signal from the ucb1x00-core driver without having to code platform specifics into these drivers. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 17 +++++++++++++---- include/linux/mfd/ucb1x00.h | 7 +++++++ 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index cc1c0dce7bd9..42eee633b864 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -530,13 +530,17 @@ 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; - struct ucb1x00_plat_data *pdata; + struct ucb1x00 *ucb; unsigned int id; 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); @@ -550,7 +554,6 @@ static int ucb1x00_probe(struct mcp *mcp) if (!ucb) goto err_disable; - pdata = mcp->attached_device.platform_data; ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; dev_set_name(&ucb->dev, "ucb1x00"); @@ -606,7 +609,7 @@ static int ucb1x00_probe(struct mcp *mcp) } mutex_unlock(&ucb1x00_mutex); - goto out; + return ret; err_irq: free_irq(ucb->irq, ucb); @@ -618,11 +621,14 @@ static int ucb1x00_probe(struct mcp *mcp) err_disable: mcp_disable(mcp); 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,6 +649,9 @@ static void ucb1x00_remove(struct mcp *mcp) free_irq(ucb->irq, ucb); device_unregister(&ucb->dev); + + if (pdata && pdata->reset) + pdata->reset(UCB_RST_REMOVE); } int ucb1x00_register_driver(struct ucb1x00_driver *drv) diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 731b23a656c0..fd088cc6a4ca 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -104,7 +104,14 @@ #define UCB_MODE_DYN_VFLAG_ENA (1 << 12) #define UCB_MODE_AUD_OFF_CAN (1 << 13) +enum ucb1x00_reset { + UCB_RST_PROBE, + UCB_RST_REMOVE, + UCB_RST_PROBE_FAIL, +}; + struct ucb1x00_plat_data { + void (*reset)(enum ucb1x00_reset); int gpio_base; }; -- cgit v1.2.3 From ddb1e04a35846b6c5b6039e92555dafaf6ee03d2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 16:33:38 +0000 Subject: MFD: ucb1x00-core: add .owner initializer and module alias Add a .owner initializer to the UCB1x00 mcp driver structure, and set an appropriate module alias to identify this driver. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 42eee633b864..c2757c11ce7b 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -714,6 +714,7 @@ static int ucb1x00_resume(struct mcp *mcp) static struct mcp_driver ucb1x00_driver = { .drv = { .name = "ucb1x00", + .owner = THIS_MODULE, }, .probe = ucb1x00_probe, .remove = ucb1x00_remove, @@ -757,6 +758,7 @@ EXPORT_SYMBOL(ucb1x00_disable_irq); EXPORT_SYMBOL(ucb1x00_register_driver); EXPORT_SYMBOL(ucb1x00_unregister_driver); +MODULE_ALIAS("mcp:ucb1x00"); MODULE_AUTHOR("Russell King "); MODULE_DESCRIPTION("UCB1x00 core driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From cae154767a96563d33924872aacfdc63d584f707 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 09:33:38 +0000 Subject: MFD: ucb1x00-core: use mutexes instead of semaphores Convert the ucb1x00 driver to use mutexes rather than the depreciated semaphores for exclusive access to the ADC. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 15 +++++++-------- include/linux/mfd/ucb1x00.h | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index c2757c11ce7b..7386f822d4cd 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -27,7 +27,6 @@ #include #include #include -#include static DEFINE_MUTEX(ucb1x00_mutex); static LIST_HEAD(ucb1x00_drivers); @@ -99,7 +98,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) { @@ -183,7 +182,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. @@ -193,7 +192,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; @@ -215,7 +214,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) { @@ -243,7 +242,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) { @@ -251,7 +250,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); } /* @@ -560,7 +559,7 @@ static int ucb1x00_probe(struct mcp *mcp) spin_lock_init(&ucb->lock); spin_lock_init(&ucb->io_lock); - sema_init(&ucb->adc_sem, 1); + mutex_init(&ucb->adc_mutex); ucb->id = id; ucb->mcp = mcp; diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index fd088cc6a4ca..a4b954381c2f 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -12,7 +12,7 @@ #include #include -#include +#include #define UCB_IO_DATA 0x00 #define UCB_IO_DIR 0x01 @@ -124,7 +124,7 @@ struct ucb1x00 { spinlock_t lock; struct mcp *mcp; unsigned int irq; - struct semaphore adc_sem; + struct mutex adc_mutex; spinlock_t io_lock; u16 id; u16 io_dir; -- cgit v1.2.3 From f5ae587f5d258bda9c24bb8387315eb2ebedeee9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 14:45:17 +0000 Subject: MFD: ucb1x00-core: clean up device handling in probe Clean up the device handling so we can use the struct device sanely. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 7386f822d4cd..74d9fcf891d5 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -553,6 +553,7 @@ static int ucb1x00_probe(struct mcp *mcp) if (!ucb) goto err_disable; + device_initialize(&ucb->dev); ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; dev_set_name(&ucb->dev, "ucb1x00"); @@ -563,11 +564,16 @@ static int ucb1x00_probe(struct mcp *mcp) ucb->id = id; ucb->mcp = mcp; + + ret = device_add(&ucb->dev); + if (ret) + goto err_dev_add; + ucb->irq = ucb1x00_detect_irq(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; @@ -581,25 +587,20 @@ static int ucb1x00_probe(struct mcp *mcp) ucb->gpio.direction_output = ucb1x00_gpio_direction_output; 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", + dev_err(&ucb->dev, "ucb1x00: unable to grab irq%d: %d\n", ucb->irq, ret); - goto err_gpio; + goto err_irq; } mcp_set_drvdata(mcp, ucb); - ret = device_register(&ucb->dev); - if (ret) - goto err_irq; - - INIT_LIST_HEAD(&ucb->devs); mutex_lock(&ucb1x00_mutex); list_add(&ucb->node, &ucb1x00_devices); @@ -611,12 +612,13 @@ static int ucb1x00_probe(struct mcp *mcp) 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_gpio_add: + err_no_irq: + device_del(&ucb->dev); + err_dev_add: + put_device(&ucb->dev); err_disable: mcp_disable(mcp); out: -- cgit v1.2.3 From 7655b2ac9ed3206fb916357b8b1065f08324507d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 14:47:00 +0000 Subject: MFD: ucb1x00-core: add owner and dev initializers to gpio structure Register the gpio device with proper .owner and .dev elements set appropraitely. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 74d9fcf891d5..30ef726f4ba0 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -579,6 +579,8 @@ static int ucb1x00_probe(struct mcp *mcp) ucb->gpio.base = -1; if (pdata && pdata->gpio_base) { ucb->gpio.label = dev_name(&ucb->dev); + 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; -- cgit v1.2.3 From 65b539bb900f64b9a3f9b761bf0f735dc84e6b70 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 15:35:01 +0000 Subject: MFD: ucb1x00-core: scan drivers in same order they're registered Cosmetic patch to scan the list of drivers in the order that the drivers are registered, rather than the reverse order. This avoids surprises when drivers get probed in the reverse order, and input devices get registered in a different order due to bind/unbind than from boot. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 30ef726f4ba0..162496de1b38 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -436,8 +436,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); } @@ -605,7 +605,7 @@ static int ucb1x00_probe(struct mcp *mcp) 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); } @@ -663,7 +663,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); } -- cgit v1.2.3 From ed442b6798eb39eda3bcea92ef9403280b603818 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 18:13:20 +0000 Subject: MFD: ucb1x00-core: add missing ucb1x00_enable()/ucb1x00_disable() ucb1x00_enable() and ucb1x00_disable() are used for power saving on the SIB interface, allowing the host supplied clock to be disabled when not required. We require drivers which access the ucb1x00 to ensure that they have enabled the clock prior to accessing the device, and they should disable it once they're done. As we don't expect gpiolib users to be aware of this detail, we must make these calls in the gpiolib interfaces. Add them. Also add them to the resume method, which needs to re-establish the GPIO pin settings. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 162496de1b38..9f8ea52f0ca8 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -116,14 +116,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) @@ -133,7 +141,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; @@ -153,6 +163,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); @@ -160,6 +171,7 @@ 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; @@ -703,8 +715,10 @@ static int ucb1x00_resume(struct mcp *mcp) struct ucb1x00 *ucb = mcp_get_drvdata(mcp); struct ucb1x00_dev *dev; + ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + ucb1x00_disable(ucb); mutex_lock(&ucb1x00_mutex); list_for_each_entry(dev, &ucb->devs, dev_node) { if (dev->drv->resume) -- cgit v1.2.3 From 2b4d9d2b001be2ff06be2ea5c52e9adaf85b0805 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 18:24:17 +0000 Subject: MFD: ucb1x00-core: disable mcp clock when bus is not required The ucb1x00-core was leaving the mcp clock enabled indefinitely after probe. This needlessly wastes power. Add the necessary disables to ensure that the clock remains off when we don't need it. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 9f8ea52f0ca8..ed2a4b2e518f 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -554,16 +554,17 @@ static int ucb1x00_probe(struct mcp *mcp) 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; @@ -581,7 +582,9 @@ static int ucb1x00_probe(struct mcp *mcp) if (ret) goto err_dev_add; + ucb1x00_enable(ucb); ucb->irq = ucb1x00_detect_irq(ucb); + ucb1x00_disable(ucb); if (ucb->irq == NO_IRQ) { dev_err(&ucb->dev, "IRQ probe failed\n"); ret = -ENODEV; @@ -633,8 +636,6 @@ static int ucb1x00_probe(struct mcp *mcp) device_del(&ucb->dev); err_dev_add: put_device(&ucb->dev); - err_disable: - mcp_disable(mcp); out: if (pdata && pdata->reset) pdata->reset(UCB_RST_PROBE_FAIL); -- cgit v1.2.3 From a4b54acf9e691a3051950444d33980741e7d63a8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 18:26:17 +0000 Subject: MFD: mcp-sa11x0: complain if mcp clock is left enabled Issue a warning if the mcp clock was left enabled by some driver when we're suspending or tearing down the core driver for the device. This is an aid for debugging missing disable calls. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-sa11x0.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 960ebc790389..c381436ed3de 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -252,6 +252,10 @@ static int mcp_sa11x0_remove(struct platform_device *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); @@ -271,6 +275,9 @@ static int mcp_sa11x0_suspend(struct device *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"); + writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m)); return 0; -- cgit v1.2.3 From 5a09b7120a965a7d7e8494d0ed509135bbce0118 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 16:36:30 +0000 Subject: MFD: ucb1x00-core: convert to use dev_pm_ops Convert the ucb1x00-core driver to use dev_pm_ops rather than the legacy members in the mcp driver. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 32 ++++++++++++++++++-------------- include/linux/mfd/ucb1x00.h | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index ed2a4b2e518f..6fab82557543 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include static DEFINE_MUTEX(ucb1x00_mutex); @@ -697,47 +698,50 @@ 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 *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); 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 *ucb = dev_get_drvdata(dev); + struct ucb1x00_dev *udev; ucb1x00_enable(ucb); ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); 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) diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index a4b954381c2f..253c12c157a6 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -154,7 +154,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); }; -- cgit v1.2.3 From cf4abfcc0df2985ff6061f74e63b8353f2a1d0bc Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 16:38:50 +0000 Subject: MFD: mcp-core: remove legacy driver suspend/resume methods The legacy driver suspend/resume methods are no longer used, so get rid of them. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/mcp-core.c | 28 ---------------------------- include/linux/mfd/mcp.h | 2 -- 2 files changed, 30 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index c409d6327140..6acf2e03f2ba 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -47,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, }; /** diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index bfcdf6d3f1bf..a9e8bd157673 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -48,8 +48,6 @@ 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 *); -- cgit v1.2.3 From a3364409c4af8bae42d04def48dc11409787e503 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 14:58:28 +0000 Subject: MFD: ucb1x00: convert to use genirq Convert the ucb1x00 driver to use genirq's interrupt services, rather than its own private implementation. This allows a wider range of drivers to use the GPIO interrupts (such as the gpio_keys driver) without being aware of the UCB1x00's private IRQ system. This prevents the UCB1x00 core driver from being built as a module, so adjust the configuration to add that restriction. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/Kconfig | 5 +- drivers/mfd/ucb1x00-core.c | 247 +++++++++++++++++--------------------------- drivers/mfd/ucb1x00-ts.c | 37 +++++-- include/linux/mfd/ucb1x00.h | 22 +--- 4 files changed, 132 insertions(+), 179 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cd13e9f2f5e6..28a301b28579 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -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" diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 6fab82557543..400604d38780 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,13 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset 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) @@ -274,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); @@ -285,157 +292,84 @@ 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); - - ucb1x00_enable(ucb); - if (edges & UCB_RISING) { - ucb->irq_ris_enbl |= 1 << idx; - ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); - } - if (edges & UCB_FALLING) { - ucb->irq_fal_enbl |= 1 << idx; - ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); - } - ucb1x00_disable(ucb); - spin_unlock_irqrestore(&ucb->lock, flags); - } } -/** - * ucb1x00_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_mask(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 void ucb1x00_irq_unmask(struct irq_data *data) { - 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; - - irq = ucb->irq_handler + idx; - ret = -ENOENT; + raw_spin_lock(&ucb->irq_lock); + ucb->irq_mask |= mask; + ucb1x00_irq_update(ucb, 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); +static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data); + unsigned mask = 1 << (data->irq - ucb->irq_base); - 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); + raw_spin_lock(&ucb->irq_lock); + if (type & IRQ_TYPE_EDGE_RISING) + ucb->irq_ris_enbl |= mask; + else + ucb->irq_ris_enbl &= ~mask; - irq->fn = NULL; - irq->devid = NULL; - ret = 0; + 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); } - spin_unlock_irq(&ucb->lock); - return ret; + raw_spin_unlock(&ucb->irq_lock); -bad: - printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx); - return -EINVAL; + 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, +}; + static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv) { struct ucb1x00_dev *dev; @@ -545,9 +479,8 @@ static int ucb1x00_probe(struct mcp *mcp) struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data; struct ucb1x00_driver *drv; struct ucb1x00 *ucb; - unsigned int id; + unsigned id, i, irq_base; int ret = -ENODEV; - int temp; /* Tell the platform to deassert the UCB1x00 reset */ if (pdata && pdata->reset) @@ -572,7 +505,7 @@ static int ucb1x00_probe(struct mcp *mcp) 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); mutex_init(&ucb->adc_mutex); @@ -593,6 +526,26 @@ static int ucb1x00_probe(struct mcp *mcp) } ucb->gpio.base = -1; + 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.dev = &ucb->dev; @@ -603,20 +556,13 @@ static int ucb1x00_probe(struct mcp *mcp) 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_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) { - dev_err(&ucb->dev, "ucb1x00: unable to grab irq%d: %d\n", - ucb->irq, ret); - goto err_irq; - } - mcp_set_drvdata(mcp, ucb); INIT_LIST_HEAD(&ucb->devs); @@ -629,10 +575,11 @@ static int ucb1x00_probe(struct mcp *mcp) return ret; - err_irq: - if (ucb->gpio.base != -1) - temp = gpiochip_remove(&ucb->gpio); 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: @@ -664,7 +611,8 @@ 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) @@ -772,11 +720,6 @@ 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); diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 742d0c7bbbc2..1e0e20c0e082 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -20,8 +20,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -41,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; @@ -237,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); /* @@ -280,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; @@ -313,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; } @@ -333,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); } @@ -358,6 +380,7 @@ 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; diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 253c12c157a6..6fb907446c33 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -112,18 +112,15 @@ enum ucb1x00_reset { struct ucb1x00_plat_data { void (*reset)(enum ucb1x00_reset); + unsigned irq_base; int gpio_base; }; -struct ucb1x00_irq { - void *devid; - void (*fn)(int, void *); -}; - struct ucb1x00 { - spinlock_t lock; + raw_spinlock_t irq_lock; struct mcp *mcp; unsigned int irq; + int irq_base; struct mutex adc_mutex; spinlock_t io_lock; u16 id; @@ -132,7 +129,7 @@ struct ucb1x00 { u16 adc_cr; u16 irq_fal_enbl; u16 irq_ris_enbl; - struct ucb1x00_irq irq_handler[16]; + u16 irq_mask; struct device dev; struct list_head node; struct list_head devs; @@ -255,15 +252,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 -- cgit v1.2.3 From 33237616771bfc29a97f17e74efe3799bb790343 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 22 Jan 2012 20:05:24 +0000 Subject: MFD: ucb1x00-core: add wakeup support Add genirq wakeup support for the ucb1x00 device. This allows an attached gpio_keys driver to wakeup the system. Touchscreen is also possible. When there are no wakeup sources, ask the platform to assert the reset signal to avoid any unexpected behaviour; this also puts the reset signal at the right level when power is removed from the device. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-core.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ucb1x00.h | 4 +++ 2 files changed, 63 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 400604d38780..70f02daeb22a 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -362,12 +362,32 @@ static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type) return 0; } +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); + + if (!pdata || !pdata->can_wakeup) + 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) @@ -565,6 +585,9 @@ static int ucb1x00_probe(struct mcp *mcp) mcp_set_drvdata(mcp, ucb); + if (pdata) + device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup); + INIT_LIST_HEAD(&ucb->devs); mutex_lock(&ucb1x00_mutex); list_add_tail(&ucb->node, &ucb1x00_devices); @@ -648,6 +671,7 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv) static int ucb1x00_suspend(struct device *dev) { + struct ucb1x00_plat_data *pdata = dev->platform_data; struct ucb1x00 *ucb = dev_get_drvdata(dev); struct ucb1x00_dev *udev; @@ -657,18 +681,53 @@ static int ucb1x00_suspend(struct device *dev) 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 device *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(udev, &ucb->devs, dev_node) { if (udev->drv->resume) diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 6fb907446c33..28af41756360 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -106,6 +106,8 @@ enum ucb1x00_reset { UCB_RST_PROBE, + UCB_RST_RESUME, + UCB_RST_SUSPEND, UCB_RST_REMOVE, UCB_RST_PROBE_FAIL, }; @@ -114,6 +116,7 @@ struct ucb1x00_plat_data { void (*reset)(enum ucb1x00_reset); unsigned irq_base; int gpio_base; + unsigned can_wakeup; }; struct ucb1x00 { @@ -130,6 +133,7 @@ struct ucb1x00 { u16 irq_fal_enbl; u16 irq_ris_enbl; u16 irq_mask; + u16 irq_wake; struct device dev; struct list_head node; struct list_head devs; -- cgit v1.2.3 From 54292a4649a5dd93562d10f188a8e201afebfe2f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 15:53:42 +0000 Subject: MFD: ucb1x00-assabet: add support for UCB1x00 GPIO switches Add support for UCB1x00 GPIO buttons found on the Assabet platform. We can now trivially support these buttons as we have standardized gpiolib, genirq and gpio keyboard support in place for this device. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- drivers/mfd/ucb1x00-assabet.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index b7be613cb503..b63c0756a669 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -11,9 +11,13 @@ */ #include #include +#include +#include #include +#include +#include +#include #include -#include #include #define UCB1X00_ATTR(name,input)\ @@ -35,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); -- cgit v1.2.3 From 6ed3e2acc7995625625592abe8cd3383c34a471b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 22 Jan 2012 19:23:33 +0000 Subject: MFD: mcp-sa11x0/assabet: move assabet reset handling out of mcp-sa11x0.c Move the assabet specific reset handling out of mcp-sa11x0.c, into its board file. This leaves the mcp code free from all board specific details. Acked-by: Jochen Friedrich Signed-off-by: Russell King --- arch/arm/mach-sa1100/assabet.c | 8 ++++++++ drivers/mfd/mcp-sa11x0.c | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/mfd') diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index c45402f866cc..b5955ad35945 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -70,6 +70,13 @@ 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); +} + + static void assabet_backlight_power(int on) { #ifndef ASSABET_PAL_VIDEO @@ -201,6 +208,7 @@ static struct irda_platform_data assabet_irda_data = { }; static struct ucb1x00_plat_data assabet_ucb1x00_data = { + .reset = assabet_ucb1x00_reset, .gpio_base = -1, }; diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index c381436ed3de..1c0ceacaa1f6 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -27,8 +27,6 @@ #include #include -#include - #define DRIVER_NAME "sa11x0-mcp" struct mcp_sa11x0 { @@ -208,10 +206,6 @@ static int mcp_sa11x0_probe(struct platform_device *dev) platform_set_drvdata(dev, mcp); - if (machine_is_assabet()) { - ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); - } - /* * Initialise device. Note that we initially * set the sampling rate to minimum. -- cgit v1.2.3