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 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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') 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 ++++++++++++++------------------------------ 2 files changed, 36 insertions(+), 79 deletions(-) (limited to 'drivers') 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; -- 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') 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') 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') 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') 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') 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') 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') 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 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 13 deletions(-) (limited to 'drivers') 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 -- 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') 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 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') 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 -- 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 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') 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, -- 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') 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 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers') 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, }; -- 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') 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') 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') 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') 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') 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') 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') 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 f3ca07824f309474b308d859c9a2cc871c6c5ab8 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 24 Aug 2011 15:28:20 +0200 Subject: leds: Convert mc13783 driver to mc13xxx MFD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mc13xxx is the more general API and most of the mc13783_... functions are going to die. Signed-off-by: David Jander Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/leds/leds-mc13783.c | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index f369e56d6547..cf71764ff44c 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -21,13 +21,13 @@ #include #include #include -#include +#include #include struct mc13783_led { struct led_classdev cdev; struct work_struct work; - struct mc13783 *master; + struct mc13xxx *master; enum led_brightness new_brightness; int id; }; @@ -111,11 +111,11 @@ static void mc13783_led_work(struct work_struct *work) break; } - mc13783_lock(led->master); + mc13xxx_lock(led->master); - mc13783_reg_rmw(led->master, reg, mask, value); + mc13xxx_reg_rmw(led->master, reg, mask, value); - mc13783_unlock(led->master); + mc13xxx_unlock(led->master); } static void mc13783_led_set(struct led_classdev *led_cdev, @@ -172,23 +172,23 @@ static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current) break; } - mc13783_lock(led->master); + mc13xxx_lock(led->master); - ret = mc13783_reg_rmw(led->master, reg, mask << shift, + ret = mc13xxx_reg_rmw(led->master, reg, mask << shift, value << shift); - mc13783_unlock(led->master); + mc13xxx_unlock(led->master); return ret; } static int __devinit mc13783_leds_prepare(struct platform_device *pdev) { struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent); + struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent); int ret = 0; int reg = 0; - mc13783_lock(dev); + mc13xxx_lock(dev); if (pdata->flags & MC13783_LED_TC1HALF) reg |= MC13783_LED_C1_TC1HALF_BIT; @@ -196,7 +196,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev) if (pdata->flags & MC13783_LED_SLEWLIMTC) reg |= MC13783_LED_Cx_SLEWLIM_BIT; - ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg); + ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg); if (ret) goto out; @@ -206,7 +206,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev) if (pdata->flags & MC13783_LED_SLEWLIMBL) reg |= MC13783_LED_Cx_SLEWLIM_BIT; - ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg); + ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg); if (ret) goto out; @@ -216,7 +216,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev) if (pdata->flags & MC13783_LED_TRIODE_TC1) reg |= MC13783_LED_Cx_TRIODE_TC_BIT; - ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg); + ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg); if (ret) goto out; @@ -226,7 +226,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev) if (pdata->flags & MC13783_LED_TRIODE_TC2) reg |= MC13783_LED_Cx_TRIODE_TC_BIT; - ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg); + ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg); if (ret) goto out; @@ -236,7 +236,7 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev) if (pdata->flags & MC13783_LED_TRIODE_TC3) reg |= MC13783_LED_Cx_TRIODE_TC_BIT; - ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg); + ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg); if (ret) goto out; @@ -255,10 +255,10 @@ static int __devinit mc13783_leds_prepare(struct platform_device *pdev) reg |= (pdata->abref & MC13783_LED_C0_ABREF_MASK) << MC13783_LED_C0_ABREF; - ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg); + ret = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg); out: - mc13783_unlock(dev); + mc13xxx_unlock(dev); return ret; } @@ -353,7 +353,7 @@ static int __devexit mc13783_led_remove(struct platform_device *pdev) { struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13783_led *led = platform_get_drvdata(pdev); - struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent); + struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent); int i; for (i = 0; i < pdata->num_leds; i++) { @@ -361,16 +361,16 @@ static int __devexit mc13783_led_remove(struct platform_device *pdev) cancel_work_sync(&led[i].work); } - mc13783_lock(dev); + mc13xxx_lock(dev); - mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0); - mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0); - mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0); - mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0); - mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0); - mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0); + mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0); + mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0); + mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0); + mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0); + mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0); + mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0); - mc13783_unlock(dev); + mc13xxx_unlock(dev); kfree(led); return 0; -- cgit v1.2.3 From fec316d63219f610e5385f5e54e6c3ea459e58e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 24 Aug 2011 15:28:21 +0200 Subject: mfd: Provide a generic version of mc13xxx adc_do_conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 +++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 45 deletions(-) (limited to 'drivers') 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) -- cgit v1.2.3 From 8dd93eeee873d2383dbca4cca1983b6731efdb75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 24 Aug 2011 15:28:22 +0200 Subject: input: Convert mc13783-ts to mc13xxx API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the first step to also support the touch interface of the mc13892 pmic chip. Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/mc13783_ts.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index c5bc62d85bb6..ede02743eac1 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -35,7 +35,7 @@ MODULE_PARM_DESC(sample_tolerance, struct mc13783_ts_priv { struct input_dev *idev; - struct mc13783 *mc13783; + struct mc13xxx *mc13xxx; struct delayed_work work; struct workqueue_struct *workq; unsigned int sample[4]; @@ -45,7 +45,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data) { struct mc13783_ts_priv *priv = data; - mc13783_irq_ack(priv->mc13783, irq); + mc13xxx_irq_ack(priv->mc13xxx, irq); /* * Kick off reading coordinates. Note that if work happens already @@ -121,10 +121,10 @@ static void mc13783_ts_work(struct work_struct *work) { struct mc13783_ts_priv *priv = container_of(work, struct mc13783_ts_priv, work.work); - unsigned int mode = MC13783_ADC_MODE_TS; + unsigned int mode = MC13XXX_ADC_MODE_TS; unsigned int channel = 12; - if (mc13783_adc_do_conversion(priv->mc13783, + if (mc13xxx_adc_do_conversion(priv->mc13xxx, mode, channel, priv->sample) == 0) mc13783_ts_report_sample(priv); } @@ -134,21 +134,21 @@ static int mc13783_ts_open(struct input_dev *dev) struct mc13783_ts_priv *priv = input_get_drvdata(dev); int ret; - mc13783_lock(priv->mc13783); + mc13xxx_lock(priv->mc13xxx); - mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TS); + mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS); - ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_TS, + ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS, mc13783_ts_handler, MC13783_TS_NAME, priv); if (ret) goto out; - ret = mc13783_reg_rmw(priv->mc13783, MC13783_ADC0, - MC13783_ADC0_TSMOD_MASK, MC13783_ADC0_TSMOD0); + ret = mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0, + MC13XXX_ADC0_TSMOD_MASK, MC13XXX_ADC0_TSMOD0); if (ret) - mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv); + mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv); out: - mc13783_unlock(priv->mc13783); + mc13xxx_unlock(priv->mc13xxx); return ret; } @@ -156,11 +156,11 @@ static void mc13783_ts_close(struct input_dev *dev) { struct mc13783_ts_priv *priv = input_get_drvdata(dev); - mc13783_lock(priv->mc13783); - mc13783_reg_rmw(priv->mc13783, MC13783_ADC0, - MC13783_ADC0_TSMOD_MASK, 0); - mc13783_irq_free(priv->mc13783, MC13783_IRQ_TS, priv); - mc13783_unlock(priv->mc13783); + mc13xxx_lock(priv->mc13xxx); + mc13xxx_reg_rmw(priv->mc13xxx, MC13XXX_ADC0, + MC13XXX_ADC0_TSMOD_MASK, 0); + mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TS, priv); + mc13xxx_unlock(priv->mc13xxx); cancel_delayed_work_sync(&priv->work); } @@ -177,7 +177,7 @@ static int __init mc13783_ts_probe(struct platform_device *pdev) goto err_free_mem; INIT_DELAYED_WORK(&priv->work, mc13783_ts_work); - priv->mc13783 = dev_get_drvdata(pdev->dev.parent); + priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); priv->idev = idev; /* -- cgit v1.2.3 From 613c27ab98dd2242bd9d62b0ab00b440bdf3259f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 24 Aug 2011 15:28:24 +0200 Subject: hwmon: Convert mc13783-adc to mc13xxx API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mc13783_... functions are going to be removed, so switch to the more generic API. Signed-off-by: Uwe Kleine-König Acked-by: Guenter Roeck Signed-off-by: Samuel Ortiz --- drivers/hwmon/mc13783-adc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index d5226c9e1201..ef65ab56b094 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -31,7 +31,7 @@ #define MC13783_ADC_NAME "mc13783-adc" struct mc13783_adc_priv { - struct mc13783 *mc13783; + struct mc13xxx *mc13xxx; struct device *hwmon_dev; }; @@ -51,8 +51,8 @@ static int mc13783_adc_read(struct device *dev, unsigned int sample[4]; int ret; - ret = mc13783_adc_do_conversion(priv->mc13783, - MC13783_ADC_MODE_MULT_CHAN, + ret = mc13xxx_adc_do_conversion(priv->mc13xxx, + MC13XXX_ADC_MODE_MULT_CHAN, channel, sample); if (ret) return ret; @@ -147,9 +147,9 @@ static const struct attribute_group mc13783_group_ts = { static int mc13783_adc_use_touchscreen(struct platform_device *pdev) { struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); - unsigned flags = mc13783_get_flags(priv->mc13783); + unsigned flags = mc13xxx_get_flags(priv->mc13xxx); - return flags & MC13783_USE_TOUCHSCREEN; + return flags & MC13XXX_USE_TOUCHSCREEN; } static int __init mc13783_adc_probe(struct platform_device *pdev) @@ -161,7 +161,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->mc13783 = dev_get_drvdata(pdev->dev.parent); + priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); platform_set_drvdata(pdev, priv); -- cgit v1.2.3 From b46880e57b4c513adeb2608c3700b352860b5662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 24 Aug 2011 15:28:25 +0200 Subject: mfd: Remove mc13783 API functions and symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') 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 = -- 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 --- drivers/gpio/Kconfig | 3 +-- drivers/i2c/busses/Kconfig | 1 - drivers/media/radio/Kconfig | 1 - drivers/mfd/Kconfig | 22 ++++------------------ 4 files changed, 5 insertions(+), 22 deletions(-) (limited to 'drivers') 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') 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 ++++++++ 3 files changed, 27 insertions(+) (limited to 'drivers') 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 = { -- 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') 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') 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') 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: =?UTF-8?q?Philippe=20R=C3=A9tornaz?= Date: Fri, 22 Jul 2011 16:17:07 +0200 Subject: mfd: Unconditionally register mc13xxx regulator subdevice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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') 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: =?UTF-8?q?Philippe=20R=C3=A9tornaz?= Date: Fri, 22 Jul 2011 16:17:08 +0200 Subject: mfd: Implicitly register mc13xxx led subdevice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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') 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: =?UTF-8?q?Philippe=20R=C3=A9tornaz?= Date: Sun, 18 Sep 2011 18:10:53 +0200 Subject: input: Add power button support for mc13783 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ++ 4 files changed, 302 insertions(+) create mode 100644 drivers/input/misc/mc13783-pwrbutton.c (limited to 'drivers') 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; } -- 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 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers') 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: -- cgit v1.2.3 From 52b7ad3a63a42b76f4f07cba876479a3c416f1e8 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sun, 18 Sep 2011 19:12:34 +0200 Subject: leds: Finish mc13783 conversion to the mc13xxx API Signed-off-by: Samuel Ortiz --- drivers/leds/leds-mc13783.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index cf71764ff44c..b3393a9f2139 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -183,7 +183,7 @@ static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current) static int __devinit mc13783_leds_prepare(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent); int ret = 0; int reg = 0; @@ -264,8 +264,8 @@ out: static int __devinit mc13783_led_probe(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct mc13783_led_platform_data *led_cur; + struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13xxx_led_platform_data *led_cur; struct mc13783_led *led, *led_dat; int ret, i; int init_led = 0; @@ -351,7 +351,7 @@ err_free: static int __devexit mc13783_led_remove(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mc13783_led *led = platform_get_drvdata(pdev); struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent); int i; -- cgit v1.2.3 From 8f1585aa73a7987cd9b5ee502f42297a28303278 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Mon, 19 Sep 2011 11:33:17 +0200 Subject: regulator: Finish mc13783 conversion to the mc13xxx API Signed-off-by: Samuel Ortiz --- drivers/regulator/mc13783-regulator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index 730f43ad415b..cb2841feeefd 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -336,9 +336,9 @@ static int __devinit mc13783_regulator_probe(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv; struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); - struct mc13783_regulator_platform_data *pdata = + struct mc13xxx_regulator_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct mc13783_regulator_init_data *init_data; + struct mc13xxx_regulator_init_data *init_data; int i, ret; dev_dbg(&pdev->dev, "%s id %d\n", __func__, pdev->id); @@ -381,7 +381,7 @@ err: static int __devexit mc13783_regulator_remove(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); - struct mc13783_regulator_platform_data *pdata = + struct mc13xxx_regulator_platform_data *pdata = dev_get_platdata(&pdev->dev); int i; -- 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') 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 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 511 insertions(+) create mode 100644 drivers/mfd/intel_msic.c (limited to 'drivers') 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"); -- 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') 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') 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 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2322 insertions(+) create mode 100644 drivers/mfd/ab5500-core.c (limited to 'drivers') 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"); -- 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') 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 --- drivers/mfd/Kconfig | 14 - drivers/mfd/Makefile | 1 - drivers/mfd/ab3550-core.c | 1380 --------------------------------------------- 3 files changed, 1395 deletions(-) delete mode 100644 drivers/mfd/ab3550-core.c (limited to 'drivers') 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"); -- cgit v1.2.3 From bb3149a93be1703ddc77ba34633df731bda5f269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 23 Sep 2011 21:51:42 +0200 Subject: mfd: Fix a sparse warning about mc13xxx_chipname not being declared MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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') 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') 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') 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 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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') 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') 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') 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') 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 --- 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 +- 7 files changed, 240 insertions(+), 355 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') 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 -- 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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