From d036c96e703c176250503194f4fcd8b96e606b52 Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Thu, 4 Aug 2011 12:32:31 +0800 Subject: mfd: Fix section mismatch warning in ab3550-core This patch fixes: WARNING: drivers/mfd/built-in.o(.data+0x9998): Section mismatch in reference from the variable ab3550_driver to the function .init.text:ab3550_probe() The variable ab3550_driver references the function __init ab3550_probe() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console Signed-off-by: Wanlong Gao Acked-by: Linus Walleij Acked-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/ab3550-core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index 56ba1943c91d..882ea7192d8b 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -1086,7 +1086,7 @@ static inline void ab3550_remove_debugfs(void) * This sets up a default config in the AB3550 chip so that it * will work as expected. */ -static int __init ab3550_setup(struct ab3550 *ab) +static int __devinit ab3550_setup(struct ab3550 *ab) { int err = 0; int i; @@ -1193,7 +1193,7 @@ struct ab_family_id { char *name; }; -static const struct ab_family_id ids[] __initdata = { +static const struct ab_family_id ids[] __devinitconst = { /* AB3550 */ { .id = AB3550_P1A, @@ -1205,7 +1205,7 @@ static const struct ab_family_id ids[] __initdata = { } }; -static int __init ab3550_probe(struct i2c_client *client, +static int __devinit ab3550_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ab3550 *ab; @@ -1326,7 +1326,7 @@ exit_no_detect: return err; } -static int __exit ab3550_remove(struct i2c_client *client) +static int __devexit ab3550_remove(struct i2c_client *client) { struct ab3550 *ab = i2c_get_clientdata(client); int num_i2c_clients = AB3550_NUM_BANKS; @@ -1359,7 +1359,7 @@ static struct i2c_driver ab3550_driver = { }, .id_table = ab3550_id, .probe = ab3550_probe, - .remove = __exit_p(ab3550_remove), + .remove = __devexit_p(ab3550_remove), }; static int __init ab3550_i2c_init(void) -- cgit v1.2.3 From 6e3ad118041f56db752a5eb2b557517d14592af7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 8 Aug 2011 17:04:40 +0900 Subject: mfd: Convert pcf50633 to use new register map API Signed-off-by: Mark Brown Tested-by: Lars-Peter Clausen Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/pcf50633-core.c | 114 ++++++++++++-------------------------- include/linux/mfd/pcf50633/core.h | 3 +- 3 files changed, 38 insertions(+), 80 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 21574bdf485f..13f0040413c4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -500,6 +500,7 @@ config MFD_WM8994 config MFD_PCF50633 tristate "Support for NXP PCF50633" depends on I2C + select REGMAP_I2C help Say yes here if you have NXP PCF50633 chip on your board. This core driver provides register access and IRQ handling diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 57868416c760..ff1a7e741ecd 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -23,45 +23,22 @@ #include #include #include +#include +#include #include -static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data) -{ - int ret; - - ret = i2c_smbus_read_i2c_block_data(pcf->i2c_client, reg, - num, data); - if (ret < 0) - dev_err(pcf->dev, "Error reading %d regs at %d\n", num, reg); - - return ret; -} - -static int __pcf50633_write(struct pcf50633 *pcf, u8 reg, int num, u8 *data) -{ - int ret; - - ret = i2c_smbus_write_i2c_block_data(pcf->i2c_client, reg, - num, data); - if (ret < 0) - dev_err(pcf->dev, "Error writing %d regs at %d\n", num, reg); - - return ret; - -} - /* Read a block of up to 32 regs */ int pcf50633_read_block(struct pcf50633 *pcf, u8 reg, int nr_regs, u8 *data) { int ret; - mutex_lock(&pcf->lock); - ret = __pcf50633_read(pcf, reg, nr_regs, data); - mutex_unlock(&pcf->lock); + ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs); + if (ret != 0) + return ret; - return ret; + return nr_regs; } EXPORT_SYMBOL_GPL(pcf50633_read_block); @@ -71,21 +48,22 @@ int pcf50633_write_block(struct pcf50633 *pcf , u8 reg, { int ret; - mutex_lock(&pcf->lock); - ret = __pcf50633_write(pcf, reg, nr_regs, data); - mutex_unlock(&pcf->lock); + ret = regmap_raw_write(pcf->regmap, reg, data, nr_regs); + if (ret != 0) + return ret; - return ret; + return nr_regs; } EXPORT_SYMBOL_GPL(pcf50633_write_block); u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg) { - u8 val; + unsigned int val; + int ret; - mutex_lock(&pcf->lock); - __pcf50633_read(pcf, reg, 1, &val); - mutex_unlock(&pcf->lock); + ret = regmap_read(pcf->regmap, reg, &val); + if (ret < 0) + return -1; return val; } @@ -93,56 +71,19 @@ EXPORT_SYMBOL_GPL(pcf50633_reg_read); int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val) { - int ret; - - mutex_lock(&pcf->lock); - ret = __pcf50633_write(pcf, reg, 1, &val); - mutex_unlock(&pcf->lock); - - return ret; + return regmap_write(pcf->regmap, reg, val); } EXPORT_SYMBOL_GPL(pcf50633_reg_write); int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val) { - int ret; - u8 tmp; - - val &= mask; - - mutex_lock(&pcf->lock); - ret = __pcf50633_read(pcf, reg, 1, &tmp); - if (ret < 0) - goto out; - - tmp &= ~mask; - tmp |= val; - ret = __pcf50633_write(pcf, reg, 1, &tmp); - -out: - mutex_unlock(&pcf->lock); - - return ret; + return regmap_update_bits(pcf->regmap, reg, mask, val); } EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask); int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val) { - int ret; - u8 tmp; - - mutex_lock(&pcf->lock); - ret = __pcf50633_read(pcf, reg, 1, &tmp); - if (ret < 0) - goto out; - - tmp &= ~val; - ret = __pcf50633_write(pcf, reg, 1, &tmp); - -out: - mutex_unlock(&pcf->lock); - - return ret; + return regmap_update_bits(pcf->regmap, reg, val, 0); } EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits); @@ -251,6 +192,11 @@ static int pcf50633_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); +static struct regmap_config pcf50633_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + static int __devinit pcf50633_probe(struct i2c_client *client, const struct i2c_device_id *ids) { @@ -272,16 +218,23 @@ static int __devinit pcf50633_probe(struct i2c_client *client, mutex_init(&pcf->lock); + pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config); + if (IS_ERR(pcf->regmap)) { + ret = PTR_ERR(pcf->regmap); + dev_err(pcf->dev, "Failed to allocate register map: %d\n", + ret); + goto err_free; + } + i2c_set_clientdata(client, pcf); pcf->dev = &client->dev; - pcf->i2c_client = client; version = pcf50633_reg_read(pcf, 0); variant = pcf50633_reg_read(pcf, 1); if (version < 0 || variant < 0) { dev_err(pcf->dev, "Unable to probe pcf50633\n"); ret = -ENODEV; - goto err_free; + goto err_regmap; } dev_info(pcf->dev, "Probed device version %d variant %d\n", @@ -328,6 +281,8 @@ static int __devinit pcf50633_probe(struct i2c_client *client, return 0; +err_regmap: + regmap_exit(pcf->regmap); err_free: kfree(pcf); @@ -351,6 +306,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client) for (i = 0; i < PCF50633_NUM_REGULATORS; i++) platform_device_unregister(pcf->regulator_pdev[i]); + regmap_exit(pcf->regmap); kfree(pcf); return 0; diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index 50d4a047118d..a80840752b4c 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h @@ -21,6 +21,7 @@ #include struct pcf50633; +struct regmap; #define PCF50633_NUM_REGULATORS 11 @@ -134,7 +135,7 @@ enum { struct pcf50633 { struct device *dev; - struct i2c_client *i2c_client; + struct regmap *regmap; struct pcf50633_platform_data *pdata; int irq; -- cgit v1.2.3 From 1693673fc003d3d5f80ae34b21e0480a1d8a36e3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 9 Aug 2011 14:05:29 +0900 Subject: mfd: Remove spurious newlines from wm831x-irq.c More annoying than usual as they're in the middle of a function. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-irq.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index ada1835a5455..a10937cfff4c 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -596,8 +596,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) "No interrupt specified - functionality limited\n"); } - - /* Enable top level interrupts, we mask at secondary level */ wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0); -- cgit v1.2.3 From 3c6e36537e40a41ddb0e27a80149cfd341a92d7d Mon Sep 17 00:00:00 2001 From: Paul Parsons Date: Tue, 9 Aug 2011 16:27:24 +0000 Subject: mfd: Fix asic3 based SD card resume after suspend The mfd/asic3 driver did not define the suspend/resume handlers for the mmc cell driver. Consequently the mmc driver did not resume properly after returning from suspend, making sd cards unusable and preventing suspend from being entered a second time. This patch adds the suspend/resume handlers, fixing the problem. Signed-off-by: Paul Parsons Signed-off-by: Samuel Ortiz --- drivers/mfd/asic3.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index c71ae09430c5..5ac2961fedfe 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -779,6 +779,8 @@ static struct mfd_cell asic3_cell_mmc = { .name = "tmio-mmc", .enable = asic3_mmc_enable, .disable = asic3_mmc_disable, + .suspend = asic3_mmc_disable, + .resume = asic3_mmc_enable, .platform_data = &asic3_mmc_data, .pdata_size = sizeof(asic3_mmc_data), .num_resources = ARRAY_SIZE(asic3_mmc_resources), -- cgit v1.2.3 From e0b13b5b6a9ad3ccadaa6662524a92e13aa7a032 Mon Sep 17 00:00:00 2001 From: Paul Parsons Date: Tue, 9 Aug 2011 16:27:33 +0000 Subject: mfd: Add asic3 based LED suspend/resume handlers Added led suspend/resume handlers to the leds/leds-asic3 and mfd/asic3 drivers. On suspend the leds will be turned off and their clocks disabled. On resume the reverse. Signed-off-by: Paul Parsons Signed-off-by: Samuel Ortiz --- drivers/leds/leds-asic3.c | 35 ++++++++++++++++++++++++++++++++++- drivers/mfd/asic3.c | 19 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c index 22f847c890c9..fbd5d88ccd8f 100644 --- a/drivers/leds/leds-asic3.c +++ b/drivers/leds/leds-asic3.c @@ -107,9 +107,10 @@ static int __devinit asic3_led_probe(struct platform_device *pdev) } led->cdev->name = led->name; - led->cdev->default_trigger = led->default_trigger; + led->cdev->flags = LED_CORE_SUSPENDRESUME; led->cdev->brightness_set = brightness_set; led->cdev->blink_set = blink_set; + led->cdev->default_trigger = led->default_trigger; ret = led_classdev_register(&pdev->dev, led->cdev); if (ret < 0) @@ -136,12 +137,44 @@ static int __devexit asic3_led_remove(struct platform_device *pdev) return mfd_cell_disable(pdev); } +static int asic3_led_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct mfd_cell *cell = mfd_get_cell(pdev); + int ret; + + ret = 0; + if (cell->suspend) + ret = (*cell->suspend)(pdev); + + return ret; +} + +static int asic3_led_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct mfd_cell *cell = mfd_get_cell(pdev); + int ret; + + ret = 0; + if (cell->resume) + ret = (*cell->resume)(pdev); + + return ret; +} + +static const struct dev_pm_ops asic3_led_pm_ops = { + .suspend = asic3_led_suspend, + .resume = asic3_led_resume, +}; + static struct platform_driver asic3_led_driver = { .probe = asic3_led_probe, .remove = __devexit_p(asic3_led_remove), .driver = { .name = "leds-asic3", .owner = THIS_MODULE, + .pm = &asic3_led_pm_ops, }, }; diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 5ac2961fedfe..2dbb8ca89730 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -813,24 +813,43 @@ static int asic3_leds_disable(struct platform_device *pdev) return 0; } +static int asic3_leds_suspend(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); + + while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0) + msleep(1); + + asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]); + + return 0; +} + static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = { [0] = { .name = "leds-asic3", .id = 0, .enable = asic3_leds_enable, .disable = asic3_leds_disable, + .suspend = asic3_leds_suspend, + .resume = asic3_leds_enable, }, [1] = { .name = "leds-asic3", .id = 1, .enable = asic3_leds_enable, .disable = asic3_leds_disable, + .suspend = asic3_leds_suspend, + .resume = asic3_leds_enable, }, [2] = { .name = "leds-asic3", .id = 2, .enable = asic3_leds_enable, .disable = asic3_leds_disable, + .suspend = asic3_leds_suspend, + .resume = asic3_leds_enable, }, }; -- cgit v1.2.3 From c29a81270bd3cbdf0c603d42f4c2d17acf94a0bc Mon Sep 17 00:00:00 2001 From: Paul Parsons Date: Tue, 9 Aug 2011 16:27:43 +0000 Subject: mfd: Make asic3_clk_enable() a void function The return value of asic3_clk_enable() was neither used nor useful. So let's make it a void function, and thereby match asic3_clk_disable(). Signed-off-by: Paul Parsons Signed-off-by: Samuel Ortiz --- drivers/mfd/asic3.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 2dbb8ca89730..3da3a901ec3e 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -584,7 +584,7 @@ static int asic3_gpio_remove(struct platform_device *pdev) return gpiochip_remove(&asic->gpio); } -static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) +static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) { unsigned long flags; u32 cdex; @@ -596,8 +596,6 @@ static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex); } spin_unlock_irqrestore(&asic->lock, flags); - - return 0; } static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk) -- cgit v1.2.3 From d8e4a88b7974ea03015f515383fb2576fbb517f1 Mon Sep 17 00:00:00 2001 From: Paul Parsons Date: Tue, 9 Aug 2011 16:27:50 +0000 Subject: mfd: Define asic3 gpio_chip label Defined the gpio_chip label in the mfd/asic3 driver for diagnostics. Signed-off-by: Paul Parsons Signed-off-by: Samuel Ortiz --- drivers/mfd/asic3.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 3da3a901ec3e..3bd85bddf6e3 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -968,6 +968,7 @@ static int __init asic3_probe(struct platform_device *pdev) goto out_unmap; } + asic->gpio.label = "asic3"; asic->gpio.base = pdata->gpio_base; asic->gpio.ngpio = ASIC3_NUM_GPIOS; asic->gpio.get = asic3_gpio_get; -- cgit v1.2.3 From 54d8e2c323b439e0e20ea44d17b875d9a43f7d66 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 9 Aug 2011 20:37:17 +0200 Subject: mfd: Add missing #ifdef around tc3589x PM block The CONFIG_PM code was unconditionally compiled in despite the dev_pm_ops only being included into the driver struct if used. Fix this by adding the missing #ifdef. Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/tc3589x.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index c27e515b0722..de979742c6fc 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -357,6 +357,7 @@ static int __devexit tc3589x_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM static int tc3589x_suspend(struct device *dev) { struct tc3589x *tc3589x = dev_get_drvdata(dev); @@ -387,6 +388,7 @@ static int tc3589x_resume(struct device *dev) static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume); +#endif static const struct i2c_device_id tc3589x_id[] = { { "tc3589x", 24 }, -- cgit v1.2.3 From 981c65a9b3e24029f64bd45c7a92f901899a033e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 10 Aug 2011 10:47:16 +0200 Subject: mfd: Clean timberdale error handling code up In the first three cases, ioremap has been called, so iounmap is needed. A new label for this is introduced, to differentiate it from err_msix, which is the first point at which msix_entries actually needs to be freed. Signed-off-by: Julia Lawall Signed-off-by: Samuel Ortiz --- drivers/mfd/timberdale.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 696879e2eef7..02d65692ceb4 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -697,7 +697,7 @@ static int __devinit timb_probe(struct pci_dev *dev, dev_err(&dev->dev, "The driver supports an older " "version of the FPGA, please update the driver to " "support %d.%d\n", priv->fw.major, priv->fw.minor); - goto err_ioremap; + goto err_config; } if (priv->fw.major < TIMB_SUPPORTED_MAJOR || priv->fw.minor < TIMB_REQUIRED_MINOR) { @@ -705,13 +705,13 @@ static int __devinit timb_probe(struct pci_dev *dev, "please upgrade the FPGA to at least: %d.%d\n", priv->fw.major, priv->fw.minor, TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR); - goto err_ioremap; + goto err_config; } msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries), GFP_KERNEL); if (!msix_entries) - goto err_ioremap; + goto err_config; for (i = 0; i < TIMBERDALE_NR_IRQS; i++) msix_entries[i].entry = i; @@ -825,6 +825,8 @@ err_mfd: err_create_file: pci_disable_msix(dev); err_msix: + kfree(msix_entries); +err_config: iounmap(priv->ctl_membase); err_ioremap: release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); @@ -833,7 +835,6 @@ err_request: err_start: pci_disable_device(dev); err_enable: - kfree(msix_entries); kfree(priv); pci_set_drvdata(dev, NULL); return -ENODEV; -- cgit v1.2.3 From bd4a40b57b13907b4fe01b6c605be56d8f3733fe Mon Sep 17 00:00:00 2001 From: Karl Komierowski Date: Wed, 10 Aug 2011 15:09:43 +0200 Subject: mfd: Refactor ab8500 GPADC API, add raw access Refactor the GPADC interface to avoid bugs in calling code: - ab8500_gpadc_[convert|read_raw|ad_to_voltage] clarifies each functions use case, *convert wraps *read_raw, and we can access raw ADC values properly. - Renamed gpadc function arguments from "input" to "channel" to clarify use, so we don't get confused again. Signed-off-by: Kalle Komierowski Reviewed-by: Mattias Wallin Reviewed-by: John Beckett Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-gpadc.c | 56 ++++++++++++++++++++++++++++++---------- include/linux/mfd/ab8500/gpadc.h | 5 +++- 2 files changed, 47 insertions(+), 14 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index f16afb234ff9..e985d1701a83 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -143,12 +143,15 @@ struct ab8500_gpadc *ab8500_gpadc_get(char *name) } EXPORT_SYMBOL(ab8500_gpadc_get); -static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input, +/** + * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage + */ +int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, int ad_value) { int res; - switch (input) { + switch (channel) { case MAIN_CHARGER_V: /* For some reason we don't have calibrated data */ if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) { @@ -232,18 +235,46 @@ static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input, } return res; } +EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage); /** * ab8500_gpadc_convert() - gpadc conversion - * @input: analog input to be converted to digital data + * @channel: analog channel to be converted to digital data * * This function converts the selected analog i/p to digital * data. */ -int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) +int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel) +{ + int ad_value; + int voltage; + + ad_value = ab8500_gpadc_read_raw(gpadc, channel); + if (ad_value < 0) { + dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel); + return ad_value; + } + + voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value); + + if (voltage < 0) + dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:" + " %d AD: 0x%x\n", channel, ad_value); + + return voltage; +} +EXPORT_SYMBOL(ab8500_gpadc_convert); + +/** + * ab8500_gpadc_read_raw() - gpadc read + * @channel: analog channel to be read + * + * This function obtains the raw ADC value, this then needs + * to be converted by calling ab8500_gpadc_ad_to_voltage() + */ +int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) { int ret; - u16 data = 0; int looplimit = 0; u8 val, low_data, high_data; @@ -278,9 +309,9 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) goto out; } - /* Select the input source and set average samples to 16 */ + /* Select the channel source and set average samples to 16 */ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16)); + AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16)); if (ret < 0) { dev_err(gpadc->dev, "gpadc_conversion: set avg samples failed\n"); @@ -292,7 +323,7 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) * charging current sense if it needed, ABB 3.0 needs some special * treatment too. */ - switch (input) { + switch (channel) { case MAIN_CHARGER_C: case USB_CHARGER_C: ret = abx500_mask_and_set_register_interruptible(gpadc->dev, @@ -359,7 +390,6 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) goto out; } - data = (high_data << 8) | low_data; /* Disable GPADC */ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, AB8500_GPADC_CTRL1_REG, DIS_GPADC); @@ -370,8 +400,8 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) /* Disable VTVout LDO this is required for GPADC */ regulator_disable(gpadc->regu); mutex_unlock(&gpadc->ab8500_gpadc_lock); - ret = ab8500_gpadc_ad_to_voltage(gpadc, input, data); - return ret; + + return (high_data << 8) | low_data; out: /* @@ -385,10 +415,10 @@ out: regulator_disable(gpadc->regu); mutex_unlock(&gpadc->ab8500_gpadc_lock); dev_err(gpadc->dev, - "gpadc_conversion: Failed to AD convert channel %d\n", input); + "gpadc_conversion: Failed to AD convert channel %d\n", channel); return ret; } -EXPORT_SYMBOL(ab8500_gpadc_convert); +EXPORT_SYMBOL(ab8500_gpadc_read_raw); /** * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion diff --git a/include/linux/mfd/ab8500/gpadc.h b/include/linux/mfd/ab8500/gpadc.h index 46b954011f16..252966769d93 100644 --- a/include/linux/mfd/ab8500/gpadc.h +++ b/include/linux/mfd/ab8500/gpadc.h @@ -27,6 +27,9 @@ struct ab8500_gpadc; struct ab8500_gpadc *ab8500_gpadc_get(char *name); -int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input); +int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel); +int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel); +int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, + u8 channel, int ad_value); #endif /* _AB8500_GPADC_H */ -- cgit v1.2.3 From 862de70c12bb6227943e155251c75e7fa4558068 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 11 Aug 2011 15:21:00 +0800 Subject: mfd: Make sure to request twl6030 IRQ before using the irq_num I was trying to fix the error handling part because in the case of request_irq failure, it should call kthread_stop instead of free_irq. But it seems more reasonable to do request_irq before calling kthread_run. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6030-irq.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index eb3b5f88e566..776402566c9e 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -331,12 +331,6 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) /* install an irq handler to demultiplex the TWL6030 interrupt */ init_completion(&irq_event); - task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq"); - if (IS_ERR(task)) { - pr_err("twl6030: could not create irq %d thread!\n", irq_num); - status = PTR_ERR(task); - goto fail_kthread; - } status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED, "TWL6030-PIH", &irq_event); @@ -344,11 +338,19 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status); goto fail_irq; } + + task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq"); + if (IS_ERR(task)) { + pr_err("twl6030: could not create irq %d thread!\n", irq_num); + status = PTR_ERR(task); + goto fail_kthread; + } return status; -fail_irq: - free_irq(irq_num, &irq_event); fail_kthread: + free_irq(irq_num, &irq_event); + +fail_irq: for (i = irq_base; i < irq_end; i++) irq_set_chip_and_handler(i, NULL, NULL); return status; -- cgit v1.2.3 From 881de67046f424fc3a6e05b1c681c12afd94e802 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 22 Aug 2011 15:43:55 +0200 Subject: mfd: Allow WM8994 LDO enable pulls to be disabled In systems where the LDO enables are always driven (for example, being connected to an always on supply rail or a GPIO which is driven by the CPU even in suspend) then we can disable the pull downs on the LDO for a small power savings. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 19 +++++++++++++++++++ include/linux/mfd/wm8994/core.h | 2 ++ include/linux/mfd/wm8994/pdata.h | 7 +++++++ 3 files changed, 28 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 96479c9b1728..1f15743460a0 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -281,6 +281,13 @@ static int wm8994_suspend(struct device *dev) return 0; } + /* Disable LDO pulldowns while the device is suspended if we + * don't know that something will be driving them. */ + if (!wm8994->ldo_ena_always_driven) + wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD); + /* GPIO configuration state is saved here since we may be configuring * the GPIO alternate functions even if we're not using the gpiolib * driver for them. @@ -350,6 +357,11 @@ static int wm8994_resume(struct device *dev) if (ret < 0) dev_err(dev, "Failed to restore GPIO registers: %d\n", ret); + /* Disable LDO pulldowns while the device is active */ + wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD, + 0); + wm8994->suspended = false; return 0; @@ -513,8 +525,15 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) pdata->gpio_defaults[i]); } } + + wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven; } + /* Disable LDO pulldowns while the device is active */ + wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD, + 0); + /* In some system designs where the regulators are not in use, * we can achieve a small reduction in leakage currents by * floating LDO outputs. This bit makes no difference if the diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index f0b69cdae41c..5ab71bd76f9c 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -63,6 +63,8 @@ struct wm8994 { void *control_data; + bool ldo_ena_always_driven; + int gpio_base; int irq_base; diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 97cf4f27d647..ea32f306dca6 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -167,6 +167,13 @@ struct wm8994_pdata { /* WM8958 microphone bias configuration */ int micbias[2]; + + /* Disable the internal pull downs on the LDOs if they are + * always driven (eg, connected to an always on supply or + * GPIO that always drives an output. If they float power + * consumption will rise. + */ + bool ldo_ena_always_driven; }; #endif -- cgit v1.2.3 From 3d6271f92e98094584fd1e609a9969cd33e61122 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Thu, 11 Aug 2011 22:33:13 -0500 Subject: mfd: Turn on the twl4030-madc MADC clock Without turning the MADC clock on, no MADC conversions occur. $ cat /sys/class/hwmon/hwmon0/device/in8_input [ 53.428436] twl4030_madc twl4030_madc: conversion timeout! cat: read error: Resource temporarily unavailable Signed-off-by: Kyle Manna Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-madc.c | 22 ++++++++++++++++++++++ include/linux/i2c/twl4030-madc.h | 4 ++++ 2 files changed, 26 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 7cbf2aa9e64f..834f824d3c11 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -740,6 +740,28 @@ static int __devinit twl4030_madc_probe(struct platform_device *pdev) TWL4030_BCI_BCICTL1); goto err_i2c; } + + /* Check that MADC clock is on */ + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + + /* If MADC clk is not on, turn it on */ + if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { + dev_info(&pdev->dev, "clk disabled, enabling\n"); + regval |= TWL4030_GPBR1_MADC_HFCLK_EN; + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, + TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + } + platform_set_drvdata(pdev, madc); mutex_init(&madc->lock); ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL, diff --git a/include/linux/i2c/twl4030-madc.h b/include/linux/i2c/twl4030-madc.h index 6427d298fbfc..530e11ba0738 100644 --- a/include/linux/i2c/twl4030-madc.h +++ b/include/linux/i2c/twl4030-madc.h @@ -129,6 +129,10 @@ enum sample_type { #define REG_BCICTL2 0x024 #define TWL4030_BCI_ITHSENS 0x007 +/* Register and bits for GPBR1 register */ +#define TWL4030_REG_GPBR1 0x0c +#define TWL4030_GPBR1_MADC_HFCLK_EN (1 << 7) + struct twl4030_madc_user_parms { int channel; int average; -- cgit v1.2.3 From 5f40c6b6508b622ea03c6b32c57b2e26eba2e4f1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 13 Aug 2011 14:37:48 +0900 Subject: mfd: Add more checks for WM8994 being active during suspend Enhancements to the WM8994 audio driver and new features on more modern devices in the series mean that we can no longer rely on VMID being active as an indication that the device is active. Add further checks for digital paths and microphone detection. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 1f15743460a0..9b01a22c3e70 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -281,6 +281,40 @@ static int wm8994_suspend(struct device *dev) return 0; } + ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_4); + if (ret < 0) { + dev_err(dev, "Failed to read power status: %d\n", ret); + } else if (ret & (WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA | + WM8994_AIF1ADC2L_ENA | WM8994_AIF1ADC2R_ENA | + WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC1R_ENA)) { + dev_dbg(dev, "CODEC still active, ignoring suspend\n"); + return 0; + } + + ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_5); + if (ret < 0) { + dev_err(dev, "Failed to read power status: %d\n", ret); + } else if (ret & (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | + WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA | + WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA)) { + dev_dbg(dev, "CODEC still active, ignoring suspend\n"); + return 0; + } + + switch (wm8994->type) { + case WM8958: + ret = wm8994_reg_read(wm8994, WM8958_MIC_DETECT_1); + if (ret < 0) { + dev_err(dev, "Failed to read power status: %d\n", ret); + } else if (ret & WM8958_MICD_ENA) { + dev_dbg(dev, "CODEC still active, ignoring suspend\n"); + return 0; + } + break; + default: + break; + } + /* Disable LDO pulldowns while the device is suspended if we * don't know that something will be driving them. */ if (!wm8994->ldo_ena_always_driven) -- cgit v1.2.3 From 01fdaab8ffced1deeee14d9c7d2745f37349484e Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Fri, 19 Aug 2011 14:39:40 +0900 Subject: mfd: Wake-up from Suspend MAX8997 support - Support wake-up from suspend-to-ram. - Handle pending interrupt after a resume. - If pdata->wakeup is enabled, by default, the device is assumed to be capable of wakeup (the interrupt pin is connected to a wakeup-source GPIO) and may wakeup the system (MAX8997 has a power button input pin). Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Samuel Ortiz --- drivers/mfd/max8997.c | 27 ++++++++++++++++++++++++++- include/linux/mfd/max8997-private.h | 1 - 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index f83103b8970d..dc58750bb71b 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -142,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c, max8997->irq_base = pdata->irq_base; max8997->ono = pdata->ono; - max8997->wakeup = pdata->wakeup; mutex_init(&max8997->iolock); @@ -169,6 +169,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto err_mfd; + /* MAX8997 has a power button input. */ + device_init_wakeup(max8997->dev, pdata->wakeup); + return ret; err_mfd: @@ -398,7 +401,29 @@ static int max8997_restore(struct device *dev) return 0; } +static int max8997_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(max8997->irq, 1); + return 0; +} + +static int max8997_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(max8997->irq, 0); + return max8997_irq_resume(max8997); +} + const struct dev_pm_ops max8997_pm = { + .suspend = max8997_suspend, + .resume = max8997_resume, .freeze = max8997_freeze, .restore = max8997_restore, }; diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 5ff2400ad46c..3f4deb62d6b0 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -326,7 +326,6 @@ struct max8997_dev { int irq; int ono; int irq_base; - bool wakeup; struct mutex irqlock; int irq_masks_cur[MAX8997_IRQ_GROUP_NR]; int irq_masks_cache[MAX8997_IRQ_GROUP_NR]; -- cgit v1.2.3 From 8cd6af2938945ce71e99182c8f092033cf9a5e17 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 30 Jun 2011 12:51:04 +0300 Subject: mfd: Remove twl4030-irq trailing whitespaces trivial patch, no functional changes. Signed-off-by: Felipe Balbi Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 8a7ee3139b86..7be97cb2075d 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -620,7 +620,7 @@ static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) static struct irq_chip twl4030_sih_irq_chip = { .name = "twl4030", - .irq_mask = twl4030_sih_mask, + .irq_mask = twl4030_sih_mask, .irq_unmask = twl4030_sih_unmask, .irq_set_type = twl4030_sih_set_type, }; -- cgit v1.2.3 From 91e3569ff487784edd206de1b324507b5ecc8439 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 30 Jun 2011 12:51:05 +0300 Subject: mfd: Implement bus_lock/bus_sync_unlock twl4030-irq methods For doing that, drop the locking and change that to a mutex. Signed-off-by: Felipe Balbi Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 7be97cb2075d..291364c3943e 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -460,8 +460,6 @@ static inline void activate_irq(int irq) /*----------------------------------------------------------------------*/ -static DEFINE_SPINLOCK(sih_agent_lock); - static struct workqueue_struct *wq; struct sih_agent { @@ -474,6 +472,8 @@ struct sih_agent { u32 edge_change; struct work_struct edge_work; + + struct mutex irq_lock; }; static void twl4030_sih_do_mask(struct work_struct *work) @@ -489,7 +489,6 @@ static void twl4030_sih_do_mask(struct work_struct *work) agent = container_of(work, struct sih_agent, mask_work); /* see what work we have */ - spin_lock_irq(&sih_agent_lock); if (agent->imr_change_pending) { sih = agent->sih; /* byte[0] gets overwritten as we write ... */ @@ -497,7 +496,6 @@ static void twl4030_sih_do_mask(struct work_struct *work) agent->imr_change_pending = false; } else sih = NULL; - spin_unlock_irq(&sih_agent_lock); if (!sih) return; @@ -520,11 +518,9 @@ static void twl4030_sih_do_edge(struct work_struct *work) agent = container_of(work, struct sih_agent, edge_work); /* see what work we have */ - spin_lock_irq(&sih_agent_lock); edge_change = agent->edge_change; agent->edge_change = 0; sih = edge_change ? agent->sih : NULL; - spin_unlock_irq(&sih_agent_lock); if (!sih) return; @@ -580,49 +576,57 @@ static void twl4030_sih_do_edge(struct work_struct *work) static void twl4030_sih_mask(struct irq_data *data) { struct sih_agent *sih = irq_data_get_irq_chip_data(data); - unsigned long flags; - spin_lock_irqsave(&sih_agent_lock, flags); sih->imr |= BIT(data->irq - sih->irq_base); sih->imr_change_pending = true; queue_work(wq, &sih->mask_work); - spin_unlock_irqrestore(&sih_agent_lock, flags); } static void twl4030_sih_unmask(struct irq_data *data) { struct sih_agent *sih = irq_data_get_irq_chip_data(data); - unsigned long flags; - spin_lock_irqsave(&sih_agent_lock, flags); sih->imr &= ~BIT(data->irq - sih->irq_base); sih->imr_change_pending = true; queue_work(wq, &sih->mask_work); - spin_unlock_irqrestore(&sih_agent_lock, flags); } static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) { struct sih_agent *sih = irq_data_get_irq_chip_data(data); - unsigned long flags; if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; - spin_lock_irqsave(&sih_agent_lock, flags); if (irqd_get_trigger_type(data) != trigger) { sih->edge_change |= BIT(data->irq - sih->irq_base); queue_work(wq, &sih->edge_work); } - spin_unlock_irqrestore(&sih_agent_lock, flags); + return 0; } +static void twl4030_sih_bus_lock(struct irq_data *data) +{ + struct sih_agent *sih = irq_data_get_irq_chip_data(data); + + mutex_lock(&sih->irq_lock); +} + +static void twl4030_sih_bus_sync_unlock(struct irq_data *data) +{ + struct sih_agent *sih = irq_data_get_irq_chip_data(data); + + mutex_unlock(&sih->irq_lock); +} + static struct irq_chip twl4030_sih_irq_chip = { .name = "twl4030", .irq_mask = twl4030_sih_mask, .irq_unmask = twl4030_sih_unmask, .irq_set_type = twl4030_sih_set_type, + .irq_bus_lock = twl4030_sih_bus_lock, + .irq_bus_sync_unlock = twl4030_sih_bus_sync_unlock, }; /*----------------------------------------------------------------------*/ @@ -718,15 +722,16 @@ int twl4030_sih_setup(int module) agent->irq_base = irq_base; agent->sih = sih; agent->imr = ~0; + mutex_init(&agent->irq_lock); INIT_WORK(&agent->mask_work, twl4030_sih_do_mask); INIT_WORK(&agent->edge_work, twl4030_sih_do_edge); for (i = 0; i < sih->bits; i++) { irq = irq_base + i; + irq_set_chip_data(irq, agent); irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip, handle_edge_irq); - irq_set_chip_data(irq, agent); activate_irq(irq); } -- cgit v1.2.3 From 7750c9b0d24c78c1f6b10d9ce0508945f250e5b2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 30 Jun 2011 12:51:06 +0300 Subject: mfd: Drop the twl4030-irq kthread ... and use threaded IRQ infrastructure. Later patches will come dropping both workqueues and setting the nested thread flag. Signed-off-by: Felipe Balbi Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 94 +++++++++++------------------------------------ 1 file changed, 21 insertions(+), 73 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 291364c3943e..e1e0944af2e6 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -278,59 +277,6 @@ static const struct sih sih_modules_twl5031[8] = { static unsigned twl4030_irq_base; -static struct completion irq_event; - -/* - * This thread processes interrupts reported by the Primary Interrupt Handler. - */ -static int twl4030_irq_thread(void *data) -{ - long irq = (long)data; - static unsigned i2c_errors; - static const unsigned max_i2c_errors = 100; - - - current->flags |= PF_NOFREEZE; - - while (!kthread_should_stop()) { - int ret; - int module_irq; - u8 pih_isr; - - /* Wait for IRQ, then read PIH irq status (also blocking) */ - wait_for_completion_interruptible(&irq_event); - - ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr, - REG_PIH_ISR_P1); - if (ret) { - pr_warning("twl4030: I2C error %d reading PIH ISR\n", - ret); - if (++i2c_errors >= max_i2c_errors) { - printk(KERN_ERR "Maximum I2C error count" - " exceeded. Terminating %s.\n", - __func__); - break; - } - complete(&irq_event); - continue; - } - - /* these handlers deal with the relevant SIH irq status */ - local_irq_disable(); - for (module_irq = twl4030_irq_base; - pih_isr; - pih_isr >>= 1, module_irq++) { - if (pih_isr & 0x1) - generic_handle_irq(module_irq); - } - local_irq_enable(); - - enable_irq(irq); - } - - return 0; -} - /* * handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt. * This is a chained interrupt, so there is no desc->action method for it. @@ -342,9 +288,25 @@ static int twl4030_irq_thread(void *data) */ static irqreturn_t handle_twl4030_pih(int irq, void *devid) { - /* Acknowledge, clear *AND* mask the interrupt... */ - disable_irq_nosync(irq); - complete(devid); + int module_irq; + irqreturn_t ret; + u8 pih_isr; + + ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr, + REG_PIH_ISR_P1); + if (ret) { + pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret); + return IRQ_NONE; + } + + /* these handlers deal with the relevant SIH irq status */ + for (module_irq = twl4030_irq_base; + pih_isr; + pih_isr >>= 1, module_irq++) { + if (pih_isr & 0x1) + generic_handle_irq(module_irq); + } + return IRQ_HANDLED; } /*----------------------------------------------------------------------*/ @@ -763,7 +725,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) int status; int i; - struct task_struct *task; /* * Mask and clear all TWL4030 interrupts since initially we do @@ -806,27 +767,14 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) } /* install an irq handler to demultiplex the TWL4030 interrupt */ - - - init_completion(&irq_event); - - status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED, - "TWL4030-PIH", &irq_event); + status = request_threaded_irq(irq_num, NULL, handle_twl4030_pih, + IRQF_DISABLED, "TWL4030-PIH", NULL); if (status < 0) { pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status); goto fail_rqirq; } - task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num, - "twl4030-irq"); - if (IS_ERR(task)) { - pr_err("twl4030: could not create irq %d thread!\n", irq_num); - status = PTR_ERR(task); - goto fail_kthread; - } return status; -fail_kthread: - free_irq(irq_num, &irq_event); fail_rqirq: /* clean up twl4030_sih_setup */ fail: -- cgit v1.2.3 From 848684246fc180a9b96f1b373a1ad70bc3ee725f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 30 Jun 2011 12:51:07 +0300 Subject: mfd: Drop twl4030-irq's mask_work ... we can do the synchronization with the hardware when calling bus_sync_unlock as we're supposed to. While at that, also make variable names uniform on all functions. Signed-off-by: Felipe Balbi Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 82 ++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 48 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index e1e0944af2e6..bf62389fc7e5 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -430,7 +430,6 @@ struct sih_agent { u32 imr; bool imr_change_pending; - struct work_struct mask_work; u32 edge_change; struct work_struct edge_work; @@ -438,37 +437,6 @@ struct sih_agent { struct mutex irq_lock; }; -static void twl4030_sih_do_mask(struct work_struct *work) -{ - struct sih_agent *agent; - const struct sih *sih; - union { - u8 bytes[4]; - u32 word; - } imr; - int status; - - agent = container_of(work, struct sih_agent, mask_work); - - /* see what work we have */ - if (agent->imr_change_pending) { - sih = agent->sih; - /* byte[0] gets overwritten as we write ... */ - imr.word = cpu_to_le32(agent->imr << 8); - agent->imr_change_pending = false; - } else - sih = NULL; - if (!sih) - return; - - /* write the whole mask ... simpler than subsetting it */ - status = twl_i2c_write(sih->module, imr.bytes, - sih->mask[irq_line].imr_offset, sih->bytes_ixr); - if (status) - pr_err("twl4030: %s, %s --> %d\n", __func__, - "write", status); -} - static void twl4030_sih_do_edge(struct work_struct *work) { struct sih_agent *agent; @@ -537,32 +505,30 @@ static void twl4030_sih_do_edge(struct work_struct *work) static void twl4030_sih_mask(struct irq_data *data) { - struct sih_agent *sih = irq_data_get_irq_chip_data(data); + struct sih_agent *agent = irq_data_get_irq_chip_data(data); - sih->imr |= BIT(data->irq - sih->irq_base); - sih->imr_change_pending = true; - queue_work(wq, &sih->mask_work); + agent->imr |= BIT(data->irq - agent->irq_base); + agent->imr_change_pending = true; } static void twl4030_sih_unmask(struct irq_data *data) { - struct sih_agent *sih = irq_data_get_irq_chip_data(data); + struct sih_agent *agent = irq_data_get_irq_chip_data(data); - sih->imr &= ~BIT(data->irq - sih->irq_base); - sih->imr_change_pending = true; - queue_work(wq, &sih->mask_work); + agent->imr &= ~BIT(data->irq - agent->irq_base); + agent->imr_change_pending = true; } static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) { - struct sih_agent *sih = irq_data_get_irq_chip_data(data); + struct sih_agent *agent = irq_data_get_irq_chip_data(data); if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; if (irqd_get_trigger_type(data) != trigger) { - sih->edge_change |= BIT(data->irq - sih->irq_base); - queue_work(wq, &sih->edge_work); + agent->edge_change |= BIT(data->irq - agent->irq_base); + queue_work(wq, &agent->edge_work); } return 0; @@ -570,16 +536,37 @@ static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) static void twl4030_sih_bus_lock(struct irq_data *data) { - struct sih_agent *sih = irq_data_get_irq_chip_data(data); + struct sih_agent *agent = irq_data_get_irq_chip_data(data); - mutex_lock(&sih->irq_lock); + mutex_lock(&agent->irq_lock); } static void twl4030_sih_bus_sync_unlock(struct irq_data *data) { - struct sih_agent *sih = irq_data_get_irq_chip_data(data); + struct sih_agent *agent = irq_data_get_irq_chip_data(data); + const struct sih *sih = agent->sih; + int status; + + if (agent->imr_change_pending) { + union { + u32 word; + u8 bytes[4]; + } imr; + + /* byte[0] gets overwriten as we write ... */ + imr.word = cpu_to_le32(agent->imr << 8); + agent->imr_change_pending = false; + + /* write the whole mask ... simpler than subsetting it */ + status = twl_i2c_write(sih->module, imr.bytes, + sih->mask[irq_line].imr_offset, + sih->bytes_ixr); + if (status) + pr_err("twl4030: %s, %s --> %d\n", __func__, + "write", status); + } - mutex_unlock(&sih->irq_lock); + mutex_unlock(&agent->irq_lock); } static struct irq_chip twl4030_sih_irq_chip = { @@ -685,7 +672,6 @@ int twl4030_sih_setup(int module) agent->sih = sih; agent->imr = ~0; mutex_init(&agent->irq_lock); - INIT_WORK(&agent->mask_work, twl4030_sih_do_mask); INIT_WORK(&agent->edge_work, twl4030_sih_do_edge); for (i = 0; i < sih->bits; i++) { -- cgit v1.2.3 From 2f2a7d5ef76477f3d2a333663e7cf7a380aebd82 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 30 Jun 2011 12:51:08 +0300 Subject: mfd: Drop twl4030-irq's edge_work ... and do all the synchronization with the hardware during bus_sync_unlock. We can now remove all the workqueues. Signed-off-by: Felipe Balbi Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 124 +++++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 72 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index bf62389fc7e5..1b9ab2f40d5b 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -422,8 +422,6 @@ static inline void activate_irq(int irq) /*----------------------------------------------------------------------*/ -static struct workqueue_struct *wq; - struct sih_agent { int irq_base; const struct sih *sih; @@ -432,68 +430,10 @@ struct sih_agent { bool imr_change_pending; u32 edge_change; - struct work_struct edge_work; struct mutex irq_lock; }; -static void twl4030_sih_do_edge(struct work_struct *work) -{ - struct sih_agent *agent; - const struct sih *sih; - u8 bytes[6]; - u32 edge_change; - int status; - - agent = container_of(work, struct sih_agent, edge_work); - - /* see what work we have */ - edge_change = agent->edge_change; - agent->edge_change = 0; - sih = edge_change ? agent->sih : NULL; - if (!sih) - return; - - /* Read, reserving first byte for write scratch. Yes, this - * could be cached for some speedup ... but be careful about - * any processor on the other IRQ line, EDR registers are - * shared. - */ - status = twl_i2c_read(sih->module, bytes + 1, - sih->edr_offset, sih->bytes_edr); - if (status) { - pr_err("twl4030: %s, %s --> %d\n", __func__, - "read", status); - return; - } - - /* Modify only the bits we know must change */ - while (edge_change) { - int i = fls(edge_change) - 1; - struct irq_data *idata = irq_get_irq_data(i + agent->irq_base); - int byte = 1 + (i >> 2); - int off = (i & 0x3) * 2; - unsigned int type; - - bytes[byte] &= ~(0x03 << off); - - type = irqd_get_trigger_type(idata); - if (type & IRQ_TYPE_EDGE_RISING) - bytes[byte] |= BIT(off + 1); - if (type & IRQ_TYPE_EDGE_FALLING) - bytes[byte] |= BIT(off + 0); - - edge_change &= ~BIT(i); - } - - /* Write */ - status = twl_i2c_write(sih->module, bytes, - sih->edr_offset, sih->bytes_edr); - if (status) - pr_err("twl4030: %s, %s --> %d\n", __func__, - "write", status); -} - /*----------------------------------------------------------------------*/ /* @@ -526,10 +466,8 @@ static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; - if (irqd_get_trigger_type(data) != trigger) { + if (irqd_get_trigger_type(data) != trigger) agent->edge_change |= BIT(data->irq - agent->irq_base); - queue_work(wq, &agent->edge_work); - } return 0; } @@ -566,6 +504,56 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data) "write", status); } + if (agent->edge_change) { + u32 edge_change; + u8 bytes[6]; + + edge_change = agent->edge_change; + agent->edge_change = 0; + + /* + * Read, reserving first byte for write scratch. Yes, this + * could be cached for some speedup ... but be careful about + * any processor on the other IRQ line, EDR registers are + * shared. + */ + status = twl_i2c_read(sih->module, bytes + 1, + sih->edr_offset, sih->bytes_edr); + if (status) { + pr_err("twl4030: %s, %s --> %d\n", __func__, + "read", status); + return; + } + + /* Modify only the bits we know must change */ + while (edge_change) { + int i = fls(edge_change) - 1; + struct irq_data *idata; + int byte = 1 + (i >> 2); + int off = (i & 0x3) * 2; + unsigned int type; + + idata = irq_get_irq_data(i + agent->irq_base); + + bytes[byte] &= ~(0x03 << off); + + type = irqd_get_trigger_type(idata); + if (type & IRQ_TYPE_EDGE_RISING) + bytes[byte] |= BIT(off + 1); + if (type & IRQ_TYPE_EDGE_FALLING) + bytes[byte] |= BIT(off + 0); + + edge_change &= ~BIT(i); + } + + /* Write */ + status = twl_i2c_write(sih->module, bytes, + sih->edr_offset, sih->bytes_edr); + if (status) + pr_err("twl4030: %s, %s --> %d\n", __func__, + "write", status); + } + mutex_unlock(&agent->irq_lock); } @@ -672,7 +660,6 @@ int twl4030_sih_setup(int module) agent->sih = sih; agent->imr = ~0; mutex_init(&agent->irq_lock); - INIT_WORK(&agent->edge_work, twl4030_sih_do_edge); for (i = 0; i < sih->bits; i++) { irq = irq_base + i; @@ -720,12 +707,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) if (status < 0) return status; - wq = create_singlethread_workqueue("twl4030-irqchip"); - if (!wq) { - pr_err("twl4030: workqueue FAIL\n"); - return -ESRCH; - } - twl4030_irq_base = irq_base; /* install an irq handler for each of the SIH modules; @@ -766,8 +747,7 @@ fail_rqirq: fail: for (i = irq_base; i < irq_end; i++) irq_set_chip_and_handler(i, NULL, NULL); - destroy_workqueue(wq); - wq = NULL; + return status; } -- cgit v1.2.3 From 925e853c24bc7966cb0d6ed3b88948c33b0a6071 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 30 Jun 2011 12:51:09 +0300 Subject: mfd: Set twl4030-irq irq nested flag Threads from twl4030's children will be called nested in the context of the demultiplexing handler on twl4030-irq.c. Signed-off-by: Felipe Balbi Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 1b9ab2f40d5b..ff16e9c3ba30 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -304,7 +304,7 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid) pih_isr; pih_isr >>= 1, module_irq++) { if (pih_isr & 0x1) - generic_handle_irq(module_irq); + handle_nested_irq(module_irq); } return IRQ_HANDLED; @@ -596,9 +596,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc) int isr; /* reading ISR acks the IRQs, using clear-on-read mode */ - local_irq_enable(); isr = sih_read_isr(sih); - local_irq_disable(); if (isr < 0) { pr_err("twl4030: %s SIH, read ISR error %d\n", @@ -613,7 +611,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc) isr &= ~BIT(irq); if (irq < sih->bits) - generic_handle_irq(agent->irq_base + irq); + handle_nested_irq(agent->irq_base + irq); else pr_err("twl4030: %s SIH, invalid ISR bit %d\n", sih->name, irq); @@ -720,6 +718,7 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) for (i = irq_base; i < irq_end; i++) { irq_set_chip_and_handler(i, &twl4030_irq_chip, handle_simple_irq); + irq_set_nested_thread(i, 1); activate_irq(i); } twl4030_irq_next = i; @@ -745,8 +744,10 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) fail_rqirq: /* clean up twl4030_sih_setup */ fail: - for (i = irq_base; i < irq_end; i++) + for (i = irq_base; i < irq_end; i++) { + irq_set_nested_thread(i, 0); irq_set_chip_and_handler(i, NULL, NULL); + } return status; } -- cgit v1.2.3 From 2b247d06c6148bfe34a431431064f5553b17f0cc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 23 Aug 2011 18:56:19 +0100 Subject: mfd: Select REGMAP_I2C from WM8400 Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 13f0040413c4..9c1347dc7a4a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -390,6 +390,7 @@ config MFD_WM8400 tristate "Support Wolfson Microelectronics WM8400" select MFD_CORE depends on I2C + select REGMAP_I2C help Support for the Wolfson Microelecronics WM8400 PMIC and audio CODEC. This driver provides common support for accessing -- cgit v1.2.3 From fec316d63219f610e5385f5e54e6c3ea459e58e9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 24 Aug 2011 15:28:21 +0200 Subject: mfd: Provide a generic version of mc13xxx adc_do_conversion This is needed to convert the touch driver away from using struct mc13783. Note this patch drops MC13783_ADC0_ADREFMODE. This is unused and doesn't exist on mc13892. Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 89 ++++++++++++++++++++++----------------------- include/linux/mfd/mc13783.h | 35 +++++++++--------- include/linux/mfd/mc13xxx.h | 19 ++++++++++ 3 files changed, 81 insertions(+), 62 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 7e4d44bf92ab..5ee5e64d586b 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -26,12 +26,12 @@ struct mc13xxx { irq_handler_t irqhandler[MC13XXX_NUM_IRQ]; void *irqdata[MC13XXX_NUM_IRQ]; + + int adcflags; }; struct mc13783 { struct mc13xxx mc13xxx; - - int adcflags; }; struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783) @@ -136,14 +136,14 @@ EXPORT_SYMBOL(mc13783_to_mc13xxx); #define MC13XXX_REVISION_FAB (0x03 << 11) #define MC13XXX_REVISION_ICIDCODE (0x3f << 13) -#define MC13783_ADC1 44 -#define MC13783_ADC1_ADEN (1 << 0) -#define MC13783_ADC1_RAND (1 << 1) -#define MC13783_ADC1_ADSEL (1 << 3) -#define MC13783_ADC1_ASC (1 << 20) -#define MC13783_ADC1_ADTRIGIGN (1 << 21) +#define MC13XXX_ADC1 44 +#define MC13XXX_ADC1_ADEN (1 << 0) +#define MC13XXX_ADC1_RAND (1 << 1) +#define MC13XXX_ADC1_ADSEL (1 << 3) +#define MC13XXX_ADC1_ASC (1 << 20) +#define MC13XXX_ADC1_ADTRIGIGN (1 << 21) -#define MC13783_ADC2 45 +#define MC13XXX_ADC2 45 #define MC13XXX_NUMREGS 0x3f @@ -569,15 +569,15 @@ int mc13xxx_get_flags(struct mc13xxx *mc13xxx) } EXPORT_SYMBOL(mc13xxx_get_flags); -#define MC13783_ADC1_CHAN0_SHIFT 5 -#define MC13783_ADC1_CHAN1_SHIFT 8 +#define MC13XXX_ADC1_CHAN0_SHIFT 5 +#define MC13XXX_ADC1_CHAN1_SHIFT 8 struct mc13xxx_adcdone_data { struct mc13xxx *mc13xxx; struct completion done; }; -static irqreturn_t mc13783_handler_adcdone(int irq, void *data) +static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data) { struct mc13xxx_adcdone_data *adcdone_data = data; @@ -588,12 +588,11 @@ static irqreturn_t mc13783_handler_adcdone(int irq, void *data) return IRQ_HANDLED; } -#define MC13783_ADC_WORKING (1 << 0) +#define MC13XXX_ADC_WORKING (1 << 0) -int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, +int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, unsigned int channel, unsigned int *sample) { - struct mc13xxx *mc13xxx = &mc13783->mc13xxx; u32 adc0, adc1, old_adc0; int i, ret; struct mc13xxx_adcdone_data adcdone_data = { @@ -605,51 +604,51 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, mc13xxx_lock(mc13xxx); - if (mc13783->adcflags & MC13783_ADC_WORKING) { + if (mc13xxx->adcflags & MC13XXX_ADC_WORKING) { ret = -EBUSY; goto out; } - mc13783->adcflags |= MC13783_ADC_WORKING; + mc13xxx->adcflags |= MC13XXX_ADC_WORKING; - mc13xxx_reg_read(mc13xxx, MC13783_ADC0, &old_adc0); + mc13xxx_reg_read(mc13xxx, MC13XXX_ADC0, &old_adc0); - adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2; - adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC; + adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2; + adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC; if (channel > 7) - adc1 |= MC13783_ADC1_ADSEL; + adc1 |= MC13XXX_ADC1_ADSEL; switch (mode) { - case MC13783_ADC_MODE_TS: - adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 | - MC13783_ADC0_TSMOD1; - adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT; + case MC13XXX_ADC_MODE_TS: + adc0 |= MC13XXX_ADC0_ADREFEN | MC13XXX_ADC0_TSMOD0 | + MC13XXX_ADC0_TSMOD1; + adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT; break; - case MC13783_ADC_MODE_SINGLE_CHAN: - adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK; - adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT; - adc1 |= MC13783_ADC1_RAND; + case MC13XXX_ADC_MODE_SINGLE_CHAN: + adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK; + adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT; + adc1 |= MC13XXX_ADC1_RAND; break; - case MC13783_ADC_MODE_MULT_CHAN: - adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK; - adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT; + case MC13XXX_ADC_MODE_MULT_CHAN: + adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK; + adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT; break; default: - mc13783_unlock(mc13783); + mc13xxx_unlock(mc13xxx); return -EINVAL; } - dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__); - mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE, - mc13783_handler_adcdone, __func__, &adcdone_data); - mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE); + dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__); + mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, + mc13xxx_handler_adcdone, __func__, &adcdone_data); + mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE); - mc13xxx_reg_write(mc13xxx, MC13783_ADC0, adc0); - mc13xxx_reg_write(mc13xxx, MC13783_ADC1, adc1); + mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0); + mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1); mc13xxx_unlock(mc13xxx); @@ -660,27 +659,27 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, mc13xxx_lock(mc13xxx); - mc13xxx_irq_free(mc13xxx, MC13783_IRQ_ADCDONE, &adcdone_data); + mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_ADCDONE, &adcdone_data); if (ret > 0) for (i = 0; i < 4; ++i) { ret = mc13xxx_reg_read(mc13xxx, - MC13783_ADC2, &sample[i]); + MC13XXX_ADC2, &sample[i]); if (ret) break; } - if (mode == MC13783_ADC_MODE_TS) + if (mode == MC13XXX_ADC_MODE_TS) /* restore TSMOD */ - mc13xxx_reg_write(mc13xxx, MC13783_ADC0, old_adc0); + mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, old_adc0); - mc13783->adcflags &= ~MC13783_ADC_WORKING; + mc13xxx->adcflags &= ~MC13XXX_ADC_WORKING; out: mc13xxx_unlock(mc13xxx); return ret; } -EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion); +EXPORT_SYMBOL_GPL(mc13xxx_adc_do_conversion); static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, const char *format, void *pdata, size_t pdata_size) diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h index 7d0f3d6a0002..e7a3c0169f63 100644 --- a/include/linux/mfd/mc13783.h +++ b/include/linux/mfd/mc13783.h @@ -89,18 +89,15 @@ static inline int mc13783_irq_ack(struct mc13783 *mc13783, int irq) return mc13xxx_irq_ack(mc13783_to_mc13xxx(mc13783), irq); } -#define MC13783_ADC0 43 -#define MC13783_ADC0_ADREFEN (1 << 10) -#define MC13783_ADC0_ADREFMODE (1 << 11) -#define MC13783_ADC0_TSMOD0 (1 << 12) -#define MC13783_ADC0_TSMOD1 (1 << 13) -#define MC13783_ADC0_TSMOD2 (1 << 14) -#define MC13783_ADC0_ADINC1 (1 << 16) -#define MC13783_ADC0_ADINC2 (1 << 17) - -#define MC13783_ADC0_TSMOD_MASK (MC13783_ADC0_TSMOD0 | \ - MC13783_ADC0_TSMOD1 | \ - MC13783_ADC0_TSMOD2) +#define MC13783_ADC0 MC13XXX_ADC0 +#define MC13783_ADC0_ADREFEN MC13XXX_ADC0_ADREFEN +#define MC13783_ADC0_TSMOD0 MC13XXX_ADC0_TSMOD0 +#define MC13783_ADC0_TSMOD1 MC13XXX_ADC0_TSMOD1 +#define MC13783_ADC0_TSMOD2 MC13XXX_ADC0_TSMOD2 +#define MC13783_ADC0_ADINC1 MC13XXX_ADC0_ADINC1 +#define MC13783_ADC0_ADINC2 MC13XXX_ADC0_ADINC2 + +#define MC13783_ADC0_TSMOD_MASK MC13XXX_ADC0_TSMOD_MASK #define mc13783_regulator_init_data mc13xxx_regulator_init_data #define mc13783_regulator_platform_data mc13xxx_regulator_platform_data @@ -115,12 +112,16 @@ static inline int mc13783_irq_ack(struct mc13783 *mc13783, int irq) #define MC13783_USE_REGULATOR MC13XXX_USE_REGULATOR #define MC13783_USE_LED MC13XXX_USE_LED -#define MC13783_ADC_MODE_TS 1 -#define MC13783_ADC_MODE_SINGLE_CHAN 2 -#define MC13783_ADC_MODE_MULT_CHAN 3 +#define MC13783_ADC_MODE_TS MC13XXX_ADC_MODE_TS +#define MC13783_ADC_MODE_SINGLE_CHAN MC13XXX_ADC_MODE_SINGLE_CHAN +#define MC13783_ADC_MODE_MULT_CHAN MC13XXX_ADC_MODE_MULT_CHAN -int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, - unsigned int channel, unsigned int *sample); +static inline int mc13783_adc_do_conversion(struct mc13783 *mc13783, + unsigned int mode, unsigned int channel, unsigned int *sample) +{ + return mc13xxx_adc_do_conversion(mc13783_to_mc13xxx(mc13783), mode, + channel, sample); +} #define MC13783_REG_SW1A 0 diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index c064beaaccb7..6e7c0ac36d09 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -37,6 +37,9 @@ int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq); int mc13xxx_get_flags(struct mc13xxx *mc13xxx); +int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, + unsigned int mode, unsigned int channel, unsigned int *sample); + #define MC13XXX_IRQ_ADCDONE 0 #define MC13XXX_IRQ_ADCBISDONE 1 #define MC13XXX_IRQ_TS 2 @@ -150,4 +153,20 @@ struct mc13xxx_platform_data { struct mc13xxx_leds_platform_data *leds; }; +#define MC13XXX_ADC_MODE_TS 1 +#define MC13XXX_ADC_MODE_SINGLE_CHAN 2 +#define MC13XXX_ADC_MODE_MULT_CHAN 3 + +#define MC13XXX_ADC0 43 +#define MC13XXX_ADC0_ADREFEN (1 << 10) +#define MC13XXX_ADC0_TSMOD0 (1 << 12) +#define MC13XXX_ADC0_TSMOD1 (1 << 13) +#define MC13XXX_ADC0_TSMOD2 (1 << 14) +#define MC13XXX_ADC0_ADINC1 (1 << 16) +#define MC13XXX_ADC0_ADINC2 (1 << 17) + +#define MC13XXX_ADC0_TSMOD_MASK (MC13XXX_ADC0_TSMOD0 | \ + MC13XXX_ADC0_TSMOD1 | \ + MC13XXX_ADC0_TSMOD2) + #endif /* ifndef __LINUX_MFD_MC13XXX_H */ -- cgit v1.2.3 From b46880e57b4c513adeb2608c3700b352860b5662 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 24 Aug 2011 15:28:25 +0200 Subject: mfd: Remove mc13783 API functions and symbols Now that all in-tree users are fixed to use the more general mc13xxx API the obsolete stuff can go away. Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 12 ----- include/linux/mfd/mc13783.h | 112 -------------------------------------------- 2 files changed, 124 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 5ee5e64d586b..5f782adffc1d 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -30,16 +30,6 @@ struct mc13xxx { int adcflags; }; -struct mc13783 { - struct mc13xxx mc13xxx; -}; - -struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783) -{ - return &mc13783->mc13xxx; -} -EXPORT_SYMBOL(mc13783_to_mc13xxx); - #define MC13XXX_IRQSTAT0 0 #define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0) #define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1) @@ -558,8 +548,6 @@ static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx) return mc13xxx_chipname[devid->driver_data]; } -#include - int mc13xxx_get_flags(struct mc13xxx *mc13xxx) { struct mc13xxx_platform_data *pdata = diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h index e7a3c0169f63..a8eeda773a7b 100644 --- a/include/linux/mfd/mc13783.h +++ b/include/linux/mfd/mc13783.h @@ -12,118 +12,6 @@ #include -struct mc13783; - -struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783); - -static inline void mc13783_lock(struct mc13783 *mc13783) -{ - mc13xxx_lock(mc13783_to_mc13xxx(mc13783)); -} - -static inline void mc13783_unlock(struct mc13783 *mc13783) -{ - mc13xxx_unlock(mc13783_to_mc13xxx(mc13783)); -} - -static inline int mc13783_reg_read(struct mc13783 *mc13783, - unsigned int offset, u32 *val) -{ - return mc13xxx_reg_read(mc13783_to_mc13xxx(mc13783), offset, val); -} - -static inline int mc13783_reg_write(struct mc13783 *mc13783, - unsigned int offset, u32 val) -{ - return mc13xxx_reg_write(mc13783_to_mc13xxx(mc13783), offset, val); -} - -static inline int mc13783_reg_rmw(struct mc13783 *mc13783, - unsigned int offset, u32 mask, u32 val) -{ - return mc13xxx_reg_rmw(mc13783_to_mc13xxx(mc13783), offset, mask, val); -} - -static inline int mc13783_get_flags(struct mc13783 *mc13783) -{ - return mc13xxx_get_flags(mc13783_to_mc13xxx(mc13783)); -} - -static inline int mc13783_irq_request(struct mc13783 *mc13783, int irq, - irq_handler_t handler, const char *name, void *dev) -{ - return mc13xxx_irq_request(mc13783_to_mc13xxx(mc13783), irq, - handler, name, dev); -} - -static inline int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq, - irq_handler_t handler, const char *name, void *dev) -{ - return mc13xxx_irq_request_nounmask(mc13783_to_mc13xxx(mc13783), irq, - handler, name, dev); -} - -static inline int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev) -{ - return mc13xxx_irq_free(mc13783_to_mc13xxx(mc13783), irq, dev); -} - -static inline int mc13783_irq_mask(struct mc13783 *mc13783, int irq) -{ - return mc13xxx_irq_mask(mc13783_to_mc13xxx(mc13783), irq); -} - -static inline int mc13783_irq_unmask(struct mc13783 *mc13783, int irq) -{ - return mc13xxx_irq_unmask(mc13783_to_mc13xxx(mc13783), irq); -} -static inline int mc13783_irq_status(struct mc13783 *mc13783, int irq, - int *enabled, int *pending) -{ - return mc13xxx_irq_status(mc13783_to_mc13xxx(mc13783), - irq, enabled, pending); -} - -static inline int mc13783_irq_ack(struct mc13783 *mc13783, int irq) -{ - return mc13xxx_irq_ack(mc13783_to_mc13xxx(mc13783), irq); -} - -#define MC13783_ADC0 MC13XXX_ADC0 -#define MC13783_ADC0_ADREFEN MC13XXX_ADC0_ADREFEN -#define MC13783_ADC0_TSMOD0 MC13XXX_ADC0_TSMOD0 -#define MC13783_ADC0_TSMOD1 MC13XXX_ADC0_TSMOD1 -#define MC13783_ADC0_TSMOD2 MC13XXX_ADC0_TSMOD2 -#define MC13783_ADC0_ADINC1 MC13XXX_ADC0_ADINC1 -#define MC13783_ADC0_ADINC2 MC13XXX_ADC0_ADINC2 - -#define MC13783_ADC0_TSMOD_MASK MC13XXX_ADC0_TSMOD_MASK - -#define mc13783_regulator_init_data mc13xxx_regulator_init_data -#define mc13783_regulator_platform_data mc13xxx_regulator_platform_data -#define mc13783_led_platform_data mc13xxx_led_platform_data -#define mc13783_leds_platform_data mc13xxx_leds_platform_data - -#define mc13783_platform_data mc13xxx_platform_data -#define MC13783_USE_TOUCHSCREEN MC13XXX_USE_TOUCHSCREEN -#define MC13783_USE_CODEC MC13XXX_USE_CODEC -#define MC13783_USE_ADC MC13XXX_USE_ADC -#define MC13783_USE_RTC MC13XXX_USE_RTC -#define MC13783_USE_REGULATOR MC13XXX_USE_REGULATOR -#define MC13783_USE_LED MC13XXX_USE_LED - -#define MC13783_ADC_MODE_TS MC13XXX_ADC_MODE_TS -#define MC13783_ADC_MODE_SINGLE_CHAN MC13XXX_ADC_MODE_SINGLE_CHAN -#define MC13783_ADC_MODE_MULT_CHAN MC13XXX_ADC_MODE_MULT_CHAN - -static inline int mc13783_adc_do_conversion(struct mc13783 *mc13783, - unsigned int mode, unsigned int channel, unsigned int *sample) -{ - return mc13xxx_adc_do_conversion(mc13783_to_mc13xxx(mc13783), mode, - channel, sample); -} - - #define MC13783_REG_SW1A 0 #define MC13783_REG_SW1B 1 #define MC13783_REG_SW2A 2 -- cgit v1.2.3 From 8a0a8e8e42a4e30a1fc4c40205fa790e264d00f3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Sep 2011 16:43:36 +0200 Subject: mfd: remove CONFIG_MFD_SUPPORT We currently have two symbols to control compilation the MFD subsystem, MFD_SUPPORT and MFD_CORE. The MFD_SUPPORT is actually not required at all, it only hides the submenu when not set, with the effect that Kconfig warns about missing dependencies when another driver selects an MFD driver while MFD_SUPPORT is disabled. Turning the MFD submenu back from menuconfig into a plain menu simplifies the Kconfig syntax for those kinds of users and avoids the surprise when the menu suddenly appears because another driver was enabled that selects this symbol. Signed-off-by: Arnd Bergmann --- arch/arm/mach-omap2/Kconfig | 1 - drivers/gpio/Kconfig | 3 +-- drivers/i2c/busses/Kconfig | 1 - drivers/media/radio/Kconfig | 1 - drivers/mfd/Kconfig | 22 ++++------------------ 5 files changed, 5 insertions(+), 23 deletions(-) (limited to 'drivers/mfd') diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 57b66d590c52..1aee224c9e47 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -14,7 +14,6 @@ config ARCH_OMAP2PLUS_TYPICAL select SERIAL_OMAP_CONSOLE select I2C select I2C_OMAP - select MFD_SUPPORT select MENELAUS if ARCH_OMAP2 select TWL4030_CORE if ARCH_OMAP3 || ARCH_OMAP4 select TWL4030_POWER if ARCH_OMAP3 || ARCH_OMAP4 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..fbc5fd449a04 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -180,7 +180,7 @@ config GPIO_SCH config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" - depends on MFD_SUPPORT && PCI + depends on PCI select MFD_CORE select MFD_VX855 help @@ -417,7 +417,6 @@ config GPIO_TIMBERDALE config GPIO_RDC321X tristate "RDC R-321x GPIO support" depends on PCI - select MFD_SUPPORT select MFD_CORE select MFD_RDC321X help diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 646068e5100b..d625a484fa85 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -110,7 +110,6 @@ config I2C_I801 config I2C_ISCH tristate "Intel SCH SMBus 1.0" depends on PCI - select MFD_CORE select LPC_SCH help Say Y here if you want to use SMBus controller on the Intel SCH diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 52798a111e16..ccd5f0d8a012 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -426,7 +426,6 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" depends on I2C && VIDEO_V4L2 - select MFD_CORE select MFD_WL1273_CORE select FW_LOADER ---help--- diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9c1347dc7a4a..ac8bd4feb047 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2,23 +2,8 @@ # Multifunction miscellaneous devices # -menuconfig MFD_SUPPORT - bool "Multifunction device drivers" - depends on HAS_IOMEM - default y - help - Multifunction devices embed several functions (e.g. GPIOs, - touchscreens, keyboards, current regulators, power management chips, - etc...) in one single integrated circuit. They usually talk to the - main CPU through one or more IRQ lines and low speed data busses (SPI, - I2C, etc..). They appear as one single device to the main system - through the data bus and the MFD framework allows for sub devices - (a.k.a. functions) to appear as discrete platform devices. - MFDs are typically found on embedded platforms. - - This option alone does not add any kernel code. - -if MFD_SUPPORT +if HAS_IOMEM +menu "Multifunction device drivers" config MFD_CORE tristate @@ -772,7 +757,8 @@ config MFD_AAT2870_CORE additional drivers must be enabled in order to use the functionality of the device. -endif # MFD_SUPPORT +endmenu +endif menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 -- cgit v1.2.3 From f09ee0451a44a4e913a7c3cec3805508f7de6c54 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 5 Sep 2011 11:26:33 +0200 Subject: mfd: Fix twl4030 dependencies for audio codec The codec for Devkit8000 (TWL4030) was not detected except when build with CONFIG_SND_SOC_ALL_CODECS. twl-core.c still uses the CONFIG_TWL4030_CODEC for twl_has_codec(). In commit 57fe7251f5bfc4332f24479376de48a1e8ca6211 the CONFIG_TWL4030_CODEC was renamed into CONFIG_MFD_TWL4030_AUDIO, thatswhy the codec was not detected. This patch renames the CONFIG_ TWL4030_CODEC into CONFIG_MFD_TWL4030_AUDIO in twl-core.c. Signed-off-by: Thomas Weber Acked-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 01ecfeee6524..b8eef462737a 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -109,7 +109,7 @@ #define twl_has_watchdog() false #endif -#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\ +#if defined(CONFIG_MFD_TWL4030_AUDIO) || defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) ||\ defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE) #define twl_has_codec() true #else -- cgit v1.2.3 From 5da721c87aee3d94cfc48384073c2ec51a0b9a3b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 15 Sep 2011 18:54:53 +0200 Subject: mfd: Support software initiated shutdown of WM831x PMICs In systems where there is no hardware signal from the processor to the PMIC to initiate the final power off sequence we must initiate the shutdown with a register write to the PMIC. Support such systems in the driver. Since this may prevent a full shutdown of the system platform data is used to enable the feature. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 11 +++++++++++ drivers/mfd/wm831x-i2c.c | 8 ++++++++ drivers/mfd/wm831x-spi.c | 8 ++++++++ include/linux/mfd/wm831x/core.h | 3 +++ include/linux/mfd/wm831x/pdata.h | 3 +++ 5 files changed, 33 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 282e76ab678f..099b6104d150 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /* Current settings - values are 2*2^(reg_val/4) microamps. These are @@ -1305,6 +1306,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) mutex_init(&wm831x->io_lock); mutex_init(&wm831x->key_lock); dev_set_drvdata(wm831x->dev, wm831x); + wm831x->soft_shutdown = pdata->soft_shutdown; ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); if (ret < 0) { @@ -1604,6 +1606,15 @@ int wm831x_device_suspend(struct wm831x *wm831x) return 0; } +void wm831x_device_shutdown(struct wm831x *wm831x) +{ + if (wm831x->soft_shutdown) { + dev_info(wm831x->dev, "Initiating shutdown...\n"); + wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON, 0); + } +} +EXPORT_SYMBOL_GPL(wm831x_device_shutdown); + MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Brown"); diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index a06cbc739716..3ff8c13db2a8 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -109,6 +109,13 @@ static int wm831x_i2c_suspend(struct device *dev) return wm831x_device_suspend(wm831x); } +static void wm831x_i2c_shutdown(struct i2c_client *i2c) +{ + struct wm831x *wm831x = i2c_get_clientdata(i2c); + + wm831x_device_shutdown(wm831x); +} + static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8310", WM8310 }, { "wm8311", WM8311 }, @@ -133,6 +140,7 @@ static struct i2c_driver wm831x_i2c_driver = { }, .probe = wm831x_i2c_probe, .remove = wm831x_i2c_remove, + .shutdown = wm831x_i2c_shutdown, .id_table = wm831x_i2c_id, }; diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index eed8e4f7a5a1..8e8138ba0267 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -121,6 +121,13 @@ static int wm831x_spi_suspend(struct device *dev) return wm831x_device_suspend(wm831x); } +static void wm831x_spi_shutdown(struct spi_device *spi) +{ + struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + + wm831x_device_shutdown(wm831x); +} + static const struct dev_pm_ops wm831x_spi_pm = { .freeze = wm831x_spi_suspend, .suspend = wm831x_spi_suspend, @@ -146,6 +153,7 @@ static struct spi_driver wm8311_spi_driver = { }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), + .shutdown = wm831x_spi_shutdown, }; static struct spi_driver wm8312_spi_driver = { diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 8dda8ded5cda..fb3e84f92e90 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -374,6 +374,8 @@ struct wm831x { int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */ int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */ + bool soft_shutdown; + /* Chip revision based flags */ unsigned has_gpio_ena:1; /* Has GPIO enable bit */ unsigned has_cs_sts:1; /* Has current sink status bit */ @@ -412,6 +414,7 @@ int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg, int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq); void wm831x_device_exit(struct wm831x *wm831x); int wm831x_device_suspend(struct wm831x *wm831x); +void wm831x_device_shutdown(struct wm831x *wm831x); int wm831x_irq_init(struct wm831x *wm831x, int irq); void wm831x_irq_exit(struct wm831x *wm831x); void wm831x_auxadc_init(struct wm831x *wm831x); diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h index 0ba24599fe51..1d7a3f7b3b5d 100644 --- a/include/linux/mfd/wm831x/pdata.h +++ b/include/linux/mfd/wm831x/pdata.h @@ -123,6 +123,9 @@ struct wm831x_pdata { /** Disable the touchscreen */ bool disable_touch; + /** The driver should initiate a power off sequence during shutdown */ + bool soft_shutdown; + int irq_base; int gpio_base; int gpio_defaults[WM831X_GPIO_NUM]; -- cgit v1.2.3 From 49dcd070d0718a8b8db344d7b3d5e278362ecd86 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Tue, 6 Sep 2011 21:29:30 +0530 Subject: mfd: Set twl6030 irq_wake infrastructure up TWL6030 devices have an interrupt line which is connected to application processor like OMAP. These devices support multiple features such as MMC card detect, USB cable detect, RTC interrupt, etc. that must wake up the application processor. With this change, TWL6030 client drivers can make use of irq_wake() if the wakeup is desirable on it's irq events. Signed-off-by: Santosh Shilimkar Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6030-irq.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 776402566c9e..f94a04ca1d37 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -187,6 +187,13 @@ static inline void activate_irq(int irq) #endif } +int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) +{ + int twl_irq = (int)irq_get_chip_data(d->irq); + + return irq_set_irq_wake(twl_irq, on); +} + /*----------------------------------------------------------------------*/ static unsigned twl6030_irq_next; @@ -318,10 +325,12 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) twl6030_irq_chip = dummy_irq_chip; twl6030_irq_chip.name = "twl6030"; twl6030_irq_chip.irq_set_type = NULL; + twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake; for (i = irq_base; i < irq_end; i++) { irq_set_chip_and_handler(i, &twl6030_irq_chip, handle_simple_irq); + irq_set_chip_data(i, (void *)irq_num); activate_irq(i); } -- cgit v1.2.3 From f742b96e42f886a415633a1fed0db2bb09d2baa3 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Thu, 15 Sep 2011 21:52:09 +0200 Subject: mfd: Remove IRQF_DISABLED This flag is a NOOP and can be removed now. Signed-off-by: Yong Zhang Signed-off-by: Samuel Ortiz --- drivers/mfd/da903x.c | 2 +- drivers/mfd/menelaus.c | 2 +- drivers/mfd/twl6030-irq.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c index 2fadbaeb1cb1..1b79c37fd599 100644 --- a/drivers/mfd/da903x.c +++ b/drivers/mfd/da903x.c @@ -523,7 +523,7 @@ static int __devinit da903x_probe(struct i2c_client *client, chip->ops->read_events(chip, &tmp); ret = request_irq(client->irq, da903x_irq_handler, - IRQF_DISABLED | IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING, "da903x", chip); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index 9cee8e7f0bcb..ded870e22155 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -1226,7 +1226,7 @@ static int menelaus_probe(struct i2c_client *client, menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73); if (client->irq > 0) { - err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED, + err = request_irq(client->irq, menelaus_irq, 0, DRIVER_NAME, menelaus); if (err) { dev_dbg(&client->dev, "can't get IRQ %d, err %d\n", diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index f94a04ca1d37..a17b42360e52 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -341,7 +341,7 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) /* install an irq handler to demultiplex the TWL6030 interrupt */ init_completion(&irq_event); - status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED, + status = request_irq(irq_num, handle_twl6030_pih, 0, "TWL6030-PIH", &irq_event); if (status < 0) { pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status); -- cgit v1.2.3 From a980bf73ba8ff76e6fab0644513c1127d852d0a8 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 15 Sep 2011 21:56:08 +0200 Subject: mfd: Remove IRQF_DISABLED flag from twl4030-irq IRQF_DISABLED is a NOOP and is scheduled for removal. Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index ff16e9c3ba30..f062c8cc6c38 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -733,8 +733,8 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) } /* install an irq handler to demultiplex the TWL4030 interrupt */ - status = request_threaded_irq(irq_num, NULL, handle_twl4030_pih, - IRQF_DISABLED, "TWL4030-PIH", NULL); + status = request_threaded_irq(irq_num, NULL, handle_twl4030_pih, 0, + "TWL4030-PIH", NULL); if (status < 0) { pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status); goto fail_rqirq; -- cgit v1.2.3 From 4a2a734306a6e3cc7766d2117ec142de9262f217 Mon Sep 17 00:00:00 2001 From: Philippe Rétornaz Date: Fri, 22 Jul 2011 16:17:07 +0200 Subject: mfd: Unconditionally register mc13xxx regulator subdevice Signed-off-by: Philippe Rétornaz Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 5f782adffc1d..dd232ea73757 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -750,10 +750,8 @@ err_revision: if (pdata->flags & MC13XXX_USE_CODEC) mc13xxx_add_subdevice(mc13xxx, "%s-codec"); - if (pdata->flags & MC13XXX_USE_REGULATOR) { - mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", - &pdata->regulators, sizeof(pdata->regulators)); - } + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", + &pdata->regulators, sizeof(pdata->regulators)); if (pdata->flags & MC13XXX_USE_RTC) mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); -- cgit v1.2.3 From f20a5ea8e3aa8cb5cfe74bd8705dbb609a24783c Mon Sep 17 00:00:00 2001 From: Philippe Rétornaz Date: Fri, 22 Jul 2011 16:17:08 +0200 Subject: mfd: Implicitly register mc13xxx led subdevice A led subdevice is registered now iff the corresponding platform data is available. Without platform data the device isn't usable so this is a sound check. Signed-off-by: Philippe Rétornaz Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index dd232ea73757..e55b22136234 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -759,7 +759,7 @@ err_revision: if (pdata->flags & MC13XXX_USE_TOUCHSCREEN) mc13xxx_add_subdevice(mc13xxx, "%s-ts"); - if (pdata->flags & MC13XXX_USE_LED) + if (pdata->leds) mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", pdata->leds, sizeof(*pdata->leds)); -- cgit v1.2.3 From 30fc7ac3f62945a714d9842edae313a757efb49d Mon Sep 17 00:00:00 2001 From: Philippe Rétornaz Date: Sun, 18 Sep 2011 18:10:53 +0200 Subject: input: Add power button support for mc13783 This adds support for the power-on buttons of MC13783 PMIC. Signed-off-by: Philippe Rétornaz Acked-by: Dmitry Torokhov Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/input/misc/Kconfig | 10 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/mc13783-pwrbutton.c | 282 +++++++++++++++++++++++++++++++++ drivers/mfd/mc13xxx-core.c | 9 ++ include/linux/mfd/mc13xxx.h | 17 ++ 5 files changed, 319 insertions(+) create mode 100644 drivers/input/misc/mc13783-pwrbutton.c (limited to 'drivers/mfd') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index c9104bb4db06..7331229e2347 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -100,6 +100,16 @@ config INPUT_MAX8925_ONKEY To compile this driver as a module, choose M here: the module will be called max8925_onkey. +config INPUT_MC13783_PWRBUTTON + tristate "MC13783 ON buttons" + depends on MFD_MC13783 + help + Support the ON buttons of MC13783 PMIC as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called mc13783-pwrbutton. + config INPUT_MMA8450 tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer" depends on I2C diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 299ad5edba84..f54ccc2d9cb5 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o +obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c new file mode 100644 index 000000000000..09b052288657 --- /dev/null +++ b/drivers/input/misc/mc13783-pwrbutton.c @@ -0,0 +1,282 @@ +/** + * Copyright (C) 2011 Philippe Rétornaz + * + * Based on twl4030-pwrbutton driver by: + * Peter De Schrijver + * Felipe Balbi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mc13783_pwrb { + struct input_dev *pwr; + struct mc13xxx *mc13783; +#define MC13783_PWRB_B1_POL_INVERT (1 << 0) +#define MC13783_PWRB_B2_POL_INVERT (1 << 1) +#define MC13783_PWRB_B3_POL_INVERT (1 << 2) + int flags; + unsigned short keymap[3]; +}; + +#define MC13783_REG_INTERRUPT_SENSE_1 5 +#define MC13783_IRQSENSE1_ONOFD1S (1 << 3) +#define MC13783_IRQSENSE1_ONOFD2S (1 << 4) +#define MC13783_IRQSENSE1_ONOFD3S (1 << 5) + +#define MC13783_REG_POWER_CONTROL_2 15 +#define MC13783_POWER_CONTROL_2_ON1BDBNC 4 +#define MC13783_POWER_CONTROL_2_ON2BDBNC 6 +#define MC13783_POWER_CONTROL_2_ON3BDBNC 8 +#define MC13783_POWER_CONTROL_2_ON1BRSTEN (1 << 1) +#define MC13783_POWER_CONTROL_2_ON2BRSTEN (1 << 2) +#define MC13783_POWER_CONTROL_2_ON3BRSTEN (1 << 3) + +static irqreturn_t button_irq(int irq, void *_priv) +{ + struct mc13783_pwrb *priv = _priv; + int val; + + mc13xxx_irq_ack(priv->mc13783, irq); + mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val); + + switch (irq) { + case MC13783_IRQ_ONOFD1: + val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B1_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[0], val); + break; + + case MC13783_IRQ_ONOFD2: + val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B2_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[1], val); + break; + + case MC13783_IRQ_ONOFD3: + val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B3_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[2], val); + break; + } + + input_sync(priv->pwr); + + return IRQ_HANDLED; +} + +static int __devinit mc13783_pwrbutton_probe(struct platform_device *pdev) +{ + const struct mc13xxx_buttons_platform_data *pdata; + struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); + struct input_dev *pwr; + struct mc13783_pwrb *priv; + int err = 0; + int reg = 0; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + return -ENOMEM; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + goto free_input_dev; + } + + reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC; + reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC; + reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC; + + priv->pwr = pwr; + priv->mc13783 = mc13783; + + mc13xxx_lock(mc13783); + + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[0] = pdata->b1on_key; + if (pdata->b1on_key != KEY_RESERVED) + __set_bit(pdata->b1on_key, pwr->keybit); + + if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B1_POL_INVERT; + + if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1, + button_irq, "b1on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq\n"); + goto free_priv; + } + } + + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[1] = pdata->b2on_key; + if (pdata->b2on_key != KEY_RESERVED) + __set_bit(pdata->b2on_key, pwr->keybit); + + if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B2_POL_INVERT; + + if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2, + button_irq, "b2on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq\n"); + goto free_irq_b1; + } + } + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[2] = pdata->b3on_key; + if (pdata->b3on_key != KEY_RESERVED) + __set_bit(pdata->b3on_key, pwr->keybit); + + if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B3_POL_INVERT; + + if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3, + button_irq, "b3on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq: %d\n", err); + goto free_irq_b2; + } + } + + mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg); + + mc13xxx_unlock(mc13783); + + pwr->name = "mc13783_pwrbutton"; + pwr->phys = "mc13783_pwrbutton/input0"; + pwr->dev.parent = &pdev->dev; + + pwr->keycode = priv->keymap; + pwr->keycodemax = ARRAY_SIZE(priv->keymap); + pwr->keycodesize = sizeof(priv->keymap[0]); + __set_bit(EV_KEY, pwr->evbit); + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); + goto free_irq; + } + + platform_set_drvdata(pdev, priv); + + return 0; + +free_irq: + mc13xxx_lock(mc13783); + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv); + +free_irq_b2: + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv); + +free_irq_b1: + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv); + +free_priv: + mc13xxx_unlock(mc13783); + kfree(priv); + +free_input_dev: + input_free_device(pwr); + + return err; +} + +static int __devexit mc13783_pwrbutton_remove(struct platform_device *pdev) +{ + struct mc13783_pwrb *priv = platform_get_drvdata(pdev); + const struct mc13xxx_buttons_platform_data *pdata; + + pdata = dev_get_platdata(&pdev->dev); + + mc13xxx_lock(priv->mc13783); + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv); + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv); + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv); + + mc13xxx_unlock(priv->mc13783); + + input_unregister_device(priv->pwr); + kfree(priv); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +struct platform_driver mc13783_pwrbutton_driver = { + .probe = mc13783_pwrbutton_probe, + .remove = __devexit_p(mc13783_pwrbutton_remove), + .driver = { + .name = "mc13783-pwrbutton", + .owner = THIS_MODULE, + }, +}; + +static int __init mc13783_pwrbutton_init(void) +{ + return platform_driver_register(&mc13783_pwrbutton_driver); +} +module_init(mc13783_pwrbutton_init); + +static void __exit mc13783_pwrbutton_exit(void) +{ + platform_driver_unregister(&mc13783_pwrbutton_driver); +} +module_exit(mc13783_pwrbutton_exit); + +MODULE_ALIAS("platform:mc13783-pwrbutton"); +MODULE_DESCRIPTION("MC13783 Power Button"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Philippe Retornaz"); diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index e55b22136234..edcd397cae11 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -703,6 +703,11 @@ static int mc13xxx_probe(struct spi_device *spi) enum mc13xxx_id id; int ret; + if (!pdata) { + dev_err(&spi->dev, "invalid platform data\n"); + return -EINVAL; + } + mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL); if (!mc13xxx) return -ENOMEM; @@ -763,6 +768,10 @@ err_revision: mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", pdata->leds, sizeof(*pdata->leds)); + if (pdata->buttons) + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton", + pdata->buttons, sizeof(*pdata->buttons)); + return 0; } diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index 1acf9cbf5f25..3816c2fac0ad 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -140,6 +140,22 @@ struct mc13xxx_leds_platform_data { char tc3_period; }; +struct mc13xxx_buttons_platform_data { +#define MC13783_BUTTON_DBNC_0MS 0 +#define MC13783_BUTTON_DBNC_30MS 1 +#define MC13783_BUTTON_DBNC_150MS 2 +#define MC13783_BUTTON_DBNC_750MS 3 +#define MC13783_BUTTON_ENABLE (1 << 2) +#define MC13783_BUTTON_POL_INVERT (1 << 3) +#define MC13783_BUTTON_RESET_EN (1 << 4) + int b1on_flags; + unsigned short b1on_key; + int b2on_flags; + unsigned short b2on_key; + int b3on_flags; + unsigned short b3on_key; +}; + struct mc13xxx_platform_data { #define MC13XXX_USE_TOUCHSCREEN (1 << 0) #define MC13XXX_USE_CODEC (1 << 1) @@ -149,6 +165,7 @@ struct mc13xxx_platform_data { struct mc13xxx_regulator_platform_data regulators; struct mc13xxx_leds_platform_data *leds; + struct mc13xxx_buttons_platform_data *buttons; }; #define MC13XXX_ADC_MODE_TS 1 -- cgit v1.2.3 From 7583a213ec3bde3082547ee37ad96214513bc1cb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 16 Sep 2011 13:21:47 +0100 Subject: mfd: Simulate active high IRQs with wm831x In order to ease system integration provide a simulation of active high IRQs on the GPIOs by polling the GPIO status when an IRQ is generated. This isn't ideal on several fronts and will miss initially active IRQs in the current implementation but it should work well for most cases. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-irq.c | 22 +++++++++++++++++++++- include/linux/mfd/wm831x/core.h | 1 + 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index a10937cfff4c..f4747a4a9a93 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -420,12 +420,19 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) switch (type) { case IRQ_TYPE_EDGE_BOTH: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE; + wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_EDGE_RISING: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; + wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_EDGE_FALLING: wm831x->gpio_update[irq] = 0x10000; + wm831x->gpio_level[irq] = false; + break; + case IRQ_TYPE_LEVEL_HIGH: + wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; + wm831x->gpio_level[irq] = true; break; default: return -EINVAL; @@ -449,7 +456,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) { struct wm831x *wm831x = data; unsigned int i; - int primary, status_addr; + int primary, status_addr, ret; int status_regs[WM831X_NUM_IRQ_REGS] = { 0 }; int read[WM831X_NUM_IRQ_REGS] = { 0 }; int *status; @@ -507,6 +514,19 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) if (*status & wm831x_irqs[i].mask) handle_nested_irq(wm831x->irq_base + i); + + /* Simulate an edge triggered IRQ by polling the input + * status. This is sucky but improves interoperability. + */ + if (primary == WM831X_GP_INT && + wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) { + ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); + while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) { + handle_nested_irq(wm831x->irq_base + i); + ret = wm831x_reg_read(wm831x, + WM831X_GPIO_LEVEL); + } + } } out: diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index fb3e84f92e90..272f7fb5f8b8 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -385,6 +385,7 @@ struct wm831x { /* Used by the interrupt controller code to post writes */ int gpio_update[WM831X_NUM_GPIO_REGS]; + bool gpio_level[WM831X_NUM_GPIO_REGS]; struct mutex auxadc_lock; struct list_head auxadc_pending; -- cgit v1.2.3 From 429c9ecc76c096cab836060cd3219620437c3221 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 19 Sep 2011 16:19:09 +0200 Subject: mfd: Make jz4740_adc_driver static Signed-off-by: Lars-Peter Clausen Signed-off-by: Samuel Ortiz --- drivers/mfd/jz4740-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index 563654c9b19e..1e9ee533eacb 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -328,7 +328,7 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev) return 0; } -struct platform_driver jz4740_adc_driver = { +static struct platform_driver jz4740_adc_driver = { .probe = jz4740_adc_probe, .remove = __devexit_p(jz4740_adc_remove), .driver = { -- cgit v1.2.3 From 1f5a371c075a7101fe75a75cde5aad928460a42e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 21 Sep 2011 13:03:07 +0200 Subject: mfd: Add Intel MSIC driver Add support for Intel MSIC chip found on Intel Medfield platforms. This chip embeds several subdevices: audio, ADC, GPIO, power button, etc. The driver creates platform device for each subdevice. We also provide an MSIC register access API which should replace the more generic SCU IPC interface currently used. Existing drivers can choose whether they convert to this new API or stick with the SCU IPC interface. Signed-off-by: Mika Westerberg Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/intel_msic.c | 501 +++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/intel_msic.h | 456 +++++++++++++++++++++++++++++++++++++ 4 files changed, 967 insertions(+) create mode 100644 drivers/mfd/intel_msic.c create mode 100644 include/linux/mfd/intel_msic.h (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ac8bd4feb047..b01fbe27822d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -757,6 +757,15 @@ config MFD_AAT2870_CORE additional drivers must be enabled in order to use the functionality of the device. +config MFD_INTEL_MSIC + bool "Support for Intel MSIC" + depends on INTEL_SCU_IPC + select MFD_CORE + help + Select this option to enable access to Intel MSIC (Avatele + Passage) chip. This chip embeds audio, battery, GPIO, etc. + devices used in Intel Medfield platforms. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c58020303d18..7d53a7c530c8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -102,3 +102,4 @@ obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o +obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c new file mode 100644 index 000000000000..bd086b9e852e --- /dev/null +++ b/drivers/mfd/intel_msic.c @@ -0,0 +1,501 @@ +/* + * Driver for Intel MSIC + * + * Copyright (C) 2011, Intel Corporation + * Author: Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define MSIC_VENDOR(id) ((id >> 6) & 3) +#define MSIC_VERSION(id) (id & 0x3f) +#define MSIC_MAJOR(id) ('A' + ((id >> 3) & 7)) +#define MSIC_MINOR(id) (id & 7) + +/* + * MSIC interrupt tree is readable from SRAM at INTEL_MSIC_IRQ_PHYS_BASE. + * Since IRQ block starts from address 0x002 we need to substract that from + * the actual IRQ status register address. + */ +#define MSIC_IRQ_STATUS(x) (INTEL_MSIC_IRQ_PHYS_BASE + ((x) - 2)) +#define MSIC_IRQ_STATUS_ACCDET MSIC_IRQ_STATUS(INTEL_MSIC_ACCDET) + +/* + * The SCU hardware has limitation of 16 bytes per read/write buffer on + * Medfield. + */ +#define SCU_IPC_RWBUF_LIMIT 16 + +/** + * struct intel_msic - an MSIC MFD instance + * @pdev: pointer to the platform device + * @vendor: vendor ID + * @version: chip version + * @irq_base: base address of the mapped MSIC SRAM interrupt tree + */ +struct intel_msic { + struct platform_device *pdev; + unsigned vendor; + unsigned version; + void __iomem *irq_base; +}; + +static struct resource msic_touch_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_adc_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_battery_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_gpio_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_audio_resources[] = { + { + .name = "IRQ", + .flags = IORESOURCE_IRQ, + }, + /* + * We will pass IRQ_BASE to the driver now but this can be removed + * when/if the driver starts to use intel_msic_irq_read(). + */ + { + .name = "IRQ_BASE", + .flags = IORESOURCE_MEM, + .start = MSIC_IRQ_STATUS_ACCDET, + .end = MSIC_IRQ_STATUS_ACCDET, + }, +}; + +static struct resource msic_hdmi_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_thermal_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_power_btn_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_ocd_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +/* + * Devices that are part of the MSIC and are available via firmware + * populated SFI DEVS table. + */ +static struct mfd_cell msic_devs[] = { + [INTEL_MSIC_BLOCK_TOUCH] = { + .name = "msic_touch", + .num_resources = ARRAY_SIZE(msic_touch_resources), + .resources = msic_touch_resources, + }, + [INTEL_MSIC_BLOCK_ADC] = { + .name = "msic_adc", + .num_resources = ARRAY_SIZE(msic_adc_resources), + .resources = msic_adc_resources, + }, + [INTEL_MSIC_BLOCK_BATTERY] = { + .name = "msic_battery", + .num_resources = ARRAY_SIZE(msic_battery_resources), + .resources = msic_battery_resources, + }, + [INTEL_MSIC_BLOCK_GPIO] = { + .name = "msic_gpio", + .num_resources = ARRAY_SIZE(msic_gpio_resources), + .resources = msic_gpio_resources, + }, + [INTEL_MSIC_BLOCK_AUDIO] = { + .name = "msic_audio", + .num_resources = ARRAY_SIZE(msic_audio_resources), + .resources = msic_audio_resources, + }, + [INTEL_MSIC_BLOCK_HDMI] = { + .name = "msic_hdmi", + .num_resources = ARRAY_SIZE(msic_hdmi_resources), + .resources = msic_hdmi_resources, + }, + [INTEL_MSIC_BLOCK_THERMAL] = { + .name = "msic_thermal", + .num_resources = ARRAY_SIZE(msic_thermal_resources), + .resources = msic_thermal_resources, + }, + [INTEL_MSIC_BLOCK_POWER_BTN] = { + .name = "msic_power_btn", + .num_resources = ARRAY_SIZE(msic_power_btn_resources), + .resources = msic_power_btn_resources, + }, + [INTEL_MSIC_BLOCK_OCD] = { + .name = "msic_ocd", + .num_resources = ARRAY_SIZE(msic_ocd_resources), + .resources = msic_ocd_resources, + }, +}; + +/* + * Other MSIC related devices which are not directly available via SFI DEVS + * table. These can be pseudo devices, regulators etc. which are needed for + * different purposes. + * + * These devices appear only after the MSIC driver itself is initialized so + * we can guarantee that the SCU IPC interface is ready. + */ +static struct mfd_cell msic_other_devs[] = { + /* Audio codec in the MSIC */ + { + .id = -1, + .name = "sn95031", + }, +}; + +/** + * intel_msic_reg_read - read a single MSIC register + * @reg: register to read + * @val: register value is placed here + * + * Read a single register from MSIC. Returns %0 on success and negative + * errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_read(unsigned short reg, u8 *val) +{ + return intel_scu_ipc_ioread8(reg, val); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_read); + +/** + * intel_msic_reg_write - write a single MSIC register + * @reg: register to write + * @val: value to write to that register + * + * Write a single MSIC register. Returns 0 on success and negative + * errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_write(unsigned short reg, u8 val) +{ + return intel_scu_ipc_iowrite8(reg, val); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_write); + +/** + * intel_msic_reg_update - update a single MSIC register + * @reg: register to update + * @val: value to write to the register + * @mask: specifies which of the bits are updated (%0 = don't update, + * %1 = update) + * + * Perform an update to a register @reg. @mask is used to specify which + * bits are updated. Returns %0 in case of success and negative errno in + * case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask) +{ + return intel_scu_ipc_update_register(reg, val, mask); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_update); + +/** + * intel_msic_bulk_read - read an array of registers + * @reg: array of register addresses to read + * @buf: array where the read values are placed + * @count: number of registers to read + * + * Function reads @count registers from the MSIC using addresses passed in + * @reg. Read values are placed in @buf. Reads are performed atomically + * wrt. MSIC. + * + * Returns %0 in case of success and negative errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count) +{ + if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT)) + return -EINVAL; + + return intel_scu_ipc_readv(reg, buf, count); +} +EXPORT_SYMBOL_GPL(intel_msic_bulk_read); + +/** + * intel_msic_bulk_write - write an array of values to the MSIC registers + * @reg: array of registers to write + * @buf: values to write to each register + * @count: number of registers to write + * + * Function writes @count registers in @buf to MSIC. Writes are performed + * atomically wrt MSIC. Returns %0 in case of success and negative errno in + * case of failure. + * + * Function may sleep. + */ +int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count) +{ + if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT)) + return -EINVAL; + + return intel_scu_ipc_writev(reg, buf, count); +} +EXPORT_SYMBOL_GPL(intel_msic_bulk_write); + +/** + * intel_msic_irq_read - read a register from an MSIC interrupt tree + * @msic: MSIC instance + * @reg: interrupt register (between %INTEL_MSIC_IRQLVL1 and + * %INTEL_MSIC_RESETIRQ2) + * @val: value of the register is placed here + * + * This function can be used by an MSIC subdevice interrupt handler to read + * a register value from the MSIC interrupt tree. In this way subdevice + * drivers don't have to map in the interrupt tree themselves but can just + * call this function instead. + * + * Function doesn't sleep and is callable from interrupt context. + * + * Returns %-EINVAL if @reg is outside of the allowed register region. + */ +int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val) +{ + if (WARN_ON(reg < INTEL_MSIC_IRQLVL1 || reg > INTEL_MSIC_RESETIRQ2)) + return -EINVAL; + + *val = readb(msic->irq_base + (reg - INTEL_MSIC_IRQLVL1)); + return 0; +} +EXPORT_SYMBOL_GPL(intel_msic_irq_read); + +static int __devinit intel_msic_init_devices(struct intel_msic *msic) +{ + struct platform_device *pdev = msic->pdev; + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + int ret, i; + + if (pdata->gpio) { + struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO]; + + cell->platform_data = pdata->gpio; + cell->pdata_size = sizeof(*pdata->gpio); + } + + if (pdata->ocd) { + unsigned gpio = pdata->ocd->gpio; + + ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio"); + if (ret) { + dev_err(&pdev->dev, "failed to register OCD GPIO\n"); + return ret; + } + + ret = gpio_to_irq(gpio); + if (ret < 0) { + dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n"); + gpio_free(gpio); + return ret; + } + + /* Update the IRQ number for the OCD */ + pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret; + } + + for (i = 0; i < ARRAY_SIZE(msic_devs); i++) { + if (!pdata->irq[i]) + continue; + + ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL, + pdata->irq[i]); + if (ret) + goto fail; + } + + ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs, + ARRAY_SIZE(msic_other_devs), NULL, 0); + if (ret) + goto fail; + + return 0; + +fail: + mfd_remove_devices(&pdev->dev); + if (pdata->ocd) + gpio_free(pdata->ocd->gpio); + + return ret; +} + +static void __devexit intel_msic_remove_devices(struct intel_msic *msic) +{ + struct platform_device *pdev = msic->pdev; + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + + mfd_remove_devices(&pdev->dev); + + if (pdata->ocd) + gpio_free(pdata->ocd->gpio); +} + +static int __devinit intel_msic_probe(struct platform_device *pdev) +{ + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + struct intel_msic *msic; + struct resource *res; + u8 id0, id1; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data passed\n"); + return -EINVAL; + } + + /* First validate that we have an MSIC in place */ + ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID0, &id0); + if (ret) { + dev_err(&pdev->dev, "failed to identify the MSIC chip (ID0)\n"); + return -ENXIO; + } + + ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID1, &id1); + if (ret) { + dev_err(&pdev->dev, "failed to identify the MSIC chip (ID1)\n"); + return -ENXIO; + } + + if (MSIC_VENDOR(id0) != MSIC_VENDOR(id1)) { + dev_err(&pdev->dev, "invalid vendor ID: %x, %x\n", id0, id1); + return -ENXIO; + } + + msic = kzalloc(sizeof(*msic), GFP_KERNEL); + if (!msic) + return -ENOMEM; + + msic->vendor = MSIC_VENDOR(id0); + msic->version = MSIC_VERSION(id0); + msic->pdev = pdev; + + /* + * Map in the MSIC interrupt tree area in SRAM. This is exposed to + * the clients via intel_msic_irq_read(). + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get SRAM iomem resource\n"); + ret = -ENODEV; + goto fail_free_msic; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + ret = -EBUSY; + goto fail_free_msic; + } + + msic->irq_base = ioremap_nocache(res->start, resource_size(res)); + if (!msic->irq_base) { + dev_err(&pdev->dev, "failed to map SRAM memory\n"); + ret = -ENOMEM; + goto fail_release_region; + } + + platform_set_drvdata(pdev, msic); + + ret = intel_msic_init_devices(msic); + if (ret) { + dev_err(&pdev->dev, "failed to initialize MSIC devices\n"); + goto fail_unmap_mem; + } + + dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n", + MSIC_MAJOR(msic->version), MSIC_MINOR(msic->version), + msic->vendor); + + return 0; + +fail_unmap_mem: + iounmap(msic->irq_base); +fail_release_region: + release_mem_region(res->start, resource_size(res)); +fail_free_msic: + kfree(msic); + + return ret; +} + +static int __devexit intel_msic_remove(struct platform_device *pdev) +{ + struct intel_msic *msic = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + intel_msic_remove_devices(msic); + platform_set_drvdata(pdev, NULL); + iounmap(msic->irq_base); + release_mem_region(res->start, resource_size(res)); + kfree(msic); + + return 0; +} + +static struct platform_driver intel_msic_driver = { + .probe = intel_msic_probe, + .remove = __devexit_p(intel_msic_remove), + .driver = { + .name = "intel_msic", + .owner = THIS_MODULE, + }, +}; + +static int __init intel_msic_init(void) +{ + return platform_driver_register(&intel_msic_driver); +} +module_init(intel_msic_init); + +static void __exit intel_msic_exit(void) +{ + platform_driver_unregister(&intel_msic_driver); +} +module_exit(intel_msic_exit); + +MODULE_DESCRIPTION("Driver for Intel MSIC"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/intel_msic.h b/include/linux/mfd/intel_msic.h new file mode 100644 index 000000000000..439a7a617bc9 --- /dev/null +++ b/include/linux/mfd/intel_msic.h @@ -0,0 +1,456 @@ +/* + * include/linux/mfd/intel_msic.h - Core interface for Intel MSIC + * + * Copyright (C) 2011, Intel Corporation + * Author: Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_MFD_INTEL_MSIC_H__ +#define __LINUX_MFD_INTEL_MSIC_H__ + +/* ID */ +#define INTEL_MSIC_ID0 0x000 /* RO */ +#define INTEL_MSIC_ID1 0x001 /* RO */ + +/* IRQ */ +#define INTEL_MSIC_IRQLVL1 0x002 +#define INTEL_MSIC_ADC1INT 0x003 +#define INTEL_MSIC_CCINT 0x004 +#define INTEL_MSIC_PWRSRCINT 0x005 +#define INTEL_MSIC_PWRSRCINT1 0x006 +#define INTEL_MSIC_CHRINT 0x007 +#define INTEL_MSIC_CHRINT1 0x008 +#define INTEL_MSIC_RTCIRQ 0x009 +#define INTEL_MSIC_GPIO0LVIRQ 0x00a +#define INTEL_MSIC_GPIO1LVIRQ 0x00b +#define INTEL_MSIC_GPIOHVIRQ 0x00c +#define INTEL_MSIC_VRINT 0x00d +#define INTEL_MSIC_OCAUDIO 0x00e +#define INTEL_MSIC_ACCDET 0x00f +#define INTEL_MSIC_RESETIRQ1 0x010 +#define INTEL_MSIC_RESETIRQ2 0x011 +#define INTEL_MSIC_MADC1INT 0x012 +#define INTEL_MSIC_MCCINT 0x013 +#define INTEL_MSIC_MPWRSRCINT 0x014 +#define INTEL_MSIC_MPWRSRCINT1 0x015 +#define INTEL_MSIC_MCHRINT 0x016 +#define INTEL_MSIC_MCHRINT1 0x017 +#define INTEL_MSIC_RTCIRQMASK 0x018 +#define INTEL_MSIC_GPIO0LVIRQMASK 0x019 +#define INTEL_MSIC_GPIO1LVIRQMASK 0x01a +#define INTEL_MSIC_GPIOHVIRQMASK 0x01b +#define INTEL_MSIC_VRINTMASK 0x01c +#define INTEL_MSIC_OCAUDIOMASK 0x01d +#define INTEL_MSIC_ACCDETMASK 0x01e +#define INTEL_MSIC_RESETIRQ1MASK 0x01f +#define INTEL_MSIC_RESETIRQ2MASK 0x020 +#define INTEL_MSIC_IRQLVL1MSK 0x021 +#define INTEL_MSIC_PBCONFIG 0x03e +#define INTEL_MSIC_PBSTATUS 0x03f /* RO */ + +/* GPIO */ +#define INTEL_MSIC_GPIO0LV7CTLO 0x040 +#define INTEL_MSIC_GPIO0LV6CTLO 0x041 +#define INTEL_MSIC_GPIO0LV5CTLO 0x042 +#define INTEL_MSIC_GPIO0LV4CTLO 0x043 +#define INTEL_MSIC_GPIO0LV3CTLO 0x044 +#define INTEL_MSIC_GPIO0LV2CTLO 0x045 +#define INTEL_MSIC_GPIO0LV1CTLO 0x046 +#define INTEL_MSIC_GPIO0LV0CTLO 0x047 +#define INTEL_MSIC_GPIO1LV7CTLOS 0x048 +#define INTEL_MSIC_GPIO1LV6CTLO 0x049 +#define INTEL_MSIC_GPIO1LV5CTLO 0x04a +#define INTEL_MSIC_GPIO1LV4CTLO 0x04b +#define INTEL_MSIC_GPIO1LV3CTLO 0x04c +#define INTEL_MSIC_GPIO1LV2CTLO 0x04d +#define INTEL_MSIC_GPIO1LV1CTLO 0x04e +#define INTEL_MSIC_GPIO1LV0CTLO 0x04f +#define INTEL_MSIC_GPIO0LV7CTLI 0x050 +#define INTEL_MSIC_GPIO0LV6CTLI 0x051 +#define INTEL_MSIC_GPIO0LV5CTLI 0x052 +#define INTEL_MSIC_GPIO0LV4CTLI 0x053 +#define INTEL_MSIC_GPIO0LV3CTLI 0x054 +#define INTEL_MSIC_GPIO0LV2CTLI 0x055 +#define INTEL_MSIC_GPIO0LV1CTLI 0x056 +#define INTEL_MSIC_GPIO0LV0CTLI 0x057 +#define INTEL_MSIC_GPIO1LV7CTLIS 0x058 +#define INTEL_MSIC_GPIO1LV6CTLI 0x059 +#define INTEL_MSIC_GPIO1LV5CTLI 0x05a +#define INTEL_MSIC_GPIO1LV4CTLI 0x05b +#define INTEL_MSIC_GPIO1LV3CTLI 0x05c +#define INTEL_MSIC_GPIO1LV2CTLI 0x05d +#define INTEL_MSIC_GPIO1LV1CTLI 0x05e +#define INTEL_MSIC_GPIO1LV0CTLI 0x05f +#define INTEL_MSIC_PWM0CLKDIV1 0x061 +#define INTEL_MSIC_PWM0CLKDIV0 0x062 +#define INTEL_MSIC_PWM1CLKDIV1 0x063 +#define INTEL_MSIC_PWM1CLKDIV0 0x064 +#define INTEL_MSIC_PWM2CLKDIV1 0x065 +#define INTEL_MSIC_PWM2CLKDIV0 0x066 +#define INTEL_MSIC_PWM0DUTYCYCLE 0x067 +#define INTEL_MSIC_PWM1DUTYCYCLE 0x068 +#define INTEL_MSIC_PWM2DUTYCYCLE 0x069 +#define INTEL_MSIC_GPIO0HV3CTLO 0x06d +#define INTEL_MSIC_GPIO0HV2CTLO 0x06e +#define INTEL_MSIC_GPIO0HV1CTLO 0x06f +#define INTEL_MSIC_GPIO0HV0CTLO 0x070 +#define INTEL_MSIC_GPIO1HV3CTLO 0x071 +#define INTEL_MSIC_GPIO1HV2CTLO 0x072 +#define INTEL_MSIC_GPIO1HV1CTLO 0x073 +#define INTEL_MSIC_GPIO1HV0CTLO 0x074 +#define INTEL_MSIC_GPIO0HV3CTLI 0x075 +#define INTEL_MSIC_GPIO0HV2CTLI 0x076 +#define INTEL_MSIC_GPIO0HV1CTLI 0x077 +#define INTEL_MSIC_GPIO0HV0CTLI 0x078 +#define INTEL_MSIC_GPIO1HV3CTLI 0x079 +#define INTEL_MSIC_GPIO1HV2CTLI 0x07a +#define INTEL_MSIC_GPIO1HV1CTLI 0x07b +#define INTEL_MSIC_GPIO1HV0CTLI 0x07c + +/* SVID */ +#define INTEL_MSIC_SVIDCTRL0 0x080 +#define INTEL_MSIC_SVIDCTRL1 0x081 +#define INTEL_MSIC_SVIDCTRL2 0x082 +#define INTEL_MSIC_SVIDTXLASTPKT3 0x083 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT2 0x084 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT1 0x085 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT0 0x086 /* RO */ +#define INTEL_MSIC_SVIDPKTOUTBYTE3 0x087 +#define INTEL_MSIC_SVIDPKTOUTBYTE2 0x088 +#define INTEL_MSIC_SVIDPKTOUTBYTE1 0x089 +#define INTEL_MSIC_SVIDPKTOUTBYTE0 0x08a +#define INTEL_MSIC_SVIDRXVPDEBUG1 0x08b +#define INTEL_MSIC_SVIDRXVPDEBUG0 0x08c +#define INTEL_MSIC_SVIDRXLASTPKT3 0x08d /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT2 0x08e /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT1 0x08f /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT0 0x090 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS3 0x091 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS2 0x092 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS1 0x093 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS0 0x094 /* RO */ + +/* VREG */ +#define INTEL_MSIC_VCCLATCH 0x0c0 +#define INTEL_MSIC_VNNLATCH 0x0c1 +#define INTEL_MSIC_VCCCNT 0x0c2 +#define INTEL_MSIC_SMPSRAMP 0x0c3 +#define INTEL_MSIC_VNNCNT 0x0c4 +#define INTEL_MSIC_VNNAONCNT 0x0c5 +#define INTEL_MSIC_VCC122AONCNT 0x0c6 +#define INTEL_MSIC_V180AONCNT 0x0c7 +#define INTEL_MSIC_V500CNT 0x0c8 +#define INTEL_MSIC_VIHFCNT 0x0c9 +#define INTEL_MSIC_LDORAMP1 0x0ca +#define INTEL_MSIC_LDORAMP2 0x0cb +#define INTEL_MSIC_VCC108AONCNT 0x0cc +#define INTEL_MSIC_VCC108ASCNT 0x0cd +#define INTEL_MSIC_VCC108CNT 0x0ce +#define INTEL_MSIC_VCCA100ASCNT 0x0cf +#define INTEL_MSIC_VCCA100CNT 0x0d0 +#define INTEL_MSIC_VCC180AONCNT 0x0d1 +#define INTEL_MSIC_VCC180CNT 0x0d2 +#define INTEL_MSIC_VCC330CNT 0x0d3 +#define INTEL_MSIC_VUSB330CNT 0x0d4 +#define INTEL_MSIC_VCCSDIOCNT 0x0d5 +#define INTEL_MSIC_VPROG1CNT 0x0d6 +#define INTEL_MSIC_VPROG2CNT 0x0d7 +#define INTEL_MSIC_VEMMCSCNT 0x0d8 +#define INTEL_MSIC_VEMMC1CNT 0x0d9 +#define INTEL_MSIC_VEMMC2CNT 0x0da +#define INTEL_MSIC_VAUDACNT 0x0db +#define INTEL_MSIC_VHSPCNT 0x0dc +#define INTEL_MSIC_VHSNCNT 0x0dd +#define INTEL_MSIC_VHDMICNT 0x0de +#define INTEL_MSIC_VOTGCNT 0x0df +#define INTEL_MSIC_V1P35CNT 0x0e0 +#define INTEL_MSIC_V330AONCNT 0x0e1 + +/* RESET */ +#define INTEL_MSIC_CHIPCNTRL 0x100 /* WO */ +#define INTEL_MSIC_ERCONFIG 0x101 + +/* BURST */ +#define INTEL_MSIC_BATCURRENTLIMIT12 0x102 +#define INTEL_MSIC_BATTIMELIMIT12 0x103 +#define INTEL_MSIC_BATTIMELIMIT3 0x104 +#define INTEL_MSIC_BATTIMEDB 0x105 +#define INTEL_MSIC_BRSTCONFIGOUTPUTS 0x106 +#define INTEL_MSIC_BRSTCONFIGACTIONS 0x107 +#define INTEL_MSIC_BURSTCONTROLSTATUS 0x108 + +/* RTC */ +#define INTEL_MSIC_RTCB1 0x140 /* RO */ +#define INTEL_MSIC_RTCB2 0x141 /* RO */ +#define INTEL_MSIC_RTCB3 0x142 /* RO */ +#define INTEL_MSIC_RTCB4 0x143 /* RO */ +#define INTEL_MSIC_RTCOB1 0x144 +#define INTEL_MSIC_RTCOB2 0x145 +#define INTEL_MSIC_RTCOB3 0x146 +#define INTEL_MSIC_RTCOB4 0x147 +#define INTEL_MSIC_RTCAB1 0x148 +#define INTEL_MSIC_RTCAB2 0x149 +#define INTEL_MSIC_RTCAB3 0x14a +#define INTEL_MSIC_RTCAB4 0x14b +#define INTEL_MSIC_RTCWAB1 0x14c +#define INTEL_MSIC_RTCWAB2 0x14d +#define INTEL_MSIC_RTCWAB3 0x14e +#define INTEL_MSIC_RTCWAB4 0x14f +#define INTEL_MSIC_RTCSC1 0x150 +#define INTEL_MSIC_RTCSC2 0x151 +#define INTEL_MSIC_RTCSC3 0x152 +#define INTEL_MSIC_RTCSC4 0x153 +#define INTEL_MSIC_RTCSTATUS 0x154 /* RO */ +#define INTEL_MSIC_RTCCONFIG1 0x155 +#define INTEL_MSIC_RTCCONFIG2 0x156 + +/* CHARGER */ +#define INTEL_MSIC_BDTIMER 0x180 +#define INTEL_MSIC_BATTRMV 0x181 +#define INTEL_MSIC_VBUSDET 0x182 +#define INTEL_MSIC_VBUSDET1 0x183 +#define INTEL_MSIC_ADPHVDET 0x184 +#define INTEL_MSIC_ADPLVDET 0x185 +#define INTEL_MSIC_ADPDETDBDM 0x186 +#define INTEL_MSIC_LOWBATTDET 0x187 +#define INTEL_MSIC_CHRCTRL 0x188 +#define INTEL_MSIC_CHRCVOLTAGE 0x189 +#define INTEL_MSIC_CHRCCURRENT 0x18a +#define INTEL_MSIC_SPCHARGER 0x18b +#define INTEL_MSIC_CHRTTIME 0x18c +#define INTEL_MSIC_CHRCTRL1 0x18d +#define INTEL_MSIC_PWRSRCLMT 0x18e +#define INTEL_MSIC_CHRSTWDT 0x18f +#define INTEL_MSIC_WDTWRITE 0x190 /* WO */ +#define INTEL_MSIC_CHRSAFELMT 0x191 +#define INTEL_MSIC_SPWRSRCINT 0x192 /* RO */ +#define INTEL_MSIC_SPWRSRCINT1 0x193 /* RO */ +#define INTEL_MSIC_CHRLEDPWM 0x194 +#define INTEL_MSIC_CHRLEDCTRL 0x195 + +/* ADC */ +#define INTEL_MSIC_ADC1CNTL1 0x1c0 +#define INTEL_MSIC_ADC1CNTL2 0x1c1 +#define INTEL_MSIC_ADC1CNTL3 0x1c2 +#define INTEL_MSIC_ADC1OFFSETH 0x1c3 /* RO */ +#define INTEL_MSIC_ADC1OFFSETL 0x1c4 /* RO */ +#define INTEL_MSIC_ADC1ADDR0 0x1c5 +#define INTEL_MSIC_ADC1ADDR1 0x1c6 +#define INTEL_MSIC_ADC1ADDR2 0x1c7 +#define INTEL_MSIC_ADC1ADDR3 0x1c8 +#define INTEL_MSIC_ADC1ADDR4 0x1c9 +#define INTEL_MSIC_ADC1ADDR5 0x1ca +#define INTEL_MSIC_ADC1ADDR6 0x1cb +#define INTEL_MSIC_ADC1ADDR7 0x1cc +#define INTEL_MSIC_ADC1ADDR8 0x1cd +#define INTEL_MSIC_ADC1ADDR9 0x1ce +#define INTEL_MSIC_ADC1ADDR10 0x1cf +#define INTEL_MSIC_ADC1ADDR11 0x1d0 +#define INTEL_MSIC_ADC1ADDR12 0x1d1 +#define INTEL_MSIC_ADC1ADDR13 0x1d2 +#define INTEL_MSIC_ADC1ADDR14 0x1d3 +#define INTEL_MSIC_ADC1SNS0H 0x1d4 /* RO */ +#define INTEL_MSIC_ADC1SNS0L 0x1d5 /* RO */ +#define INTEL_MSIC_ADC1SNS1H 0x1d6 /* RO */ +#define INTEL_MSIC_ADC1SNS1L 0x1d7 /* RO */ +#define INTEL_MSIC_ADC1SNS2H 0x1d8 /* RO */ +#define INTEL_MSIC_ADC1SNS2L 0x1d9 /* RO */ +#define INTEL_MSIC_ADC1SNS3H 0x1da /* RO */ +#define INTEL_MSIC_ADC1SNS3L 0x1db /* RO */ +#define INTEL_MSIC_ADC1SNS4H 0x1dc /* RO */ +#define INTEL_MSIC_ADC1SNS4L 0x1dd /* RO */ +#define INTEL_MSIC_ADC1SNS5H 0x1de /* RO */ +#define INTEL_MSIC_ADC1SNS5L 0x1df /* RO */ +#define INTEL_MSIC_ADC1SNS6H 0x1e0 /* RO */ +#define INTEL_MSIC_ADC1SNS6L 0x1e1 /* RO */ +#define INTEL_MSIC_ADC1SNS7H 0x1e2 /* RO */ +#define INTEL_MSIC_ADC1SNS7L 0x1e3 /* RO */ +#define INTEL_MSIC_ADC1SNS8H 0x1e4 /* RO */ +#define INTEL_MSIC_ADC1SNS8L 0x1e5 /* RO */ +#define INTEL_MSIC_ADC1SNS9H 0x1e6 /* RO */ +#define INTEL_MSIC_ADC1SNS9L 0x1e7 /* RO */ +#define INTEL_MSIC_ADC1SNS10H 0x1e8 /* RO */ +#define INTEL_MSIC_ADC1SNS10L 0x1e9 /* RO */ +#define INTEL_MSIC_ADC1SNS11H 0x1ea /* RO */ +#define INTEL_MSIC_ADC1SNS11L 0x1eb /* RO */ +#define INTEL_MSIC_ADC1SNS12H 0x1ec /* RO */ +#define INTEL_MSIC_ADC1SNS12L 0x1ed /* RO */ +#define INTEL_MSIC_ADC1SNS13H 0x1ee /* RO */ +#define INTEL_MSIC_ADC1SNS13L 0x1ef /* RO */ +#define INTEL_MSIC_ADC1SNS14H 0x1f0 /* RO */ +#define INTEL_MSIC_ADC1SNS14L 0x1f1 /* RO */ +#define INTEL_MSIC_ADC1BV0H 0x1f2 /* RO */ +#define INTEL_MSIC_ADC1BV0L 0x1f3 /* RO */ +#define INTEL_MSIC_ADC1BV1H 0x1f4 /* RO */ +#define INTEL_MSIC_ADC1BV1L 0x1f5 /* RO */ +#define INTEL_MSIC_ADC1BV2H 0x1f6 /* RO */ +#define INTEL_MSIC_ADC1BV2L 0x1f7 /* RO */ +#define INTEL_MSIC_ADC1BV3H 0x1f8 /* RO */ +#define INTEL_MSIC_ADC1BV3L 0x1f9 /* RO */ +#define INTEL_MSIC_ADC1BI0H 0x1fa /* RO */ +#define INTEL_MSIC_ADC1BI0L 0x1fb /* RO */ +#define INTEL_MSIC_ADC1BI1H 0x1fc /* RO */ +#define INTEL_MSIC_ADC1BI1L 0x1fd /* RO */ +#define INTEL_MSIC_ADC1BI2H 0x1fe /* RO */ +#define INTEL_MSIC_ADC1BI2L 0x1ff /* RO */ +#define INTEL_MSIC_ADC1BI3H 0x200 /* RO */ +#define INTEL_MSIC_ADC1BI3L 0x201 /* RO */ +#define INTEL_MSIC_CCCNTL 0x202 +#define INTEL_MSIC_CCOFFSETH 0x203 /* RO */ +#define INTEL_MSIC_CCOFFSETL 0x204 /* RO */ +#define INTEL_MSIC_CCADCHA 0x205 /* RO */ +#define INTEL_MSIC_CCADCLA 0x206 /* RO */ + +/* AUDIO */ +#define INTEL_MSIC_AUDPLLCTRL 0x240 +#define INTEL_MSIC_DMICBUF0123 0x241 +#define INTEL_MSIC_DMICBUF45 0x242 +#define INTEL_MSIC_DMICGPO 0x244 +#define INTEL_MSIC_DMICMUX 0x245 +#define INTEL_MSIC_DMICCLK 0x246 +#define INTEL_MSIC_MICBIAS 0x247 +#define INTEL_MSIC_ADCCONFIG 0x248 +#define INTEL_MSIC_MICAMP1 0x249 +#define INTEL_MSIC_MICAMP2 0x24a +#define INTEL_MSIC_NOISEMUX 0x24b +#define INTEL_MSIC_AUDIOMUX12 0x24c +#define INTEL_MSIC_AUDIOMUX34 0x24d +#define INTEL_MSIC_AUDIOSINC 0x24e +#define INTEL_MSIC_AUDIOTXEN 0x24f +#define INTEL_MSIC_HSEPRXCTRL 0x250 +#define INTEL_MSIC_IHFRXCTRL 0x251 +#define INTEL_MSIC_VOICETXVOL 0x252 +#define INTEL_MSIC_SIDETONEVOL 0x253 +#define INTEL_MSIC_MUSICSHARVOL 0x254 +#define INTEL_MSIC_VOICETXCTRL 0x255 +#define INTEL_MSIC_HSMIXER 0x256 +#define INTEL_MSIC_DACCONFIG 0x257 +#define INTEL_MSIC_SOFTMUTE 0x258 +#define INTEL_MSIC_HSLVOLCTRL 0x259 +#define INTEL_MSIC_HSRVOLCTRL 0x25a +#define INTEL_MSIC_IHFLVOLCTRL 0x25b +#define INTEL_MSIC_IHFRVOLCTRL 0x25c +#define INTEL_MSIC_DRIVEREN 0x25d +#define INTEL_MSIC_LINEOUTCTRL 0x25e +#define INTEL_MSIC_VIB1CTRL1 0x25f +#define INTEL_MSIC_VIB1CTRL2 0x260 +#define INTEL_MSIC_VIB1CTRL3 0x261 +#define INTEL_MSIC_VIB1SPIPCM_1 0x262 +#define INTEL_MSIC_VIB1SPIPCM_2 0x263 +#define INTEL_MSIC_VIB1CTRL5 0x264 +#define INTEL_MSIC_VIB2CTRL1 0x265 +#define INTEL_MSIC_VIB2CTRL2 0x266 +#define INTEL_MSIC_VIB2CTRL3 0x267 +#define INTEL_MSIC_VIB2SPIPCM_1 0x268 +#define INTEL_MSIC_VIB2SPIPCM_2 0x269 +#define INTEL_MSIC_VIB2CTRL5 0x26a +#define INTEL_MSIC_BTNCTRL1 0x26b +#define INTEL_MSIC_BTNCTRL2 0x26c +#define INTEL_MSIC_PCM1TXSLOT01 0x26d +#define INTEL_MSIC_PCM1TXSLOT23 0x26e +#define INTEL_MSIC_PCM1TXSLOT45 0x26f +#define INTEL_MSIC_PCM1RXSLOT0123 0x270 +#define INTEL_MSIC_PCM1RXSLOT045 0x271 +#define INTEL_MSIC_PCM2TXSLOT01 0x272 +#define INTEL_MSIC_PCM2TXSLOT23 0x273 +#define INTEL_MSIC_PCM2TXSLOT45 0x274 +#define INTEL_MSIC_PCM2RXSLOT01 0x275 +#define INTEL_MSIC_PCM2RXSLOT23 0x276 +#define INTEL_MSIC_PCM2RXSLOT45 0x277 +#define INTEL_MSIC_PCM1CTRL1 0x278 +#define INTEL_MSIC_PCM1CTRL2 0x279 +#define INTEL_MSIC_PCM1CTRL3 0x27a +#define INTEL_MSIC_PCM2CTRL1 0x27b +#define INTEL_MSIC_PCM2CTRL2 0x27c + +/* HDMI */ +#define INTEL_MSIC_HDMIPUEN 0x280 +#define INTEL_MSIC_HDMISTATUS 0x281 /* RO */ + +/* Physical address of the start of the MSIC interrupt tree in SRAM */ +#define INTEL_MSIC_IRQ_PHYS_BASE 0xffff7fc0 + +/** + * struct intel_msic_gpio_pdata - platform data for the MSIC GPIO driver + * @gpio_base: base number for the GPIOs + */ +struct intel_msic_gpio_pdata { + unsigned gpio_base; +}; + +/** + * struct intel_msic_ocd_pdata - platform data for the MSIC OCD driver + * @gpio: GPIO number used for OCD interrupts + * + * The MSIC MFD driver converts @gpio into an IRQ number and passes it to + * the OCD driver as %IORESOURCE_IRQ. + */ +struct intel_msic_ocd_pdata { + unsigned gpio; +}; + +/* MSIC embedded blocks (subdevices) */ +enum intel_msic_block { + INTEL_MSIC_BLOCK_TOUCH, + INTEL_MSIC_BLOCK_ADC, + INTEL_MSIC_BLOCK_BATTERY, + INTEL_MSIC_BLOCK_GPIO, + INTEL_MSIC_BLOCK_AUDIO, + INTEL_MSIC_BLOCK_HDMI, + INTEL_MSIC_BLOCK_THERMAL, + INTEL_MSIC_BLOCK_POWER_BTN, + INTEL_MSIC_BLOCK_OCD, + + INTEL_MSIC_BLOCK_LAST, +}; + +/** + * struct intel_msic_platform_data - platform data for the MSIC driver + * @irq: array of interrupt numbers, one per device. If @irq is set to %0 + * for a given block, the corresponding platform device is not + * created. For devices which don't have an interrupt, use %0xff + * (this is same as in SFI spec). + * @gpio: platform data for the MSIC GPIO driver + * @ocd: platform data for the MSIC OCD driver + * + * Once the MSIC driver is initialized, the register interface is ready to + * use. All the platform devices for subdevices are created after the + * register interface is ready so that we can guarantee its availability to + * the subdevice drivers. + * + * Interrupt numbers are passed to the subdevices via %IORESOURCE_IRQ + * resources of the created platform device. + */ +struct intel_msic_platform_data { + int irq[INTEL_MSIC_BLOCK_LAST]; + struct intel_msic_gpio_pdata *gpio; + struct intel_msic_ocd_pdata *ocd; +}; + +struct intel_msic; + +extern int intel_msic_reg_read(unsigned short reg, u8 *val); +extern int intel_msic_reg_write(unsigned short reg, u8 val); +extern int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask); +extern int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count); +extern int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count); + +/* + * pdev_to_intel_msic - gets an MSIC instance from the platform device + * @pdev: platform device pointer + * + * The client drivers need to have pointer to the MSIC instance if they + * want to call intel_msic_irq_read(). This macro can be used for + * convenience to get the MSIC pointer from @pdev where needed. This is + * _only_ valid for devices which are managed by the MSIC. + */ +#define pdev_to_intel_msic(pdev) (dev_get_drvdata(pdev->dev.parent)) + +extern int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, + u8 *val); + +#endif /* __LINUX_MFD_INTEL_MSIC_H__ */ -- cgit v1.2.3 From 2f8491d321d9313dfd854001dd196fd7835b6ac2 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 26 Sep 2011 10:47:05 +0200 Subject: mfd: Fix ab3100 initconst section Signed-off-by: Andi Kleen Signed-off-by: Samuel Ortiz --- drivers/mfd/ab3100-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index a20e1c41bed2..4f5725508ac0 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -809,7 +809,7 @@ struct ab_family_id { char *name; }; -static const struct ab_family_id ids[] __devinitdata = { +static const struct ab_family_id ids[] __devinitconst = { /* AB3100 */ { .id = 0xc0, -- cgit v1.2.3 From 677df0c9012ca3a6e0081f29f81506e5578d74f3 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Tue, 20 Sep 2011 14:13:06 -0700 Subject: mfd: Do not leak init_data in tps65912_device_init() We neglect to free init_data on successful exit. I also moved two assignments to just before they are needed. This avoids doing them in case we hit an earlier error exit from the function. Signed-off-by: Jesper Juhl Cc: Margarita Olaya Cabrera Signed-off-by: Andrew Morton Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65912-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c index 955bc00e4b20..5fec23a9ac03 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -131,9 +131,6 @@ int tps65912_device_init(struct tps65912 *tps65912) if (init_data == NULL) return -ENOMEM; - init_data->irq = pmic_plat_data->irq; - init_data->irq_base = pmic_plat_data->irq; - mutex_init(&tps65912->io_mutex); dev_set_drvdata(tps65912->dev, tps65912); @@ -153,10 +150,13 @@ int tps65912_device_init(struct tps65912 *tps65912) if (ret < 0) goto err; + init_data->irq = pmic_plat_data->irq; + init_data->irq_base = pmic_plat_data->irq; ret = tps65912_irq_init(tps65912, init_data->irq, init_data); if (ret < 0) goto err; + kfree(init_data); return ret; err: -- cgit v1.2.3 From 3d5e2cabf11a65685e5067382ba4c4a76f18fcb7 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Thu, 22 Sep 2011 08:22:18 +0200 Subject: mfd: ab5500 chip register access The analog baseband chip ab5500 is a multi functional chip containing regulators, charging, gpio, USB and accessory detect. It also contain various multimedia functionalities like digital encoder and audio codec. The core driver added with this patch provides register access via i2c via PRCMU. Event handling implemented as irq_chip will come in future patches since it depends on PRCMU functionality not yet implemented. Signed-off-by: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/ab5500-core.c | 2312 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/ab5500/ab5500.h | 140 +++ include/linux/mfd/abx500.h | 4 +- 5 files changed, 2464 insertions(+), 2 deletions(-) create mode 100644 drivers/mfd/ab5500-core.c create mode 100644 include/linux/mfd/ab5500/ab5500.h (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b01fbe27822d..8594de8b86a0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -563,6 +563,15 @@ config EZX_PCAP This enables the PCAP ASIC present on EZX Phones. This is needed for MMC, TouchScreen, Sound, USB, etc.. +config AB5500_CORE + bool "ST-Ericsson AB5500 Mixed Signal Power Management chip" + depends on ABX500_CORE && MFD_DB5500_PRCMU + select MFD_CORE + help + Select this option to enable access to AB5500 power management + chip. This connects to the db5500 chip via the I2C bus via PRCMU. + This chip embeds various other multimedia funtionalities as well. + config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7d53a7c530c8..457fed8474cf 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB3550_CORE) += ab3550-core.o +obj-$(CONFIG_AB5500_CORE) += ab5500-core.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c new file mode 100644 index 000000000000..afec0f2ede42 --- /dev/null +++ b/drivers/mfd/ab5500-core.c @@ -0,0 +1,2312 @@ +/* + * Copyright (C) 2007-2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Low-level core for exclusive access to the AB5500 IC on the I2C bus + * and some basic chip-configuration. + * Author: Bengt Jonsson + * Author: Mattias Nilsson + * Author: Mattias Wallin + * Author: Rickard Andersson + * Author: Karl Komierowski + * Author: Bibek Basu + * + * TODO: Event handling with irq_chip. Waiting for PRCMU fw support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AB5500_NUM_EVENT_REG 23 +#define AB5500_IT_LATCH0_REG 0x40 +#define AB5500_IT_MASK0_REG 0x60 + +/* Read/write operation values. */ +#define AB5500_PERM_RD (0x01) +#define AB5500_PERM_WR (0x02) + +/* Read/write permissions. */ +#define AB5500_PERM_RO (AB5500_PERM_RD) +#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR) + +#define AB5500_MASK_BASE (0x60) +#define AB5500_MASK_END (0x79) +#define AB5500_CHIP_ID (0x20) + +/** + * struct ab5500_bank + * @slave_addr: I2C slave_addr found in AB5500 specification + * @name: Documentation name of the bank. For reference + */ +struct ab5500_bank { + u8 slave_addr; + const char *name; +}; + +static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = { + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"}, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"}, + [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"}, + [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"}, + [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"}, + [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"}, + [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"}, + [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"}, + [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"}, + [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"}, + [AB5500_BANK_FG_BATTCOM_ACC] = { + AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"}, + [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"}, + [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"}, + [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"}, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"}, +}; + +/** + * struct ab5500_reg_range + * @first: the first address of the range + * @last: the last address of the range + * @perm: access permissions for the range + */ +struct ab5500_reg_range { + u8 first; + u8 last; + u8 perm; +}; + +/** + * struct ab5500_i2c_ranges + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_ranges { + u8 nranges; + u8 bankid; + const struct ab5500_reg_range *range; +}; + +/** + * struct ab5500_i2c_banks + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_banks { + u8 nbanks; + const struct ab5500_i2c_ranges *bank; +}; + +/* + * Permissible register ranges for reading and writing per device and bank. + * + * The ranges must be listed in increasing address order, and no overlaps are + * allowed. It is assumed that write permission implies read permission + * (i.e. only RO and RW permissions should be used). Ranges with write + * permission must not be split up. + */ + +#define NO_RANGE {.count = 0, .range = NULL,} +static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = { + [AB5500_DEVID_USB] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_USB, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x83, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x87, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8B, + .last = 0x8B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x91, + .last = 0x92, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x93, + .last = 0x93, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x94, + .last = 0x94, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xA8, + .last = 0xB0, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB2, + .last = 0xB2, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB4, + .last = 0xBC, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xBF, + .last = 0xBF, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xC1, + .last = 0xC5, + .perm = AB5500_PERM_RO, + }, + }, + }, + }, + }, + [AB5500_DEVID_ADC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_ADC, + .nranges = 6, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x1F, + .last = 0x22, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x23, + .last = 0x24, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x26, + .last = 0x2D, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x37, + .last = 0x57, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x58, + .last = 0x58, + .perm = AB5500_PERM_RO, + }, + }, + }, + }, + }, + [AB5500_DEVID_LEDS] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_LED, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_VIDEO] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_VDENC, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x09, + .last = 0x09, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0A, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x15, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1B, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x27, + .last = 0x2C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x41, + .last = 0x41, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x45, + .last = 0x5B, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x5D, + .last = 0x5D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x69, + .last = 0x69, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x6C, + .last = 0x6D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x81, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_REGULATORS] = { + .nbanks = 2, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_STARTUP, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1F, + .last = 0x1F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x2E, + .last = 0x2E, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x51, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x61, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x66, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8C, + .last = 0x96, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xAA, + .last = 0xB4, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xB7, + .last = 0xBF, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xC1, + .last = 0xCA, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xD3, + .last = 0xE0, + .perm = AB5500_PERM_RW, + }, + }, + }, + { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_SIM] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_RTC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_RTC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x04, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x06, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_CHARGER] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_CHG, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x11, + .last = 0x11, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x12, + .last = 0x1B, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_FUELGAUGE] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_FG_BATTCOM_ACC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0C, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_VIBRATOR] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_VIBRA, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xFE, + .last = 0xFE, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_CODEC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_AUDIO_HEADSETUSB, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x48, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xEB, + .last = 0xFB, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_POWER] = { + .nbanks = 2, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_STARTUP, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x30, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + }, + }, + { + .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, +}; + +#define AB5500_IRQ(bank, bit) ((bank) * 8 + (bit)) + +/* I appologize for the resource names beeing a mix of upper case + * and lower case but I want them to be exact as the documentation */ +static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = { + [AB5500_DEVID_LEDS] = { + .name = "ab5500-leds", + .id = AB5500_DEVID_LEDS, + }, + [AB5500_DEVID_POWER] = { + .name = "ab5500-power", + .id = AB5500_DEVID_POWER, + }, + [AB5500_DEVID_REGULATORS] = { + .name = "ab5500-regulator", + .id = AB5500_DEVID_REGULATORS, + }, + [AB5500_DEVID_SIM] = { + .name = "ab5500-sim", + .id = AB5500_DEVID_SIM, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "SIMOFF", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(2, 0), /*rising*/ + .end = AB5500_IRQ(2, 1), /*falling*/ + }, + }, + }, + [AB5500_DEVID_RTC] = { + .name = "ab5500-rtc", + .id = AB5500_DEVID_RTC, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "RTC_Alarm", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 7), + .end = AB5500_IRQ(1, 7), + } + }, + }, + [AB5500_DEVID_CHARGER] = { + .name = "ab5500-charger", + .id = AB5500_DEVID_CHARGER, + }, + [AB5500_DEVID_ADC] = { + .name = "ab5500-adc", + .id = AB5500_DEVID_ADC, + .num_resources = 10, + .resources = (struct resource[]) { + { + .name = "TRIGGER-0", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 0), + .end = AB5500_IRQ(0, 0), + }, + { + .name = "TRIGGER-1", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 1), + .end = AB5500_IRQ(0, 1), + }, + { + .name = "TRIGGER-2", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 2), + .end = AB5500_IRQ(0, 2), + }, + { + .name = "TRIGGER-3", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 3), + .end = AB5500_IRQ(0, 3), + }, + { + .name = "TRIGGER-4", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 4), + .end = AB5500_IRQ(0, 4), + }, + { + .name = "TRIGGER-5", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 5), + .end = AB5500_IRQ(0, 5), + }, + { + .name = "TRIGGER-6", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 6), + .end = AB5500_IRQ(0, 6), + }, + { + .name = "TRIGGER-7", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 7), + .end = AB5500_IRQ(0, 7), + }, + { + .name = "TRIGGER-VBAT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 8), + .end = AB5500_IRQ(0, 8), + }, + { + .name = "TRIGGER-VBAT-TXON", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 9), + .end = AB5500_IRQ(0, 9), + }, + }, + }, + [AB5500_DEVID_FUELGAUGE] = { + .name = "ab5500-fuelgauge", + .id = AB5500_DEVID_FUELGAUGE, + .num_resources = 6, + .resources = (struct resource[]) { + { + .name = "Batt_attach", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 5), + .end = AB5500_IRQ(7, 5), + }, + { + .name = "Batt_removal", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 6), + .end = AB5500_IRQ(7, 6), + }, + { + .name = "UART_framing", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 7), + .end = AB5500_IRQ(7, 7), + }, + { + .name = "UART_overrun", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 0), + .end = AB5500_IRQ(8, 0), + }, + { + .name = "UART_Rdy_RX", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 1), + .end = AB5500_IRQ(8, 1), + }, + { + .name = "UART_Rdy_TX", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 2), + .end = AB5500_IRQ(8, 2), + }, + }, + }, + [AB5500_DEVID_VIBRATOR] = { + .name = "ab5500-vibrator", + .id = AB5500_DEVID_VIBRATOR, + }, + [AB5500_DEVID_CODEC] = { + .name = "ab5500-codec", + .id = AB5500_DEVID_CODEC, + .num_resources = 3, + .resources = (struct resource[]) { + { + .name = "audio_spkr1_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 5), + .end = AB5500_IRQ(9, 5), + }, + { + .name = "audio_plllocked", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 6), + .end = AB5500_IRQ(9, 6), + }, + { + .name = "audio_spkr2_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 4), + .end = AB5500_IRQ(17, 4), + }, + }, + }, + [AB5500_DEVID_USB] = { + .name = "ab5500-usb", + .id = AB5500_DEVID_USB, + .num_resources = 36, + .resources = (struct resource[]) { + { + .name = "Link_Update", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(22, 1), + .end = AB5500_IRQ(22, 1), + }, + { + .name = "DCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 3), + .end = AB5500_IRQ(8, 4), + }, + { + .name = "VBUS_R", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 5), + .end = AB5500_IRQ(8, 5), + }, + { + .name = "VBUS_F", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 6), + .end = AB5500_IRQ(8, 6), + }, + { + .name = "CHGstate_10_PCVBUSchg", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 7), + .end = AB5500_IRQ(8, 7), + }, + { + .name = "DCIOreverse_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 0), + .end = AB5500_IRQ(9, 0), + }, + { + .name = "USBCharDetDone", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 1), + .end = AB5500_IRQ(9, 1), + }, + { + .name = "DCIO_no_limit", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 2), + .end = AB5500_IRQ(9, 2), + }, + { + .name = "USB_suspend", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 3), + .end = AB5500_IRQ(9, 3), + }, + { + .name = "DCIOreverse_fwdcurrent", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 4), + .end = AB5500_IRQ(9, 4), + }, + { + .name = "Vbus_Imeasmax_change", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 5), + .end = AB5500_IRQ(9, 6), + }, + { + .name = "OVV", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 5), + .end = AB5500_IRQ(14, 5), + }, + { + .name = "USBcharging_NOTok", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 3), + .end = AB5500_IRQ(15, 3), + }, + { + .name = "usb_adp_sensoroff", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 6), + .end = AB5500_IRQ(15, 6), + }, + { + .name = "usb_adp_probeplug", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 7), + .end = AB5500_IRQ(15, 7), + }, + { + .name = "usb_adp_sinkerror", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 0), + .end = AB5500_IRQ(16, 6), + }, + { + .name = "usb_adp_sourceerror", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 1), + .end = AB5500_IRQ(16, 1), + }, + { + .name = "usb_idgnd_r", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 2), + .end = AB5500_IRQ(16, 2), + }, + { + .name = "usb_idgnd_f", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 3), + .end = AB5500_IRQ(16, 3), + }, + { + .name = "usb_iddetR1", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 4), + .end = AB5500_IRQ(16, 5), + }, + { + .name = "usb_iddetR2", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 6), + .end = AB5500_IRQ(16, 7), + }, + { + .name = "usb_iddetR3", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 0), + .end = AB5500_IRQ(17, 1), + }, + { + .name = "usb_iddetR4", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 2), + .end = AB5500_IRQ(17, 3), + }, + { + .name = "CharTempWindowOk", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 7), + .end = AB5500_IRQ(18, 0), + }, + { + .name = "USB_SprDetect", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 1), + .end = AB5500_IRQ(18, 1), + }, + { + .name = "usb_adp_probe_unplug", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 2), + .end = AB5500_IRQ(18, 2), + }, + { + .name = "VBUSChDrop", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 3), + .end = AB5500_IRQ(18, 4), + }, + { + .name = "dcio_char_rec_done", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 5), + .end = AB5500_IRQ(18, 5), + }, + { + .name = "Charging_stopped_by_temp", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 6), + .end = AB5500_IRQ(18, 6), + }, + { + .name = "CHGstate_11_SafeModeVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 1), + .end = AB5500_IRQ(21, 2), + }, + { + .name = "CHGstate_12_comletedVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 2), + .end = AB5500_IRQ(21, 2), + }, + { + .name = "CHGstate_13_completedVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 3), + .end = AB5500_IRQ(21, 3), + }, + { + .name = "CHGstate_14_FullChgDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 4), + .end = AB5500_IRQ(21, 4), + }, + { + .name = "CHGstate_15_SafeModeDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 5), + .end = AB5500_IRQ(21, 5), + }, + { + .name = "CHGstate_16_OFFsuspendDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 6), + .end = AB5500_IRQ(21, 6), + }, + { + .name = "CHGstate_17_completedDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 7), + .end = AB5500_IRQ(21, 7), + }, + }, + }, + [AB5500_DEVID_OTP] = { + .name = "ab5500-otp", + .id = AB5500_DEVID_OTP, + }, + [AB5500_DEVID_VIDEO] = { + .name = "ab5500-video", + .id = AB5500_DEVID_VIDEO, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "plugTVdet", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(22, 2), + .end = AB5500_IRQ(22, 2), + }, + }, + }, + [AB5500_DEVID_DBIECI] = { + .name = "ab5500-dbieci", + .id = AB5500_DEVID_DBIECI, + .num_resources = 10, + .resources = (struct resource[]) { + { + .name = "COLL", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 0), + .end = AB5500_IRQ(14, 0), + }, + { + .name = "RESERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 1), + .end = AB5500_IRQ(14, 1), + }, + { + .name = "FRAERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 2), + .end = AB5500_IRQ(14, 2), + }, + { + .name = "COMERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 3), + .end = AB5500_IRQ(14, 3), + }, + { + .name = "BSI_indicator", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 4), + .end = AB5500_IRQ(14, 4), + }, + { + .name = "SPDSET", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 6), + .end = AB5500_IRQ(14, 6), + }, + { + .name = "DSENT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 7), + .end = AB5500_IRQ(14, 7), + }, + { + .name = "DREC", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 0), + .end = AB5500_IRQ(15, 0), + }, + { + .name = "ACCINT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 1), + .end = AB5500_IRQ(15, 1), + }, + { + .name = "NOPINT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 2), + .end = AB5500_IRQ(15, 2), + }, + }, + }, + [AB5500_DEVID_ONSWA] = { + .name = "ab5500-onswa", + .id = AB5500_DEVID_ONSWA, + .num_resources = 2, + .resources = (struct resource[]) { + { + .name = "ONSWAn_rising", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 3), + .end = AB5500_IRQ(1, 3), + }, + { + .name = "ONSWAn_falling", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 4), + .end = AB5500_IRQ(1, 4), + }, + }, + }, +}; + +/* + * Functionality for getting/setting register values. + */ +static int get_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, + u8 *value) +{ + int err; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, reg, value, 1); + + mutex_unlock(&ab->access_mutex); + return err; +} + +static int get_register_page_interruptible(struct ab5500 *ab, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + int err; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + while (numregs) { + /* The hardware limit for get page is 4 */ + u8 curnum = min_t(u8, numregs, 4u); + + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, + first_reg, regvals, curnum); + if (err) + goto out; + + numregs -= curnum; + first_reg += curnum; + regvals += curnum; + } + +out: + mutex_unlock(&ab->access_mutex); + return err; +} + +static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + int err = 0; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + if (bitmask) { + u8 buf; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + if (bitmask == 0xFF) /* No need to read in this case. */ + buf = bitvalues; + else { /* Read and modify the register value. */ + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, + reg, &buf, 1); + if (err) + return err; + + buf = ((~bitmask & buf) | (bitmask & bitvalues)); + } + /* Write the new value. */ + err = db5500_prcmu_abb_write(bankinfo[bank].slave_addr, reg, + &buf, 1); + + mutex_unlock(&ab->access_mutex); + } + return err; +} + +static int +set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value) +{ + return mask_and_set_register_interruptible(ab, bank, reg, 0xff, value); +} + +/* + * Read/write permission checking functions. + */ +static const struct ab5500_i2c_ranges *get_bankref(u8 devid, u8 bank) +{ + u8 i; + + if (devid < AB5500_NUM_DEVICES) { + for (i = 0; i < ab5500_bank_ranges[devid].nbanks; i++) { + if (ab5500_bank_ranges[devid].bank[i].bankid == bank) + return &ab5500_bank_ranges[devid].bank[i]; + } + } + return NULL; +} + +static bool page_write_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg) +{ + u8 i; /* range loop index */ + const struct ab5500_i2c_ranges *bankref; + + bankref = get_bankref(devid, bank); + if (bankref == NULL || last_reg < first_reg) + return false; + + for (i = 0; i < bankref->nranges; i++) { + if (first_reg < bankref->range[i].first) + break; + if ((last_reg <= bankref->range[i].last) && + (bankref->range[i].perm & AB5500_PERM_WR)) + return true; + } + return false; +} + +static bool reg_write_allowed(u8 devid, u8 bank, u8 reg) +{ + return page_write_allowed(devid, bank, reg, reg); +} + +static bool page_read_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg) +{ + u8 i; + const struct ab5500_i2c_ranges *bankref; + + bankref = get_bankref(devid, bank); + if (bankref == NULL || last_reg < first_reg) + return false; + + + /* Find the range (if it exists in the list) that includes first_reg. */ + for (i = 0; i < bankref->nranges; i++) { + if (first_reg < bankref->range[i].first) + return false; + if (first_reg <= bankref->range[i].last) + break; + } + /* Make sure that the entire range up to and including last_reg is + * readable. This may span several of the ranges in the list. + */ + while ((i < bankref->nranges) && + (bankref->range[i].perm & AB5500_PERM_RD)) { + if (last_reg <= bankref->range[i].last) + return true; + if ((++i >= bankref->nranges) || + (bankref->range[i].first != + (bankref->range[i - 1].last + 1))) { + break; + } + } + return false; +} + +static bool reg_read_allowed(u8 devid, u8 bank, u8 reg) +{ + return page_read_allowed(devid, bank, reg, reg); +} + + +/* + * The exported register access functionality. + */ +static int ab5500_get_chip_id(struct device *dev) +{ + struct ab5500 *ab = dev_get_drvdata(dev->parent); + + return (int)ab->chip_id; +} + +static int ab5500_mask_and_set_register_interruptible(struct device *dev, + u8 bank, u8 reg, u8 bitmask, u8 bitvalues) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !reg_write_allowed(pdev->id, bank, reg)) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return mask_and_set_register_interruptible(ab, bank, reg, + bitmask, bitvalues); +} + +static int ab5500_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 value) +{ + return ab5500_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, + value); +} + +static int ab5500_get_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 *value) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !reg_read_allowed(pdev->id, bank, reg)) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return get_register_interruptible(ab, bank, reg, value); +} + +static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !page_read_allowed(pdev->id, bank, + first_reg, (first_reg + numregs - 1))) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return get_register_page_interruptible(ab, bank, first_reg, regvals, + numregs); +} + +static int +ab5500_event_registers_startup_state_get(struct device *dev, u8 *event) +{ + struct ab5500 *ab; + + ab = dev_get_drvdata(dev->parent); + if (!ab->startup_events_read) + return -EAGAIN; /* Try again later */ + + memcpy(event, ab->startup_events, AB5500_NUM_EVENT_REG); + return 0; +} + +static struct abx500_ops ab5500_ops = { + .get_chip_id = ab5500_get_chip_id, + .get_register = ab5500_get_register_interruptible, + .set_register = ab5500_set_register_interruptible, + .get_register_page = ab5500_get_register_page_interruptible, + .set_register_page = NULL, + .mask_and_set_register = ab5500_mask_and_set_register_interruptible, + .event_registers_startup_state_get = + ab5500_event_registers_startup_state_get, + .startup_irq_enabled = NULL, +}; + +#ifdef CONFIG_DEBUG_FS +static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = { + [AB5500_BANK_LED] = { + .bankid = AB5500_BANK_LED, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_ADC] = { + .bankid = AB5500_BANK_ADC, + .nranges = 6, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x1F, + .last = 0x22, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x23, + .last = 0x24, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x26, + .last = 0x2D, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x37, + .last = 0x57, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x58, + .last = 0x58, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_RTC] = { + .bankid = AB5500_BANK_RTC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x04, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x06, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_STARTUP] = { + .bankid = AB5500_BANK_STARTUP, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1F, + .last = 0x1F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x2E, + .last = 0x2E, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x51, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x61, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x66, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8C, + .last = 0x96, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xAA, + .last = 0xB4, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xB7, + .last = 0xBF, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xC1, + .last = 0xCA, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xD3, + .last = 0xE0, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_DBI_ECI] = { + .bankid = AB5500_BANK_DBI_ECI, + .nranges = 3, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x07, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x10, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x13, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_CHG] = { + .bankid = AB5500_BANK_CHG, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x11, + .last = 0x11, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x12, + .last = 0x1B, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_FG_BATTCOM_ACC] = { + .bankid = AB5500_BANK_FG_BATTCOM_ACC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0C, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_USB] = { + .bankid = AB5500_BANK_USB, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x83, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x87, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8B, + .last = 0x8B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x91, + .last = 0x92, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x93, + .last = 0x93, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x94, + .last = 0x94, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xA8, + .last = 0xB0, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB2, + .last = 0xB2, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB4, + .last = 0xBC, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xBF, + .last = 0xBF, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xC1, + .last = 0xC5, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_IT] = { + .bankid = AB5500_BANK_IT, + .nranges = 4, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x20, + .last = 0x36, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x40, + .last = 0x56, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x60, + .last = 0x76, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST, + .nranges = 7, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x12, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x44, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x54, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x64, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x74, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + .nranges = 13, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0D, + .last = 0x0F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1C, + .last = 0x1C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1E, + .last = 0x1E, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x20, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x25, + .last = 0x25, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x28, + .last = 0x2A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x33, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x43, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x53, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x63, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x73, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIBRA] = { + .bankid = AB5500_BANK_VIBRA, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xFE, + .last = 0xFE, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + .bankid = AB5500_BANK_AUDIO_HEADSETUSB, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x48, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xEB, + .last = 0xFB, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_SIM_USBSIM] = { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VDENC] = { + .bankid = AB5500_BANK_VDENC, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x09, + .last = 0x09, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0A, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x15, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1B, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x27, + .last = 0x2C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x41, + .last = 0x41, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x45, + .last = 0x5B, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x5D, + .last = 0x5D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x69, + .last = 0x69, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x6C, + .last = 0x6D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x81, + .perm = AB5500_PERM_RW, + }, + }, + }, +}; +static int ab5500_registers_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + unsigned int i; + u8 bank = (u8)ab->debug_bank; + + seq_printf(s, "ab5500 register values:\n"); + for (bank = 0; bank < AB5500_NUM_BANKS; bank++) { + seq_printf(s, " bank %u, %s (0x%x):\n", bank, + bankinfo[bank].name, + bankinfo[bank].slave_addr); + for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) { + u8 reg; + int err; + + for (reg = ab5500_reg_ranges[bank].range[i].first; + reg <= ab5500_reg_ranges[bank].range[i].last; + reg++) { + u8 value; + + err = get_register_interruptible(ab, bank, reg, + &value); + if (err < 0) { + dev_err(ab->dev, "get_reg failed %d" + "bank 0x%x reg 0x%x\n", + err, bank, reg); + return err; + } + + err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n", + bank, reg, value); + if (err < 0) { + dev_err(ab->dev, + "seq_printf overflow\n"); + /* + * Error is not returned here since + * the output is wanted in any case + */ + return 0; + } + } + } + } + return 0; +} + +static int ab5500_registers_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_registers_print, inode->i_private); +} + +static const struct file_operations ab5500_registers_fops = { + .open = ab5500_registers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab5500_bank_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "%d\n", ab->debug_bank); + return 0; +} + +static int ab5500_bank_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_bank_print, inode->i_private); +} + +static ssize_t ab5500_bank_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_bank; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_bank); + if (err) + return -EINVAL; + + if (user_bank >= AB5500_NUM_BANKS) { + dev_err(ab->dev, + "debugfs error input > number of banks\n"); + return -EINVAL; + } + + ab->debug_bank = user_bank; + + return buf_size; +} + +static int ab5500_address_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "0x%02X\n", ab->debug_address); + return 0; +} + +static int ab5500_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_address_print, inode->i_private); +} + +static ssize_t ab5500_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_address; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_address); + if (err) + return -EINVAL; + if (user_address > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + ab->debug_address = user_address; + return buf_size; +} + +static int ab5500_val_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + int err; + u8 regvalue; + + err = get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) { + dev_err(ab->dev, "get_reg failed %d, bank 0x%x" + ", reg 0x%x\n", err, ab->debug_bank, + ab->debug_address); + return -EINVAL; + } + seq_printf(s, "0x%02X\n", regvalue); + + return 0; +} + +static int ab5500_val_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_val_print, inode->i_private); +} + +static ssize_t ab5500_val_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_val; + int err; + u8 regvalue; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_val); + if (err) + return -EINVAL; + if (user_val > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + err = mask_and_set_register_interruptible( + ab, (u8)ab->debug_bank, + (u8)ab->debug_address, 0xFF, (u8)user_val); + if (err) + return -EINVAL; + + get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + + return buf_size; +} + +static const struct file_operations ab5500_bank_fops = { + .open = ab5500_bank_open, + .write = ab5500_bank_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_address_fops = { + .open = ab5500_address_open, + .write = ab5500_address_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_val_fops = { + .open = ab5500_val_open, + .write = ab5500_val_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct dentry *ab5500_dir; +static struct dentry *ab5500_reg_file; +static struct dentry *ab5500_bank_file; +static struct dentry *ab5500_address_file; +static struct dentry *ab5500_val_file; + +static inline void ab5500_setup_debugfs(struct ab5500 *ab) +{ + ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP; + ab->debug_address = AB5500_CHIP_ID; + + ab5500_dir = debugfs_create_dir("ab5500", NULL); + if (!ab5500_dir) + goto exit_no_debugfs; + + ab5500_reg_file = debugfs_create_file("all-bank-registers", + S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops); + if (!ab5500_reg_file) + goto exit_destroy_dir; + + ab5500_bank_file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops); + if (!ab5500_bank_file) + goto exit_destroy_reg; + + ab5500_address_file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops); + if (!ab5500_address_file) + goto exit_destroy_bank; + + ab5500_val_file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops); + if (!ab5500_val_file) + goto exit_destroy_address; + + return; + +exit_destroy_address: + debugfs_remove(ab5500_address_file); +exit_destroy_bank: + debugfs_remove(ab5500_bank_file); +exit_destroy_reg: + debugfs_remove(ab5500_reg_file); +exit_destroy_dir: + debugfs_remove(ab5500_dir); +exit_no_debugfs: + dev_err(ab->dev, "failed to create debugfs entries.\n"); + return; +} + +static inline void ab5500_remove_debugfs(void) +{ + debugfs_remove(ab5500_val_file); + debugfs_remove(ab5500_address_file); + debugfs_remove(ab5500_bank_file); + debugfs_remove(ab5500_reg_file); + debugfs_remove(ab5500_dir); +} + +#else /* !CONFIG_DEBUG_FS */ +static inline void ab5500_setup_debugfs(struct ab5500 *ab) +{ +} +static inline void ab5500_remove_debugfs(void) +{ +} +#endif + +/* + * ab5500_setup : Basic set-up, datastructure creation/destruction + * and I2C interface.This sets up a default config + * in the AB5500 chip so that it will work as expected. + * @ab : Pointer to ab5500 structure + * @settings : Pointer to struct abx500_init_settings + * @size : Size of init data + */ +static int __init ab5500_setup(struct ab5500 *ab, + struct abx500_init_settings *settings, unsigned int size) +{ + int err = 0; + int i; + + for (i = 0; i < size; i++) { + err = mask_and_set_register_interruptible(ab, + settings[i].bank, + settings[i].reg, + 0xFF, settings[i].setting); + if (err) + goto exit_no_setup; + + /* If event mask register update the event mask in ab5500 */ + if ((settings[i].bank == AB5500_BANK_IT) && + (AB5500_MASK_BASE <= settings[i].reg) && + (settings[i].reg <= AB5500_MASK_END)) { + ab->mask[settings[i].reg - AB5500_MASK_BASE] = + settings[i].setting; + } + } +exit_no_setup: + return err; +} + +struct ab_family_id { + u8 id; + char *name; +}; + +static const struct ab_family_id ids[] __initdata = { + /* AB5500 */ + { + .id = AB5500_1_0, + .name = "1.0" + }, + { + .id = AB5500_1_1, + .name = "1.1" + }, + /* Terminator */ + { + .id = 0x00, + } +}; + +static int __init ab5500_probe(struct platform_device *pdev) +{ + struct ab5500 *ab; + struct ab5500_platform_data *ab5500_plf_data = + pdev->dev.platform_data; + int err; + int i; + + ab = kzalloc(sizeof(struct ab5500), GFP_KERNEL); + if (!ab) { + dev_err(&pdev->dev, + "could not allocate ab5500 device\n"); + return -ENOMEM; + } + + /* Initialize data structure */ + mutex_init(&ab->access_mutex); + mutex_init(&ab->irq_lock); + ab->dev = &pdev->dev; + + platform_set_drvdata(pdev, ab); + + /* Read chip ID register */ + err = get_register_interruptible(ab, AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + AB5500_CHIP_ID, &ab->chip_id); + if (err) { + dev_err(&pdev->dev, "could not communicate with the analog " + "baseband chip\n"); + goto exit_no_detect; + } + + for (i = 0; ids[i].id != 0x0; i++) { + if (ids[i].id == ab->chip_id) { + snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1, + "AB5500 %s", ids[i].name); + break; + } + } + if (ids[i].id == 0x0) { + dev_err(&pdev->dev, "unknown analog baseband chip id: 0x%x\n", + ab->chip_id); + dev_err(&pdev->dev, "driver not started!\n"); + goto exit_no_detect; + } + + /* Clear and mask all interrupts */ + for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) { + u8 latchreg = AB5500_IT_LATCH0_REG + i; + u8 maskreg = AB5500_IT_MASK0_REG + i; + u8 val; + + get_register_interruptible(ab, AB5500_BANK_IT, latchreg, &val); + set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff); + ab->mask[i] = ab->oldmask[i] = 0xff; + } + + err = abx500_register_ops(&pdev->dev, &ab5500_ops); + if (err) { + dev_err(&pdev->dev, "ab5500_register ops error\n"); + goto exit_no_detect; + } + + /* Set up and register the platform devices. */ + for (i = 0; i < AB5500_NUM_DEVICES; i++) { + ab5500_devs[i].platform_data = ab5500_plf_data->dev_data[i]; + ab5500_devs[i].pdata_size = + sizeof(ab5500_plf_data->dev_data[i]); + } + + err = mfd_add_devices(&pdev->dev, 0, ab5500_devs, + ARRAY_SIZE(ab5500_devs), NULL, + ab5500_plf_data->irq.base); + if (err) { + dev_err(&pdev->dev, "ab5500_mfd_add_device error\n"); + goto exit_no_detect; + } + + err = ab5500_setup(ab, ab5500_plf_data->init_settings, + ab5500_plf_data->init_settings_sz); + if (err) { + dev_err(&pdev->dev, "ab5500_setup error\n"); + goto exit_no_detect; + } + + ab5500_setup_debugfs(ab); + + dev_info(&pdev->dev, "detected AB chip: %s\n", &ab->chip_name[0]); + return 0; + +exit_no_detect: + kfree(ab); + return err; +} + +static int __exit ab5500_remove(struct platform_device *pdev) +{ + struct ab5500 *ab = platform_get_drvdata(pdev); + + ab5500_remove_debugfs(); + mfd_remove_devices(&pdev->dev); + kfree(ab); + return 0; +} + +static struct platform_driver ab5500_driver = { + .driver = { + .name = "ab5500-core", + .owner = THIS_MODULE, + }, + .remove = __exit_p(ab5500_remove), +}; + +static int __init ab5500_core_init(void) +{ + return platform_driver_probe(&ab5500_driver, ab5500_probe); +} + +static void __exit ab5500_core_exit(void) +{ + platform_driver_unregister(&ab5500_driver); +} + +subsys_initcall(ab5500_core_init); +module_exit(ab5500_core_exit); + +MODULE_AUTHOR("Mattias Wallin "); +MODULE_DESCRIPTION("AB5500 core driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/ab5500/ab5500.h b/include/linux/mfd/ab5500/ab5500.h new file mode 100644 index 000000000000..a720051ae933 --- /dev/null +++ b/include/linux/mfd/ab5500/ab5500.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) ST-Ericsson 2011 + * + * License Terms: GNU General Public License v2 + */ +#ifndef MFD_AB5500_H +#define MFD_AB5500_H + +#include + +enum ab5500_devid { + AB5500_DEVID_ADC, + AB5500_DEVID_LEDS, + AB5500_DEVID_POWER, + AB5500_DEVID_REGULATORS, + AB5500_DEVID_SIM, + AB5500_DEVID_RTC, + AB5500_DEVID_CHARGER, + AB5500_DEVID_FUELGAUGE, + AB5500_DEVID_VIBRATOR, + AB5500_DEVID_CODEC, + AB5500_DEVID_USB, + AB5500_DEVID_OTP, + AB5500_DEVID_VIDEO, + AB5500_DEVID_DBIECI, + AB5500_DEVID_ONSWA, + AB5500_NUM_DEVICES, +}; + +enum ab5500_banks { + AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP = 0, + AB5500_BANK_VDDDIG_IO_I2C_CLK_TST = 1, + AB5500_BANK_VDENC = 2, + AB5500_BANK_SIM_USBSIM = 3, + AB5500_BANK_LED = 4, + AB5500_BANK_ADC = 5, + AB5500_BANK_RTC = 6, + AB5500_BANK_STARTUP = 7, + AB5500_BANK_DBI_ECI = 8, + AB5500_BANK_CHG = 9, + AB5500_BANK_FG_BATTCOM_ACC = 10, + AB5500_BANK_USB = 11, + AB5500_BANK_IT = 12, + AB5500_BANK_VIBRA = 13, + AB5500_BANK_AUDIO_HEADSETUSB = 14, + AB5500_NUM_BANKS = 15, +}; + +enum ab5500_banks_addr { + AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP = 0x4A, + AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST = 0x4B, + AB5500_ADDR_VDENC = 0x06, + AB5500_ADDR_SIM_USBSIM = 0x04, + AB5500_ADDR_LED = 0x10, + AB5500_ADDR_ADC = 0x0A, + AB5500_ADDR_RTC = 0x0F, + AB5500_ADDR_STARTUP = 0x03, + AB5500_ADDR_DBI_ECI = 0x07, + AB5500_ADDR_CHG = 0x0B, + AB5500_ADDR_FG_BATTCOM_ACC = 0x0C, + AB5500_ADDR_USB = 0x05, + AB5500_ADDR_IT = 0x0E, + AB5500_ADDR_VIBRA = 0x02, + AB5500_ADDR_AUDIO_HEADSETUSB = 0x0D, +}; + +/* + * Interrupt register offsets + * Bank : 0x0E + */ +#define AB5500_IT_SOURCE0_REG 0x20 +#define AB5500_IT_SOURCE1_REG 0x21 +#define AB5500_IT_SOURCE2_REG 0x22 +#define AB5500_IT_SOURCE3_REG 0x23 +#define AB5500_IT_SOURCE4_REG 0x24 +#define AB5500_IT_SOURCE5_REG 0x25 +#define AB5500_IT_SOURCE6_REG 0x26 +#define AB5500_IT_SOURCE7_REG 0x27 +#define AB5500_IT_SOURCE8_REG 0x28 +#define AB5500_IT_SOURCE9_REG 0x29 +#define AB5500_IT_SOURCE10_REG 0x2A +#define AB5500_IT_SOURCE11_REG 0x2B +#define AB5500_IT_SOURCE12_REG 0x2C +#define AB5500_IT_SOURCE13_REG 0x2D +#define AB5500_IT_SOURCE14_REG 0x2E +#define AB5500_IT_SOURCE15_REG 0x2F +#define AB5500_IT_SOURCE16_REG 0x30 +#define AB5500_IT_SOURCE17_REG 0x31 +#define AB5500_IT_SOURCE18_REG 0x32 +#define AB5500_IT_SOURCE19_REG 0x33 +#define AB5500_IT_SOURCE20_REG 0x34 +#define AB5500_IT_SOURCE21_REG 0x35 +#define AB5500_IT_SOURCE22_REG 0x36 +#define AB5500_IT_SOURCE23_REG 0x37 + +#define AB5500_NUM_IRQ_REGS 23 + +/** + * struct ab5500 + * @access_mutex: lock out concurrent accesses to the AB registers + * @dev: a pointer to the device struct for this chip driver + * @ab5500_irq: the analog baseband irq + * @irq_base: the platform configuration irq base for subdevices + * @chip_name: name of this chip variant + * @chip_id: 8 bit chip ID for this chip variant + * @irq_lock: a lock to protect the mask + * @abb_events: a local bit mask of the prcmu wakeup events + * @event_mask: a local copy of the mask event registers + * @last_event_mask: a copy of the last event_mask written to hardware + * @startup_events: a copy of the first reading of the event registers + * @startup_events_read: whether the first events have been read + */ +struct ab5500 { + struct mutex access_mutex; + struct device *dev; + unsigned int ab5500_irq; + unsigned int irq_base; + char chip_name[32]; + u8 chip_id; + struct mutex irq_lock; + u32 abb_events; + u8 mask[AB5500_NUM_IRQ_REGS]; + u8 oldmask[AB5500_NUM_IRQ_REGS]; + u8 startup_events[AB5500_NUM_IRQ_REGS]; + bool startup_events_read; +#ifdef CONFIG_DEBUG_FS + unsigned int debug_bank; + unsigned int debug_address; +#endif +}; + +struct ab5500_platform_data { + struct {unsigned int base; unsigned int count; } irq; + void *dev_data[AB5500_NUM_DEVICES]; + struct abx500_init_settings *init_settings; + unsigned int init_settings_sz; + bool pm_power_off; +}; + +#endif /* MFD_AB5500_H */ diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 896b5e47f16e..79ec2c7b5fab 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -31,8 +31,8 @@ #define AB3100_R2B 0xc8 #define AB3550_P1A 0x10 #define AB5500_1_0 0x20 -#define AB5500_2_0 0x21 -#define AB5500_2_1 0x22 +#define AB5500_1_1 0x21 +#define AB5500_2_0 0x24 /* AB8500 CIDs*/ #define AB8500_CUTEARLY 0x00 -- cgit v1.2.3 From 94be70d4b279ba1b35119a3340833ffcc798c2e2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 Sep 2011 08:22:33 +0200 Subject: mfd: Break out ab5500 debugfs code This breaks the debugfs portions of the AB5500 driver into its own file. Split off a _raw function to access registers since we don't want to expose a generically named function globally. Move all required data structures to a shared ab5500-core.h file. Cc: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 1 + drivers/mfd/ab5500-core.c | 907 +------------------------------------------ drivers/mfd/ab5500-core.h | 87 +++++ drivers/mfd/ab5500-debugfs.c | 806 ++++++++++++++++++++++++++++++++++++++ drivers/mfd/ab5500-debugfs.h | 22 ++ 6 files changed, 941 insertions(+), 890 deletions(-) create mode 100644 drivers/mfd/ab5500-core.h create mode 100644 drivers/mfd/ab5500-debugfs.c create mode 100644 drivers/mfd/ab5500-debugfs.h (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8594de8b86a0..f22bd2f0ecc3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -572,6 +572,14 @@ config AB5500_CORE chip. This connects to the db5500 chip via the I2C bus via PRCMU. This chip embeds various other multimedia funtionalities as well. +config AB5500_DEBUG + bool "Enable debug info via debugfs" + depends on AB5500_CORE && DEBUG_FS + default y if DEBUG_FS + help + Select this option if you want debug information from the AB5500 + using the debug filesystem, debugfs. + config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 457fed8474cf..7ed553d8157a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB3550_CORE) += ab3550-core.o obj-$(CONFIG_AB5500_CORE) += ab5500-core.o +obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c index afec0f2ede42..4175544b491b 100644 --- a/drivers/mfd/ab5500-core.c +++ b/drivers/mfd/ab5500-core.c @@ -21,9 +21,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -33,87 +30,13 @@ #include #include +#include "ab5500-core.h" +#include "ab5500-debugfs.h" + #define AB5500_NUM_EVENT_REG 23 #define AB5500_IT_LATCH0_REG 0x40 #define AB5500_IT_MASK0_REG 0x60 -/* Read/write operation values. */ -#define AB5500_PERM_RD (0x01) -#define AB5500_PERM_WR (0x02) - -/* Read/write permissions. */ -#define AB5500_PERM_RO (AB5500_PERM_RD) -#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR) - -#define AB5500_MASK_BASE (0x60) -#define AB5500_MASK_END (0x79) -#define AB5500_CHIP_ID (0x20) - -/** - * struct ab5500_bank - * @slave_addr: I2C slave_addr found in AB5500 specification - * @name: Documentation name of the bank. For reference - */ -struct ab5500_bank { - u8 slave_addr; - const char *name; -}; - -static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = { - [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { - AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"}, - [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { - AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"}, - [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"}, - [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"}, - [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"}, - [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"}, - [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"}, - [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"}, - [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"}, - [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"}, - [AB5500_BANK_FG_BATTCOM_ACC] = { - AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"}, - [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"}, - [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"}, - [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"}, - [AB5500_BANK_AUDIO_HEADSETUSB] = { - AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"}, -}; - -/** - * struct ab5500_reg_range - * @first: the first address of the range - * @last: the last address of the range - * @perm: access permissions for the range - */ -struct ab5500_reg_range { - u8 first; - u8 last; - u8 perm; -}; - -/** - * struct ab5500_i2c_ranges - * @count: the number of ranges in the list - * @range: the list of register ranges - */ -struct ab5500_i2c_ranges { - u8 nranges; - u8 bankid; - const struct ab5500_reg_range *range; -}; - -/** - * struct ab5500_i2c_banks - * @count: the number of ranges in the list - * @range: the list of register ranges - */ -struct ab5500_i2c_banks { - u8 nbanks; - const struct ab5500_i2c_ranges *bank; -}; - /* * Permissible register ranges for reading and writing per device and bank. * @@ -1073,8 +996,9 @@ static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = { /* * Functionality for getting/setting register values. */ -static int get_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, - u8 *value) +int ab5500_get_register_interruptible_raw(struct ab5500 *ab, + u8 bank, u8 reg, + u8 *value) { int err; @@ -1121,7 +1045,7 @@ out: return err; } -static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank, +int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { int err = 0; @@ -1158,7 +1082,8 @@ static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank, static int set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value) { - return mask_and_set_register_interruptible(ab, bank, reg, 0xff, value); + return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg, + 0xff, value); } /* @@ -1261,7 +1186,7 @@ static int ab5500_mask_and_set_register_interruptible(struct device *dev, return -EINVAL; ab = dev_get_drvdata(dev->parent); - return mask_and_set_register_interruptible(ab, bank, reg, + return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg, bitmask, bitvalues); } @@ -1283,7 +1208,7 @@ static int ab5500_get_register_interruptible(struct device *dev, u8 bank, return -EINVAL; ab = dev_get_drvdata(dev->parent); - return get_register_interruptible(ab, bank, reg, value); + return ab5500_get_register_interruptible_raw(ab, bank, reg, value); } static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank, @@ -1327,806 +1252,6 @@ static struct abx500_ops ab5500_ops = { .startup_irq_enabled = NULL, }; -#ifdef CONFIG_DEBUG_FS -static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = { - [AB5500_BANK_LED] = { - .bankid = AB5500_BANK_LED, - .nranges = 1, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x0C, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_ADC] = { - .bankid = AB5500_BANK_ADC, - .nranges = 6, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x1F, - .last = 0x22, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x23, - .last = 0x24, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x26, - .last = 0x2D, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x2F, - .last = 0x34, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x37, - .last = 0x57, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x58, - .last = 0x58, - .perm = AB5500_PERM_RO, - }, - }, - }, - [AB5500_BANK_RTC] = { - .bankid = AB5500_BANK_RTC, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x04, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x06, - .last = 0x0C, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_STARTUP] = { - .bankid = AB5500_BANK_STARTUP, - .nranges = 12, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x01, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x1F, - .last = 0x1F, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x2E, - .last = 0x2E, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x2F, - .last = 0x30, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x50, - .last = 0x51, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x60, - .last = 0x61, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x66, - .last = 0x8A, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x8C, - .last = 0x96, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xAA, - .last = 0xB4, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xB7, - .last = 0xBF, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xC1, - .last = 0xCA, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xD3, - .last = 0xE0, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_DBI_ECI] = { - .bankid = AB5500_BANK_DBI_ECI, - .nranges = 3, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x07, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x10, - .last = 0x10, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x13, - .last = 0x13, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_CHG] = { - .bankid = AB5500_BANK_CHG, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x11, - .last = 0x11, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x12, - .last = 0x1B, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_FG_BATTCOM_ACC] = { - .bankid = AB5500_BANK_FG_BATTCOM_ACC, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x0B, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x0C, - .last = 0x10, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_USB] = { - .bankid = AB5500_BANK_USB, - .nranges = 12, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x01, - .last = 0x01, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x80, - .last = 0x83, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x87, - .last = 0x8A, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x8B, - .last = 0x8B, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x91, - .last = 0x92, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x93, - .last = 0x93, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x94, - .last = 0x94, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xA8, - .last = 0xB0, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xB2, - .last = 0xB2, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xB4, - .last = 0xBC, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xBF, - .last = 0xBF, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xC1, - .last = 0xC5, - .perm = AB5500_PERM_RO, - }, - }, - }, - [AB5500_BANK_IT] = { - .bankid = AB5500_BANK_IT, - .nranges = 4, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x02, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x20, - .last = 0x36, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x40, - .last = 0x56, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x60, - .last = 0x76, - .perm = AB5500_PERM_RO, - }, - }, - }, - [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { - .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST, - .nranges = 7, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x02, - .last = 0x02, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x12, - .last = 0x12, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x30, - .last = 0x34, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x40, - .last = 0x44, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x50, - .last = 0x54, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x60, - .last = 0x64, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x70, - .last = 0x74, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { - .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, - .nranges = 13, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x01, - .last = 0x01, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x02, - .last = 0x02, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x0D, - .last = 0x0F, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x1C, - .last = 0x1C, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x1E, - .last = 0x1E, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x20, - .last = 0x21, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x25, - .last = 0x25, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x28, - .last = 0x2A, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x30, - .last = 0x33, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x40, - .last = 0x43, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x50, - .last = 0x53, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x60, - .last = 0x63, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x70, - .last = 0x73, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_VIBRA] = { - .bankid = AB5500_BANK_VIBRA, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x10, - .last = 0x13, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xFE, - .last = 0xFE, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_AUDIO_HEADSETUSB] = { - .bankid = AB5500_BANK_AUDIO_HEADSETUSB, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x48, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xEB, - .last = 0xFB, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_SIM_USBSIM] = { - .bankid = AB5500_BANK_SIM_USBSIM, - .nranges = 1, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x13, - .last = 0x19, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_VDENC] = { - .bankid = AB5500_BANK_VDENC, - .nranges = 12, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x08, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x09, - .last = 0x09, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x0A, - .last = 0x12, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x15, - .last = 0x19, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x1B, - .last = 0x21, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x27, - .last = 0x2C, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x41, - .last = 0x41, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x45, - .last = 0x5B, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x5D, - .last = 0x5D, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x69, - .last = 0x69, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x6C, - .last = 0x6D, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x80, - .last = 0x81, - .perm = AB5500_PERM_RW, - }, - }, - }, -}; -static int ab5500_registers_print(struct seq_file *s, void *p) -{ - struct ab5500 *ab = s->private; - unsigned int i; - u8 bank = (u8)ab->debug_bank; - - seq_printf(s, "ab5500 register values:\n"); - for (bank = 0; bank < AB5500_NUM_BANKS; bank++) { - seq_printf(s, " bank %u, %s (0x%x):\n", bank, - bankinfo[bank].name, - bankinfo[bank].slave_addr); - for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) { - u8 reg; - int err; - - for (reg = ab5500_reg_ranges[bank].range[i].first; - reg <= ab5500_reg_ranges[bank].range[i].last; - reg++) { - u8 value; - - err = get_register_interruptible(ab, bank, reg, - &value); - if (err < 0) { - dev_err(ab->dev, "get_reg failed %d" - "bank 0x%x reg 0x%x\n", - err, bank, reg); - return err; - } - - err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n", - bank, reg, value); - if (err < 0) { - dev_err(ab->dev, - "seq_printf overflow\n"); - /* - * Error is not returned here since - * the output is wanted in any case - */ - return 0; - } - } - } - } - return 0; -} - -static int ab5500_registers_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab5500_registers_print, inode->i_private); -} - -static const struct file_operations ab5500_registers_fops = { - .open = ab5500_registers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab5500_bank_print(struct seq_file *s, void *p) -{ - struct ab5500 *ab = s->private; - - seq_printf(s, "%d\n", ab->debug_bank); - return 0; -} - -static int ab5500_bank_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab5500_bank_print, inode->i_private); -} - -static ssize_t ab5500_bank_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_bank; - int err; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf) - 1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_bank); - if (err) - return -EINVAL; - - if (user_bank >= AB5500_NUM_BANKS) { - dev_err(ab->dev, - "debugfs error input > number of banks\n"); - return -EINVAL; - } - - ab->debug_bank = user_bank; - - return buf_size; -} - -static int ab5500_address_print(struct seq_file *s, void *p) -{ - struct ab5500 *ab = s->private; - - seq_printf(s, "0x%02X\n", ab->debug_address); - return 0; -} - -static int ab5500_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab5500_address_print, inode->i_private); -} - -static ssize_t ab5500_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_address; - int err; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf) - 1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_address); - if (err) - return -EINVAL; - if (user_address > 0xff) { - dev_err(ab->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - ab->debug_address = user_address; - return buf_size; -} - -static int ab5500_val_print(struct seq_file *s, void *p) -{ - struct ab5500 *ab = s->private; - int err; - u8 regvalue; - - err = get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) { - dev_err(ab->dev, "get_reg failed %d, bank 0x%x" - ", reg 0x%x\n", err, ab->debug_bank, - ab->debug_address); - return -EINVAL; - } - seq_printf(s, "0x%02X\n", regvalue); - - return 0; -} - -static int ab5500_val_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab5500_val_print, inode->i_private); -} - -static ssize_t ab5500_val_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_val; - int err; - u8 regvalue; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf)-1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_val); - if (err) - return -EINVAL; - if (user_val > 0xff) { - dev_err(ab->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - err = mask_and_set_register_interruptible( - ab, (u8)ab->debug_bank, - (u8)ab->debug_address, 0xFF, (u8)user_val); - if (err) - return -EINVAL; - - get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) - return -EINVAL; - - return buf_size; -} - -static const struct file_operations ab5500_bank_fops = { - .open = ab5500_bank_open, - .write = ab5500_bank_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab5500_address_fops = { - .open = ab5500_address_open, - .write = ab5500_address_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab5500_val_fops = { - .open = ab5500_val_open, - .write = ab5500_val_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static struct dentry *ab5500_dir; -static struct dentry *ab5500_reg_file; -static struct dentry *ab5500_bank_file; -static struct dentry *ab5500_address_file; -static struct dentry *ab5500_val_file; - -static inline void ab5500_setup_debugfs(struct ab5500 *ab) -{ - ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP; - ab->debug_address = AB5500_CHIP_ID; - - ab5500_dir = debugfs_create_dir("ab5500", NULL); - if (!ab5500_dir) - goto exit_no_debugfs; - - ab5500_reg_file = debugfs_create_file("all-bank-registers", - S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops); - if (!ab5500_reg_file) - goto exit_destroy_dir; - - ab5500_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops); - if (!ab5500_bank_file) - goto exit_destroy_reg; - - ab5500_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops); - if (!ab5500_address_file) - goto exit_destroy_bank; - - ab5500_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops); - if (!ab5500_val_file) - goto exit_destroy_address; - - return; - -exit_destroy_address: - debugfs_remove(ab5500_address_file); -exit_destroy_bank: - debugfs_remove(ab5500_bank_file); -exit_destroy_reg: - debugfs_remove(ab5500_reg_file); -exit_destroy_dir: - debugfs_remove(ab5500_dir); -exit_no_debugfs: - dev_err(ab->dev, "failed to create debugfs entries.\n"); - return; -} - -static inline void ab5500_remove_debugfs(void) -{ - debugfs_remove(ab5500_val_file); - debugfs_remove(ab5500_address_file); - debugfs_remove(ab5500_bank_file); - debugfs_remove(ab5500_reg_file); - debugfs_remove(ab5500_dir); -} - -#else /* !CONFIG_DEBUG_FS */ -static inline void ab5500_setup_debugfs(struct ab5500 *ab) -{ -} -static inline void ab5500_remove_debugfs(void) -{ -} -#endif - /* * ab5500_setup : Basic set-up, datastructure creation/destruction * and I2C interface.This sets up a default config @@ -2142,7 +1267,7 @@ static int __init ab5500_setup(struct ab5500 *ab, int i; for (i = 0; i < size; i++) { - err = mask_and_set_register_interruptible(ab, + err = ab5500_mask_and_set_register_interruptible_raw(ab, settings[i].bank, settings[i].reg, 0xFF, settings[i].setting); @@ -2205,8 +1330,9 @@ static int __init ab5500_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ab); /* Read chip ID register */ - err = get_register_interruptible(ab, AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, - AB5500_CHIP_ID, &ab->chip_id); + err = ab5500_get_register_interruptible_raw(ab, + AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + AB5500_CHIP_ID, &ab->chip_id); if (err) { dev_err(&pdev->dev, "could not communicate with the analog " "baseband chip\n"); @@ -2233,7 +1359,8 @@ static int __init ab5500_probe(struct platform_device *pdev) u8 maskreg = AB5500_IT_MASK0_REG + i; u8 val; - get_register_interruptible(ab, AB5500_BANK_IT, latchreg, &val); + ab5500_get_register_interruptible_raw(ab, AB5500_BANK_IT, + latchreg, &val); set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff); ab->mask[i] = ab->oldmask[i] = 0xff; } diff --git a/drivers/mfd/ab5500-core.h b/drivers/mfd/ab5500-core.h new file mode 100644 index 000000000000..63b30b17e4f3 --- /dev/null +++ b/drivers/mfd/ab5500-core.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Shared definitions and data structures for the AB5500 MFD driver + */ + +/* Read/write operation values. */ +#define AB5500_PERM_RD (0x01) +#define AB5500_PERM_WR (0x02) + +/* Read/write permissions. */ +#define AB5500_PERM_RO (AB5500_PERM_RD) +#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR) + +#define AB5500_MASK_BASE (0x60) +#define AB5500_MASK_END (0x79) +#define AB5500_CHIP_ID (0x20) + +/** + * struct ab5500_reg_range + * @first: the first address of the range + * @last: the last address of the range + * @perm: access permissions for the range + */ +struct ab5500_reg_range { + u8 first; + u8 last; + u8 perm; +}; + +/** + * struct ab5500_i2c_ranges + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_ranges { + u8 nranges; + u8 bankid; + const struct ab5500_reg_range *range; +}; + +/** + * struct ab5500_i2c_banks + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_banks { + u8 nbanks; + const struct ab5500_i2c_ranges *bank; +}; + +/** + * struct ab5500_bank + * @slave_addr: I2C slave_addr found in AB5500 specification + * @name: Documentation name of the bank. For reference + */ +struct ab5500_bank { + u8 slave_addr; + const char *name; +}; + +static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = { + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"}, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"}, + [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"}, + [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"}, + [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"}, + [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"}, + [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"}, + [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"}, + [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"}, + [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"}, + [AB5500_BANK_FG_BATTCOM_ACC] = { + AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"}, + [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"}, + [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"}, + [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"}, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"}, +}; + +int ab5500_get_register_interruptible_raw(struct ab5500 *ab, u8 bank, u8 reg, + u8 *value); +int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues); diff --git a/drivers/mfd/ab5500-debugfs.c b/drivers/mfd/ab5500-debugfs.c new file mode 100644 index 000000000000..6be1fe6b5f9a --- /dev/null +++ b/drivers/mfd/ab5500-debugfs.c @@ -0,0 +1,806 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Debugfs support for the AB5500 MFD driver + */ + +#include +#include +#include +#include +#include + +#include "ab5500-core.h" +#include "ab5500-debugfs.h" + +static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = { + [AB5500_BANK_LED] = { + .bankid = AB5500_BANK_LED, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_ADC] = { + .bankid = AB5500_BANK_ADC, + .nranges = 6, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x1F, + .last = 0x22, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x23, + .last = 0x24, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x26, + .last = 0x2D, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x37, + .last = 0x57, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x58, + .last = 0x58, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_RTC] = { + .bankid = AB5500_BANK_RTC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x04, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x06, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_STARTUP] = { + .bankid = AB5500_BANK_STARTUP, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1F, + .last = 0x1F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x2E, + .last = 0x2E, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x51, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x61, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x66, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8C, + .last = 0x96, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xAA, + .last = 0xB4, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xB7, + .last = 0xBF, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xC1, + .last = 0xCA, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xD3, + .last = 0xE0, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_DBI_ECI] = { + .bankid = AB5500_BANK_DBI_ECI, + .nranges = 3, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x07, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x10, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x13, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_CHG] = { + .bankid = AB5500_BANK_CHG, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x11, + .last = 0x11, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x12, + .last = 0x1B, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_FG_BATTCOM_ACC] = { + .bankid = AB5500_BANK_FG_BATTCOM_ACC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0C, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_USB] = { + .bankid = AB5500_BANK_USB, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x83, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x87, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8B, + .last = 0x8B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x91, + .last = 0x92, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x93, + .last = 0x93, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x94, + .last = 0x94, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xA8, + .last = 0xB0, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB2, + .last = 0xB2, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB4, + .last = 0xBC, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xBF, + .last = 0xBF, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xC1, + .last = 0xC5, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_IT] = { + .bankid = AB5500_BANK_IT, + .nranges = 4, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x20, + .last = 0x36, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x40, + .last = 0x56, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x60, + .last = 0x76, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST, + .nranges = 7, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x12, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x44, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x54, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x64, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x74, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + .nranges = 13, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0D, + .last = 0x0F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1C, + .last = 0x1C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1E, + .last = 0x1E, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x20, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x25, + .last = 0x25, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x28, + .last = 0x2A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x33, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x43, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x53, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x63, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x73, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIBRA] = { + .bankid = AB5500_BANK_VIBRA, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xFE, + .last = 0xFE, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + .bankid = AB5500_BANK_AUDIO_HEADSETUSB, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x48, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xEB, + .last = 0xFB, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_SIM_USBSIM] = { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VDENC] = { + .bankid = AB5500_BANK_VDENC, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x09, + .last = 0x09, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0A, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x15, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1B, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x27, + .last = 0x2C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x41, + .last = 0x41, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x45, + .last = 0x5B, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x5D, + .last = 0x5D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x69, + .last = 0x69, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x6C, + .last = 0x6D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x81, + .perm = AB5500_PERM_RW, + }, + }, + }, +}; + +static int ab5500_registers_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + unsigned int i; + u8 bank = (u8)ab->debug_bank; + + seq_printf(s, "ab5500 register values:\n"); + for (bank = 0; bank < AB5500_NUM_BANKS; bank++) { + seq_printf(s, " bank %u, %s (0x%x):\n", bank, + bankinfo[bank].name, + bankinfo[bank].slave_addr); + for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) { + u8 reg; + int err; + + for (reg = ab5500_reg_ranges[bank].range[i].first; + reg <= ab5500_reg_ranges[bank].range[i].last; + reg++) { + u8 value; + + err = ab5500_get_register_interruptible_raw(ab, + bank, reg, + &value); + if (err < 0) { + dev_err(ab->dev, "get_reg failed %d" + "bank 0x%x reg 0x%x\n", + err, bank, reg); + return err; + } + + err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n", + bank, reg, value); + if (err < 0) { + dev_err(ab->dev, + "seq_printf overflow\n"); + /* + * Error is not returned here since + * the output is wanted in any case + */ + return 0; + } + } + } + } + return 0; +} + +static int ab5500_registers_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_registers_print, inode->i_private); +} + +static const struct file_operations ab5500_registers_fops = { + .open = ab5500_registers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab5500_bank_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "%d\n", ab->debug_bank); + return 0; +} + +static int ab5500_bank_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_bank_print, inode->i_private); +} + +static ssize_t ab5500_bank_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_bank; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_bank); + if (err) + return -EINVAL; + + if (user_bank >= AB5500_NUM_BANKS) { + dev_err(ab->dev, + "debugfs error input > number of banks\n"); + return -EINVAL; + } + + ab->debug_bank = user_bank; + + return buf_size; +} + +static int ab5500_address_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "0x%02X\n", ab->debug_address); + return 0; +} + +static int ab5500_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_address_print, inode->i_private); +} + +static ssize_t ab5500_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_address; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_address); + if (err) + return -EINVAL; + if (user_address > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + ab->debug_address = user_address; + return buf_size; +} + +static int ab5500_val_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + int err; + u8 regvalue; + + err = ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) { + dev_err(ab->dev, "get_reg failed %d, bank 0x%x" + ", reg 0x%x\n", err, ab->debug_bank, + ab->debug_address); + return -EINVAL; + } + seq_printf(s, "0x%02X\n", regvalue); + + return 0; +} + +static int ab5500_val_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_val_print, inode->i_private); +} + +static ssize_t ab5500_val_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_val; + int err; + u8 regvalue; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_val); + if (err) + return -EINVAL; + if (user_val > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + err = ab5500_mask_and_set_register_interruptible_raw( + ab, (u8)ab->debug_bank, + (u8)ab->debug_address, 0xFF, (u8)user_val); + if (err) + return -EINVAL; + + ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + + return buf_size; +} + +static const struct file_operations ab5500_bank_fops = { + .open = ab5500_bank_open, + .write = ab5500_bank_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_address_fops = { + .open = ab5500_address_open, + .write = ab5500_address_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_val_fops = { + .open = ab5500_val_open, + .write = ab5500_val_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct dentry *ab5500_dir; +static struct dentry *ab5500_reg_file; +static struct dentry *ab5500_bank_file; +static struct dentry *ab5500_address_file; +static struct dentry *ab5500_val_file; + +void __init ab5500_setup_debugfs(struct ab5500 *ab) +{ + ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP; + ab->debug_address = AB5500_CHIP_ID; + + ab5500_dir = debugfs_create_dir("ab5500", NULL); + if (!ab5500_dir) + goto exit_no_debugfs; + + ab5500_reg_file = debugfs_create_file("all-bank-registers", + S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops); + if (!ab5500_reg_file) + goto exit_destroy_dir; + + ab5500_bank_file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops); + if (!ab5500_bank_file) + goto exit_destroy_reg; + + ab5500_address_file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops); + if (!ab5500_address_file) + goto exit_destroy_bank; + + ab5500_val_file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops); + if (!ab5500_val_file) + goto exit_destroy_address; + + return; + +exit_destroy_address: + debugfs_remove(ab5500_address_file); +exit_destroy_bank: + debugfs_remove(ab5500_bank_file); +exit_destroy_reg: + debugfs_remove(ab5500_reg_file); +exit_destroy_dir: + debugfs_remove(ab5500_dir); +exit_no_debugfs: + dev_err(ab->dev, "failed to create debugfs entries.\n"); + return; +} + +void __exit ab5500_remove_debugfs(void) +{ + debugfs_remove(ab5500_val_file); + debugfs_remove(ab5500_address_file); + debugfs_remove(ab5500_bank_file); + debugfs_remove(ab5500_reg_file); + debugfs_remove(ab5500_dir); +} diff --git a/drivers/mfd/ab5500-debugfs.h b/drivers/mfd/ab5500-debugfs.h new file mode 100644 index 000000000000..7330a9b6afa6 --- /dev/null +++ b/drivers/mfd/ab5500-debugfs.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Debugfs interface to the AB5500 core driver + */ + +#ifdef CONFIG_DEBUG_FS + +void ab5500_setup_debugfs(struct ab5500 *ab); +void ab5500_remove_debugfs(void); + +#else /* !CONFIG_DEBUG_FS */ + +static inline void ab5500_setup_debugfs(struct ab5500 *ab) +{ +} + +static inline void ab5500_remove_debugfs(void) +{ +} + +#endif -- cgit v1.2.3 From 8959e74399c798b45c0f5d477972b927c28f8dc9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 26 Sep 2011 11:45:30 +0200 Subject: mfd: Delete ab3550 driver The AB3550 never passed the prototype stage. Instead it was used as a precursor to AB5500 for testing basic building blocks used in that chip, since they had large similarities. Since AB3550 will not see the light of day in product form and since the prototypes are no longer used, let's delete the driver and any references to it. Cc: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- arch/arm/mach-u300/i2c.c | 51 +- arch/arm/mach-u300/include/mach/irqs.h | 7 - drivers/mfd/Kconfig | 14 - drivers/mfd/Makefile | 1 - drivers/mfd/ab3550-core.c | 1380 -------------------------------- include/linux/mfd/abx500.h | 48 +- 6 files changed, 2 insertions(+), 1499 deletions(-) delete mode 100644 drivers/mfd/ab3550-core.c (limited to 'drivers/mfd') diff --git a/arch/arm/mach-u300/i2c.c b/arch/arm/mach-u300/i2c.c index f0394baa11fa..5140deeddf7b 100644 --- a/arch/arm/mach-u300/i2c.c +++ b/arch/arm/mach-u300/i2c.c @@ -256,57 +256,8 @@ static struct ab3100_platform_data ab3100_plf_data = { }; #endif -#ifdef CONFIG_AB3550_CORE -static struct abx500_init_settings ab3550_init_settings[] = { - { - .bank = 0, - .reg = AB3550_IMR1, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR2, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR3, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR4, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR5, - /* The two most significant bits are not used */ - .setting = 0x3f - }, -}; - -static struct ab3550_platform_data ab3550_plf_data = { - .irq = { - .base = IRQ_AB3550_BASE, - .count = (IRQ_AB3550_END - IRQ_AB3550_BASE + 1), - }, - .dev_data = { - }, - .init_settings = ab3550_init_settings, - .init_settings_sz = ARRAY_SIZE(ab3550_init_settings), -}; -#endif - static struct i2c_board_info __initdata bus0_i2c_board_info[] = { -#if defined(CONFIG_AB3550_CORE) - { - .type = "ab3550", - .addr = 0x4A, - .irq = IRQ_U300_IRQ0_EXT, - .platform_data = &ab3550_plf_data, - }, -#elif defined(CONFIG_AB3100_CORE) +#ifdef CONFIG_AB3100_CORE { .type = "ab3100", .addr = 0x48, diff --git a/arch/arm/mach-u300/include/mach/irqs.h b/arch/arm/mach-u300/include/mach/irqs.h index 09b1b28fa8fd..a6867b12773e 100644 --- a/arch/arm/mach-u300/include/mach/irqs.h +++ b/arch/arm/mach-u300/include/mach/irqs.h @@ -109,13 +109,6 @@ #define U300_NR_IRQS 48 #endif -#ifdef CONFIG_AB3550_CORE -#define IRQ_AB3550_BASE (U300_NR_IRQS) -#define IRQ_AB3550_END (IRQ_AB3550_BASE + 37) - -#define NR_IRQS (IRQ_AB3550_END + 1) -#else #define NR_IRQS U300_NR_IRQS -#endif #endif diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f22bd2f0ecc3..74d4893a8f21 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -616,20 +616,6 @@ config AB8500_GPADC help AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage -config AB3550_CORE - bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions" - select MFD_CORE - depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE - help - Select this to enable the AB3550 Mixed Signal IC core - functionality. This connects to a AB3550 on the I2C bus - and expose a number of symbols needed for dependent devices - to read and write registers and subscribe to events from - this multi-functional IC. This is needed to use other features - of the AB3550 such as battery-backed RTC, charging control, - LEDs, vibrator, system power and temperature, power management - and ALSA sound. - config MFD_DB8500_PRCMU bool "ST-Ericsson DB8500 Power Reset Control Management Unit" depends on UX500_SOC_DB8500 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7ed553d8157a..b2292eb75242 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -79,7 +79,6 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o -obj-$(CONFIG_AB3550_CORE) += ab3550-core.o obj-$(CONFIG_AB5500_CORE) += ab5500-core.o obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c deleted file mode 100644 index 882ea7192d8b..000000000000 --- a/drivers/mfd/ab3550-core.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * Copyright (C) 2007-2010 ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - * Low-level core for exclusive access to the AB3550 IC on the I2C bus - * and some basic chip-configuration. - * Author: Bengt Jonsson - * Author: Mattias Nilsson - * Author: Mattias Wallin - * Author: Rickard Andersson - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AB3550_NAME_STRING "ab3550" -#define AB3550_ID_FORMAT_STRING "AB3550 %s" -#define AB3550_NUM_BANKS 2 -#define AB3550_NUM_EVENT_REG 5 - -/* These are the only registers inside AB3550 used in this main file */ - -/* Chip ID register */ -#define AB3550_CID_REG 0x20 - -/* Interrupt event registers */ -#define AB3550_EVENT_BANK 0 -#define AB3550_EVENT_REG 0x22 - -/* Read/write operation values. */ -#define AB3550_PERM_RD (0x01) -#define AB3550_PERM_WR (0x02) - -/* Read/write permissions. */ -#define AB3550_PERM_RO (AB3550_PERM_RD) -#define AB3550_PERM_RW (AB3550_PERM_RD | AB3550_PERM_WR) - -/** - * struct ab3550 - * @access_mutex: lock out concurrent accesses to the AB registers - * @i2c_client: I2C client for this chip - * @chip_name: name of this chip variant - * @chip_id: 8 bit chip ID for this chip variant - * @mask_work: a worker for writing to mask registers - * @event_lock: a lock to protect the event_mask - * @event_mask: a local copy of the mask event registers - * @startup_events: a copy of the first reading of the event registers - * @startup_events_read: whether the first events have been read - */ -struct ab3550 { - struct mutex access_mutex; - struct i2c_client *i2c_client[AB3550_NUM_BANKS]; - char chip_name[32]; - u8 chip_id; - struct work_struct mask_work; - spinlock_t event_lock; - u8 event_mask[AB3550_NUM_EVENT_REG]; - u8 startup_events[AB3550_NUM_EVENT_REG]; - bool startup_events_read; -#ifdef CONFIG_DEBUG_FS - unsigned int debug_bank; - unsigned int debug_address; -#endif -}; - -/** - * struct ab3550_reg_range - * @first: the first address of the range - * @last: the last address of the range - * @perm: access permissions for the range - */ -struct ab3550_reg_range { - u8 first; - u8 last; - u8 perm; -}; - -/** - * struct ab3550_reg_ranges - * @count: the number of ranges in the list - * @range: the list of register ranges - */ -struct ab3550_reg_ranges { - u8 count; - const struct ab3550_reg_range *range; -}; - -/* - * Permissible register ranges for reading and writing per device and bank. - * - * The ranges must be listed in increasing address order, and no overlaps are - * allowed. It is assumed that write permission implies read permission - * (i.e. only RO and RW permissions should be used). Ranges with write - * permission must not be split up. - */ - -#define NO_RANGE {.count = 0, .range = NULL,} - -static struct -ab3550_reg_ranges ab3550_reg_ranges[AB3550_NUM_DEVICES][AB3550_NUM_BANKS] = { - [AB3550_DEVID_DAC] = { - NO_RANGE, - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0xb0, - .last = 0xba, - .perm = AB3550_PERM_RW, - }, - { - .first = 0xbc, - .last = 0xc3, - .perm = AB3550_PERM_RW, - }, - }, - }, - }, - [AB3550_DEVID_LEDS] = { - NO_RANGE, - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x5a, - .last = 0x88, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x8a, - .last = 0xad, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_POWER] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_REGULATORS] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x69, - .last = 0xa3, - .perm = AB3550_PERM_RW, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x14, - .last = 0x16, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_SIM] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x14, - .last = 0x17, - .perm = AB3550_PERM_RW, - }, - } - - }, - }, - [AB3550_DEVID_UART] = { - NO_RANGE, - NO_RANGE, - }, - [AB3550_DEVID_RTC] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0c, - .perm = AB3550_PERM_RW, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_CHARGER] = { - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x10, - .last = 0x1a, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_ADC] = { - NO_RANGE, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x20, - .last = 0x56, - .perm = AB3550_PERM_RW, - }, - - } - }, - }, - [AB3550_DEVID_FUELGAUGE] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_VIBRATOR] = { - NO_RANGE, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x10, - .last = 0x13, - .perm = AB3550_PERM_RW, - }, - - } - }, - }, - [AB3550_DEVID_CODEC] = { - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x31, - .last = 0x63, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x65, - .last = 0x68, - .perm = AB3550_PERM_RW, - }, - } - }, - NO_RANGE, - }, -}; - -static struct mfd_cell ab3550_devs[AB3550_NUM_DEVICES] = { - [AB3550_DEVID_DAC] = { - .name = "ab3550-dac", - .id = AB3550_DEVID_DAC, - .num_resources = 0, - }, - [AB3550_DEVID_LEDS] = { - .name = "ab3550-leds", - .id = AB3550_DEVID_LEDS, - }, - [AB3550_DEVID_POWER] = { - .name = "ab3550-power", - .id = AB3550_DEVID_POWER, - }, - [AB3550_DEVID_REGULATORS] = { - .name = "ab3550-regulators", - .id = AB3550_DEVID_REGULATORS, - }, - [AB3550_DEVID_SIM] = { - .name = "ab3550-sim", - .id = AB3550_DEVID_SIM, - }, - [AB3550_DEVID_UART] = { - .name = "ab3550-uart", - .id = AB3550_DEVID_UART, - }, - [AB3550_DEVID_RTC] = { - .name = "ab3550-rtc", - .id = AB3550_DEVID_RTC, - }, - [AB3550_DEVID_CHARGER] = { - .name = "ab3550-charger", - .id = AB3550_DEVID_CHARGER, - }, - [AB3550_DEVID_ADC] = { - .name = "ab3550-adc", - .id = AB3550_DEVID_ADC, - .num_resources = 10, - .resources = (struct resource[]) { - { - .name = "TRIGGER-0", - .flags = IORESOURCE_IRQ, - .start = 16, - .end = 16, - }, - { - .name = "TRIGGER-1", - .flags = IORESOURCE_IRQ, - .start = 17, - .end = 17, - }, - { - .name = "TRIGGER-2", - .flags = IORESOURCE_IRQ, - .start = 18, - .end = 18, - }, - { - .name = "TRIGGER-3", - .flags = IORESOURCE_IRQ, - .start = 19, - .end = 19, - }, - { - .name = "TRIGGER-4", - .flags = IORESOURCE_IRQ, - .start = 20, - .end = 20, - }, - { - .name = "TRIGGER-5", - .flags = IORESOURCE_IRQ, - .start = 21, - .end = 21, - }, - { - .name = "TRIGGER-6", - .flags = IORESOURCE_IRQ, - .start = 22, - .end = 22, - }, - { - .name = "TRIGGER-7", - .flags = IORESOURCE_IRQ, - .start = 23, - .end = 23, - }, - { - .name = "TRIGGER-VBAT-TXON", - .flags = IORESOURCE_IRQ, - .start = 13, - .end = 13, - }, - { - .name = "TRIGGER-VBAT", - .flags = IORESOURCE_IRQ, - .start = 12, - .end = 12, - }, - }, - }, - [AB3550_DEVID_FUELGAUGE] = { - .name = "ab3550-fuelgauge", - .id = AB3550_DEVID_FUELGAUGE, - }, - [AB3550_DEVID_VIBRATOR] = { - .name = "ab3550-vibrator", - .id = AB3550_DEVID_VIBRATOR, - }, - [AB3550_DEVID_CODEC] = { - .name = "ab3550-codec", - .id = AB3550_DEVID_CODEC, - }, -}; - -/* - * I2C transactions with error messages. - */ -static int ab3550_i2c_master_send(struct ab3550 *ab, u8 bank, u8 *data, - u8 count) -{ - int err; - - err = i2c_master_send(ab->i2c_client[bank], data, count); - if (err < 0) { - dev_err(&ab->i2c_client[0]->dev, "send error: %d\n", err); - return err; - } - return 0; -} - -static int ab3550_i2c_master_recv(struct ab3550 *ab, u8 bank, u8 *data, - u8 count) -{ - int err; - - err = i2c_master_recv(ab->i2c_client[bank], data, count); - if (err < 0) { - dev_err(&ab->i2c_client[0]->dev, "receive error: %d\n", err); - return err; - } - return 0; -} - -/* - * Functionality for getting/setting register values. - */ -static int get_register_interruptible(struct ab3550 *ab, u8 bank, u8 reg, - u8 *value) -{ - int err; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - err = ab3550_i2c_master_send(ab, bank, ®, 1); - if (!err) - err = ab3550_i2c_master_recv(ab, bank, value, 1); - - mutex_unlock(&ab->access_mutex); - return err; -} - -static int get_register_page_interruptible(struct ab3550 *ab, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) -{ - int err; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - err = ab3550_i2c_master_send(ab, bank, &first_reg, 1); - if (!err) - err = ab3550_i2c_master_recv(ab, bank, regvals, numregs); - - mutex_unlock(&ab->access_mutex); - return err; -} - -static int mask_and_set_register_interruptible(struct ab3550 *ab, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues) -{ - int err = 0; - - if (likely(bitmask)) { - u8 reg_bits[2] = {reg, 0}; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - if (bitmask == 0xFF) /* No need to read in this case. */ - reg_bits[1] = bitvalues; - else { /* Read and modify the register value. */ - u8 bits; - - err = ab3550_i2c_master_send(ab, bank, ®, 1); - if (err) - goto unlock_and_return; - err = ab3550_i2c_master_recv(ab, bank, &bits, 1); - if (err) - goto unlock_and_return; - reg_bits[1] = ((~bitmask & bits) | - (bitmask & bitvalues)); - } - /* Write the new value. */ - err = ab3550_i2c_master_send(ab, bank, reg_bits, 2); -unlock_and_return: - mutex_unlock(&ab->access_mutex); - } - return err; -} - -/* - * Read/write permission checking functions. - */ -static bool page_write_allowed(const struct ab3550_reg_ranges *ranges, - u8 first_reg, u8 last_reg) -{ - u8 i; - - if (last_reg < first_reg) - return false; - - for (i = 0; i < ranges->count; i++) { - if (first_reg < ranges->range[i].first) - break; - if ((last_reg <= ranges->range[i].last) && - (ranges->range[i].perm & AB3550_PERM_WR)) - return true; - } - return false; -} - -static bool reg_write_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) -{ - return page_write_allowed(ranges, reg, reg); -} - -static bool page_read_allowed(const struct ab3550_reg_ranges *ranges, - u8 first_reg, u8 last_reg) -{ - u8 i; - - if (last_reg < first_reg) - return false; - /* Find the range (if it exists in the list) that includes first_reg. */ - for (i = 0; i < ranges->count; i++) { - if (first_reg < ranges->range[i].first) - return false; - if (first_reg <= ranges->range[i].last) - break; - } - /* Make sure that the entire range up to and including last_reg is - * readable. This may span several of the ranges in the list. - */ - while ((i < ranges->count) && - (ranges->range[i].perm & AB3550_PERM_RD)) { - if (last_reg <= ranges->range[i].last) - return true; - if ((++i >= ranges->count) || - (ranges->range[i].first != - (ranges->range[i - 1].last + 1))) { - break; - } - } - return false; -} - -static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) -{ - return page_read_allowed(ranges, reg, reg); -} - -/* - * The register access functionality. - */ -static int ab3550_get_chip_id(struct device *dev) -{ - struct ab3550 *ab = dev_get_drvdata(dev->parent); - return (int)ab->chip_id; -} - -static int ab3550_mask_and_set_register_interruptible(struct device *dev, - u8 bank, u8 reg, u8 bitmask, u8 bitvalues) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !reg_write_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return mask_and_set_register_interruptible(ab, bank, reg, - bitmask, bitvalues); -} - -static int ab3550_set_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 value) -{ - return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, - value); -} - -static int ab3550_get_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 *value) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !reg_read_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return get_register_interruptible(ab, bank, reg, value); -} - -static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !page_read_allowed(&ab3550_reg_ranges[pdev->id][bank], - first_reg, (first_reg + numregs - 1))) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return get_register_page_interruptible(ab, bank, first_reg, regvals, - numregs); -} - -static int ab3550_event_registers_startup_state_get(struct device *dev, - u8 *event) -{ - struct ab3550 *ab; - - ab = dev_get_drvdata(dev->parent); - if (!ab->startup_events_read) - return -EAGAIN; /* Try again later */ - - memcpy(event, ab->startup_events, AB3550_NUM_EVENT_REG); - return 0; -} - -static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) -{ - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - bool val; - - ab = irq_get_chip_data(irq); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq -= plf_data->irq.base; - val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0); - - return val; -} - -static struct abx500_ops ab3550_ops = { - .get_chip_id = ab3550_get_chip_id, - .get_register = ab3550_get_register_interruptible, - .set_register = ab3550_set_register_interruptible, - .get_register_page = ab3550_get_register_page_interruptible, - .set_register_page = NULL, - .mask_and_set_register = ab3550_mask_and_set_register_interruptible, - .event_registers_startup_state_get = - ab3550_event_registers_startup_state_get, - .startup_irq_enabled = ab3550_startup_irq_enabled, -}; - -static irqreturn_t ab3550_irq_handler(int irq, void *data) -{ - struct ab3550 *ab = data; - int err; - unsigned int i; - u8 e[AB3550_NUM_EVENT_REG]; - u8 *events; - unsigned long flags; - - events = (ab->startup_events_read ? e : ab->startup_events); - - err = get_register_page_interruptible(ab, AB3550_EVENT_BANK, - AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG); - if (err) - goto err_event_rd; - - if (!ab->startup_events_read) { - dev_info(&ab->i2c_client[0]->dev, - "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n", - ab->startup_events[0], ab->startup_events[1], - ab->startup_events[2], ab->startup_events[3], - ab->startup_events[4]); - ab->startup_events_read = true; - goto out; - } - - /* The two highest bits in event[4] are not used. */ - events[4] &= 0x3f; - - spin_lock_irqsave(&ab->event_lock, flags); - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) - events[i] &= ~ab->event_mask[i]; - spin_unlock_irqrestore(&ab->event_lock, flags); - - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { - u8 bit; - u8 event_reg; - - dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n", - i, events[i]); - - event_reg = events[i]; - for (bit = 0; event_reg; bit++, event_reg /= 2) { - if (event_reg % 2) { - unsigned int irq; - struct ab3550_platform_data *plf_data; - - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = plf_data->irq.base + (i * 8) + bit; - handle_nested_irq(irq); - } - } - } -out: - return IRQ_HANDLED; - -err_event_rd: - dev_dbg(&ab->i2c_client[0]->dev, "error reading event registers\n"); - return IRQ_HANDLED; -} - -#ifdef CONFIG_DEBUG_FS -static struct ab3550_reg_ranges debug_ranges[AB3550_NUM_BANKS] = { - { - .count = 6, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - }, - { - .first = 0x10, - .last = 0x1a, - }, - { - .first = 0x1e, - .last = 0x4f, - }, - { - .first = 0x51, - .last = 0x63, - }, - { - .first = 0x65, - .last = 0xa3, - }, - { - .first = 0xa5, - .last = 0xa8, - }, - } - }, - { - .count = 8, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - }, - { - .first = 0x10, - .last = 0x17, - }, - { - .first = 0x1a, - .last = 0x1c, - }, - { - .first = 0x20, - .last = 0x56, - }, - { - .first = 0x5a, - .last = 0x88, - }, - { - .first = 0x8a, - .last = 0xad, - }, - { - .first = 0xb0, - .last = 0xba, - }, - { - .first = 0xbc, - .last = 0xc3, - }, - } - }, -}; - -static int ab3550_registers_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - int bank; - - seq_printf(s, AB3550_NAME_STRING " register values:\n"); - - for (bank = 0; bank < AB3550_NUM_BANKS; bank++) { - unsigned int i; - - seq_printf(s, " bank %d:\n", bank); - for (i = 0; i < debug_ranges[bank].count; i++) { - u8 reg; - - for (reg = debug_ranges[bank].range[i].first; - reg <= debug_ranges[bank].range[i].last; - reg++) { - u8 value; - - get_register_interruptible(ab, bank, reg, - &value); - seq_printf(s, " [%d/0x%02X]: 0x%02X\n", bank, - reg, value); - } - } - } - return 0; -} - -static int ab3550_registers_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_registers_print, inode->i_private); -} - -static const struct file_operations ab3550_registers_fops = { - .open = ab3550_registers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab3550_bank_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - - seq_printf(s, "%d\n", ab->debug_bank); - return 0; -} - -static int ab3550_bank_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_bank_print, inode->i_private); -} - -static ssize_t ab3550_bank_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_bank; - int err; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_bank); - if (err) - return err; - - if (user_bank >= AB3550_NUM_BANKS) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > number of banks\n"); - return -EINVAL; - } - - ab->debug_bank = user_bank; - - return count; -} - -static int ab3550_address_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - - seq_printf(s, "0x%02X\n", ab->debug_address); - return 0; -} - -static int ab3550_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_address_print, inode->i_private); -} - -static ssize_t ab3550_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_address; - int err; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_address); - if (err) - return err; - - if (user_address > 0xff) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - ab->debug_address = user_address; - return count; -} - -static int ab3550_val_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - int err; - u8 regvalue; - - err = get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) - return -EINVAL; - seq_printf(s, "0x%02X\n", regvalue); - - return 0; -} - -static int ab3550_val_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_val_print, inode->i_private); -} - -static ssize_t ab3550_val_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - u8 regvalue; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val > 0xff) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - err = mask_and_set_register_interruptible( - ab, (u8)ab->debug_bank, - (u8)ab->debug_address, 0xFF, (u8)user_val); - if (err) - return -EINVAL; - - get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) - return -EINVAL; - - return count; -} - -static const struct file_operations ab3550_bank_fops = { - .open = ab3550_bank_open, - .write = ab3550_bank_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab3550_address_fops = { - .open = ab3550_address_open, - .write = ab3550_address_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab3550_val_fops = { - .open = ab3550_val_open, - .write = ab3550_val_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static struct dentry *ab3550_dir; -static struct dentry *ab3550_reg_file; -static struct dentry *ab3550_bank_file; -static struct dentry *ab3550_address_file; -static struct dentry *ab3550_val_file; - -static inline void ab3550_setup_debugfs(struct ab3550 *ab) -{ - ab->debug_bank = 0; - ab->debug_address = 0x00; - - ab3550_dir = debugfs_create_dir(AB3550_NAME_STRING, NULL); - if (!ab3550_dir) - goto exit_no_debugfs; - - ab3550_reg_file = debugfs_create_file("all-registers", - S_IRUGO, ab3550_dir, ab, &ab3550_registers_fops); - if (!ab3550_reg_file) - goto exit_destroy_dir; - - ab3550_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_bank_fops); - if (!ab3550_bank_file) - goto exit_destroy_reg; - - ab3550_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_address_fops); - if (!ab3550_address_file) - goto exit_destroy_bank; - - ab3550_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops); - if (!ab3550_val_file) - goto exit_destroy_address; - - return; - -exit_destroy_address: - debugfs_remove(ab3550_address_file); -exit_destroy_bank: - debugfs_remove(ab3550_bank_file); -exit_destroy_reg: - debugfs_remove(ab3550_reg_file); -exit_destroy_dir: - debugfs_remove(ab3550_dir); -exit_no_debugfs: - dev_err(&ab->i2c_client[0]->dev, "failed to create debugfs entries.\n"); - return; -} - -static inline void ab3550_remove_debugfs(void) -{ - debugfs_remove(ab3550_val_file); - debugfs_remove(ab3550_address_file); - debugfs_remove(ab3550_bank_file); - debugfs_remove(ab3550_reg_file); - debugfs_remove(ab3550_dir); -} - -#else /* !CONFIG_DEBUG_FS */ -static inline void ab3550_setup_debugfs(struct ab3550 *ab) -{ -} -static inline void ab3550_remove_debugfs(void) -{ -} -#endif - -/* - * Basic set-up, datastructure creation/destruction and I2C interface. - * This sets up a default config in the AB3550 chip so that it - * will work as expected. - */ -static int __devinit ab3550_setup(struct ab3550 *ab) -{ - int err = 0; - int i; - struct ab3550_platform_data *plf_data; - struct abx500_init_settings *settings; - - plf_data = ab->i2c_client[0]->dev.platform_data; - settings = plf_data->init_settings; - - for (i = 0; i < plf_data->init_settings_sz; i++) { - err = mask_and_set_register_interruptible(ab, - settings[i].bank, - settings[i].reg, - 0xFF, settings[i].setting); - if (err) - goto exit_no_setup; - - /* If event mask register update the event mask in ab3550 */ - if ((settings[i].bank == 0) && - (AB3550_IMR1 <= settings[i].reg) && - (settings[i].reg <= AB3550_IMR5)) { - ab->event_mask[settings[i].reg - AB3550_IMR1] = - settings[i].setting; - } - } -exit_no_setup: - return err; -} - -static void ab3550_mask_work(struct work_struct *work) -{ - struct ab3550 *ab = container_of(work, struct ab3550, mask_work); - int i; - unsigned long flags; - u8 mask[AB3550_NUM_EVENT_REG]; - - spin_lock_irqsave(&ab->event_lock, flags); - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) - mask[i] = ab->event_mask[i]; - spin_unlock_irqrestore(&ab->event_lock, flags); - - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { - int err; - - err = mask_and_set_register_interruptible(ab, 0, - (AB3550_IMR1 + i), ~0, mask[i]); - if (err) - dev_err(&ab->i2c_client[0]->dev, - "ab3550_mask_work failed 0x%x,0x%x\n", - (AB3550_IMR1 + i), mask[i]); - } -} - -static void ab3550_mask(struct irq_data *data) -{ - unsigned long flags; - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - int irq; - - ab = irq_data_get_irq_chip_data(data); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = data->irq - plf_data->irq.base; - - spin_lock_irqsave(&ab->event_lock, flags); - ab->event_mask[irq / 8] |= BIT(irq % 8); - spin_unlock_irqrestore(&ab->event_lock, flags); - - schedule_work(&ab->mask_work); -} - -static void ab3550_unmask(struct irq_data *data) -{ - unsigned long flags; - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - int irq; - - ab = irq_data_get_irq_chip_data(data); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = data->irq - plf_data->irq.base; - - spin_lock_irqsave(&ab->event_lock, flags); - ab->event_mask[irq / 8] &= ~BIT(irq % 8); - spin_unlock_irqrestore(&ab->event_lock, flags); - - schedule_work(&ab->mask_work); -} - -static void noop(struct irq_data *data) -{ -} - -static struct irq_chip ab3550_irq_chip = { - .name = "ab3550-core", /* Keep the same name as the request */ - .irq_disable = ab3550_mask, /* No default to mask in chip.c */ - .irq_ack = noop, - .irq_mask = ab3550_mask, - .irq_unmask = ab3550_unmask, -}; - -struct ab_family_id { - u8 id; - char *name; -}; - -static const struct ab_family_id ids[] __devinitconst = { - /* AB3550 */ - { - .id = AB3550_P1A, - .name = "P1A" - }, - /* Terminator */ - { - .id = 0x00, - } -}; - -static int __devinit ab3550_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ab3550 *ab; - struct ab3550_platform_data *ab3550_plf_data = - client->dev.platform_data; - int err; - int i; - int num_i2c_clients = 0; - - ab = kzalloc(sizeof(struct ab3550), GFP_KERNEL); - if (!ab) { - dev_err(&client->dev, - "could not allocate " AB3550_NAME_STRING " device\n"); - return -ENOMEM; - } - - /* Initialize data structure */ - mutex_init(&ab->access_mutex); - spin_lock_init(&ab->event_lock); - ab->i2c_client[0] = client; - - i2c_set_clientdata(client, ab); - - /* Read chip ID register */ - err = get_register_interruptible(ab, 0, AB3550_CID_REG, &ab->chip_id); - if (err) { - dev_err(&client->dev, "could not communicate with the analog " - "baseband chip\n"); - goto exit_no_detect; - } - - for (i = 0; ids[i].id != 0x0; i++) { - if (ids[i].id == ab->chip_id) { - snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1, - AB3550_ID_FORMAT_STRING, ids[i].name); - break; - } - } - - if (ids[i].id == 0x0) { - dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n", - ab->chip_id); - dev_err(&client->dev, "driver not started!\n"); - goto exit_no_detect; - } - - dev_info(&client->dev, "detected AB chip: %s\n", &ab->chip_name[0]); - - /* Attach other dummy I2C clients. */ - while (++num_i2c_clients < AB3550_NUM_BANKS) { - ab->i2c_client[num_i2c_clients] = - i2c_new_dummy(client->adapter, - (client->addr + num_i2c_clients)); - if (!ab->i2c_client[num_i2c_clients]) { - err = -ENOMEM; - goto exit_no_dummy_client; - } - strlcpy(ab->i2c_client[num_i2c_clients]->name, id->name, - sizeof(ab->i2c_client[num_i2c_clients]->name)); - } - - err = ab3550_setup(ab); - if (err) - goto exit_no_setup; - - INIT_WORK(&ab->mask_work, ab3550_mask_work); - - for (i = 0; i < ab3550_plf_data->irq.count; i++) { - unsigned int irq; - - irq = ab3550_plf_data->irq.base + i; - irq_set_chip_data(irq, ab); - irq_set_chip_and_handler(irq, &ab3550_irq_chip, - handle_simple_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - err = request_threaded_irq(client->irq, NULL, ab3550_irq_handler, - IRQF_ONESHOT, "ab3550-core", ab); - /* This real unpredictable IRQ is of course sampled for entropy */ - rand_initialize_irq(client->irq); - - if (err) - goto exit_no_irq; - - err = abx500_register_ops(&client->dev, &ab3550_ops); - if (err) - goto exit_no_ops; - - /* Set up and register the platform devices. */ - for (i = 0; i < AB3550_NUM_DEVICES; i++) { - ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i]; - ab3550_devs[i].pdata_size = ab3550_plf_data->dev_data_sz[i]; - } - - err = mfd_add_devices(&client->dev, 0, ab3550_devs, - ARRAY_SIZE(ab3550_devs), NULL, - ab3550_plf_data->irq.base); - - ab3550_setup_debugfs(ab); - - return 0; - -exit_no_ops: -exit_no_irq: -exit_no_setup: -exit_no_dummy_client: - /* Unregister the dummy i2c clients. */ - while (--num_i2c_clients) - i2c_unregister_device(ab->i2c_client[num_i2c_clients]); -exit_no_detect: - kfree(ab); - return err; -} - -static int __devexit ab3550_remove(struct i2c_client *client) -{ - struct ab3550 *ab = i2c_get_clientdata(client); - int num_i2c_clients = AB3550_NUM_BANKS; - - mfd_remove_devices(&client->dev); - ab3550_remove_debugfs(); - - while (--num_i2c_clients) - i2c_unregister_device(ab->i2c_client[num_i2c_clients]); - - /* - * At this point, all subscribers should have unregistered - * their notifiers so deactivate IRQ - */ - free_irq(client->irq, ab); - kfree(ab); - return 0; -} - -static const struct i2c_device_id ab3550_id[] = { - {AB3550_NAME_STRING, 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, ab3550_id); - -static struct i2c_driver ab3550_driver = { - .driver = { - .name = AB3550_NAME_STRING, - .owner = THIS_MODULE, - }, - .id_table = ab3550_id, - .probe = ab3550_probe, - .remove = __devexit_p(ab3550_remove), -}; - -static int __init ab3550_i2c_init(void) -{ - return i2c_add_driver(&ab3550_driver); -} - -static void __exit ab3550_i2c_exit(void) -{ - i2c_del_driver(&ab3550_driver); -} - -subsys_initcall(ab3550_i2c_init); -module_exit(ab3550_i2c_exit); - -MODULE_AUTHOR("Mattias Wallin "); -MODULE_DESCRIPTION("AB3550 core driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 79ec2c7b5fab..6d096e8b7746 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -6,7 +6,7 @@ * * ABX500 core access functions. * The abx500 interface is used for the Analog Baseband chip - * ab3100, ab3550, ab5500, and ab8500. + * ab3100, ab5500, and ab8500. * * Author: Mattias Wallin * Author: Mattias Nilsson @@ -29,7 +29,6 @@ #define AB3100_P1G 0xc6 #define AB3100_R2A 0xc7 #define AB3100_R2B 0xc8 -#define AB3550_P1A 0x10 #define AB5500_1_0 0x20 #define AB5500_1_1 0x21 #define AB5500_2_0 0x24 @@ -143,39 +142,6 @@ int ab3100_event_register(struct ab3100 *ab3100, int ab3100_event_unregister(struct ab3100 *ab3100, struct notifier_block *nb); -/* AB3550, STR register flags */ -#define AB3550_STR_ONSWA (0x01) -#define AB3550_STR_ONSWB (0x02) -#define AB3550_STR_ONSWC (0x04) -#define AB3550_STR_DCIO (0x08) -#define AB3550_STR_BOOT_MODE (0x10) -#define AB3550_STR_SIM_OFF (0x20) -#define AB3550_STR_BATT_REMOVAL (0x40) -#define AB3550_STR_VBUS (0x80) - -/* Interrupt mask registers */ -#define AB3550_IMR1 0x29 -#define AB3550_IMR2 0x2a -#define AB3550_IMR3 0x2b -#define AB3550_IMR4 0x2c -#define AB3550_IMR5 0x2d - -enum ab3550_devid { - AB3550_DEVID_ADC, - AB3550_DEVID_DAC, - AB3550_DEVID_LEDS, - AB3550_DEVID_POWER, - AB3550_DEVID_REGULATORS, - AB3550_DEVID_SIM, - AB3550_DEVID_UART, - AB3550_DEVID_RTC, - AB3550_DEVID_CHARGER, - AB3550_DEVID_FUELGAUGE, - AB3550_DEVID_VIBRATOR, - AB3550_DEVID_CODEC, - AB3550_NUM_DEVICES, -}; - /** * struct abx500_init_setting * Initial value of the registers for driver to use during setup. @@ -186,18 +152,6 @@ struct abx500_init_settings { u8 setting; }; -/** - * struct ab3550_platform_data - * Data supplied to initialize board connections to the AB3550 - */ -struct ab3550_platform_data { - struct {unsigned int base; unsigned int count; } irq; - void *dev_data[AB3550_NUM_DEVICES]; - size_t dev_data_sz[AB3550_NUM_DEVICES]; - struct abx500_init_settings *init_settings; - unsigned int init_settings_sz; -}; - int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, u8 value); int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, -- cgit v1.2.3 From bb3149a93be1703ddc77ba34633df731bda5f269 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 23 Sep 2011 21:51:42 +0200 Subject: mfd: Fix a sparse warning about mc13xxx_chipname not being declared Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index edcd397cae11..e9619acc0237 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -477,7 +477,7 @@ enum mc13xxx_id { MC13XXX_ID_INVALID, }; -const char *mc13xxx_chipname[] = { +static const char *mc13xxx_chipname[] = { [MC13XXX_ID_MC13783] = "mc13783", [MC13XXX_ID_MC13892] = "mc13892", }; -- cgit v1.2.3 From ab2b9260df67e29d5bd69d989f2f84f8c2ed4238 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 4 Oct 2011 11:52:29 +0200 Subject: mfd: Fix twl6030 lockdep recursion warning on setting wake IRQs LOCKDEP explicitly sets all irq_desc locks as a single lock-class, causing "possible recursive locking detected" when the TWL RTC driver calls through enable_irq_wake to twl6030_irq_set_wake, which recursively calls irq_set_irq_wake. Although the irq_desc and lock are different, LOCKDEP treats these as equivalent, presumably due to problems that can be incurred when locking more than one irq_desc, so best to avoid this. Suspend/resume actions implemented as PM notifiers to avoid touch the TWL core for this. Signed-off-by: Todd Poynor Acked-by: Santosh Shilimkar Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6030-irq.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index a17b42360e52..a014ec489e68 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "twl-core.h" @@ -83,8 +84,42 @@ static int twl6030_interrupt_mapping[24] = { /*----------------------------------------------------------------------*/ static unsigned twl6030_irq_base; +static int twl_irq; +static bool twl_irq_wake_enabled; static struct completion irq_event; +static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0); + +static int twl6030_irq_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + int chained_wakeups; + + switch (pm_event) { + case PM_SUSPEND_PREPARE: + chained_wakeups = atomic_read(&twl6030_wakeirqs); + + if (chained_wakeups && !twl_irq_wake_enabled) { + if (enable_irq_wake(twl_irq)) + pr_err("twl6030 IRQ wake enable failed\n"); + else + twl_irq_wake_enabled = true; + } else if (!chained_wakeups && twl_irq_wake_enabled) { + disable_irq_wake(twl_irq); + twl_irq_wake_enabled = false; + } + + break; + default: + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block twl6030_irq_pm_notifier_block = { + .notifier_call = twl6030_irq_pm_notifier, +}; /* * This thread processes interrupts reported by the Primary Interrupt Handler. @@ -189,9 +224,12 @@ static inline void activate_irq(int irq) int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) { - int twl_irq = (int)irq_get_chip_data(d->irq); + if (on) + atomic_inc(&twl6030_wakeirqs); + else + atomic_dec(&twl6030_wakeirqs); - return irq_set_irq_wake(twl_irq, on); + return 0; } /*----------------------------------------------------------------------*/ @@ -354,6 +392,9 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) status = PTR_ERR(task); goto fail_kthread; } + + twl_irq = irq_num; + register_pm_notifier(&twl6030_irq_pm_notifier_block); return status; fail_kthread: @@ -367,6 +408,7 @@ fail_irq: int twl6030_exit_irq(void) { + unregister_pm_notifier(&twl6030_irq_pm_notifier_block); if (twl6030_irq_base) { pr_err("twl6030: can't yet clean up IRQs?\n"); -- cgit v1.2.3 From 782baa20b201100e14ce065bb7b890169e7a5d3c Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 26 Sep 2011 16:44:24 -0700 Subject: mfd: Disable twl6030 IRQ during suspend Module IRQs may still be disabled by DPM at the time the TWL6030 ISR runs, causing handle_simple_irq() to silently do nothing. This may result in missing TWL RTC alarm wakeups, for example, since the RTC child module ISR is not called to ack the IRQ. Disable the TWL6030 IRQ during suspend, enable it at DPM resume time, at which time the child module IRQs will be re-enabled. Signed-off-by: Todd Poynor Acked-by: Santosh Shilimkar Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6030-irq.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index a014ec489e68..deec3ec858bf 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -109,7 +109,13 @@ static int twl6030_irq_pm_notifier(struct notifier_block *notifier, twl_irq_wake_enabled = false; } + disable_irq(twl_irq); break; + + case PM_POST_SUSPEND: + enable_irq(twl_irq); + break; + default: break; } -- cgit v1.2.3 From c38d66ac924e84ea3606c408b117157415df07b3 Mon Sep 17 00:00:00 2001 From: Jin Park Date: Tue, 4 Oct 2011 12:35:07 +0200 Subject: mfd: Fix aat2870 build failure for x86_64 Without this fix, we get: error: call to ‘copy_from_user_overflow’ declared with attribute error: copy_from_user() buffer size is not provably correct make[3]: *** [drivers/mfd/aat2870-core.o] Error 1 And this was triggered by commit da417bacc9143b934f1a480a25d0fb2bb648a820 Signed-off-by: Jin Park Signed-off-by: Samuel Ortiz --- drivers/mfd/aat2870-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c index 345dc658ef06..02c42015ba51 100644 --- a/drivers/mfd/aat2870-core.c +++ b/drivers/mfd/aat2870-core.c @@ -295,7 +295,7 @@ static ssize_t aat2870_reg_write_file(struct file *file, { struct aat2870_data *aat2870 = file->private_data; char buf[32]; - int buf_size; + ssize_t buf_size; char *start = buf; unsigned long addr, val; int ret; -- cgit v1.2.3 From c553b3ca12046884af1a72ffb6e9d841a026adb9 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 12 Aug 2011 10:27:20 +0200 Subject: mfd: Refactor DB8500 PRCMU reg access Instead of carrying around the __PRCMU_BASE in every read or write to the PRCMU registers, move it out to the register definition file and define registers along with their base offset so that the code gets easier to read. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu-regs.h | 266 +++++++++++++++++++++++----------------- drivers/mfd/db8500-prcmu.c | 159 ++++++++++++------------ 2 files changed, 229 insertions(+), 196 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu-regs.h b/drivers/mfd/db8500-prcmu-regs.h index 3bbf04d58043..ec22e9f15d32 100644 --- a/drivers/mfd/db8500-prcmu-regs.h +++ b/drivers/mfd/db8500-prcmu-regs.h @@ -9,99 +9,170 @@ * * PRCM Unit registers */ + #ifndef __DB8500_PRCMU_REGS_H #define __DB8500_PRCMU_REGS_H -#include #include #define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end)) -#define PRCM_ARM_PLLDIVPS 0x118 -#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE BITS(0, 5) -#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xF - -#define PRCM_PLLARM_LOCKP 0x0A8 -#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 BIT(1) - -#define PRCM_ARM_CHGCLKREQ 0x114 -#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0) - -#define PRCM_PLLARM_ENABLE 0x98 -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE BIT(0) -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON BIT(8) - -#define PRCM_ARMCLKFIX_MGT 0x0 -#define PRCM_A9_RESETN_CLR 0x1f4 -#define PRCM_A9_RESETN_SET 0x1f0 -#define PRCM_ARM_LS_CLAMP 0x30C -#define PRCM_SRAM_A9 0x308 +#define PRCM_SVACLK_MGT_OFF 0x008 +#define PRCM_SIACLK_MGT_OFF 0x00C +#define PRCM_SGACLK_MGT_OFF 0x014 +#define PRCM_UARTCLK_MGT_OFF 0x018 +#define PRCM_MSP02CLK_MGT_OFF 0x01C +#define PRCM_I2CCLK_MGT_OFF 0x020 +#define PRCM_SDMMCCLK_MGT_OFF 0x024 +#define PRCM_SLIMCLK_MGT_OFF 0x028 +#define PRCM_PER1CLK_MGT_OFF 0x02C +#define PRCM_PER2CLK_MGT_OFF 0x030 +#define PRCM_PER3CLK_MGT_OFF 0x034 +#define PRCM_PER5CLK_MGT_OFF 0x038 +#define PRCM_PER6CLK_MGT_OFF 0x03C +#define PRCM_PER7CLK_MGT_OFF 0x040 +#define PRCM_PWMCLK_MGT_OFF 0x044 /* for DB5500 */ +#define PRCM_IRDACLK_MGT_OFF 0x048 /* for DB5500 */ +#define PRCM_IRRCCLK_MGT_OFF 0x04C /* for DB5500 */ +#define PRCM_LCDCLK_MGT_OFF 0x044 +#define PRCM_BMLCLK_MGT_OFF 0x04C +#define PRCM_HSITXCLK_MGT_OFF 0x050 +#define PRCM_HSIRXCLK_MGT_OFF 0x054 +#define PRCM_HDMICLK_MGT_OFF 0x058 +#define PRCM_APEATCLK_MGT_OFF 0x05C +#define PRCM_APETRACECLK_MGT_OFF 0x060 +#define PRCM_MCDECLK_MGT_OFF 0x064 +#define PRCM_IPI2CCLK_MGT_OFF 0x068 +#define PRCM_DSIALTCLK_MGT_OFF 0x06C +#define PRCM_DMACLK_MGT_OFF 0x074 +#define PRCM_B2R2CLK_MGT_OFF 0x078 +#define PRCM_TVCLK_MGT_OFF 0x07C +#define PRCM_UNIPROCLK_MGT_OFF 0x278 +#define PRCM_SSPCLK_MGT_OFF 0x280 +#define PRCM_RNGCLK_MGT_OFF 0x284 +#define PRCM_UICCCLK_MGT_OFF 0x27C +#define PRCM_MSP1CLK_MGT_OFF 0x288 + +#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) +#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f +#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf + +#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8) +#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 + +#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) +#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 + +#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) +#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 +#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100 + +#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) +#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C) +#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) +#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) +#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c) +#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308) + +#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0) +#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1) /* ARM WFI Standby signal register */ -#define PRCM_ARM_WFI_STANDBY 0x130 -#define PRCM_IOCR 0x310 -#define PRCM_IOCR_IOFORCE BIT(0) +#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) +#define PRCM_IOCR (_PRCMU_BASE + 0x310) +#define PRCM_IOCR_IOFORCE 0x1 /* CPU mailbox registers */ -#define PRCM_MBOX_CPU_VAL 0x0FC -#define PRCM_MBOX_CPU_SET 0x100 +#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) +#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100) +#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104) /* Dual A9 core interrupt management unit registers */ -#define PRCM_A9_MASK_REQ 0x328 -#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ BIT(0) - -#define PRCM_A9_MASK_ACK 0x32C -#define PRCM_ARMITMSK31TO0 0x11C -#define PRCM_ARMITMSK63TO32 0x120 -#define PRCM_ARMITMSK95TO64 0x124 -#define PRCM_ARMITMSK127TO96 0x128 -#define PRCM_POWER_STATE_VAL 0x25C -#define PRCM_ARMITVAL31TO0 0x260 -#define PRCM_ARMITVAL63TO32 0x264 -#define PRCM_ARMITVAL95TO64 0x268 -#define PRCM_ARMITVAL127TO96 0x26C - -#define PRCM_HOSTACCESS_REQ 0x334 -#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ BIT(0) - -#define PRCM_ARM_IT1_CLR 0x48C -#define PRCM_ARM_IT1_VAL 0x494 - -#define PRCM_ITSTATUS0 0x148 -#define PRCM_ITSTATUS1 0x150 -#define PRCM_ITSTATUS2 0x158 -#define PRCM_ITSTATUS3 0x160 -#define PRCM_ITSTATUS4 0x168 -#define PRCM_ITSTATUS5 0x484 -#define PRCM_ITCLEAR5 0x488 -#define PRCM_ARMIT_MASKXP70_IT 0x1018 +#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) +#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 + +#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) +#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) +#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) +#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124) +#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128) +#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C) +#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260) +#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264) +#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268) +#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C) + +#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) +#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1 +#define ARM_WAKEUP_MODEM 0x1 + +#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C) +#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494) +#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174) + +#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2) + +#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148) +#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150) +#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158) +#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160) +#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168) +#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484) +#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488) +#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018) /* System reset register */ -#define PRCM_APE_SOFTRST 0x228 +#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) /* Level shifter and clamp control registers */ -#define PRCM_MMIP_LS_CLAMP_SET 0x420 -#define PRCM_MMIP_LS_CLAMP_CLR 0x424 +#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) +#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) + +/* PRCMU clock/PLL/reset registers */ +#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500) +#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504) +#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508) +#define PRCM_LCDCLK_MGT (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF) +#define PRCM_MCDECLK_MGT (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF) +#define PRCM_HDMICLK_MGT (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF) +#define PRCM_TVCLK_MGT (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF) +#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530) +#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C) +#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508) +#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4) +#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8) + +#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC) +#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0) +#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13) +#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16) +#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29) + +/* ePOD and memory power signal control registers */ +#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) +#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304) + +/* Debug power control unit registers */ +#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254) + +/* Miscellaneous unit registers */ +#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) +#define PRCM_GPIOCR (_PRCMU_BASE + 0x138) +#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800 +#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1 /* PRCMU HW semaphore */ -#define PRCM_SEM 0x400 +#define PRCM_SEM (_PRCMU_BASE + 0x400) #define PRCM_SEM_PRCM_SEM BIT(0) -/* PRCMU clock/PLL/reset registers */ -#define PRCM_PLLDSI_FREQ 0x500 -#define PRCM_PLLDSI_ENABLE 0x504 -#define PRCM_PLLDSI_LOCKP 0x508 -#define PRCM_DSI_PLLOUT_SEL 0x530 -#define PRCM_DSITVCLK_DIV 0x52C -#define PRCM_APE_RESETN_SET 0x1E4 -#define PRCM_APE_RESETN_CLR 0x1E8 - -#define PRCM_TCR 0x1C8 -#define PRCM_TCR_TENSEL_MASK BITS(0, 7) -#define PRCM_TCR_STOP_TIMERS BIT(16) -#define PRCM_TCR_DOZE_MODE BIT(17) - -#define PRCM_CLKOCR 0x1CC +#define PRCM_TCR (_PRCMU_BASE + 0x1C8) +#define PRCM_TCR_TENSEL_MASK BITS(0, 7) +#define PRCM_TCR_STOP_TIMERS BIT(16) +#define PRCM_TCR_DOZE_MODE BIT(17) + #define PRCM_CLKOCR_CLKODIV0_SHIFT 0 #define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5) #define PRCM_CLKOCR_CLKOSEL0_SHIFT 6 @@ -112,55 +183,22 @@ #define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24) #define PRCM_CLKOCR_CLK1TYPE BIT(28) -#define PRCM_SGACLK_MGT 0x014 -#define PRCM_UARTCLK_MGT 0x018 -#define PRCM_MSP02CLK_MGT 0x01C -#define PRCM_MSP1CLK_MGT 0x288 -#define PRCM_I2CCLK_MGT 0x020 -#define PRCM_SDMMCCLK_MGT 0x024 -#define PRCM_SLIMCLK_MGT 0x028 -#define PRCM_PER1CLK_MGT 0x02C -#define PRCM_PER2CLK_MGT 0x030 -#define PRCM_PER3CLK_MGT 0x034 -#define PRCM_PER5CLK_MGT 0x038 -#define PRCM_PER6CLK_MGT 0x03C -#define PRCM_PER7CLK_MGT 0x040 -#define PRCM_LCDCLK_MGT 0x044 -#define PRCM_BMLCLK_MGT 0x04C -#define PRCM_HSITXCLK_MGT 0x050 -#define PRCM_HSIRXCLK_MGT 0x054 -#define PRCM_HDMICLK_MGT 0x058 -#define PRCM_APEATCLK_MGT 0x05C -#define PRCM_APETRACECLK_MGT 0x060 -#define PRCM_MCDECLK_MGT 0x064 -#define PRCM_IPI2CCLK_MGT 0x068 -#define PRCM_DSIALTCLK_MGT 0x06C -#define PRCM_DMACLK_MGT 0x074 -#define PRCM_B2R2CLK_MGT 0x078 -#define PRCM_TVCLK_MGT 0x07C -#define PRCM_UNIPROCLK_MGT 0x278 -#define PRCM_SSPCLK_MGT 0x280 -#define PRCM_RNGCLK_MGT 0x284 -#define PRCM_UICCCLK_MGT 0x27C - #define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4) #define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7) #define PRCM_CLK_MGT_CLKEN BIT(8) -/* ePOD and memory power signal control registers */ -#define PRCM_EPOD_C_SET 0x410 -#define PRCM_SRAM_LS_SLEEP 0x304 +/* GPIOCR register */ +#define PRCM_GPIOCR_SPI2_SELECT BIT(23) -/* Debug power control unit registers */ -#define PRCM_POWER_STATE_SET 0x254 +#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438) +#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134) +#define PRCM_CGATING_BYPASS_ICN2 BIT(6) /* Miscellaneous unit registers */ -#define PRCM_DSI_SW_RESET 0x324 -#define PRCM_GPIOCR 0x138 - -/* GPIOCR register */ -#define PRCM_GPIOCR_SPI2_SELECT BIT(23) +#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214) +#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218) -#define PRCM_DDR_SUBSYS_APE_MINBW 0x438 +/* System reset register */ +#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) #endif /* __DB8500_PRCMU_REGS_H */ diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 02a15d7cb3b0..b5bd245ca892 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -412,7 +412,7 @@ struct clk_mgt { static DEFINE_SPINLOCK(clk_mgt_lock); -#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT), 0 } +#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT_OFF), 0 } struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { CLK_MGT_ENTRY(SGACLK), CLK_MGT_ENTRY(UARTCLK), @@ -499,45 +499,41 @@ int prcmu_enable_dsipll(void) unsigned int plldsifreq; /* Clear DSIPLL_RESETN */ - writel(PRCMU_RESET_DSIPLL, (_PRCMU_BASE + PRCM_APE_RESETN_CLR)); + writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR); /* Unclamp DSIPLL in/out */ - writel(PRCMU_UNCLAMP_DSIPLL, (_PRCMU_BASE + PRCM_MMIP_LS_CLAMP_CLR)); + writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR); if (prcmu_is_u8400()) plldsifreq = PRCMU_PLLDSI_FREQ_SETTING_U8400; else plldsifreq = PRCMU_PLLDSI_FREQ_SETTING; /* Set DSI PLL FREQ */ - writel(plldsifreq, (_PRCMU_BASE + PRCM_PLLDSI_FREQ)); - writel(PRCMU_DSI_PLLOUT_SEL_SETTING, - (_PRCMU_BASE + PRCM_DSI_PLLOUT_SEL)); + writel(plldsifreq, PRCM_PLLDSI_FREQ); + writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL); /* Enable Escape clocks */ - writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, - (_PRCMU_BASE + PRCM_DSITVCLK_DIV)); + writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV); /* Start DSI PLL */ - writel(PRCMU_ENABLE_PLLDSI, (_PRCMU_BASE + PRCM_PLLDSI_ENABLE)); + writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE); /* Reset DSI PLL */ - writel(PRCMU_DSI_RESET_SW, (_PRCMU_BASE + PRCM_DSI_SW_RESET)); + writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET); for (i = 0; i < 10; i++) { - if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) & - PRCMU_PLLDSI_LOCKP_LOCKED) + if ((readl(PRCM_PLLDSI_LOCKP) & PRCMU_PLLDSI_LOCKP_LOCKED) == PRCMU_PLLDSI_LOCKP_LOCKED) break; udelay(100); } /* Set DSIPLL_RESETN */ - writel(PRCMU_RESET_DSIPLL, (_PRCMU_BASE + PRCM_APE_RESETN_SET)); + writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET); return 0; } int prcmu_disable_dsipll(void) { /* Disable dsi pll */ - writel(PRCMU_DISABLE_PLLDSI, (_PRCMU_BASE + PRCM_PLLDSI_ENABLE)); + writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE); /* Disable escapeclock */ - writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, - (_PRCMU_BASE + PRCM_DSITVCLK_DIV)); + writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV); return 0; } @@ -554,15 +550,15 @@ int prcmu_set_display_clocks(void) spin_lock_irqsave(&clk_mgt_lock, flags); /* Grab the HW semaphore. */ - while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) + while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) cpu_relax(); - writel(dsiclk, (_PRCMU_BASE + PRCM_HDMICLK_MGT)); - writel(PRCMU_DSI_LP_CLOCK_SETTING, (_PRCMU_BASE + PRCM_TVCLK_MGT)); - writel(PRCMU_DPI_CLOCK_SETTING, (_PRCMU_BASE + PRCM_LCDCLK_MGT)); + writel(dsiclk, PRCM_HDMICLK_MGT); + writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT); + writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT); /* Release the HW semaphore. */ - writel(0, (_PRCMU_BASE + PRCM_SEM)); + writel(0, PRCM_SEM); spin_unlock_irqrestore(&clk_mgt_lock, flags); @@ -578,8 +574,8 @@ void prcmu_enable_spi2(void) unsigned long flags; spin_lock_irqsave(&gpiocr_lock, flags); - reg = readl(_PRCMU_BASE + PRCM_GPIOCR); - writel(reg | PRCM_GPIOCR_SPI2_SELECT, _PRCMU_BASE + PRCM_GPIOCR); + reg = readl(PRCM_GPIOCR); + writel(reg | PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR); spin_unlock_irqrestore(&gpiocr_lock, flags); } @@ -592,8 +588,8 @@ void prcmu_disable_spi2(void) unsigned long flags; spin_lock_irqsave(&gpiocr_lock, flags); - reg = readl(_PRCMU_BASE + PRCM_GPIOCR); - writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, _PRCMU_BASE + PRCM_GPIOCR); + reg = readl(PRCM_GPIOCR); + writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR); spin_unlock_irqrestore(&gpiocr_lock, flags); } @@ -701,7 +697,7 @@ int prcmu_config_clkout(u8 clkout, u8 source, u8 div) spin_lock_irqsave(&clkout_lock, flags); - val = readl(_PRCMU_BASE + PRCM_CLKOCR); + val = readl(PRCM_CLKOCR); if (val & div_mask) { if (div) { if ((val & mask) != bits) { @@ -715,7 +711,7 @@ int prcmu_config_clkout(u8 clkout, u8 source, u8 div) } } } - writel((bits | (val & ~mask)), (_PRCMU_BASE + PRCM_CLKOCR)); + writel((bits | (val & ~mask)), PRCM_CLKOCR); requests[clkout] += (div ? 1 : -1); unlock_and_return: @@ -732,7 +728,7 @@ int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) spin_lock_irqsave(&mb0_transfer.lock, flags); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) cpu_relax(); writeb(MB0H_POWER_STATE_TRANS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0)); @@ -741,7 +737,7 @@ int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) writeb((keep_ulp_clk ? 1 : 0), (tcdm_base + PRCM_REQ_MB0_ULP_CLOCK_STATE)); writeb(0, (tcdm_base + PRCM_REQ_MB0_DO_NOT_WFI)); - writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET); spin_unlock_irqrestore(&mb0_transfer.lock, flags); @@ -770,12 +766,12 @@ static void config_wakeups(void) return; for (i = 0; i < 2; i++) { - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) cpu_relax(); writel(dbb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_8500)); writel(abb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_4500)); writeb(header[i], (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0)); - writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET); } last_dbb_events = dbb_events; last_abb_events = abb_events; @@ -840,14 +836,14 @@ int prcmu_set_arm_opp(u8 opp) mutex_lock(&mb1_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) cpu_relax(); writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); writeb(opp, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); writeb(APE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_APE_OPP)); - writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); wait_for_completion(&mb1_transfer.work); if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) || @@ -876,7 +872,7 @@ int prcmu_get_arm_opp(void) */ int prcmu_get_ddr_opp(void) { - return readb(_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW); + return readb(PRCM_DDR_SUBSYS_APE_MINBW); } /** @@ -892,7 +888,7 @@ int prcmu_set_ddr_opp(u8 opp) return -EINVAL; /* Changing the DDR OPP can hang the hardware pre-v21 */ if (cpu_is_u8500v20_or_later() && !cpu_is_u8500v20()) - writeb(opp, (_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW)); + writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW); return 0; } @@ -909,14 +905,14 @@ int prcmu_set_ape_opp(u8 opp) mutex_lock(&mb1_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) cpu_relax(); writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP)); - writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); wait_for_completion(&mb1_transfer.work); if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) || @@ -966,12 +962,12 @@ int prcmu_request_ape_opp_100_voltage(bool enable) header = MB1H_RELEASE_APE_OPP_100_VOLT; } - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) cpu_relax(); writeb(header, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); - writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); wait_for_completion(&mb1_transfer.work); if ((mb1_transfer.ack.header != header) || @@ -995,13 +991,13 @@ int prcmu_release_usb_wakeup_state(void) mutex_lock(&mb1_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) cpu_relax(); writeb(MB1H_RELEASE_USB_WAKEUP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); - writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); wait_for_completion(&mb1_transfer.work); if ((mb1_transfer.ack.header != MB1H_RELEASE_USB_WAKEUP) || @@ -1048,7 +1044,7 @@ int prcmu_set_epod(u16 epod_id, u8 epod_state) mutex_lock(&mb2_transfer.lock); /* wait for mailbox */ - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(2)) cpu_relax(); /* fill in mailbox */ @@ -1058,7 +1054,7 @@ int prcmu_set_epod(u16 epod_id, u8 epod_state) writeb(MB2H_DPS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB2)); - writel(MBOX_BIT(2), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(2), PRCM_MBOX_CPU_SET); /* * The current firmware version does not handle errors correctly, @@ -1145,13 +1141,13 @@ static int request_sysclk(bool enable) spin_lock_irqsave(&mb3_transfer.lock, flags); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(3)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(3)) cpu_relax(); writeb((enable ? ON : OFF), (tcdm_base + PRCM_REQ_MB3_SYSCLK_MGT)); writeb(MB3H_SYSCLK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB3)); - writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(3), PRCM_MBOX_CPU_SET); spin_unlock_irqrestore(&mb3_transfer.lock, flags); @@ -1177,7 +1173,7 @@ static int request_timclk(bool enable) if (!enable) val |= PRCM_TCR_STOP_TIMERS; - writel(val, (_PRCMU_BASE + PRCM_TCR)); + writel(val, PRCM_TCR); return 0; } @@ -1190,7 +1186,7 @@ static int request_reg_clock(u8 clock, bool enable) spin_lock_irqsave(&clk_mgt_lock, flags); /* Grab the HW semaphore. */ - while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) + while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) cpu_relax(); val = readl(_PRCMU_BASE + clk_mgt[clock].offset); @@ -1203,7 +1199,7 @@ static int request_reg_clock(u8 clock, bool enable) writel(val, (_PRCMU_BASE + clk_mgt[clock].offset)); /* Release the HW semaphore. */ - writel(0, (_PRCMU_BASE + PRCM_SEM)); + writel(0, PRCM_SEM); spin_unlock_irqrestore(&clk_mgt_lock, flags); @@ -1238,7 +1234,7 @@ int prcmu_config_esram0_deep_sleep(u8 state) mutex_lock(&mb4_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) cpu_relax(); writeb(MB4H_MEM_ST, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); @@ -1248,7 +1244,7 @@ int prcmu_config_esram0_deep_sleep(u8 state) (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE)); writeb(state, (tcdm_base + PRCM_REQ_MB4_ESRAM0_ST)); - writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); wait_for_completion(&mb4_transfer.work); mutex_unlock(&mb4_transfer.lock); @@ -1260,13 +1256,13 @@ int prcmu_config_hotdog(u8 threshold) { mutex_lock(&mb4_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) cpu_relax(); writeb(threshold, (tcdm_base + PRCM_REQ_MB4_HOTDOG_THRESHOLD)); writeb(MB4H_HOTDOG, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); - writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); wait_for_completion(&mb4_transfer.work); mutex_unlock(&mb4_transfer.lock); @@ -1278,7 +1274,7 @@ int prcmu_config_hotmon(u8 low, u8 high) { mutex_lock(&mb4_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) cpu_relax(); writeb(low, (tcdm_base + PRCM_REQ_MB4_HOTMON_LOW)); @@ -1287,7 +1283,7 @@ int prcmu_config_hotmon(u8 low, u8 high) (tcdm_base + PRCM_REQ_MB4_HOTMON_CONFIG)); writeb(MB4H_HOTMON, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); - writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); wait_for_completion(&mb4_transfer.work); mutex_unlock(&mb4_transfer.lock); @@ -1299,13 +1295,13 @@ static int config_hot_period(u16 val) { mutex_lock(&mb4_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) cpu_relax(); writew(val, (tcdm_base + PRCM_REQ_MB4_HOT_PERIOD)); writeb(MB4H_HOT_PERIOD, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); - writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); wait_for_completion(&mb4_transfer.work); mutex_unlock(&mb4_transfer.lock); @@ -1345,7 +1341,7 @@ int prcmu_set_clock_divider(u8 clock, u8 divider) spin_lock_irqsave(&clk_mgt_lock, flags); /* Grab the HW semaphore. */ - while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) + while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) cpu_relax(); val = readl(_PRCMU_BASE + clk_mgt[clock].offset); @@ -1354,7 +1350,7 @@ int prcmu_set_clock_divider(u8 clock, u8 divider) writel(val, (_PRCMU_BASE + clk_mgt[clock].offset)); /* Release the HW semaphore. */ - writel(0, (_PRCMU_BASE + PRCM_SEM)); + writel(0, PRCM_SEM); spin_unlock_irqrestore(&clk_mgt_lock, flags); @@ -1380,7 +1376,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) mutex_lock(&mb5_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) cpu_relax(); writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP)); @@ -1388,7 +1384,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG)); writeb(0, (tcdm_base + PRCM_REQ_MB5_I2C_VAL)); - writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); if (!wait_for_completion_timeout(&mb5_transfer.work, msecs_to_jiffies(20000))) { @@ -1426,7 +1422,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) mutex_lock(&mb5_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) cpu_relax(); writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP)); @@ -1434,7 +1430,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG)); writeb(*value, (tcdm_base + PRCM_REQ_MB5_I2C_VAL)); - writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); if (!wait_for_completion_timeout(&mb5_transfer.work, msecs_to_jiffies(20000))) { @@ -1459,14 +1455,13 @@ void prcmu_ac_wake_req(void) mutex_lock(&mb0_transfer.ac_wake_lock); - val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ); + val = readl(PRCM_HOSTACCESS_REQ); if (val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ) goto unlock_and_return; atomic_set(&ac_wake_req_state, 1); - writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), - (_PRCMU_BASE + PRCM_HOSTACCESS_REQ)); + writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ); if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(20000))) { @@ -1487,12 +1482,12 @@ void prcmu_ac_sleep_req() mutex_lock(&mb0_transfer.ac_wake_lock); - val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ); + val = readl(PRCM_HOSTACCESS_REQ); if (!(val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ)) goto unlock_and_return; writel((val & ~PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), - (_PRCMU_BASE + PRCM_HOSTACCESS_REQ)); + PRCM_HOSTACCESS_REQ); if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(20000))) { @@ -1520,7 +1515,7 @@ bool prcmu_is_ac_wake_requested(void) void prcmu_system_reset(u16 reset_code) { writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON)); - writel(1, (_PRCMU_BASE + PRCM_APE_SOFTRST)); + writel(1, PRCM_APE_SOFTRST); } /** @@ -1530,11 +1525,11 @@ void prcmu_modem_reset(void) { mutex_lock(&mb1_transfer.lock); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) cpu_relax(); writeb(MB1H_RESET_MODEM, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); - writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); wait_for_completion(&mb1_transfer.work); /* @@ -1551,11 +1546,11 @@ static void ack_dbb_wakeup(void) spin_lock_irqsave(&mb0_transfer.lock, flags); - while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) cpu_relax(); writeb(MB0H_READ_WAKEUP_ACK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0)); - writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET); spin_unlock_irqrestore(&mb0_transfer.lock, flags); } @@ -1600,7 +1595,7 @@ static bool read_mailbox_0(void) r = false; break; } - writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR); return r; } @@ -1613,7 +1608,7 @@ static bool read_mailbox_1(void) PRCM_ACK_MB1_CURRENT_APE_OPP); mb1_transfer.ack.ape_voltage_status = readb(tcdm_base + PRCM_ACK_MB1_APE_VOLTAGE_STATUS); - writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR); complete(&mb1_transfer.work); return false; } @@ -1621,14 +1616,14 @@ static bool read_mailbox_1(void) static bool read_mailbox_2(void) { mb2_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB2_DPS_STATUS); - writel(MBOX_BIT(2), (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR); complete(&mb2_transfer.work); return false; } static bool read_mailbox_3(void) { - writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR); return false; } @@ -1650,7 +1645,7 @@ static bool read_mailbox_4(void) break; } - writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR); if (do_complete) complete(&mb4_transfer.work); @@ -1662,20 +1657,20 @@ static bool read_mailbox_5(void) { mb5_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB5_I2C_STATUS); mb5_transfer.ack.value = readb(tcdm_base + PRCM_ACK_MB5_I2C_VAL); - writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR); complete(&mb5_transfer.work); return false; } static bool read_mailbox_6(void) { - writel(MBOX_BIT(6), (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_7(void) { - writel(MBOX_BIT(7), (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR); return false; } @@ -1696,7 +1691,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data) u8 n; irqreturn_t r; - bits = (readl(_PRCMU_BASE + PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS); + bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS); if (unlikely(!bits)) return IRQ_NONE; @@ -2025,7 +2020,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) return -ENODEV; /* Clean up the mailbox interrupts after pre-kernel code. */ - writel(ALL_MBOX_BITS, (_PRCMU_BASE + PRCM_ARM_IT1_CLR)); + writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler, prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL); -- cgit v1.2.3 From a592c2e20fe2ba696cc7cd16d02abec8ac16ea41 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 12 Aug 2011 10:27:41 +0200 Subject: mfd: Extend DB8500 PRCMU mailbox defs We have a few more mailboxes and fixed messages in the DB8500 PRCMU, update to match the latest specification. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index b5bd245ca892..dcc690efdc25 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -131,12 +131,14 @@ #define MB1H_REQUEST_APE_OPP_100_VOLT 0x3 #define MB1H_RELEASE_APE_OPP_100_VOLT 0x4 #define MB1H_RELEASE_USB_WAKEUP 0x5 +#define MB1H_PLL_ON_OFF 0x6 /* Mailbox 1 Requests */ #define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0) #define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1) -#define PRCM_REQ_MB1_APE_OPP_100_RESTORE (PRCM_REQ_MB1 + 0x4) -#define PRCM_REQ_MB1_ARM_OPP_100_RESTORE (PRCM_REQ_MB1 + 0x8) +#define PRCM_REQ_MB1_PLL_ON_OFF (PRCM_REQ_MB1 + 0x4) +#define PLL_SOC1_OFF 0x4 +#define PLL_SOC1_ON 0x8 /* Mailbox 1 ACKs */ #define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0) @@ -184,6 +186,11 @@ #define MB4H_HOTDOG 0x12 #define MB4H_HOTMON 0x13 #define MB4H_HOT_PERIOD 0x14 +#define MB4H_A9WDOG_CONF 0x16 +#define MB4H_A9WDOG_EN 0x17 +#define MB4H_A9WDOG_DIS 0x18 +#define MB4H_A9WDOG_LOAD 0x19 +#define MB4H_A9WDOG_KICK 0x20 /* Mailbox 4 Requests */ #define PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE (PRCM_REQ_MB4 + 0x0) @@ -196,6 +203,13 @@ #define PRCM_REQ_MB4_HOT_PERIOD (PRCM_REQ_MB4 + 0x0) #define HOTMON_CONFIG_LOW BIT(0) #define HOTMON_CONFIG_HIGH BIT(1) +#define PRCM_REQ_MB4_A9WDOG_0 (PRCM_REQ_MB4 + 0x0) +#define PRCM_REQ_MB4_A9WDOG_1 (PRCM_REQ_MB4 + 0x1) +#define PRCM_REQ_MB4_A9WDOG_2 (PRCM_REQ_MB4 + 0x2) +#define PRCM_REQ_MB4_A9WDOG_3 (PRCM_REQ_MB4 + 0x3) +#define A9WDOG_AUTO_OFF_EN BIT(7) +#define A9WDOG_AUTO_OFF_DIS 0 +#define A9WDOG_ID_MASK 0xf /* Mailbox 5 Requests */ #define PRCM_REQ_MB5_I2C_SLAVE_OP (PRCM_REQ_MB5 + 0x0) @@ -1638,6 +1652,11 @@ static bool read_mailbox_4(void) case MB4H_HOTDOG: case MB4H_HOTMON: case MB4H_HOT_PERIOD: + case MB4H_A9WDOG_CONF: + case MB4H_A9WDOG_EN: + case MB4H_A9WDOG_DIS: + case MB4H_A9WDOG_LOAD: + case MB4H_A9WDOG_KICK: break; default: print_unknown_header_warning(4, header); -- cgit v1.2.3 From d65e12d70436cfb2728a78aec3f7bc1cd79eaa34 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 12 Aug 2011 10:27:50 +0200 Subject: mfd: Initialize DB8500 PRCMU regs Some clocks may be force enabled when we probe the driver, but they need to be turned off by default so we have a known state. We call this the register initialization function if we need more stuff in there in the future. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index dcc690efdc25..e2c4a26a9eb1 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1840,6 +1840,16 @@ void __init prcmu_early_init(void) } } +static void __init init_prcm_registers(void) +{ + u32 val; + + val = readl(PRCM_A9PL_FORCE_CLKEN); + val &= ~(PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN | + PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN); + writel(val, (PRCM_A9PL_FORCE_CLKEN)); +} + /* * Power domain switches (ePODs) modeled as regulators for the DB8500 SoC */ @@ -2038,6 +2048,8 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) if (ux500_is_svp()) return -ENODEV; + init_prcm_registers(); + /* Clean up the mailbox interrupts after pre-kernel code. */ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); -- cgit v1.2.3 From 73180f85f4ffbb66843f8248811b2ade29b22df2 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 12 Aug 2011 10:28:10 +0200 Subject: mfd: Move to the new db500 PRCMU API Now that we have a shared API between the DB8500 and DB5500 PRCMU's, switch to using this neutral API instead. We delete the parts of db8500-prcmu.h that is now PRCMU-neutral, and calls will be diverted to respective driver. Common registers are in dbx500-prcmu-regs.h and common accessors and defines in This way we get a a lot more abstraction and code reuse. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- arch/arm/mach-ux500/cpu.c | 2 +- drivers/cpufreq/db8500-cpufreq.c | 2 +- drivers/mfd/db5500-prcmu-regs.h | 115 ------------ drivers/mfd/db5500-prcmu.c | 22 +-- drivers/mfd/db8500-prcmu-regs.h | 204 ---------------------- drivers/mfd/db8500-prcmu.c | 46 ++--- drivers/mfd/dbx500-prcmu-regs.h | 204 ++++++++++++++++++++++ drivers/regulator/db8500-prcmu.c | 2 +- include/linux/mfd/db5500-prcmu.h | 102 +++++++++-- include/linux/mfd/db8500-prcmu.h | 368 +++++++++------------------------------ include/linux/mfd/dbx500-prcmu.h | 8 +- 11 files changed, 415 insertions(+), 660 deletions(-) delete mode 100644 drivers/mfd/db5500-prcmu-regs.h delete mode 100644 drivers/mfd/db8500-prcmu-regs.h create mode 100644 drivers/mfd/dbx500-prcmu-regs.h (limited to 'drivers/mfd') diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index 1da23bb87c16..bb5653993ca2 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -53,7 +53,7 @@ void __init ux500_init_irq(void) if (cpu_is_u5500()) db5500_prcmu_early_init(); if (cpu_is_u8500()) - prcmu_early_init(); + db8500_prcmu_early_init(); clk_init(); } diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c index d90456a809f9..8e89dcf9d94d 100644 --- a/drivers/cpufreq/db8500-cpufreq.c +++ b/drivers/cpufreq/db8500-cpufreq.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include static struct cpufreq_frequency_table freq_table[] = { diff --git a/drivers/mfd/db5500-prcmu-regs.h b/drivers/mfd/db5500-prcmu-regs.h deleted file mode 100644 index 9a8e9e4ddd33..000000000000 --- a/drivers/mfd/db5500-prcmu-regs.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) STMicroelectronics 2009 - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Kumar Sanghvi - * Author: Sundar Iyer - * - * License Terms: GNU General Public License v2 - * - * PRCM Unit registers - */ - -#ifndef __MACH_PRCMU_REGS_H -#define __MACH_PRCMU_REGS_H - -#include - -#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) -#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f -#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf - -#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8) -#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 - -#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) -#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 - -#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100 - -#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) -#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) -#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) -#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c) -#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308) - -/* ARM WFI Standby signal register */ -#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) -#define PRCM_IOCR (_PRCMU_BASE + 0x310) -#define PRCM_IOCR_IOFORCE 0x1 - -/* CPU mailbox registers */ -#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) -#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100) -#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104) - -/* Dual A9 core interrupt management unit registers */ -#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) -#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 - -#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) -#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) -#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) -#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124) -#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128) -#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C) -#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260) -#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264) -#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268) -#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C) - -#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) -#define ARM_WAKEUP_MODEM 0x1 - -#define PRCM_ARM_IT1_CLEAR (_PRCMU_BASE + 0x48C) -#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494) -#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174) - -#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148) -#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150) -#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158) -#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160) -#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168) -#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484) -#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488) -#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018) - -/* System reset register */ -#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) - -/* Level shifter and clamp control registers */ -#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) -#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) - -/* PRCMU clock/PLL/reset registers */ -#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500) -#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504) -#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508) -#define PRCM_LCDCLK_MGT (_PRCMU_BASE + 0x044) -#define PRCM_MCDECLK_MGT (_PRCMU_BASE + 0x064) -#define PRCM_HDMICLK_MGT (_PRCMU_BASE + 0x058) -#define PRCM_TVCLK_MGT (_PRCMU_BASE + 0x07c) -#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530) -#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C) -#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508) -#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4) -#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8) -#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC) - -/* ePOD and memory power signal control registers */ -#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) -#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304) - -/* Debug power control unit registers */ -#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254) - -/* Miscellaneous unit registers */ -#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) -#define PRCM_GPIOCR (_PRCMU_BASE + 0x138) -#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800 -#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1 - - -#endif /* __MACH_PRCMU__REGS_H */ diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c index 9dbb3cab4a6f..dc215878835a 100644 --- a/drivers/mfd/db5500-prcmu.c +++ b/drivers/mfd/db5500-prcmu.c @@ -20,11 +20,11 @@ #include #include #include -#include +#include #include #include #include -#include "db5500-prcmu-regs.h" +#include "dbx500-prcmu-regs.h" #define _PRCM_MB_HEADER (tcdm_base + 0xFE8) #define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0) @@ -315,31 +315,31 @@ static bool read_mailbox_0(void) r = false; break; } - writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR); return r; } static bool read_mailbox_1(void) { - writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_2(void) { - writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_3(void) { - writel(MBOX_BIT(3), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_4(void) { - writel(MBOX_BIT(4), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR); return false; } @@ -360,19 +360,19 @@ static bool read_mailbox_5(void) print_unknown_header_warning(5, header); break; } - writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_6(void) { - writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_7(void) { - writel(MBOX_BIT(7), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR); return false; } @@ -434,7 +434,7 @@ int __init db5500_prcmu_init(void) return -ENODEV; /* Clean up the mailbox interrupts after pre-kernel code. */ - writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLEAR); + writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler, prcmu_irq_thread_fn, 0, "prcmu", NULL); diff --git a/drivers/mfd/db8500-prcmu-regs.h b/drivers/mfd/db8500-prcmu-regs.h deleted file mode 100644 index ec22e9f15d32..000000000000 --- a/drivers/mfd/db8500-prcmu-regs.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) STMicroelectronics 2009 - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Kumar Sanghvi - * Author: Sundar Iyer - * - * License Terms: GNU General Public License v2 - * - * PRCM Unit registers - */ - -#ifndef __DB8500_PRCMU_REGS_H -#define __DB8500_PRCMU_REGS_H - -#include - -#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end)) - -#define PRCM_SVACLK_MGT_OFF 0x008 -#define PRCM_SIACLK_MGT_OFF 0x00C -#define PRCM_SGACLK_MGT_OFF 0x014 -#define PRCM_UARTCLK_MGT_OFF 0x018 -#define PRCM_MSP02CLK_MGT_OFF 0x01C -#define PRCM_I2CCLK_MGT_OFF 0x020 -#define PRCM_SDMMCCLK_MGT_OFF 0x024 -#define PRCM_SLIMCLK_MGT_OFF 0x028 -#define PRCM_PER1CLK_MGT_OFF 0x02C -#define PRCM_PER2CLK_MGT_OFF 0x030 -#define PRCM_PER3CLK_MGT_OFF 0x034 -#define PRCM_PER5CLK_MGT_OFF 0x038 -#define PRCM_PER6CLK_MGT_OFF 0x03C -#define PRCM_PER7CLK_MGT_OFF 0x040 -#define PRCM_PWMCLK_MGT_OFF 0x044 /* for DB5500 */ -#define PRCM_IRDACLK_MGT_OFF 0x048 /* for DB5500 */ -#define PRCM_IRRCCLK_MGT_OFF 0x04C /* for DB5500 */ -#define PRCM_LCDCLK_MGT_OFF 0x044 -#define PRCM_BMLCLK_MGT_OFF 0x04C -#define PRCM_HSITXCLK_MGT_OFF 0x050 -#define PRCM_HSIRXCLK_MGT_OFF 0x054 -#define PRCM_HDMICLK_MGT_OFF 0x058 -#define PRCM_APEATCLK_MGT_OFF 0x05C -#define PRCM_APETRACECLK_MGT_OFF 0x060 -#define PRCM_MCDECLK_MGT_OFF 0x064 -#define PRCM_IPI2CCLK_MGT_OFF 0x068 -#define PRCM_DSIALTCLK_MGT_OFF 0x06C -#define PRCM_DMACLK_MGT_OFF 0x074 -#define PRCM_B2R2CLK_MGT_OFF 0x078 -#define PRCM_TVCLK_MGT_OFF 0x07C -#define PRCM_UNIPROCLK_MGT_OFF 0x278 -#define PRCM_SSPCLK_MGT_OFF 0x280 -#define PRCM_RNGCLK_MGT_OFF 0x284 -#define PRCM_UICCCLK_MGT_OFF 0x27C -#define PRCM_MSP1CLK_MGT_OFF 0x288 - -#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) -#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f -#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf - -#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8) -#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 - -#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) -#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 - -#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100 - -#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) -#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C) -#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) -#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) -#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c) -#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308) - -#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0) -#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1) - -/* ARM WFI Standby signal register */ -#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) -#define PRCM_IOCR (_PRCMU_BASE + 0x310) -#define PRCM_IOCR_IOFORCE 0x1 - -/* CPU mailbox registers */ -#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) -#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100) -#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104) - -/* Dual A9 core interrupt management unit registers */ -#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) -#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 - -#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) -#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) -#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) -#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124) -#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128) -#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C) -#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260) -#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264) -#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268) -#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C) - -#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) -#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1 -#define ARM_WAKEUP_MODEM 0x1 - -#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C) -#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494) -#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174) - -#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0) -#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0) -#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1) -#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2) - -#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148) -#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150) -#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158) -#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160) -#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168) -#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484) -#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488) -#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018) - -/* System reset register */ -#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) - -/* Level shifter and clamp control registers */ -#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) -#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) - -/* PRCMU clock/PLL/reset registers */ -#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500) -#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504) -#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508) -#define PRCM_LCDCLK_MGT (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF) -#define PRCM_MCDECLK_MGT (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF) -#define PRCM_HDMICLK_MGT (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF) -#define PRCM_TVCLK_MGT (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF) -#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530) -#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C) -#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508) -#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4) -#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8) - -#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC) -#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0) -#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13) -#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16) -#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29) - -/* ePOD and memory power signal control registers */ -#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) -#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304) - -/* Debug power control unit registers */ -#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254) - -/* Miscellaneous unit registers */ -#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) -#define PRCM_GPIOCR (_PRCMU_BASE + 0x138) -#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800 -#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1 - -/* PRCMU HW semaphore */ -#define PRCM_SEM (_PRCMU_BASE + 0x400) -#define PRCM_SEM_PRCM_SEM BIT(0) - -#define PRCM_TCR (_PRCMU_BASE + 0x1C8) -#define PRCM_TCR_TENSEL_MASK BITS(0, 7) -#define PRCM_TCR_STOP_TIMERS BIT(16) -#define PRCM_TCR_DOZE_MODE BIT(17) - -#define PRCM_CLKOCR_CLKODIV0_SHIFT 0 -#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5) -#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6 -#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8) -#define PRCM_CLKOCR_CLKODIV1_SHIFT 16 -#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21) -#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22 -#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24) -#define PRCM_CLKOCR_CLK1TYPE BIT(28) - -#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4) -#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7) -#define PRCM_CLK_MGT_CLKEN BIT(8) - -/* GPIOCR register */ -#define PRCM_GPIOCR_SPI2_SELECT BIT(23) - -#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438) -#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134) -#define PRCM_CGATING_BYPASS_ICN2 BIT(6) - -/* Miscellaneous unit registers */ -#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214) -#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218) - -/* System reset register */ -#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) - -#endif /* __DB8500_PRCMU_REGS_H */ diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index e2c4a26a9eb1..cea814509a6f 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -27,14 +27,14 @@ #include #include #include -#include +#include #include #include #include #include #include #include -#include "db8500-prcmu-regs.h" +#include "dbx500-prcmu-regs.h" /* Offset for the firmware version within the TCPM */ #define PRCMU_FW_VERSION_OFFSET 0xA4 @@ -507,7 +507,7 @@ static struct { } prcmu_version; -int prcmu_enable_dsipll(void) +int db8500_prcmu_enable_dsipll(void) { int i; unsigned int plldsifreq; @@ -542,7 +542,7 @@ int prcmu_enable_dsipll(void) return 0; } -int prcmu_disable_dsipll(void) +int db8500_prcmu_disable_dsipll(void) { /* Disable dsi pll */ writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE); @@ -551,7 +551,7 @@ int prcmu_disable_dsipll(void) return 0; } -int prcmu_set_display_clocks(void) +int db8500_prcmu_set_display_clocks(void) { unsigned long flags; unsigned int dsiclk; @@ -734,7 +734,7 @@ unlock_and_return: return r; } -int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) +int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) { unsigned long flags; @@ -791,7 +791,7 @@ static void config_wakeups(void) last_abb_events = abb_events; } -void prcmu_enable_wakeups(u32 wakeups) +void db8500_prcmu_enable_wakeups(u32 wakeups) { unsigned long flags; u32 bits; @@ -812,7 +812,7 @@ void prcmu_enable_wakeups(u32 wakeups) spin_unlock_irqrestore(&mb0_transfer.lock, flags); } -void prcmu_config_abb_event_readout(u32 abb_events) +void db8500_prcmu_config_abb_event_readout(u32 abb_events) { unsigned long flags; @@ -824,7 +824,7 @@ void prcmu_config_abb_event_readout(u32 abb_events) spin_unlock_irqrestore(&mb0_transfer.lock, flags); } -void prcmu_get_abb_event_buffer(void __iomem **buf) +void db8500_prcmu_get_abb_event_buffer(void __iomem **buf) { if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1) *buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_1_4500); @@ -833,13 +833,13 @@ void prcmu_get_abb_event_buffer(void __iomem **buf) } /** - * prcmu_set_arm_opp - set the appropriate ARM OPP + * db8500_prcmu_set_arm_opp - set the appropriate ARM OPP * @opp: The new ARM operating point to which transition is to be made * Returns: 0 on success, non-zero on failure * * This function sets the the operating point of the ARM. */ -int prcmu_set_arm_opp(u8 opp) +int db8500_prcmu_set_arm_opp(u8 opp) { int r; @@ -870,11 +870,11 @@ int prcmu_set_arm_opp(u8 opp) } /** - * prcmu_get_arm_opp - get the current ARM OPP + * db8500_prcmu_get_arm_opp - get the current ARM OPP * * Returns: the current ARM OPP */ -int prcmu_get_arm_opp(void) +int db8500_prcmu_get_arm_opp(void) { return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP); } @@ -1024,14 +1024,14 @@ int prcmu_release_usb_wakeup_state(void) } /** - * prcmu_set_epod - set the state of a EPOD (power domain) + * db8500_prcmu_set_epod - set the state of a EPOD (power domain) * @epod_id: The EPOD to set * @epod_state: The new EPOD state * * This function sets the state of a EPOD (power domain). It may not be called * from interrupt context. */ -int prcmu_set_epod(u16 epod_id, u8 epod_state) +int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) { int r = 0; bool ram_retention = false; @@ -1221,14 +1221,14 @@ static int request_reg_clock(u8 clock, bool enable) } /** - * prcmu_request_clock() - Request for a clock to be enabled or disabled. + * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled. * @clock: The clock for which the request is made. * @enable: Whether the clock should be enabled (true) or disabled (false). * * This function should only be used by the clock implementation. * Do not use it from any other place! */ -int prcmu_request_clock(u8 clock, bool enable) +int db8500_prcmu_request_clock(u8 clock, bool enable) { if (clock < PRCMU_NUM_REG_CLOCKS) return request_reg_clock(clock, enable); @@ -1240,7 +1240,7 @@ int prcmu_request_clock(u8 clock, bool enable) return -EINVAL; } -int prcmu_config_esram0_deep_sleep(u8 state) +int db8500_prcmu_config_esram0_deep_sleep(u8 state) { if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) || (state < ESRAM0_DEEP_SLEEP_STATE_OFF)) @@ -1515,18 +1515,18 @@ unlock_and_return: mutex_unlock(&mb0_transfer.ac_wake_lock); } -bool prcmu_is_ac_wake_requested(void) +bool db8500_prcmu_is_ac_wake_requested(void) { return (atomic_read(&ac_wake_req_state) != 0); } /** - * prcmu_system_reset - System reset + * db8500_prcmu_system_reset - System reset * - * Saves the reset reason code and then sets the APE_SOFRST register which + * Saves the reset reason code and then sets the APE_SOFTRST register which * fires interrupt to fw */ -void prcmu_system_reset(u16 reset_code) +void db8500_prcmu_system_reset(u16 reset_code) { writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON)); writel(1, PRCM_APE_SOFTRST); @@ -1782,7 +1782,7 @@ static struct irq_chip prcmu_irq_chip = { .irq_unmask = prcmu_irq_unmask, }; -void __init prcmu_early_init(void) +void __init db8500_prcmu_early_init(void) { unsigned int i; diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h new file mode 100644 index 000000000000..ec22e9f15d32 --- /dev/null +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) STMicroelectronics 2009 + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Kumar Sanghvi + * Author: Sundar Iyer + * + * License Terms: GNU General Public License v2 + * + * PRCM Unit registers + */ + +#ifndef __DB8500_PRCMU_REGS_H +#define __DB8500_PRCMU_REGS_H + +#include + +#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end)) + +#define PRCM_SVACLK_MGT_OFF 0x008 +#define PRCM_SIACLK_MGT_OFF 0x00C +#define PRCM_SGACLK_MGT_OFF 0x014 +#define PRCM_UARTCLK_MGT_OFF 0x018 +#define PRCM_MSP02CLK_MGT_OFF 0x01C +#define PRCM_I2CCLK_MGT_OFF 0x020 +#define PRCM_SDMMCCLK_MGT_OFF 0x024 +#define PRCM_SLIMCLK_MGT_OFF 0x028 +#define PRCM_PER1CLK_MGT_OFF 0x02C +#define PRCM_PER2CLK_MGT_OFF 0x030 +#define PRCM_PER3CLK_MGT_OFF 0x034 +#define PRCM_PER5CLK_MGT_OFF 0x038 +#define PRCM_PER6CLK_MGT_OFF 0x03C +#define PRCM_PER7CLK_MGT_OFF 0x040 +#define PRCM_PWMCLK_MGT_OFF 0x044 /* for DB5500 */ +#define PRCM_IRDACLK_MGT_OFF 0x048 /* for DB5500 */ +#define PRCM_IRRCCLK_MGT_OFF 0x04C /* for DB5500 */ +#define PRCM_LCDCLK_MGT_OFF 0x044 +#define PRCM_BMLCLK_MGT_OFF 0x04C +#define PRCM_HSITXCLK_MGT_OFF 0x050 +#define PRCM_HSIRXCLK_MGT_OFF 0x054 +#define PRCM_HDMICLK_MGT_OFF 0x058 +#define PRCM_APEATCLK_MGT_OFF 0x05C +#define PRCM_APETRACECLK_MGT_OFF 0x060 +#define PRCM_MCDECLK_MGT_OFF 0x064 +#define PRCM_IPI2CCLK_MGT_OFF 0x068 +#define PRCM_DSIALTCLK_MGT_OFF 0x06C +#define PRCM_DMACLK_MGT_OFF 0x074 +#define PRCM_B2R2CLK_MGT_OFF 0x078 +#define PRCM_TVCLK_MGT_OFF 0x07C +#define PRCM_UNIPROCLK_MGT_OFF 0x278 +#define PRCM_SSPCLK_MGT_OFF 0x280 +#define PRCM_RNGCLK_MGT_OFF 0x284 +#define PRCM_UICCCLK_MGT_OFF 0x27C +#define PRCM_MSP1CLK_MGT_OFF 0x288 + +#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) +#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f +#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf + +#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8) +#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 + +#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) +#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 + +#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) +#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 +#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100 + +#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) +#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C) +#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) +#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) +#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c) +#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308) + +#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0) +#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1) + +/* ARM WFI Standby signal register */ +#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) +#define PRCM_IOCR (_PRCMU_BASE + 0x310) +#define PRCM_IOCR_IOFORCE 0x1 + +/* CPU mailbox registers */ +#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) +#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100) +#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104) + +/* Dual A9 core interrupt management unit registers */ +#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) +#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 + +#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) +#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) +#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) +#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124) +#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128) +#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C) +#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260) +#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264) +#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268) +#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C) + +#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) +#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1 +#define ARM_WAKEUP_MODEM 0x1 + +#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C) +#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494) +#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174) + +#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2) + +#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148) +#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150) +#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158) +#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160) +#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168) +#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484) +#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488) +#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018) + +/* System reset register */ +#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) + +/* Level shifter and clamp control registers */ +#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) +#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) + +/* PRCMU clock/PLL/reset registers */ +#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500) +#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504) +#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508) +#define PRCM_LCDCLK_MGT (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF) +#define PRCM_MCDECLK_MGT (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF) +#define PRCM_HDMICLK_MGT (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF) +#define PRCM_TVCLK_MGT (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF) +#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530) +#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C) +#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508) +#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4) +#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8) + +#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC) +#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0) +#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13) +#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16) +#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29) + +/* ePOD and memory power signal control registers */ +#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) +#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304) + +/* Debug power control unit registers */ +#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254) + +/* Miscellaneous unit registers */ +#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) +#define PRCM_GPIOCR (_PRCMU_BASE + 0x138) +#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800 +#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1 + +/* PRCMU HW semaphore */ +#define PRCM_SEM (_PRCMU_BASE + 0x400) +#define PRCM_SEM_PRCM_SEM BIT(0) + +#define PRCM_TCR (_PRCMU_BASE + 0x1C8) +#define PRCM_TCR_TENSEL_MASK BITS(0, 7) +#define PRCM_TCR_STOP_TIMERS BIT(16) +#define PRCM_TCR_DOZE_MODE BIT(17) + +#define PRCM_CLKOCR_CLKODIV0_SHIFT 0 +#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5) +#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6 +#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8) +#define PRCM_CLKOCR_CLKODIV1_SHIFT 16 +#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21) +#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22 +#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24) +#define PRCM_CLKOCR_CLK1TYPE BIT(28) + +#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4) +#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7) +#define PRCM_CLK_MGT_CLKEN BIT(8) + +/* GPIOCR register */ +#define PRCM_GPIOCR_SPI2_SELECT BIT(23) + +#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438) +#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134) +#define PRCM_CGATING_BYPASS_ICN2 BIT(6) + +/* Miscellaneous unit registers */ +#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214) +#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218) + +/* System reset register */ +#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) + +#endif /* __DB8500_PRCMU_REGS_H */ diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 2bb8f451cc06..2d014a144365 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/linux/mfd/db5500-prcmu.h b/include/linux/mfd/db5500-prcmu.h index f0977986402c..9890687f582d 100644 --- a/include/linux/mfd/db5500-prcmu.h +++ b/include/linux/mfd/db5500-prcmu.h @@ -5,21 +5,35 @@ * * U5500 PRCMU API. */ -#ifndef __MACH_PRCMU_U5500_H -#define __MACH_PRCMU_U5500_H +#ifndef __MFD_DB5500_PRCMU_H +#define __MFD_DB5500_PRCMU_H -#ifdef CONFIG_UX500_SOC_DB5500 +#ifdef CONFIG_MFD_DB5500_PRCMU void db5500_prcmu_early_init(void); - +int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state); +int db5500_prcmu_set_display_clocks(void); +int db5500_prcmu_disable_dsipll(void); +int db5500_prcmu_enable_dsipll(void); int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); +void db5500_prcmu_enable_wakeups(u32 wakeups); +int db5500_prcmu_request_clock(u8 clock, bool enable); +void db5500_prcmu_config_abb_event_readout(u32 abb_events); +void db5500_prcmu_get_abb_event_buffer(void __iomem **buf); +int prcmu_resetout(u8 resoutn, u8 state); +int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll); +int db5500_prcmu_config_esram0_deep_sleep(u8 state); +void db5500_prcmu_system_reset(u16 reset_code); +u16 db5500_prcmu_get_reset_code(void); +bool db5500_prcmu_is_ac_wake_requested(void); +int db5500_prcmu_set_arm_opp(u8 opp); +int db5500_prcmu_get_arm_opp(void); #else /* !CONFIG_UX500_SOC_DB5500 */ -static inline void db5500_prcmu_early_init(void) -{ -} +static inline void db5500_prcmu_early_init(void) {} static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) { @@ -31,15 +45,75 @@ static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) return -ENOSYS; } -#endif /* CONFIG_UX500_SOC_DB5500 */ +static inline int db5500_prcmu_request_clock(u8 clock, bool enable) +{ + return 0; +} + +static inline int db5500_prcmu_set_display_clocks(void) +{ + return 0; +} + +static inline int db5500_prcmu_disable_dsipll(void) +{ + return 0; +} + +static inline int db5500_prcmu_enable_dsipll(void) +{ + return 0; +} -static inline int db5500_prcmu_config_abb_event_readout(u32 abb_events) +static inline int db5500_prcmu_config_esram0_deep_sleep(u8 state) { -#ifdef CONFIG_MACH_U5500_SIMULATOR return 0; -#else - return -1; -#endif } -#endif /* __MACH_PRCMU_U5500_H */ +static inline void db5500_prcmu_enable_wakeups(u32 wakeups) {} + +static inline int prcmu_resetout(u8 resoutn, u8 state) +{ + return 0; +} + +static inline int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state) +{ + return 0; +} + +static inline void db5500_prcmu_get_abb_event_buffer(void __iomem **buf) {} +static inline void db5500_prcmu_config_abb_event_readout(u32 abb_events) {} + +static inline int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll) +{ + return 0; +} + +static inline void db5500_prcmu_system_reset(u16 reset_code) {} + +static inline u16 db5500_prcmu_get_reset_code(void) +{ + return 0; +} + +static inline bool db5500_prcmu_is_ac_wake_requested(void) +{ + return 0; +} + +static inline int db5500_prcmu_set_arm_opp(u8 opp) +{ + return 0; +} + +static inline int db5500_prcmu_get_arm_opp(void) +{ + return 0; +} + + +#endif /* CONFIG_MFD_DB5500_PRCMU */ + +#endif /* __MFD_DB5500_PRCMU_H */ diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h index 917dbcab701c..60d27f7bfc1f 100644 --- a/include/linux/mfd/db8500-prcmu.h +++ b/include/linux/mfd/db8500-prcmu.h @@ -11,7 +11,6 @@ #define __MFD_DB8500_PRCMU_H #include -#include /* This portion previously known as */ @@ -133,7 +132,7 @@ enum ap_pwrst { * @APEXECUTE_TO_APIDLE: Power state transition from ApExecute to ApIdle */ enum ap_pwrst_trans { - NO_TRANSITION = 0x00, + PRCMU_AP_NO_CHANGE = 0x00, APEXECUTE_TO_APSLEEP = 0x01, APIDLE_TO_APSLEEP = 0x02, /* To be removed */ PRCMU_AP_SLEEP = 0x01, @@ -145,54 +144,6 @@ enum ap_pwrst_trans { PRCMU_AP_DEEP_IDLE = 0x07, }; -/** - * enum ddr_pwrst - DDR power states definition - * @DDR_PWR_STATE_UNCHANGED: SDRAM and DDR controller state is unchanged - * @DDR_PWR_STATE_ON: - * @DDR_PWR_STATE_OFFLOWLAT: - * @DDR_PWR_STATE_OFFHIGHLAT: - */ -enum ddr_pwrst { - DDR_PWR_STATE_UNCHANGED = 0x00, - DDR_PWR_STATE_ON = 0x01, - DDR_PWR_STATE_OFFLOWLAT = 0x02, - DDR_PWR_STATE_OFFHIGHLAT = 0x03 -}; - -/** - * enum arm_opp - ARM OPP states definition - * @ARM_OPP_INIT: - * @ARM_NO_CHANGE: The ARM operating point is unchanged - * @ARM_100_OPP: The new ARM operating point is arm100opp - * @ARM_50_OPP: The new ARM operating point is arm50opp - * @ARM_MAX_OPP: Operating point is "max" (more than 100) - * @ARM_MAX_FREQ100OPP: Set max opp if available, else 100 - * @ARM_EXTCLK: The new ARM operating point is armExtClk - */ -enum arm_opp { - ARM_OPP_INIT = 0x00, - ARM_NO_CHANGE = 0x01, - ARM_100_OPP = 0x02, - ARM_50_OPP = 0x03, - ARM_MAX_OPP = 0x04, - ARM_MAX_FREQ100OPP = 0x05, - ARM_EXTCLK = 0x07 -}; - -/** - * enum ape_opp - APE OPP states definition - * @APE_OPP_INIT: - * @APE_NO_CHANGE: The APE operating point is unchanged - * @APE_100_OPP: The new APE operating point is ape100opp - * @APE_50_OPP: 50% - */ -enum ape_opp { - APE_OPP_INIT = 0x00, - APE_NO_CHANGE = 0x01, - APE_100_OPP = 0x02, - APE_50_OPP = 0x03 -}; - /** * enum hw_acc_state - State definition for hardware accelerator * @HW_NO_CHANGE: The hardware accelerator state must remain unchanged @@ -469,26 +420,6 @@ enum auto_enable { /* End of file previously known as prcmu-fw-defs_v1.h */ -/* PRCMU Wakeup defines */ -enum prcmu_wakeup_index { - PRCMU_WAKEUP_INDEX_RTC, - PRCMU_WAKEUP_INDEX_RTT0, - PRCMU_WAKEUP_INDEX_RTT1, - PRCMU_WAKEUP_INDEX_HSI0, - PRCMU_WAKEUP_INDEX_HSI1, - PRCMU_WAKEUP_INDEX_USB, - PRCMU_WAKEUP_INDEX_ABB, - PRCMU_WAKEUP_INDEX_ABB_FIFO, - PRCMU_WAKEUP_INDEX_ARM, - NUM_PRCMU_WAKEUP_INDICES -}; -#define PRCMU_WAKEUP(_name) (BIT(PRCMU_WAKEUP_INDEX_##_name)) - -/* PRCMU QoS APE OPP class */ -#define PRCMU_QOS_APE_OPP 1 -#define PRCMU_QOS_DDR_OPP 2 -#define PRCMU_QOS_DEFAULT_VALUE -1 - /** * enum hw_acc_dev - enum for hw accelerators * @HW_ACC_SVAMMDSP: for SVAMMDSP @@ -526,64 +457,6 @@ enum hw_acc_dev { NUM_HW_ACC }; -/* - * Ids for all EPODs (power domains) - * - EPOD_ID_SVAMMDSP: power domain for SVA MMDSP - * - EPOD_ID_SVAPIPE: power domain for SVA pipe - * - EPOD_ID_SIAMMDSP: power domain for SIA MMDSP - * - EPOD_ID_SIAPIPE: power domain for SIA pipe - * - EPOD_ID_SGA: power domain for SGA - * - EPOD_ID_B2R2_MCDE: power domain for B2R2 and MCDE - * - EPOD_ID_ESRAM12: power domain for ESRAM 1 and 2 - * - EPOD_ID_ESRAM34: power domain for ESRAM 3 and 4 - * - NUM_EPOD_ID: number of power domains - */ -#define EPOD_ID_SVAMMDSP 0 -#define EPOD_ID_SVAPIPE 1 -#define EPOD_ID_SIAMMDSP 2 -#define EPOD_ID_SIAPIPE 3 -#define EPOD_ID_SGA 4 -#define EPOD_ID_B2R2_MCDE 5 -#define EPOD_ID_ESRAM12 6 -#define EPOD_ID_ESRAM34 7 -#define NUM_EPOD_ID 8 - -/* - * state definition for EPOD (power domain) - * - EPOD_STATE_NO_CHANGE: The EPOD should remain unchanged - * - EPOD_STATE_OFF: The EPOD is switched off - * - EPOD_STATE_RAMRET: The EPOD is switched off with its internal RAM in - * retention - * - EPOD_STATE_ON_CLK_OFF: The EPOD is switched on, clock is still off - * - EPOD_STATE_ON: Same as above, but with clock enabled - */ -#define EPOD_STATE_NO_CHANGE 0x00 -#define EPOD_STATE_OFF 0x01 -#define EPOD_STATE_RAMRET 0x02 -#define EPOD_STATE_ON_CLK_OFF 0x03 -#define EPOD_STATE_ON 0x04 - -/* - * CLKOUT sources - */ -#define PRCMU_CLKSRC_CLK38M 0x00 -#define PRCMU_CLKSRC_ACLK 0x01 -#define PRCMU_CLKSRC_SYSCLK 0x02 -#define PRCMU_CLKSRC_LCDCLK 0x03 -#define PRCMU_CLKSRC_SDMMCCLK 0x04 -#define PRCMU_CLKSRC_TVCLK 0x05 -#define PRCMU_CLKSRC_TIMCLK 0x06 -#define PRCMU_CLKSRC_CLK009 0x07 -/* These are only valid for CLKOUT1: */ -#define PRCMU_CLKSRC_SIAMMDSPCLK 0x40 -#define PRCMU_CLKSRC_I2CCLK 0x41 -#define PRCMU_CLKSRC_MSP02CLK 0x42 -#define PRCMU_CLKSRC_ARMPLL_OBSCLK 0x43 -#define PRCMU_CLKSRC_HSIRXCLK 0x44 -#define PRCMU_CLKSRC_HSITXCLK 0x45 -#define PRCMU_CLKSRC_ARMCLKFIX 0x46 -#define PRCMU_CLKSRC_HDMICLK 0x47 - /* * Definitions for autonomous power management configuration. */ @@ -620,88 +493,12 @@ struct prcmu_auto_pm_config { u8 sva_policy; }; -/** - * enum ddr_opp - DDR OPP states definition - * @DDR_100_OPP: The new DDR operating point is ddr100opp - * @DDR_50_OPP: The new DDR operating point is ddr50opp - * @DDR_25_OPP: The new DDR operating point is ddr25opp - */ -enum ddr_opp { - DDR_100_OPP = 0x00, - DDR_50_OPP = 0x01, - DDR_25_OPP = 0x02, -}; - -/* - * Clock identifiers. - */ -enum prcmu_clock { - PRCMU_SGACLK, - PRCMU_UARTCLK, - PRCMU_MSP02CLK, - PRCMU_MSP1CLK, - PRCMU_I2CCLK, - PRCMU_SDMMCCLK, - PRCMU_SLIMCLK, - PRCMU_PER1CLK, - PRCMU_PER2CLK, - PRCMU_PER3CLK, - PRCMU_PER5CLK, - PRCMU_PER6CLK, - PRCMU_PER7CLK, - PRCMU_LCDCLK, - PRCMU_BMLCLK, - PRCMU_HSITXCLK, - PRCMU_HSIRXCLK, - PRCMU_HDMICLK, - PRCMU_APEATCLK, - PRCMU_APETRACECLK, - PRCMU_MCDECLK, - PRCMU_IPI2CCLK, - PRCMU_DSIALTCLK, - PRCMU_DMACLK, - PRCMU_B2R2CLK, - PRCMU_TVCLK, - PRCMU_SSPCLK, - PRCMU_RNGCLK, - PRCMU_UICCCLK, - PRCMU_NUM_REG_CLOCKS, - PRCMU_SYSCLK = PRCMU_NUM_REG_CLOCKS, - PRCMU_TIMCLK, -}; - -/* - * Definitions for controlling ESRAM0 in deep sleep. - */ -#define ESRAM0_DEEP_SLEEP_STATE_OFF 1 -#define ESRAM0_DEEP_SLEEP_STATE_RET 2 - -#ifdef CONFIG_MFD_DB8500_PRCMU -void __init prcmu_early_init(void); -int prcmu_set_display_clocks(void); -int prcmu_disable_dsipll(void); -int prcmu_enable_dsipll(void); -#else -static inline void __init prcmu_early_init(void) {} -#endif - #ifdef CONFIG_MFD_DB8500_PRCMU +void db8500_prcmu_early_init(void); int prcmu_set_rc_a2p(enum romcode_write); enum romcode_read prcmu_get_rc_p2a(void); enum ap_pwrst prcmu_get_xp70_current_state(void); -int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); - -void prcmu_enable_wakeups(u32 wakeups); -static inline void prcmu_disable_wakeups(void) -{ - prcmu_enable_wakeups(0); -} - -void prcmu_config_abb_event_readout(u32 abb_events); -void prcmu_get_abb_event_buffer(void __iomem **buf); -int prcmu_set_arm_opp(u8 opp); -int prcmu_get_arm_opp(void); bool prcmu_has_arm_maxopp(void); bool prcmu_is_u8400(void); int prcmu_set_ape_opp(u8 opp); @@ -710,19 +507,14 @@ int prcmu_request_ape_opp_100_voltage(bool enable); int prcmu_release_usb_wakeup_state(void); int prcmu_set_ddr_opp(u8 opp); int prcmu_get_ddr_opp(void); -unsigned long prcmu_qos_get_cpufreq_opp_delay(void); -void prcmu_qos_set_cpufreq_opp_delay(unsigned long); /* NOTE! Use regulator framework instead */ int prcmu_set_hwacc(u16 hw_acc_dev, u8 state); -int prcmu_set_epod(u16 epod_id, u8 epod_state); void prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep, struct prcmu_auto_pm_config *idle); bool prcmu_is_auto_pm_enabled(void); int prcmu_config_clkout(u8 clkout, u8 source, u8 div); -int prcmu_request_clock(u8 clock, bool enable); int prcmu_set_clock_divider(u8 clock, u8 divider); -int prcmu_config_esram0_deep_sleep(u8 state); int prcmu_config_hotdog(u8 threshold); int prcmu_config_hotmon(u8 low, u8 high); int prcmu_start_temp_sense(u16 cycles32k); @@ -732,14 +524,36 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); void prcmu_ac_wake_req(void); void prcmu_ac_sleep_req(void); -void prcmu_system_reset(u16 reset_code); void prcmu_modem_reset(void); -bool prcmu_is_ac_wake_requested(void); void prcmu_enable_spi2(void); void prcmu_disable_spi2(void); +int prcmu_config_a9wdog(u8 num, bool sleep_auto_off); +int prcmu_enable_a9wdog(u8 id); +int prcmu_disable_a9wdog(u8 id); +int prcmu_kick_a9wdog(u8 id); +int prcmu_load_a9wdog(u8 id, u32 val); + +void db8500_prcmu_system_reset(u16 reset_code); +int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); +void db8500_prcmu_enable_wakeups(u32 wakeups); +int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state); +int db8500_prcmu_request_clock(u8 clock, bool enable); +int db8500_prcmu_set_display_clocks(void); +int db8500_prcmu_disable_dsipll(void); +int db8500_prcmu_enable_dsipll(void); +void db8500_prcmu_config_abb_event_readout(u32 abb_events); +void db8500_prcmu_get_abb_event_buffer(void __iomem **buf); +int db8500_prcmu_config_esram0_deep_sleep(u8 state); +u16 db8500_prcmu_get_reset_code(void); +bool db8500_prcmu_is_ac_wake_requested(void); +int db8500_prcmu_set_arm_opp(u8 opp); +int db8500_prcmu_get_arm_opp(void); + #else /* !CONFIG_MFD_DB8500_PRCMU */ +static inline void db8500_prcmu_early_init(void) {} + static inline int prcmu_set_rc_a2p(enum romcode_write code) { return 0; @@ -755,34 +569,12 @@ static inline enum ap_pwrst prcmu_get_xp70_current_state(void) return AP_EXECUTE; } -static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, - bool keep_ap_pll) -{ - return 0; -} - -static inline void prcmu_enable_wakeups(u32 wakeups) {} - -static inline void prcmu_disable_wakeups(void) {} - -static inline void prcmu_config_abb_event_readout(u32 abb_events) {} - -static inline int prcmu_set_arm_opp(u8 opp) -{ - return 0; -} - -static inline int prcmu_get_arm_opp(void) -{ - return ARM_100_OPP; -} - -static bool prcmu_has_arm_maxopp(void) +static inline bool prcmu_has_arm_maxopp(void) { return false; } -static bool prcmu_is_u8400(void) +static inline bool prcmu_is_u8400(void) { return false; } @@ -817,13 +609,6 @@ static inline int prcmu_get_ddr_opp(void) return DDR_100_OPP; } -static inline unsigned long prcmu_qos_get_cpufreq_opp_delay(void) -{ - return 0; -} - -static inline void prcmu_qos_set_cpufreq_opp_delay(unsigned long n) {} - static inline int prcmu_set_hwacc(u16 hw_acc_dev, u8 state) { return 0; @@ -844,21 +629,11 @@ static inline int prcmu_config_clkout(u8 clkout, u8 source, u8 div) return 0; } -static inline int prcmu_request_clock(u8 clock, bool enable) -{ - return 0; -} - static inline int prcmu_set_clock_divider(u8 clock, u8 divider) { return 0; } -int prcmu_config_esram0_deep_sleep(u8 state) -{ - return 0; -} - static inline int prcmu_config_hotdog(u8 threshold) { return 0; @@ -893,86 +668,107 @@ static inline void prcmu_ac_wake_req(void) {} static inline void prcmu_ac_sleep_req(void) {} -static inline void prcmu_system_reset(u16 reset_code) {} - static inline void prcmu_modem_reset(void) {} -static inline bool prcmu_is_ac_wake_requested(void) +static inline int prcmu_enable_spi2(void) { - return false; + return 0; } -#ifndef CONFIG_UX500_SOC_DB5500 -static inline int prcmu_set_display_clocks(void) +static inline int prcmu_disable_spi2(void) { return 0; } -static inline int prcmu_disable_dsipll(void) +static inline void db8500_prcmu_system_reset(u16 reset_code) {} + +static inline int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll) { return 0; } -static inline int prcmu_enable_dsipll(void) +static inline void db8500_prcmu_enable_wakeups(u32 wakeups) {} + +static inline int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) { return 0; } -#endif -static inline int prcmu_enable_spi2(void) +static inline int db8500_prcmu_request_clock(u8 clock, bool enable) { return 0; } -static inline int prcmu_disable_spi2(void) +static inline int db8500_prcmu_set_display_clocks(void) { return 0; } -#endif /* !CONFIG_MFD_DB8500_PRCMU */ +static inline int db8500_prcmu_disable_dsipll(void) +{ + return 0; +} + +static inline int db8500_prcmu_enable_dsipll(void) +{ + return 0; +} + +static inline int db8500_prcmu_config_esram0_deep_sleep(u8 state) +{ + return 0; +} + +static inline void db8500_prcmu_config_abb_event_readout(u32 abb_events) {} -#ifdef CONFIG_UX500_PRCMU_QOS_POWER -int prcmu_qos_requirement(int pm_qos_class); -int prcmu_qos_add_requirement(int pm_qos_class, char *name, s32 value); -int prcmu_qos_update_requirement(int pm_qos_class, char *name, s32 new_value); -void prcmu_qos_remove_requirement(int pm_qos_class, char *name); -int prcmu_qos_add_notifier(int prcmu_qos_class, - struct notifier_block *notifier); -int prcmu_qos_remove_notifier(int prcmu_qos_class, - struct notifier_block *notifier); -#else -static inline int prcmu_qos_requirement(int prcmu_qos_class) +static inline void db8500_prcmu_get_abb_event_buffer(void __iomem **buf) {} + +static inline u16 db8500_prcmu_get_reset_code(void) { return 0; } -static inline int prcmu_qos_add_requirement(int prcmu_qos_class, - char *name, s32 value) +static inline int prcmu_config_a9wdog(u8 num, bool sleep_auto_off) { return 0; } -static inline int prcmu_qos_update_requirement(int prcmu_qos_class, - char *name, s32 new_value) +static inline int prcmu_enable_a9wdog(u8 id) { return 0; } -static inline void prcmu_qos_remove_requirement(int prcmu_qos_class, char *name) +static inline int prcmu_disable_a9wdog(u8 id) { + return 0; } -static inline int prcmu_qos_add_notifier(int prcmu_qos_class, - struct notifier_block *notifier) +static inline int prcmu_kick_a9wdog(u8 id) { return 0; } -static inline int prcmu_qos_remove_notifier(int prcmu_qos_class, - struct notifier_block *notifier) + +static inline int prcmu_load_a9wdog(u8 id, u32 val) { return 0; } -#endif +static inline bool db8500_prcmu_is_ac_wake_requested(void) +{ + return 0; +} + +static inline int db8500_prcmu_set_arm_opp(u8 opp) +{ + return 0; +} + +static inline int db8500_prcmu_get_arm_opp(void) +{ + return 0; +} + +#endif /* !CONFIG_MFD_DB8500_PRCMU */ #endif /* __MFD_DB8500_PRCMU_H */ diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 6c7584d69d8f..bac942f959c1 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -240,7 +240,7 @@ static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, static inline int prcmu_set_epod(u16 epod_id, u8 epod_state) { if (machine_is_u5500()) - return db5500_prcmu_set_epod(epod_id, epod_state); + return -EINVAL; else return db8500_prcmu_set_epod(epod_id, epod_state); } @@ -295,7 +295,7 @@ int prcmu_get_ddr_opp(void); static inline int prcmu_set_arm_opp(u8 opp) { if (machine_is_u5500()) - return db5500_prcmu_set_arm_opp(opp); + return -EINVAL; else return db8500_prcmu_set_arm_opp(opp); } @@ -303,7 +303,7 @@ static inline int prcmu_set_arm_opp(u8 opp) static inline int prcmu_get_arm_opp(void) { if (machine_is_u5500()) - return db5500_prcmu_get_arm_opp(); + return -EINVAL; else return db8500_prcmu_get_arm_opp(); } @@ -362,7 +362,7 @@ static inline int prcmu_enable_dsipll(void) static inline int prcmu_config_esram0_deep_sleep(u8 state) { if (machine_is_u5500()) - return db5500_prcmu_config_esram0_deep_sleep(state); + return -EINVAL; else return db8500_prcmu_config_esram0_deep_sleep(state); } -- cgit v1.2.3 From 0837bb7260a17283b4518e11206546ffc92265fc Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 12 Aug 2011 10:28:18 +0200 Subject: mfd: Add db8500-prcmu accessors for PLL and SGA clock This extends the DB8500 PRCMU driver with accessor calls for the PRCMU PLL and SGA clocks. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 83 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index cea814509a6f..af369995b013 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -459,6 +459,35 @@ struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { CLK_MGT_ENTRY(UICCCLK), }; +static struct regulator *hwacc_regulator[NUM_HW_ACC]; +static struct regulator *hwacc_ret_regulator[NUM_HW_ACC]; + +static bool hwacc_enabled[NUM_HW_ACC]; +static bool hwacc_ret_enabled[NUM_HW_ACC]; + +static const char *hwacc_regulator_name[NUM_HW_ACC] = { + [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp", + [HW_ACC_SVAPIPE] = "hwacc-sva-pipe", + [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp", + [HW_ACC_SIAPIPE] = "hwacc-sia-pipe", + [HW_ACC_SGA] = "hwacc-sga", + [HW_ACC_B2R2] = "hwacc-b2r2", + [HW_ACC_MCDE] = "hwacc-mcde", + [HW_ACC_ESRAM1] = "hwacc-esram1", + [HW_ACC_ESRAM2] = "hwacc-esram2", + [HW_ACC_ESRAM3] = "hwacc-esram3", + [HW_ACC_ESRAM4] = "hwacc-esram4", +}; + +static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = { + [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp-ret", + [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp-ret", + [HW_ACC_ESRAM1] = "hwacc-esram1-ret", + [HW_ACC_ESRAM2] = "hwacc-esram2-ret", + [HW_ACC_ESRAM3] = "hwacc-esram3-ret", + [HW_ACC_ESRAM4] = "hwacc-esram4-ret", +}; + /* * Used by MCDE to setup all necessary PRCMU registers */ @@ -1023,6 +1052,34 @@ int prcmu_release_usb_wakeup_state(void) return r; } +static int request_pll(u8 clock, bool enable) +{ + int r = 0; + + if (clock == PRCMU_PLLSOC1) + clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF); + else + return -EINVAL; + + mutex_lock(&mb1_transfer.lock); + + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) + cpu_relax(); + + writeb(MB1H_PLL_ON_OFF, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); + writeb(clock, (tcdm_base + PRCM_REQ_MB1_PLL_ON_OFF)); + + writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); + wait_for_completion(&mb1_transfer.work); + + if (mb1_transfer.ack.header != MB1H_PLL_ON_OFF) + r = -EIO; + + mutex_unlock(&mb1_transfer.lock); + + return r; +} + /** * db8500_prcmu_set_epod - set the state of a EPOD (power domain) * @epod_id: The EPOD to set @@ -1220,6 +1277,26 @@ static int request_reg_clock(u8 clock, bool enable) return 0; } +static int request_sga_clock(u8 clock, bool enable) +{ + u32 val; + int ret; + + if (enable) { + val = readl(PRCM_CGATING_BYPASS); + writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS); + } + + ret = request_reg_clock(clock, enable); + + if (!ret && !enable) { + val = readl(PRCM_CGATING_BYPASS); + writel(val & ~PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS); + } + + return ret; +} + /** * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled. * @clock: The clock for which the request is made. @@ -1230,12 +1307,16 @@ static int request_reg_clock(u8 clock, bool enable) */ int db8500_prcmu_request_clock(u8 clock, bool enable) { - if (clock < PRCMU_NUM_REG_CLOCKS) + if (clock == PRCMU_SGACLK) + return request_sga_clock(clock, enable); + else if (clock < PRCMU_NUM_REG_CLOCKS) return request_reg_clock(clock, enable); else if (clock == PRCMU_TIMCLK) return request_timclk(enable); else if (clock == PRCMU_SYSCLK) return request_sysclk(enable); + else if (clock == PRCMU_PLLSOC1) + return request_pll(clock, enable); else return -EINVAL; } -- cgit v1.2.3 From 0b9199e3186e1998a8e066fbcf15bcf18cdbfc42 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Fri, 12 Aug 2011 10:28:25 +0200 Subject: mfd: hwacc power state db8500-prcmu accessor This implements the accessor function for hardware accelerator power state settings. Signed-off-by: Bengt Jonsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 126 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index af369995b013..28a60906f5f7 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1080,6 +1080,132 @@ static int request_pll(u8 clock, bool enable) return r; } +/** + * prcmu_set_hwacc - set the power state of a h/w accelerator + * @hwacc_dev: The hardware accelerator (enum hw_acc_dev). + * @state: The new power state (enum hw_acc_state). + * + * This function sets the power state of a hardware accelerator. + * This function should not be called from interrupt context. + * + * NOTE! Deprecated, to be removed when all users switched over to use the + * regulator framework API. + */ +int prcmu_set_hwacc(u16 hwacc_dev, u8 state) +{ + int r = 0; + bool ram_retention = false; + bool enable, enable_ret; + + /* check argument */ + BUG_ON(hwacc_dev >= NUM_HW_ACC); + + /* get state of switches */ + enable = hwacc_enabled[hwacc_dev]; + enable_ret = hwacc_ret_enabled[hwacc_dev]; + + /* set flag if retention is possible */ + switch (hwacc_dev) { + case HW_ACC_SVAMMDSP: + case HW_ACC_SIAMMDSP: + case HW_ACC_ESRAM1: + case HW_ACC_ESRAM2: + case HW_ACC_ESRAM3: + case HW_ACC_ESRAM4: + ram_retention = true; + break; + } + + /* check argument */ + BUG_ON(state > HW_ON); + BUG_ON(state == HW_OFF_RAMRET && !ram_retention); + + /* modify enable flags */ + switch (state) { + case HW_OFF: + enable_ret = false; + enable = false; + break; + case HW_ON: + enable = true; + break; + case HW_OFF_RAMRET: + enable_ret = true; + enable = false; + break; + } + + /* get regulator (lazy) */ + if (hwacc_regulator[hwacc_dev] == NULL) { + hwacc_regulator[hwacc_dev] = regulator_get(NULL, + hwacc_regulator_name[hwacc_dev]); + if (IS_ERR(hwacc_regulator[hwacc_dev])) { + pr_err("prcmu: failed to get supply %s\n", + hwacc_regulator_name[hwacc_dev]); + r = PTR_ERR(hwacc_regulator[hwacc_dev]); + goto out; + } + } + + if (ram_retention) { + if (hwacc_ret_regulator[hwacc_dev] == NULL) { + hwacc_ret_regulator[hwacc_dev] = regulator_get(NULL, + hwacc_ret_regulator_name[hwacc_dev]); + if (IS_ERR(hwacc_ret_regulator[hwacc_dev])) { + pr_err("prcmu: failed to get supply %s\n", + hwacc_ret_regulator_name[hwacc_dev]); + r = PTR_ERR(hwacc_ret_regulator[hwacc_dev]); + goto out; + } + } + } + + /* set regulators */ + if (ram_retention) { + if (enable_ret && !hwacc_ret_enabled[hwacc_dev]) { + r = regulator_enable(hwacc_ret_regulator[hwacc_dev]); + if (r < 0) { + pr_err("prcmu_set_hwacc: ret enable failed\n"); + goto out; + } + hwacc_ret_enabled[hwacc_dev] = true; + } + } + + if (enable && !hwacc_enabled[hwacc_dev]) { + r = regulator_enable(hwacc_regulator[hwacc_dev]); + if (r < 0) { + pr_err("prcmu_set_hwacc: enable failed\n"); + goto out; + } + hwacc_enabled[hwacc_dev] = true; + } + + if (!enable && hwacc_enabled[hwacc_dev]) { + r = regulator_disable(hwacc_regulator[hwacc_dev]); + if (r < 0) { + pr_err("prcmu_set_hwacc: disable failed\n"); + goto out; + } + hwacc_enabled[hwacc_dev] = false; + } + + if (ram_retention) { + if (!enable_ret && hwacc_ret_enabled[hwacc_dev]) { + r = regulator_disable(hwacc_ret_regulator[hwacc_dev]); + if (r < 0) { + pr_err("prcmu_set_hwacc: ret disable failed\n"); + goto out; + } + hwacc_ret_enabled[hwacc_dev] = false; + } + } + +out: + return r; +} +EXPORT_SYMBOL(prcmu_set_hwacc); + /** * db8500_prcmu_set_epod - set the state of a EPOD (power domain) * @epod_id: The EPOD to set -- cgit v1.2.3 From 84165b805972320050892e34fa28d09fe86a25eb Mon Sep 17 00:00:00 2001 From: Jonas Aberg Date: Fri, 12 Aug 2011 10:28:33 +0200 Subject: mfd: Add db8500-pcmu watchdog accessor functions for watchdog This implements the watchdog accessor functions for the DB8500 PRCMU, making it possible to implement the watchdog driver. Signed-off-by: Jonas Aberg Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 28a60906f5f7..95498f80c905 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1543,6 +1543,78 @@ int prcmu_stop_temp_sense(void) return config_hot_period(0xFFFF); } +static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3) +{ + + mutex_lock(&mb4_transfer.lock); + + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) + cpu_relax(); + + writeb(d0, (tcdm_base + PRCM_REQ_MB4_A9WDOG_0)); + writeb(d1, (tcdm_base + PRCM_REQ_MB4_A9WDOG_1)); + writeb(d2, (tcdm_base + PRCM_REQ_MB4_A9WDOG_2)); + writeb(d3, (tcdm_base + PRCM_REQ_MB4_A9WDOG_3)); + + writeb(cmd, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4)); + + writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET); + wait_for_completion(&mb4_transfer.work); + + mutex_unlock(&mb4_transfer.lock); + + return 0; + +} + +int prcmu_config_a9wdog(u8 num, bool sleep_auto_off) +{ + BUG_ON(num == 0 || num > 0xf); + return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0, + sleep_auto_off ? A9WDOG_AUTO_OFF_EN : + A9WDOG_AUTO_OFF_DIS); +} + +int prcmu_enable_a9wdog(u8 id) +{ + return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0); +} + +int prcmu_disable_a9wdog(u8 id) +{ + return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0); +} + +int prcmu_kick_a9wdog(u8 id) +{ + return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0); +} + +/* + * timeout is 28 bit, in ms. + */ +#define MAX_WATCHDOG_TIMEOUT 131000 +int prcmu_load_a9wdog(u8 id, u32 timeout) +{ + if (timeout > MAX_WATCHDOG_TIMEOUT) + /* + * Due to calculation bug in prcmu fw, timeouts + * can't be bigger than 131 seconds. + */ + return -EINVAL; + + return prcmu_a9wdog(MB4H_A9WDOG_LOAD, + (id & A9WDOG_ID_MASK) | + /* + * Put the lowest 28 bits of timeout at + * offset 4. Four first bits are used for id. + */ + (u8)((timeout << 4) & 0xf0), + (u8)((timeout >> 4) & 0xff), + (u8)((timeout >> 12) & 0xff), + (u8)((timeout >> 20) & 0xff)); +} + /** * prcmu_set_clock_divider() - Configure the clock divider. * @clock: The clock for which the request is made. -- cgit v1.2.3 From d6e3002e493e43aa40473935e1803849cf37b6bb Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 12 Aug 2011 10:28:43 +0200 Subject: mfd: db8500-prcmu tweak for modem wakeup This is a tweak for the case where the modem goes to sleep while emitting the AC_WAKE_ACK anyway. Also print the modem errors as critical, since they jeopardize the entire platform when they occur. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 95498f80c905..cb58e44b1e45 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1745,6 +1745,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) void prcmu_ac_wake_req(void) { u32 val; + u32 status; mutex_lock(&mb0_transfer.ac_wake_lock); @@ -1754,11 +1755,34 @@ void prcmu_ac_wake_req(void) atomic_set(&ac_wake_req_state, 1); +retry: writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ); if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, - msecs_to_jiffies(20000))) { - pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", + msecs_to_jiffies(5000))) { + panic("prcmu: %s timed out (5 s) waiting for a reply.\n", + __func__); + goto unlock_and_return; + } + + /* + * The modem can generate an AC_WAKE_ACK, and then still go to sleep. + * As a workaround, we wait, and then check that the modem is indeed + * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS + * register, which may not be the whole truth). + */ + udelay(400); + status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2)); + if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE | + PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) { + pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n", + __func__, status); + udelay(1200); + writel(val, PRCM_HOSTACCESS_REQ); + if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work, + msecs_to_jiffies(5000))) + goto retry; + panic("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n", __func__); } @@ -1783,8 +1807,8 @@ void prcmu_ac_sleep_req() PRCM_HOSTACCESS_REQ); if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, - msecs_to_jiffies(20000))) { - pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", + msecs_to_jiffies(5000))) { + panic("prcmu: %s timed out (5 s) waiting for a reply.\n", __func__); } -- cgit v1.2.3 From 597045de35caaef68a11b6defbb618710e1a1e52 Mon Sep 17 00:00:00 2001 From: Sebastian Rasmussen Date: Fri, 12 Aug 2011 10:28:53 +0200 Subject: mfd: db8500-prcmu reset code retrieval This implements the reset code retrieval function so we can ipso facto get to know how the system was reset. Signed-off-by: Sebastian Rasmussen Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index cb58e44b1e45..6fd4e2993520 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1835,6 +1835,17 @@ void db8500_prcmu_system_reset(u16 reset_code) writel(1, PRCM_APE_SOFTRST); } +/** + * db8500_prcmu_get_reset_code - Retrieve SW reset reason code + * + * Retrieves the reset reason code stored by prcmu_system_reset() before + * last restart. + */ +u16 db8500_prcmu_get_reset_code(void) +{ + return readw(tcdm_base + PRCM_SW_RST_REASON); +} + /** * prcmu_reset_modem - ask the PRCMU to reset modem */ -- cgit v1.2.3 From 624e87c25133b30a748ddb97068b92ac07d9d524 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Fri, 12 Aug 2011 10:29:02 +0200 Subject: mfd: db8500-prcmu voltage domain consumers additions This rectifies the device name of the MCDE voltage domain regulator consumer and adds a number of other consumers to the voltage domains. Cc: Liam Girdwood Cc: Mark Brown Signed-off-by: Bengt Jonsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 52 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 6fd4e2993520..91a5e8cce051 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2199,7 +2199,42 @@ static struct regulator_consumer_supply db8500_vsmps2_consumers[] = { static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = { REGULATOR_SUPPLY("vsupply", "b2r2.0"), - REGULATOR_SUPPLY("vsupply", "mcde.0"), + REGULATOR_SUPPLY("vsupply", "mcde"), +}; + +/* SVA MMDSP regulator switch */ +static struct regulator_consumer_supply db8500_svammdsp_consumers[] = { + REGULATOR_SUPPLY("sva-mmdsp", "cm_control"), +}; + +/* SVA pipe regulator switch */ +static struct regulator_consumer_supply db8500_svapipe_consumers[] = { + REGULATOR_SUPPLY("sva-pipe", "cm_control"), +}; + +/* SIA MMDSP regulator switch */ +static struct regulator_consumer_supply db8500_siammdsp_consumers[] = { + REGULATOR_SUPPLY("sia-mmdsp", "cm_control"), +}; + +/* SIA pipe regulator switch */ +static struct regulator_consumer_supply db8500_siapipe_consumers[] = { + REGULATOR_SUPPLY("sia-pipe", "cm_control"), +}; + +static struct regulator_consumer_supply db8500_sga_consumers[] = { + REGULATOR_SUPPLY("v-mali", NULL), +}; + +/* ESRAM1 and 2 regulator switch */ +static struct regulator_consumer_supply db8500_esram12_consumers[] = { + REGULATOR_SUPPLY("esram12", "cm_control"), +}; + +/* ESRAM3 and 4 regulator switch */ +static struct regulator_consumer_supply db8500_esram34_consumers[] = { + REGULATOR_SUPPLY("v-esram34", "mcde"), + REGULATOR_SUPPLY("esram34", "cm_control"), }; static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { @@ -2261,6 +2296,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .name = "db8500-sva-mmdsp", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, + .consumer_supplies = db8500_svammdsp_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_svammdsp_consumers), }, [DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = { .constraints = { @@ -2275,6 +2312,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .name = "db8500-sva-pipe", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, + .consumer_supplies = db8500_svapipe_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers), }, [DB8500_REGULATOR_SWITCH_SIAMMDSP] = { .supply_regulator = "db8500-vape", @@ -2282,6 +2321,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .name = "db8500-sia-mmdsp", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, + .consumer_supplies = db8500_siammdsp_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_siammdsp_consumers), }, [DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = { .constraints = { @@ -2295,6 +2336,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .name = "db8500-sia-pipe", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, + .consumer_supplies = db8500_siapipe_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_siapipe_consumers), }, [DB8500_REGULATOR_SWITCH_SGA] = { .supply_regulator = "db8500-vape", @@ -2302,6 +2345,9 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .name = "db8500-sga", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, + .consumer_supplies = db8500_sga_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_sga_consumers), + }, [DB8500_REGULATOR_SWITCH_B2R2_MCDE] = { .supply_regulator = "db8500-vape", @@ -2318,6 +2364,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .name = "db8500-esram12", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, + .consumer_supplies = db8500_esram12_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_esram12_consumers), }, [DB8500_REGULATOR_SWITCH_ESRAM12RET] = { .constraints = { @@ -2331,6 +2379,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .name = "db8500-esram34", .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, + .consumer_supplies = db8500_esram34_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_esram34_consumers), }, [DB8500_REGULATOR_SWITCH_ESRAM34RET] = { .constraints = { -- cgit v1.2.3 From 10b3ecdbd4a54d682903daff99fa6066e6cb68c2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 12 Aug 2011 10:29:10 +0200 Subject: mfd: Fix db5500-prcmu defines This fixes two erroneous defines for the PLLs and adds new defines for the reset pin controls. Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db5500-prcmu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c index dc215878835a..bb115b2f04e9 100644 --- a/drivers/mfd/db5500-prcmu.c +++ b/drivers/mfd/db5500-prcmu.c @@ -109,15 +109,18 @@ enum mb5_header { #define PRCMU_DSI_CLOCK_SETTING 0x00000128 /* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */ #define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135 -#define PRCMU_PLLDSI_FREQ_SETTING 0x0004013C +#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121 #define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002 -#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000101 +#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201 #define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101 #define PRCMU_ENABLE_PLLDSI 0x00000001 #define PRCMU_DISABLE_PLLDSI 0x00000000 #define PRCMU_DSI_RESET_SW 0x00000003 +#define PRCMU_RESOUTN0_PIN 0x00000001 +#define PRCMU_RESOUTN1_PIN 0x00000002 +#define PRCMU_RESOUTN2_PIN 0x00000004 #define PRCMU_PLLDSI_LOCKP_LOCKED 0x3 -- cgit v1.2.3 From 1934dae220ed1f1a832a191a86c1becfcbb3a094 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 5 Oct 2011 18:50:47 +0200 Subject: mfd: Rename db8500-prcmu init function This renames the PRCMU clock force initialization function to have a less generic name. Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 91a5e8cce051..98da431af044 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2154,7 +2154,7 @@ void __init db8500_prcmu_early_init(void) } } -static void __init init_prcm_registers(void) +static void __init db8500_prcmu_init_clkforce(void) { u32 val; @@ -2412,7 +2412,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) if (ux500_is_svp()) return -ENODEV; - init_prcm_registers(); + db8500_prcmu_init_clkforce(); /* Clean up the mailbox interrupts after pre-kernel code. */ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); -- cgit v1.2.3 From e62ccf3a7a21d62d92520f47ba5bd7e2112d8c1b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 10 Oct 2011 12:14:14 +0200 Subject: mfd: Refactor db8500-prcmu request_clock() function This refactors the mfd/dbx500-prcmu drivers to use a switch() statement rather than nested if/else-construction. Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 98da431af044..e996d11980fa 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1433,18 +1433,21 @@ static int request_sga_clock(u8 clock, bool enable) */ int db8500_prcmu_request_clock(u8 clock, bool enable) { - if (clock == PRCMU_SGACLK) + switch(clock) { + case PRCMU_SGACLK: return request_sga_clock(clock, enable); - else if (clock < PRCMU_NUM_REG_CLOCKS) - return request_reg_clock(clock, enable); - else if (clock == PRCMU_TIMCLK) + case PRCMU_TIMCLK: return request_timclk(enable); - else if (clock == PRCMU_SYSCLK) + case PRCMU_SYSCLK: return request_sysclk(enable); - else if (clock == PRCMU_PLLSOC1) + case PRCMU_PLLSOC1: return request_pll(clock, enable); - else - return -EINVAL; + default: + break; + } + if (clock < PRCMU_NUM_REG_CLOCKS) + return request_reg_clock(clock, enable); + return -EINVAL; } int db8500_prcmu_config_esram0_deep_sleep(u8 state) -- cgit v1.2.3 From 57265bc114fd71e721a314f481902cd4f4d3d05f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 10 Oct 2011 13:04:44 +0200 Subject: mfd: Convert db8500-prcmu panic() into pr_crit() panic() is too heavy for this, indeed the PRCMU is critical for the system but not to the point that we should stop everything, if we can still get a prompt or so. Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index e996d11980fa..a25ab9c6b5af 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1763,7 +1763,7 @@ retry: if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) { - panic("prcmu: %s timed out (5 s) waiting for a reply.\n", + pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", __func__); goto unlock_and_return; } @@ -1785,7 +1785,7 @@ retry: if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) goto retry; - panic("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n", + pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n", __func__); } @@ -1811,7 +1811,7 @@ void prcmu_ac_sleep_req() if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) { - panic("prcmu: %s timed out (5 s) waiting for a reply.\n", + pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", __func__); } -- cgit v1.2.3 From e6f9306e2762a651fe6b735a36fcb696d05e8ed2 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Tue, 11 Oct 2011 10:49:17 +0200 Subject: mfd: Add ab8500 interrupt disable hook Add the missing interrupt disable hook in the irq_chip callbacks for ab8500. Signed-off-by: Virupax Sadashivpetimath Reviewed-by: Srinidhi Kasagar Signed-off-by: Philippe Langlais Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 387705e494b9..eb238ee77e68 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -293,6 +293,7 @@ static struct irq_chip ab8500_irq_chip = { .irq_bus_lock = ab8500_irq_lock, .irq_bus_sync_unlock = ab8500_irq_sync_unlock, .irq_mask = ab8500_irq_mask, + .irq_disable = ab8500_irq_mask, .irq_unmask = ab8500_irq_unmask, }; -- cgit v1.2.3 From 0e9049ecc251e721e6ddc4cda7f33062b2e338c7 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Tue, 11 Oct 2011 10:49:32 +0200 Subject: mfd: Add support for ab8500 v3.3 This patch adds support for ab8500 cut 3, MetalFix 3 or v3.3. Signed-off-by: Mattias Wallin Reviewed-by: Jonas Aaberg Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index eb238ee77e68..14ee1896691e 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -849,6 +849,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500) case AB8500_CUT1P1: case AB8500_CUT2P0: case AB8500_CUT3P0: + case AB8500_CUT3P3: dev_info(ab8500->dev, "detected chip, revision: %#x\n", value); break; default: -- cgit v1.2.3 From 9b626ddd7f87f9443e9b6dfac54b5e8f52fb8f52 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Tue, 11 Oct 2011 10:49:39 +0200 Subject: mfd: Remove support for early drop ab8500 chip This patch removes the early drop version of ab8500 which have the really bad version nr 0x0. This chip should not be found in the wild and only exist as ST-Ericsson scrap equipment. Signed-off-by: Mattias Wallin Reviewed-by: Jonas Aaberg Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 14ee1896691e..41da9735a960 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -844,7 +844,6 @@ int __devinit ab8500_init(struct ab8500 *ab8500) return ret; switch (value) { - case AB8500_CUTEARLY: case AB8500_CUT1P0: case AB8500_CUT1P1: case AB8500_CUT2P0: -- cgit v1.2.3 From b4a310373209b87ba455f45227b5361cb746b946 Mon Sep 17 00:00:00 2001 From: Andrew Lynn Date: Tue, 11 Oct 2011 10:49:47 +0200 Subject: mfd: Expose TurnOnStatus in ab8500 sysfs Expose TurnOnStatus (Power key, RTC alarm, Vbus detect, etc) in sysfs. This magic value can be read by system users to determine what caused the platform to turn on last (this) time. Signed-off-by: Andrew Lynn Reviewed-by: Srinidhi Kasagar Reviewed-by: Jonas Aaberg Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 41da9735a960..1e9173804ede 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -92,6 +92,8 @@ #define AB8500_REV_REG 0x80 #define AB8500_SWITCH_OFF_STATUS 0x00 +#define AB8500_TURN_ON_STATUS 0x00 + /* * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt * numbers are indexed into this array with (num / 8). @@ -812,12 +814,40 @@ static ssize_t show_switch_off_status(struct device *dev, return sprintf(buf, "%#x\n", value); } +/* + * ab8500 has turned on due to (TURN_ON_STATUS): + * 0x01 PORnVbat + * 0x02 PonKey1dbF + * 0x04 PonKey2dbF + * 0x08 RTCAlarm + * 0x10 MainChDet + * 0x20 VbusDet + * 0x40 UsbIDDetect + * 0x80 Reserved + */ +static ssize_t show_turn_on_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + u8 value; + struct ab8500 *ab8500; + + ab8500 = dev_get_drvdata(dev); + ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK, + AB8500_TURN_ON_STATUS, &value); + if (ret < 0) + return ret; + return sprintf(buf, "%#x\n", value); +} + static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL); static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL); +static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL); static struct attribute *ab8500_sysfs_entries[] = { &dev_attr_chip_id.attr, &dev_attr_switch_off_status.attr, + &dev_attr_turn_on_status.attr, NULL, }; -- cgit v1.2.3 From 5f6cb769f479314c74e44a84e0180dd520333223 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 21 Oct 2011 11:56:26 +0300 Subject: mfd: Add missing include to intel_msic Without this header we might get following compilation errors: drivers/mfd/intel_msic.c:303:2: error: implicit declaration of function 'readb' drivers/mfd/intel_msic.c:433:2: error: implicit declaration of function 'ioremap_nocache' drivers/mfd/intel_msic.c:433:17: warning: assignment makes pointer from integer without a cast drivers/mfd/intel_msic.c:455:2: error: implicit declaration of function 'iounmap' Signed-off-by: Mika Westerberg Signed-off-by: Samuel Ortiz --- drivers/mfd/intel_msic.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c index bd086b9e852e..97c27762174f 100644 --- a/drivers/mfd/intel_msic.c +++ b/drivers/mfd/intel_msic.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include -- cgit v1.2.3