From 0dcc9a9d83dde6e34429b1914c9ac10aa447c7cb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 3 Feb 2012 11:05:37 +0000 Subject: mfd: twl-core: Don't specify regulator consumers by struct device This has been deprecated for considerable time now and support has been removed from the regulator API. dev_name should be used instead. Reported-by: Stephen Rothwell Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 8ce3959..c1e4f1a 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -753,9 +753,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) /* we need to connect regulators to this transceiver */ if (twl_has_regulator() && child) { - usb1v5.dev = child; - usb1v8.dev = child; - usb3v1.dev = child; + usb1v5.dev_name = dev_name(child); + usb1v8.dev_name = dev_name(child); + usb3v1.dev_name = dev_name(child); } } if (twl_has_usb() && pdata->usb && twl_class_is_6030()) { @@ -801,7 +801,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); /* we need to connect regulators to this transceiver */ if (twl_has_regulator() && child) - usb3v3.dev = child; + usb3v3.dev_name = dev_name(child); } else if (twl_has_regulator() && twl_class_is_6030()) { if (features & TWL6025_SUBCLASS) child = add_regulator(TWL6025_REG_LDOUSB, -- cgit v1.1 From d48f411c10f2badaf88e6050cd3d3acd52197356 Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Wed, 11 Jan 2012 16:11:41 +0530 Subject: mfd: Add new mfd device for TPS65217 The TPS65217 chip is a power management IC for Portable Navigation Systems and Tablet Computing devices. It contains the following components: - Regulators - White LED - USB battery charger This patch adds support for tps65217 mfd device. At this time only the regulator functionality is made available. Signed-off-by: AnilKumar Ch Reviwed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 15 +++ drivers/mfd/Makefile | 1 + drivers/mfd/tps65217.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 drivers/mfd/tps65217.c (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f147395..c559d35 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -143,6 +143,21 @@ config TPS6507X This driver can also be built as a module. If so, the module will be called tps6507x. +config MFD_TPS65217 + tristate "TPS65217 Power Management / White LED chips" + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + If you say yes here you get support for the TPS65217 series of + Power Management / White LED chips. + These include voltage regulators, lithium ion/polymer battery + charger, wled and other features that are often used in portable + devices. + + This driver can also be built as a module. If so, the module + will be called tps65217. + config MFD_TPS6586X bool "TPS6586x Power Management chips" depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b953bab..27430d3 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o wm8994-regmap.o obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o +obj-$(CONFIG_MFD_TPS65217) += tps65217.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o tps65912-objs := tps65912-core.o tps65912-irq.o obj-$(CONFIG_MFD_TPS65912) += tps65912.o diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c new file mode 100644 index 0000000..f7d854e --- /dev/null +++ b/drivers/mfd/tps65217.c @@ -0,0 +1,242 @@ +/* + * tps65217.c + * + * TPS65217 chip family multi-function driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * tps65217_reg_read: Read a single tps65217 register. + * + * @tps: Device to read from. + * @reg: Register to read. + * @val: Contians the value + */ +int tps65217_reg_read(struct tps65217 *tps, unsigned int reg, + unsigned int *val) +{ + return regmap_read(tps->regmap, reg, val); +} +EXPORT_SYMBOL_GPL(tps65217_reg_read); + +/** + * tps65217_reg_write: Write a single tps65217 register. + * + * @tps65217: Device to write to. + * @reg: Register to write to. + * @val: Value to write. + * @level: Password protected level + */ +int tps65217_reg_write(struct tps65217 *tps, unsigned int reg, + unsigned int val, unsigned int level) +{ + int ret; + unsigned int xor_reg_val; + + switch (level) { + case TPS65217_PROTECT_NONE: + return regmap_write(tps->regmap, reg, val); + case TPS65217_PROTECT_L1: + xor_reg_val = reg ^ TPS65217_PASSWORD_REGS_UNLOCK; + ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD, + xor_reg_val); + if (ret < 0) + return ret; + + return regmap_write(tps->regmap, reg, val); + case TPS65217_PROTECT_L2: + xor_reg_val = reg ^ TPS65217_PASSWORD_REGS_UNLOCK; + ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD, + xor_reg_val); + if (ret < 0) + return ret; + ret = regmap_write(tps->regmap, reg, val); + if (ret < 0) + return ret; + ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD, + xor_reg_val); + if (ret < 0) + return ret; + return regmap_write(tps->regmap, reg, val); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(tps65217_reg_write); + +/** + * tps65217_update_bits: Modify bits w.r.t mask, val and level. + * + * @tps65217: Device to write to. + * @reg: Register to read-write to. + * @mask: Mask. + * @val: Value to write. + * @level: Password protected level + */ +int tps65217_update_bits(struct tps65217 *tps, unsigned int reg, + unsigned int mask, unsigned int val, unsigned int level) +{ + int ret; + unsigned int data; + + ret = tps65217_reg_read(tps, reg, &data); + if (ret) { + dev_err(tps->dev, "Read from reg 0x%x failed\n", reg); + return ret; + } + + data &= ~mask; + data |= val & mask; + + ret = tps65217_reg_write(tps, reg, data, level); + if (ret) + dev_err(tps->dev, "Write for reg 0x%x failed\n", reg); + + return ret; +} + +int tps65217_set_bits(struct tps65217 *tps, unsigned int reg, + unsigned int mask, unsigned int val, unsigned int level) +{ + return tps65217_update_bits(tps, reg, mask, val, level); +} +EXPORT_SYMBOL_GPL(tps65217_set_bits); + +int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg, + unsigned int mask, unsigned int level) +{ + return tps65217_update_bits(tps, reg, mask, 0, level); +} +EXPORT_SYMBOL_GPL(tps65217_clear_bits); + +static struct regmap_config tps65217_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int __devinit tps65217_probe(struct i2c_client *client, + const struct i2c_device_id *ids) +{ + struct tps65217 *tps; + struct tps65217_board *pdata = client->dev.platform_data; + int i, ret; + unsigned int version; + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + tps->pdata = pdata; + tps->regmap = regmap_init_i2c(client, &tps65217_regmap_config); + if (IS_ERR(tps->regmap)) { + ret = PTR_ERR(tps->regmap); + dev_err(tps->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + i2c_set_clientdata(client, tps); + tps->dev = &client->dev; + + ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version); + if (ret < 0) { + dev_err(tps->dev, "Failed to read revision" + " register: %d\n", ret); + goto err_regmap; + } + + dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n", + (version & TPS65217_CHIPID_CHIP_MASK) >> 4, + version & TPS65217_CHIPID_REV_MASK); + + for (i = 0; i < TPS65217_NUM_REGULATOR; i++) { + struct platform_device *pdev; + + pdev = platform_device_alloc("tps65217-pmic", i); + if (!pdev) { + dev_err(tps->dev, "Cannot create regulator %d\n", i); + continue; + } + + pdev->dev.parent = tps->dev; + platform_device_add_data(pdev, &pdata->tps65217_init_data[i], + sizeof(pdata->tps65217_init_data[i])); + tps->regulator_pdev[i] = pdev; + + platform_device_add(pdev); + } + + return 0; + +err_regmap: + regmap_exit(tps->regmap); + + return ret; +} + +static int __devexit tps65217_remove(struct i2c_client *client) +{ + struct tps65217 *tps = i2c_get_clientdata(client); + int i; + + for (i = 0; i < TPS65217_NUM_REGULATOR; i++) + platform_device_unregister(tps->regulator_pdev[i]); + + regmap_exit(tps->regmap); + + return 0; +} + +static const struct i2c_device_id tps65217_id_table[] = { + {"tps65217", 0xF0}, + {/* end of list */} +}; +MODULE_DEVICE_TABLE(i2c, tps65217_id_table); + +static struct i2c_driver tps65217_driver = { + .driver = { + .name = "tps65217", + }, + .id_table = tps65217_id_table, + .probe = tps65217_probe, + .remove = __devexit_p(tps65217_remove), +}; + +static int __init tps65217_init(void) +{ + return i2c_add_driver(&tps65217_driver); +} +subsys_initcall(tps65217_init); + +static void __exit tps65217_exit(void) +{ + i2c_del_driver(&tps65217_driver); +} +module_exit(tps65217_exit); + +MODULE_AUTHOR("AnilKumar Ch "); +MODULE_DESCRIPTION("TPS65217 chip family multi-function driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 4d75dd61dfb53eaa286c54fb121e5b51b106c272 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 10 Jan 2012 15:09:56 +0800 Subject: mfd: Add missing regmap_exit to free a previously allocated da9052 register map Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/da9052-i2c.c | 7 +++++-- drivers/mfd/da9052-spi.c | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 44b97c7..fdc2c6a 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -74,14 +74,16 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client, ret = da9052_i2c_enable_multiwrite(da9052); if (ret < 0) - goto err; + goto err_regmap; ret = da9052_device_init(da9052, id->driver_data); if (ret != 0) - goto err; + goto err_regmap; return 0; +err_regmap: + regmap_exit(da9052->regmap); err: kfree(da9052); return ret; @@ -92,6 +94,7 @@ static int da9052_i2c_remove(struct i2c_client *client) struct da9052 *da9052 = i2c_get_clientdata(client); da9052_device_exit(da9052); + regmap_exit(da9052->regmap); kfree(da9052); return 0; diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index cdbc7ca..76bb9ea08 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -52,10 +52,12 @@ static int da9052_spi_probe(struct spi_device *spi) ret = da9052_device_init(da9052, id->driver_data); if (ret != 0) - goto err; + goto err_regmap; return 0; +err_regmap: + regmap_exit(da9052->regmap); err: kfree(da9052); return ret; @@ -66,6 +68,7 @@ static int da9052_spi_remove(struct spi_device *spi) struct da9052 *da9052 = dev_get_drvdata(&spi->dev); da9052_device_exit(da9052); + regmap_exit(da9052->regmap); kfree(da9052); return 0; -- cgit v1.1 From 1039d762d03b573de4d46603c8583051c6d79094 Mon Sep 17 00:00:00 2001 From: Michael Thalmeier Date: Mon, 20 Feb 2012 12:18:13 +0100 Subject: mfd: Add pdata to set mc13783-ts conversion delay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MC13783 can be programmed to wait some clock cycles between the touchscreen polarization and the resistance conversion. This is needed to adjust for touchscreens with high capacitance between plates. Signed-off-by: Michael Thalmeier Acked-by: Uwe Kleine-König Acked-by: Dmitry Torokhov Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 7122386..9fd4f63 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -560,6 +560,8 @@ EXPORT_SYMBOL(mc13xxx_get_flags); #define MC13XXX_ADC1_CHAN0_SHIFT 5 #define MC13XXX_ADC1_CHAN1_SHIFT 8 +#define MC13783_ADC1_ATO_SHIFT 11 +#define MC13783_ADC1_ATOX (1 << 19) struct mc13xxx_adcdone_data { struct mc13xxx *mc13xxx; @@ -580,7 +582,8 @@ static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data) #define MC13XXX_ADC_WORKING (1 << 0) int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, - unsigned int channel, unsigned int *sample) + unsigned int channel, u8 ato, bool atox, + unsigned int *sample) { u32 adc0, adc1, old_adc0; int i, ret; @@ -631,6 +634,9 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, return -EINVAL; } + adc1 |= ato << MC13783_ADC1_ATO_SHIFT; + if (atox) + adc1 |= MC13783_ADC1_ATOX; dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__); mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, mc13xxx_handler_adcdone, __func__, &adcdone_data); @@ -813,7 +819,8 @@ err_revision: mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN) - mc13xxx_add_subdevice(mc13xxx, "%s-ts"); + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts", + &pdata->touch, sizeof(pdata->touch)); if (pdata) { mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", -- cgit v1.1 From e536b62095301271d974983044a011c29fcb2ea2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 11 Jan 2012 17:27:16 +0800 Subject: mfd: Fix section mismatch warning for da9052-spi Add __devinit annotation for da9052_spi_probe to fix below build warning: WARNING: drivers/built-in.o(.text+0x349b4): Section mismatch in reference from the function da9052_spi_probe() to the function .devinit.text:da9052_device_init() The function da9052_spi_probe() references the function __devinit da9052_device_init(). This is often because da9052_spi_probe lacks a __devinit annotation or the annotation of da9052_device_init is wrong. Also add __devexit annotation for da9052_spi_remove because we have __devexit_p around it in the remove callback. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/da9052-spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 76bb9ea08..6faf149 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -21,7 +21,7 @@ #include -static int da9052_spi_probe(struct spi_device *spi) +static int __devinit da9052_spi_probe(struct spi_device *spi) { int ret; const struct spi_device_id *id = spi_get_device_id(spi); @@ -63,7 +63,7 @@ err: return ret; } -static int da9052_spi_remove(struct spi_device *spi) +static int __devexit da9052_spi_remove(struct spi_device *spi) { struct da9052 *da9052 = dev_get_drvdata(&spi->dev); -- cgit v1.1 From c72fe851df21603cd149320df49064eb2f903707 Mon Sep 17 00:00:00 2001 From: Daniel Willerud Date: Fri, 13 Jan 2012 16:20:03 +0100 Subject: mfd: Remove db8500-prcmu U8400 legacy This removes the U8400 legacy from PRCMU and cpufreq drivers. This platform has no current in-kernel users. Signed-off-by: Daniel Willerud Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index af8e0ef..b911963 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -503,9 +503,6 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = { /* PLLDIV=12, PLLSW=4 (PLLDDR) */ #define PRCMU_DSI_CLOCK_SETTING 0x0000008C -/* PLLDIV=8, PLLSW=4 (PLLDDR) */ -#define PRCMU_DSI_CLOCK_SETTING_U8400 0x00000088 - /* DPI 50000000 Hz */ #define PRCMU_DPI_CLOCK_SETTING ((1 << PRCMU_CLK_PLL_SW_SHIFT) | \ (16 << PRCMU_CLK_PLL_DIV_SHIFT)) @@ -514,9 +511,6 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = { /* D=101, N=1, R=4, SELDIV2=0 */ #define PRCMU_PLLDSI_FREQ_SETTING 0x00040165 -/* D=70, N=1, R=3, SELDIV2=0 */ -#define PRCMU_PLLDSI_FREQ_SETTING_U8400 0x00030146 - #define PRCMU_ENABLE_PLLDSI 0x00000001 #define PRCMU_DISABLE_PLLDSI 0x00000000 #define PRCMU_RELEASE_RESET_DSS 0x0000400C @@ -539,19 +533,14 @@ static struct { int db8500_prcmu_enable_dsipll(void) { int i; - unsigned int plldsifreq; /* Clear DSIPLL_RESETN */ writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR); /* Unclamp DSIPLL in/out */ 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, PRCM_PLLDSI_FREQ); + writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ); writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL); /* Enable Escape clocks */ writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV); @@ -583,12 +572,6 @@ int db8500_prcmu_disable_dsipll(void) int db8500_prcmu_set_display_clocks(void) { unsigned long flags; - unsigned int dsiclk; - - if (prcmu_is_u8400()) - dsiclk = PRCMU_DSI_CLOCK_SETTING_U8400; - else - dsiclk = PRCMU_DSI_CLOCK_SETTING; spin_lock_irqsave(&clk_mgt_lock, flags); @@ -596,7 +579,7 @@ int db8500_prcmu_set_display_clocks(void) while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) cpu_relax(); - writel(dsiclk, PRCM_HDMICLK_MGT); + writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT); writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT); writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT); @@ -642,11 +625,6 @@ bool prcmu_has_arm_maxopp(void) PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK; } -bool prcmu_is_u8400(void) -{ - return prcmu_version.project_number == PRCMU_PROJECT_ID_8400V2_0; -} - /** * prcmu_get_boot_status - PRCMU boot status checking * Returns: the current PRCMU boot status -- cgit v1.1 From b58d12fe6ccd16030e1a69b5c443075f7bed0f6d Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 13 Jan 2012 16:20:10 +0100 Subject: mfd: Function for obtaining the db8500 prcmu firmware version This patch exports a function that can be used to tell which version of the DB8500 PRCMU firmware is available, and revamps the firmware detection code a bit. Reviewed-by: Bengt Jonsson Reviewed-by: Jonas Aberg Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 51 ++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 18 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index b911963..1251993 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -39,11 +39,6 @@ /* Offset for the firmware version within the TCPM */ #define PRCMU_FW_VERSION_OFFSET 0xA4 -/* PRCMU project numbers, defined by PRCMU FW */ -#define PRCMU_PROJECT_ID_8500V1_0 1 -#define PRCMU_PROJECT_ID_8500V2_0 2 -#define PRCMU_PROJECT_ID_8400V2_0 3 - /* Index of different voltages to be used when accessing AVSData */ #define PRCM_AVS_BASE 0x2FC #define PRCM_AVS_VBB_RET (PRCM_AVS_BASE + 0x0) @@ -266,6 +261,11 @@ #define WAKEUP_BIT_GPIO7 BIT(30) #define WAKEUP_BIT_GPIO8 BIT(31) +static struct { + bool valid; + struct prcmu_fw_version version; +} fw_info; + /* * This vector maps irq numbers to the bits in the bit field used in * communication with the PRCMU firmware. @@ -522,14 +522,6 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = { #define PRCMU_PLLDSI_LOCKP_LOCKED 0x3 -static struct { - u8 project_number; - u8 api_version; - u8 func_version; - u8 errata; -} prcmu_version; - - int db8500_prcmu_enable_dsipll(void) { int i; @@ -619,6 +611,11 @@ void prcmu_disable_spi2(void) spin_unlock_irqrestore(&gpiocr_lock, flags); } +struct prcmu_fw_version *prcmu_get_fw_version(void) +{ + return fw_info.valid ? &fw_info.version : NULL; +} + bool prcmu_has_arm_maxopp(void) { return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) & @@ -2077,6 +2074,22 @@ static struct irq_chip prcmu_irq_chip = { .irq_unmask = prcmu_irq_unmask, }; +static char *fw_project_name(u8 project) +{ + switch (project) { + case PRCMU_FW_PROJECT_U8500: + return "U8500"; + case PRCMU_FW_PROJECT_U8500_C2: + return "U8500 C2"; + case PRCMU_FW_PROJECT_U9500: + return "U9500"; + case PRCMU_FW_PROJECT_U9500_C2: + return "U9500 C2"; + default: + return "Unknown"; + } +} + void __init db8500_prcmu_early_init(void) { unsigned int i; @@ -2086,11 +2099,13 @@ void __init db8500_prcmu_early_init(void) if (tcpm_base != NULL) { u32 version; version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET); - prcmu_version.project_number = version & 0xFF; - prcmu_version.api_version = (version >> 8) & 0xFF; - prcmu_version.func_version = (version >> 16) & 0xFF; - prcmu_version.errata = (version >> 24) & 0xFF; - pr_info("PRCMU firmware version %d.%d.%d\n", + fw_info.version.project = version & 0xFF; + fw_info.version.api_version = (version >> 8) & 0xFF; + fw_info.version.func_version = (version >> 16) & 0xFF; + fw_info.version.errata = (version >> 24) & 0xFF; + fw_info.valid = true; + pr_info("PRCMU firmware: %s, version %d.%d.%d\n", + fw_project_name(fw_info.version.project), (version >> 8) & 0xFF, (version >> 16) & 0xFF, (version >> 24) & 0xFF); iounmap(tcpm_base); -- cgit v1.1 From 0508901ca794d411efb09befb88b8194d8387428 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 13 Jan 2012 16:20:20 +0100 Subject: mfd: Update abstract dbx500 interface This prefixes a number of accessor functions with db8500_* since they are DB8500-specific and we need to move to this naming scheme. We also replace numerous instances of machine_is() with cpu_is() which covers the right type of ASICs rather than entire machines i.e. boards. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 1251993..5179abf 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -884,23 +884,23 @@ int db8500_prcmu_get_arm_opp(void) } /** - * prcmu_get_ddr_opp - get the current DDR OPP + * db8500_prcmu_get_ddr_opp - get the current DDR OPP * * Returns: the current DDR OPP */ -int prcmu_get_ddr_opp(void) +int db8500_prcmu_get_ddr_opp(void) { return readb(PRCM_DDR_SUBSYS_APE_MINBW); } /** - * set_ddr_opp - set the appropriate DDR OPP + * db8500_set_ddr_opp - set the appropriate DDR OPP * @opp: The new DDR operating point to which transition is to be made * Returns: 0 on success, non-zero on failure * * This function sets the operating point of the DDR. */ -int prcmu_set_ddr_opp(u8 opp) +int db8500_prcmu_set_ddr_opp(u8 opp) { if (opp < DDR_100_OPP || opp > DDR_25_OPP) return -EINVAL; @@ -911,13 +911,13 @@ int prcmu_set_ddr_opp(u8 opp) return 0; } /** - * set_ape_opp - set the appropriate APE OPP + * db8500_set_ape_opp - set the appropriate APE OPP * @opp: The new APE operating point to which transition is to be made * Returns: 0 on success, non-zero on failure * * This function sets the operating point of the APE. */ -int prcmu_set_ape_opp(u8 opp) +int db8500_prcmu_set_ape_opp(u8 opp) { int r = 0; @@ -943,11 +943,11 @@ int prcmu_set_ape_opp(u8 opp) } /** - * prcmu_get_ape_opp - get the current APE OPP + * db8500_prcmu_get_ape_opp - get the current APE OPP * * Returns: the current APE OPP */ -int prcmu_get_ape_opp(void) +int db8500_prcmu_get_ape_opp(void) { return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_APE_OPP); } @@ -1451,7 +1451,7 @@ int db8500_prcmu_config_esram0_deep_sleep(u8 state) return 0; } -int prcmu_config_hotdog(u8 threshold) +int db8500_prcmu_config_hotdog(u8 threshold) { mutex_lock(&mb4_transfer.lock); @@ -1469,7 +1469,7 @@ int prcmu_config_hotdog(u8 threshold) return 0; } -int prcmu_config_hotmon(u8 low, u8 high) +int db8500_prcmu_config_hotmon(u8 low, u8 high) { mutex_lock(&mb4_transfer.lock); @@ -1508,7 +1508,7 @@ static int config_hot_period(u16 val) return 0; } -int prcmu_start_temp_sense(u16 cycles32k) +int db8500_prcmu_start_temp_sense(u16 cycles32k) { if (cycles32k == 0xFFFF) return -EINVAL; @@ -1516,7 +1516,7 @@ int prcmu_start_temp_sense(u16 cycles32k) return config_hot_period(cycles32k); } -int prcmu_stop_temp_sense(void) +int db8500_prcmu_stop_temp_sense(void) { return config_hot_period(0xFFFF); } @@ -1545,7 +1545,7 @@ static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3) } -int prcmu_config_a9wdog(u8 num, bool sleep_auto_off) +int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off) { BUG_ON(num == 0 || num > 0xf); return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0, @@ -1553,17 +1553,17 @@ int prcmu_config_a9wdog(u8 num, bool sleep_auto_off) A9WDOG_AUTO_OFF_DIS); } -int prcmu_enable_a9wdog(u8 id) +int db8500_prcmu_enable_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0); } -int prcmu_disable_a9wdog(u8 id) +int db8500_prcmu_disable_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0); } -int prcmu_kick_a9wdog(u8 id) +int db8500_prcmu_kick_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0); } @@ -1572,7 +1572,7 @@ int prcmu_kick_a9wdog(u8 id) * timeout is 28 bit, in ms. */ #define MAX_WATCHDOG_TIMEOUT 131000 -int prcmu_load_a9wdog(u8 id, u32 timeout) +int db8500_prcmu_load_a9wdog(u8 id, u32 timeout) { if (timeout > MAX_WATCHDOG_TIMEOUT) /* @@ -1825,9 +1825,9 @@ u16 db8500_prcmu_get_reset_code(void) } /** - * prcmu_reset_modem - ask the PRCMU to reset modem + * db8500_prcmu_reset_modem - ask the PRCMU to reset modem */ -void prcmu_modem_reset(void) +void db8500_prcmu_modem_reset(void) { mutex_lock(&mb1_transfer.lock); @@ -2147,7 +2147,7 @@ void __init db8500_prcmu_early_init(void) } } -static void __init db8500_prcmu_init_clkforce(void) +static void __init init_prcm_registers(void) { u32 val; @@ -2405,7 +2405,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) if (ux500_is_svp()) return -ENODEV; - db8500_prcmu_init_clkforce(); + init_prcm_registers(); /* Clean up the mailbox interrupts after pre-kernel code. */ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); -- cgit v1.1 From 6b6fae2b890826c99f9e62cceec4f859c98ee575 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 13 Jan 2012 16:20:28 +0100 Subject: mfd: db8500 clock handling update This updates the clock handling in the DB8500 PRCMU driver with the latest findings and API changes related to changes in the backing firmware in the PRCMU. - Add the necessary interfaces to get the frequencies of the clocks and set the rate of some of the clocks. - Add support for controlling the clocks PLLSOC0, PLLDSI, DSI0, DSI1 and DSI escape clocks (DSInESCCLK). - Correct the PLLSDI enable/disable sequence by using the DSIPLL_CLAMPI bit. After this we will have the interfaces and code to implement the U8500 clock framework properly. Reviewed-by: Jonas Aberg Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 666 +++++++++++++++++++++++++++++++++++----- drivers/mfd/dbx500-prcmu-regs.h | 128 +++++--- 2 files changed, 673 insertions(+), 121 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 5179abf..9459327 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -132,6 +132,8 @@ #define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0) #define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1) #define PRCM_REQ_MB1_PLL_ON_OFF (PRCM_REQ_MB1 + 0x4) +#define PLL_SOC0_OFF 0x1 +#define PLL_SOC0_ON 0x2 #define PLL_SOC1_OFF 0x4 #define PLL_SOC1_ON 0x8 @@ -420,43 +422,95 @@ static DEFINE_SPINLOCK(gpiocr_lock); static __iomem void *tcdm_base; struct clk_mgt { - unsigned int offset; + void __iomem *reg; u32 pllsw; + int branch; + bool clk38div; +}; + +enum { + PLL_RAW, + PLL_FIX, + PLL_DIV }; static DEFINE_SPINLOCK(clk_mgt_lock); -#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT_OFF), 0 } +#define CLK_MGT_ENTRY(_name, _branch, _clk38div)[PRCMU_##_name] = \ + { (PRCM_##_name##_MGT), 0 , _branch, _clk38div} struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { - CLK_MGT_ENTRY(SGACLK), - CLK_MGT_ENTRY(UARTCLK), - CLK_MGT_ENTRY(MSP02CLK), - CLK_MGT_ENTRY(MSP1CLK), - CLK_MGT_ENTRY(I2CCLK), - CLK_MGT_ENTRY(SDMMCCLK), - CLK_MGT_ENTRY(SLIMCLK), - CLK_MGT_ENTRY(PER1CLK), - CLK_MGT_ENTRY(PER2CLK), - CLK_MGT_ENTRY(PER3CLK), - CLK_MGT_ENTRY(PER5CLK), - CLK_MGT_ENTRY(PER6CLK), - CLK_MGT_ENTRY(PER7CLK), - CLK_MGT_ENTRY(LCDCLK), - CLK_MGT_ENTRY(BMLCLK), - CLK_MGT_ENTRY(HSITXCLK), - CLK_MGT_ENTRY(HSIRXCLK), - CLK_MGT_ENTRY(HDMICLK), - CLK_MGT_ENTRY(APEATCLK), - CLK_MGT_ENTRY(APETRACECLK), - CLK_MGT_ENTRY(MCDECLK), - CLK_MGT_ENTRY(IPI2CCLK), - CLK_MGT_ENTRY(DSIALTCLK), - CLK_MGT_ENTRY(DMACLK), - CLK_MGT_ENTRY(B2R2CLK), - CLK_MGT_ENTRY(TVCLK), - CLK_MGT_ENTRY(SSPCLK), - CLK_MGT_ENTRY(RNGCLK), - CLK_MGT_ENTRY(UICCCLK), + CLK_MGT_ENTRY(SGACLK, PLL_DIV, false), + CLK_MGT_ENTRY(UARTCLK, PLL_FIX, true), + CLK_MGT_ENTRY(MSP02CLK, PLL_FIX, true), + CLK_MGT_ENTRY(MSP1CLK, PLL_FIX, true), + CLK_MGT_ENTRY(I2CCLK, PLL_FIX, true), + CLK_MGT_ENTRY(SDMMCCLK, PLL_DIV, true), + CLK_MGT_ENTRY(SLIMCLK, PLL_FIX, true), + CLK_MGT_ENTRY(PER1CLK, PLL_DIV, true), + CLK_MGT_ENTRY(PER2CLK, PLL_DIV, true), + CLK_MGT_ENTRY(PER3CLK, PLL_DIV, true), + CLK_MGT_ENTRY(PER5CLK, PLL_DIV, true), + CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true), + CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true), + CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true), + CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true), + CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true), + CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true), + CLK_MGT_ENTRY(HDMICLK, PLL_FIX, false), + CLK_MGT_ENTRY(APEATCLK, PLL_DIV, true), + CLK_MGT_ENTRY(APETRACECLK, PLL_DIV, true), + CLK_MGT_ENTRY(MCDECLK, PLL_DIV, true), + CLK_MGT_ENTRY(IPI2CCLK, PLL_FIX, true), + CLK_MGT_ENTRY(DSIALTCLK, PLL_FIX, false), + CLK_MGT_ENTRY(DMACLK, PLL_DIV, true), + CLK_MGT_ENTRY(B2R2CLK, PLL_DIV, true), + CLK_MGT_ENTRY(TVCLK, PLL_FIX, true), + CLK_MGT_ENTRY(SSPCLK, PLL_FIX, true), + CLK_MGT_ENTRY(RNGCLK, PLL_FIX, true), + CLK_MGT_ENTRY(UICCCLK, PLL_FIX, false), +}; + +struct dsiclk { + u32 divsel_mask; + u32 divsel_shift; + u32 divsel; +}; + +static struct dsiclk dsiclk[2] = { + { + .divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK, + .divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT, + .divsel = PRCM_DSI_PLLOUT_SEL_PHI, + }, + { + .divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK, + .divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT, + .divsel = PRCM_DSI_PLLOUT_SEL_PHI, + } +}; + +struct dsiescclk { + u32 en; + u32 div_mask; + u32 div_shift; +}; + +static struct dsiescclk dsiescclk[3] = { + { + .en = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN, + .div_mask = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK, + .div_shift = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT, + }, + { + .en = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN, + .div_mask = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK, + .div_shift = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT, + }, + { + .en = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN, + .div_mask = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK, + .div_shift = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT, + } }; static struct regulator *hwacc_regulator[NUM_HW_ACC]; @@ -910,6 +964,7 @@ int db8500_prcmu_set_ddr_opp(u8 opp) return 0; } + /** * db8500_set_ape_opp - set the appropriate APE OPP * @opp: The new APE operating point to which transition is to be made @@ -1031,7 +1086,9 @@ static int request_pll(u8 clock, bool enable) { int r = 0; - if (clock == PRCMU_PLLSOC1) + if (clock == PRCMU_PLLSOC0) + clock = (enable ? PLL_SOC0_ON : PLL_SOC0_OFF); + else if (clock == PRCMU_PLLSOC1) clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF); else return -EINVAL; @@ -1350,7 +1407,7 @@ static int request_timclk(bool enable) return 0; } -static int request_reg_clock(u8 clock, bool enable) +static int request_clock(u8 clock, bool enable) { u32 val; unsigned long flags; @@ -1361,14 +1418,14 @@ static int request_reg_clock(u8 clock, bool enable) while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) cpu_relax(); - val = readl(_PRCMU_BASE + clk_mgt[clock].offset); + val = readl(clk_mgt[clock].reg); if (enable) { val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw); } else { clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK); val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK); } - writel(val, (_PRCMU_BASE + clk_mgt[clock].offset)); + writel(val, clk_mgt[clock].reg); /* Release the HW semaphore. */ writel(0, PRCM_SEM); @@ -1388,7 +1445,7 @@ static int request_sga_clock(u8 clock, bool enable) writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS); } - ret = request_reg_clock(clock, enable); + ret = request_clock(clock, enable); if (!ret && !enable) { val = readl(PRCM_CGATING_BYPASS); @@ -1398,6 +1455,78 @@ static int request_sga_clock(u8 clock, bool enable) return ret; } +static inline bool plldsi_locked(void) +{ + return (readl(PRCM_PLLDSI_LOCKP) & + (PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 | + PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3)) == + (PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 | + PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3); +} + +static int request_plldsi(bool enable) +{ + int r = 0; + u32 val; + + writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP | + PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), (enable ? + PRCM_MMIP_LS_CLAMP_CLR : PRCM_MMIP_LS_CLAMP_SET)); + + val = readl(PRCM_PLLDSI_ENABLE); + if (enable) + val |= PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; + else + val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; + writel(val, PRCM_PLLDSI_ENABLE); + + if (enable) { + unsigned int i; + bool locked = plldsi_locked(); + + for (i = 10; !locked && (i > 0); --i) { + udelay(100); + locked = plldsi_locked(); + } + if (locked) { + writel(PRCM_APE_RESETN_DSIPLL_RESETN, + PRCM_APE_RESETN_SET); + } else { + writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP | + PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), + PRCM_MMIP_LS_CLAMP_SET); + val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; + writel(val, PRCM_PLLDSI_ENABLE); + r = -EAGAIN; + } + } else { + writel(PRCM_APE_RESETN_DSIPLL_RESETN, PRCM_APE_RESETN_CLR); + } + return r; +} + +static int request_dsiclk(u8 n, bool enable) +{ + u32 val; + + val = readl(PRCM_DSI_PLLOUT_SEL); + val &= ~dsiclk[n].divsel_mask; + val |= ((enable ? dsiclk[n].divsel : PRCM_DSI_PLLOUT_SEL_OFF) << + dsiclk[n].divsel_shift); + writel(val, PRCM_DSI_PLLOUT_SEL); + return 0; +} + +static int request_dsiescclk(u8 n, bool enable) +{ + u32 val; + + val = readl(PRCM_DSITVCLK_DIV); + enable ? (val |= dsiescclk[n].en) : (val &= ~dsiescclk[n].en); + writel(val, PRCM_DSITVCLK_DIV); + return 0; +} + /** * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled. * @clock: The clock for which the request is made. @@ -1408,21 +1537,435 @@ static int request_sga_clock(u8 clock, bool enable) */ int db8500_prcmu_request_clock(u8 clock, bool enable) { - switch(clock) { - case PRCMU_SGACLK: + if (clock == PRCMU_SGACLK) return request_sga_clock(clock, enable); - case PRCMU_TIMCLK: + else if (clock < PRCMU_NUM_REG_CLOCKS) + return request_clock(clock, enable); + else if (clock == PRCMU_TIMCLK) return request_timclk(enable); - case PRCMU_SYSCLK: + else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) + return request_dsiclk((clock - PRCMU_DSI0CLK), enable); + else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) + return request_dsiescclk((clock - PRCMU_DSI0ESCCLK), enable); + else if (clock == PRCMU_PLLDSI) + return request_plldsi(enable); + else if (clock == PRCMU_SYSCLK) return request_sysclk(enable); - case PRCMU_PLLSOC1: + else if ((clock == PRCMU_PLLSOC0) || (clock == PRCMU_PLLSOC1)) return request_pll(clock, enable); + else + return -EINVAL; +} + +static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate, + int branch) +{ + u64 rate; + u32 val; + u32 d; + u32 div = 1; + + val = readl(reg); + + rate = src_rate; + rate *= ((val & PRCM_PLL_FREQ_D_MASK) >> PRCM_PLL_FREQ_D_SHIFT); + + d = ((val & PRCM_PLL_FREQ_N_MASK) >> PRCM_PLL_FREQ_N_SHIFT); + if (d > 1) + div *= d; + + d = ((val & PRCM_PLL_FREQ_R_MASK) >> PRCM_PLL_FREQ_R_SHIFT); + if (d > 1) + div *= d; + + if (val & PRCM_PLL_FREQ_SELDIV2) + div *= 2; + + if ((branch == PLL_FIX) || ((branch == PLL_DIV) && + (val & PRCM_PLL_FREQ_DIV2EN) && + ((reg == PRCM_PLLSOC0_FREQ) || + (reg == PRCM_PLLDDR_FREQ)))) + div *= 2; + + (void)do_div(rate, div); + + return (unsigned long)rate; +} + +#define ROOT_CLOCK_RATE 38400000 + +static unsigned long clock_rate(u8 clock) +{ + u32 val; + u32 pllsw; + unsigned long rate = ROOT_CLOCK_RATE; + + val = readl(clk_mgt[clock].reg); + + if (val & PRCM_CLK_MGT_CLK38) { + if (clk_mgt[clock].clk38div && (val & PRCM_CLK_MGT_CLK38DIV)) + rate /= 2; + return rate; + } + + val |= clk_mgt[clock].pllsw; + pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK); + + if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC0) + rate = pll_rate(PRCM_PLLSOC0_FREQ, rate, clk_mgt[clock].branch); + else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC1) + rate = pll_rate(PRCM_PLLSOC1_FREQ, rate, clk_mgt[clock].branch); + else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_DDR) + rate = pll_rate(PRCM_PLLDDR_FREQ, rate, clk_mgt[clock].branch); + else + return 0; + + if ((clock == PRCMU_SGACLK) && + (val & PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN)) { + u64 r = (rate * 10); + + (void)do_div(r, 25); + return (unsigned long)r; + } + val &= PRCM_CLK_MGT_CLKPLLDIV_MASK; + if (val) + return rate / val; + else + return 0; +} + +static unsigned long dsiclk_rate(u8 n) +{ + u32 divsel; + u32 div = 1; + + divsel = readl(PRCM_DSI_PLLOUT_SEL); + divsel = ((divsel & dsiclk[n].divsel_mask) >> dsiclk[n].divsel_shift); + + if (divsel == PRCM_DSI_PLLOUT_SEL_OFF) + divsel = dsiclk[n].divsel; + + switch (divsel) { + case PRCM_DSI_PLLOUT_SEL_PHI_4: + div *= 2; + case PRCM_DSI_PLLOUT_SEL_PHI_2: + div *= 2; + case PRCM_DSI_PLLOUT_SEL_PHI: + return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), + PLL_RAW) / div; default: - break; + return 0; } +} + +static unsigned long dsiescclk_rate(u8 n) +{ + u32 div; + + div = readl(PRCM_DSITVCLK_DIV); + div = ((div & dsiescclk[n].div_mask) >> (dsiescclk[n].div_shift)); + return clock_rate(PRCMU_TVCLK) / max((u32)1, div); +} + +unsigned long prcmu_clock_rate(u8 clock) +{ if (clock < PRCMU_NUM_REG_CLOCKS) - return request_reg_clock(clock, enable); - return -EINVAL; + return clock_rate(clock); + else if (clock == PRCMU_TIMCLK) + return ROOT_CLOCK_RATE / 16; + else if (clock == PRCMU_SYSCLK) + return ROOT_CLOCK_RATE; + else if (clock == PRCMU_PLLSOC0) + return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW); + else if (clock == PRCMU_PLLSOC1) + return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW); + else if (clock == PRCMU_PLLDDR) + return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW); + else if (clock == PRCMU_PLLDSI) + return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), + PLL_RAW); + else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) + return dsiclk_rate(clock - PRCMU_DSI0CLK); + else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) + return dsiescclk_rate(clock - PRCMU_DSI0ESCCLK); + else + return 0; +} + +static unsigned long clock_source_rate(u32 clk_mgt_val, int branch) +{ + if (clk_mgt_val & PRCM_CLK_MGT_CLK38) + return ROOT_CLOCK_RATE; + clk_mgt_val &= PRCM_CLK_MGT_CLKPLLSW_MASK; + if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC0) + return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, branch); + else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC1) + return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, branch); + else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_DDR) + return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, branch); + else + return 0; +} + +static u32 clock_divider(unsigned long src_rate, unsigned long rate) +{ + u32 div; + + div = (src_rate / rate); + if (div == 0) + return 1; + if (rate < (src_rate / div)) + div++; + return div; +} + +static long round_clock_rate(u8 clock, unsigned long rate) +{ + u32 val; + u32 div; + unsigned long src_rate; + long rounded_rate; + + val = readl(clk_mgt[clock].reg); + src_rate = clock_source_rate((val | clk_mgt[clock].pllsw), + clk_mgt[clock].branch); + div = clock_divider(src_rate, rate); + if (val & PRCM_CLK_MGT_CLK38) { + if (clk_mgt[clock].clk38div) { + if (div > 2) + div = 2; + } else { + div = 1; + } + } else if ((clock == PRCMU_SGACLK) && (div == 3)) { + u64 r = (src_rate * 10); + + (void)do_div(r, 25); + if (r <= rate) + return (unsigned long)r; + } + rounded_rate = (src_rate / min(div, (u32)31)); + + return rounded_rate; +} + +#define MIN_PLL_VCO_RATE 600000000ULL +#define MAX_PLL_VCO_RATE 1680640000ULL + +static long round_plldsi_rate(unsigned long rate) +{ + long rounded_rate = 0; + unsigned long src_rate; + unsigned long rem; + u32 r; + + src_rate = clock_rate(PRCMU_HDMICLK); + rem = rate; + + for (r = 7; (rem > 0) && (r > 0); r--) { + u64 d; + + d = (r * rate); + (void)do_div(d, src_rate); + if (d < 6) + d = 6; + else if (d > 255) + d = 255; + d *= src_rate; + if (((2 * d) < (r * MIN_PLL_VCO_RATE)) || + ((r * MAX_PLL_VCO_RATE) < (2 * d))) + continue; + (void)do_div(d, r); + if (rate < d) { + if (rounded_rate == 0) + rounded_rate = (long)d; + break; + } + if ((rate - d) < rem) { + rem = (rate - d); + rounded_rate = (long)d; + } + } + return rounded_rate; +} + +static long round_dsiclk_rate(unsigned long rate) +{ + u32 div; + unsigned long src_rate; + long rounded_rate; + + src_rate = pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), + PLL_RAW); + div = clock_divider(src_rate, rate); + rounded_rate = (src_rate / ((div > 2) ? 4 : div)); + + return rounded_rate; +} + +static long round_dsiescclk_rate(unsigned long rate) +{ + u32 div; + unsigned long src_rate; + long rounded_rate; + + src_rate = clock_rate(PRCMU_TVCLK); + div = clock_divider(src_rate, rate); + rounded_rate = (src_rate / min(div, (u32)255)); + + return rounded_rate; +} + +long prcmu_round_clock_rate(u8 clock, unsigned long rate) +{ + if (clock < PRCMU_NUM_REG_CLOCKS) + return round_clock_rate(clock, rate); + else if (clock == PRCMU_PLLDSI) + return round_plldsi_rate(rate); + else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) + return round_dsiclk_rate(rate); + else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) + return round_dsiescclk_rate(rate); + else + return (long)prcmu_clock_rate(clock); +} + +static void set_clock_rate(u8 clock, unsigned long rate) +{ + u32 val; + u32 div; + unsigned long src_rate; + unsigned long flags; + + spin_lock_irqsave(&clk_mgt_lock, flags); + + /* Grab the HW semaphore. */ + while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) + cpu_relax(); + + val = readl(clk_mgt[clock].reg); + src_rate = clock_source_rate((val | clk_mgt[clock].pllsw), + clk_mgt[clock].branch); + div = clock_divider(src_rate, rate); + if (val & PRCM_CLK_MGT_CLK38) { + if (clk_mgt[clock].clk38div) { + if (div > 1) + val |= PRCM_CLK_MGT_CLK38DIV; + else + val &= ~PRCM_CLK_MGT_CLK38DIV; + } + } else if (clock == PRCMU_SGACLK) { + val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK | + PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN); + if (div == 3) { + u64 r = (src_rate * 10); + + (void)do_div(r, 25); + if (r <= rate) { + val |= PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN; + div = 0; + } + } + val |= min(div, (u32)31); + } else { + val &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK; + val |= min(div, (u32)31); + } + writel(val, clk_mgt[clock].reg); + + /* Release the HW semaphore. */ + writel(0, PRCM_SEM); + + spin_unlock_irqrestore(&clk_mgt_lock, flags); +} + +static int set_plldsi_rate(unsigned long rate) +{ + unsigned long src_rate; + unsigned long rem; + u32 pll_freq = 0; + u32 r; + + src_rate = clock_rate(PRCMU_HDMICLK); + rem = rate; + + for (r = 7; (rem > 0) && (r > 0); r--) { + u64 d; + u64 hwrate; + + d = (r * rate); + (void)do_div(d, src_rate); + if (d < 6) + d = 6; + else if (d > 255) + d = 255; + hwrate = (d * src_rate); + if (((2 * hwrate) < (r * MIN_PLL_VCO_RATE)) || + ((r * MAX_PLL_VCO_RATE) < (2 * hwrate))) + continue; + (void)do_div(hwrate, r); + if (rate < hwrate) { + if (pll_freq == 0) + pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) | + (r << PRCM_PLL_FREQ_R_SHIFT)); + break; + } + if ((rate - hwrate) < rem) { + rem = (rate - hwrate); + pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) | + (r << PRCM_PLL_FREQ_R_SHIFT)); + } + } + if (pll_freq == 0) + return -EINVAL; + + pll_freq |= (1 << PRCM_PLL_FREQ_N_SHIFT); + writel(pll_freq, PRCM_PLLDSI_FREQ); + + return 0; +} + +static void set_dsiclk_rate(u8 n, unsigned long rate) +{ + u32 val; + u32 div; + + div = clock_divider(pll_rate(PRCM_PLLDSI_FREQ, + clock_rate(PRCMU_HDMICLK), PLL_RAW), rate); + + dsiclk[n].divsel = (div == 1) ? PRCM_DSI_PLLOUT_SEL_PHI : + (div == 2) ? PRCM_DSI_PLLOUT_SEL_PHI_2 : + /* else */ PRCM_DSI_PLLOUT_SEL_PHI_4; + + val = readl(PRCM_DSI_PLLOUT_SEL); + val &= ~dsiclk[n].divsel_mask; + val |= (dsiclk[n].divsel << dsiclk[n].divsel_shift); + writel(val, PRCM_DSI_PLLOUT_SEL); +} + +static void set_dsiescclk_rate(u8 n, unsigned long rate) +{ + u32 val; + u32 div; + + div = clock_divider(clock_rate(PRCMU_TVCLK), rate); + val = readl(PRCM_DSITVCLK_DIV); + val &= ~dsiescclk[n].div_mask; + val |= (min(div, (u32)255) << dsiescclk[n].div_shift); + writel(val, PRCM_DSITVCLK_DIV); +} + +int prcmu_set_clock_rate(u8 clock, unsigned long rate) +{ + if (clock < PRCMU_NUM_REG_CLOCKS) + set_clock_rate(clock, rate); + else if (clock == PRCMU_PLLDSI) + return set_plldsi_rate(rate); + else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) + set_dsiclk_rate((clock - PRCMU_DSI0CLK), rate); + else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) + set_dsiescclk_rate((clock - PRCMU_DSI0ESCCLK), rate); + return 0; } int db8500_prcmu_config_esram0_deep_sleep(u8 state) @@ -1594,41 +2137,6 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 timeout) } /** - * prcmu_set_clock_divider() - Configure the clock divider. - * @clock: The clock for which the request is made. - * @divider: The clock divider. (< 32) - * - * This function should only be used by the clock implementation. - * Do not use it from any other place! - */ -int prcmu_set_clock_divider(u8 clock, u8 divider) -{ - u32 val; - unsigned long flags; - - if ((clock >= PRCMU_NUM_REG_CLOCKS) || (divider < 1) || (31 < divider)) - return -EINVAL; - - spin_lock_irqsave(&clk_mgt_lock, flags); - - /* Grab the HW semaphore. */ - while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) - cpu_relax(); - - val = readl(_PRCMU_BASE + clk_mgt[clock].offset); - val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK); - val |= (u32)divider; - writel(val, (_PRCMU_BASE + clk_mgt[clock].offset)); - - /* Release the HW semaphore. */ - writel(0, PRCM_SEM); - - spin_unlock_irqrestore(&clk_mgt_lock, flags); - - return 0; -} - -/** * prcmu_abb_read() - Read register value(s) from the ABB. * @slave: The I2C slave address. * @reg: The (start) register address. diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index ec22e9f..b9ab4ce 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -17,41 +17,41 @@ #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_CLK_MGT(_offset) (void __iomem *)(IO_ADDRESS(U8500_PRCMU_BASE) \ + + _offset) +#define PRCM_ACLK_MGT PRCM_CLK_MGT(0x004) +#define PRCM_SVACLK_MGT PRCM_CLK_MGT(0x008) +#define PRCM_SIACLK_MGT PRCM_CLK_MGT(0x00C) +#define PRCM_SGACLK_MGT PRCM_CLK_MGT(0x014) +#define PRCM_UARTCLK_MGT PRCM_CLK_MGT(0x018) +#define PRCM_MSP02CLK_MGT PRCM_CLK_MGT(0x01C) +#define PRCM_I2CCLK_MGT PRCM_CLK_MGT(0x020) +#define PRCM_SDMMCCLK_MGT PRCM_CLK_MGT(0x024) +#define PRCM_SLIMCLK_MGT PRCM_CLK_MGT(0x028) +#define PRCM_PER1CLK_MGT PRCM_CLK_MGT(0x02C) +#define PRCM_PER2CLK_MGT PRCM_CLK_MGT(0x030) +#define PRCM_PER3CLK_MGT PRCM_CLK_MGT(0x034) +#define PRCM_PER5CLK_MGT PRCM_CLK_MGT(0x038) +#define PRCM_PER6CLK_MGT PRCM_CLK_MGT(0x03C) +#define PRCM_PER7CLK_MGT PRCM_CLK_MGT(0x040) +#define PRCM_LCDCLK_MGT PRCM_CLK_MGT(0x044) +#define PRCM_BMLCLK_MGT PRCM_CLK_MGT(0x04C) +#define PRCM_HSITXCLK_MGT PRCM_CLK_MGT(0x050) +#define PRCM_HSIRXCLK_MGT PRCM_CLK_MGT(0x054) +#define PRCM_HDMICLK_MGT PRCM_CLK_MGT(0x058) +#define PRCM_APEATCLK_MGT PRCM_CLK_MGT(0x05C) +#define PRCM_APETRACECLK_MGT PRCM_CLK_MGT(0x060) +#define PRCM_MCDECLK_MGT PRCM_CLK_MGT(0x064) +#define PRCM_IPI2CCLK_MGT PRCM_CLK_MGT(0x068) +#define PRCM_DSIALTCLK_MGT PRCM_CLK_MGT(0x06C) +#define PRCM_DMACLK_MGT PRCM_CLK_MGT(0x074) +#define PRCM_B2R2CLK_MGT PRCM_CLK_MGT(0x078) +#define PRCM_TVCLK_MGT PRCM_CLK_MGT(0x07C) +#define PRCM_UNIPROCLK_MGT PRCM_CLK_MGT(0x278) +#define PRCM_SSPCLK_MGT PRCM_CLK_MGT(0x280) +#define PRCM_RNGCLK_MGT PRCM_CLK_MGT(0x284) +#define PRCM_UICCCLK_MGT PRCM_CLK_MGT(0x27C) +#define PRCM_MSP1CLK_MGT PRCM_CLK_MGT(0x288) #define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) #define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f @@ -131,20 +131,58 @@ #define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) #define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) +#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP BIT(11) +#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI BIT(22) + /* PRCMU clock/PLL/reset registers */ +#define PRCM_PLLSOC0_FREQ (_PRCMU_BASE + 0x080) +#define PRCM_PLLSOC1_FREQ (_PRCMU_BASE + 0x084) +#define PRCM_PLLDDR_FREQ (_PRCMU_BASE + 0x08C) +#define PRCM_PLL_FREQ_D_SHIFT 0 +#define PRCM_PLL_FREQ_D_MASK BITS(0, 7) +#define PRCM_PLL_FREQ_N_SHIFT 8 +#define PRCM_PLL_FREQ_N_MASK BITS(8, 13) +#define PRCM_PLL_FREQ_R_SHIFT 16 +#define PRCM_PLL_FREQ_R_MASK BITS(16, 18) +#define PRCM_PLL_FREQ_SELDIV2 BIT(24) +#define PRCM_PLL_FREQ_DIV2EN BIT(25) + #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_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE BIT(0) + +#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 BIT(0) +#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3 BIT(1) + +#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT 0 +#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK BITS(0, 2) +#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT 8 +#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK BITS(8, 10) + +#define PRCM_DSI_PLLOUT_SEL_OFF 0 +#define PRCM_DSI_PLLOUT_SEL_PHI 1 +#define PRCM_DSI_PLLOUT_SEL_PHI_2 2 +#define PRCM_DSI_PLLOUT_SEL_PHI_4 3 + +#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT 0 +#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK BITS(0, 7) +#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT 8 +#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK BITS(8, 15) +#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT 16 +#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK BITS(16, 23) +#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN BIT(24) +#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN BIT(25) +#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN BIT(26) + +#define PRCM_APE_RESETN_DSIPLL_RESETN BIT(14) + #define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC) #define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0) #define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13) @@ -183,9 +221,15 @@ #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) +#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4) +#define PRCM_CLK_MGT_CLKPLLSW_SOC0 BIT(5) +#define PRCM_CLK_MGT_CLKPLLSW_SOC1 BIT(6) +#define PRCM_CLK_MGT_CLKPLLSW_DDR BIT(7) +#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7) +#define PRCM_CLK_MGT_CLKEN BIT(8) +#define PRCM_CLK_MGT_CLK38 BIT(9) +#define PRCM_CLK_MGT_CLK38DIV BIT(11) +#define PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN BIT(12) /* GPIOCR register */ #define PRCM_GPIOCR_SPI2_SELECT BIT(23) -- cgit v1.1 From 992b133a5d85ced4ff0fbdab22e9196cf571e0c9 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Fri, 13 Jan 2012 16:20:36 +0100 Subject: mfd: Spawned db8500 regulators update As drivers have progressed and got some more review we have consequently moved things around: - Some device drivers have changed name and some were wrong from the beginning. - We removed the dependency from some domains to the VAPE domain - SIA MMDSP, SIA PIPE, SVA MMDSP and SVA PIPE power domains are handled outside the framework while ESRAM12 and ESRAM34 are actually handled inside the PRCMU. Reviewed-by: Rickard Andersson Reviewed-by: Jonas Aberg Signed-off-by: Bengt Jonsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 9459327..1385639 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2687,19 +2687,17 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = { REGULATOR_SUPPLY("vcore", "uart1"), REGULATOR_SUPPLY("vcore", "uart2"), REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"), + REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"), }; static struct regulator_consumer_supply db8500_vsmps2_consumers[] = { - /* CG2900 and CW1200 power to off-chip peripherals */ - REGULATOR_SUPPLY("gbf_1v8", "cg2900-uart.0"), - REGULATOR_SUPPLY("wlan_1v8", "cw1200.0"), REGULATOR_SUPPLY("musb_1v8", "ab8500-usb.0"), /* AV8100 regulator */ REGULATOR_SUPPLY("hdmi_1v8", "0-0070"), }; static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = { - REGULATOR_SUPPLY("vsupply", "b2r2.0"), + REGULATOR_SUPPLY("vsupply", "b2r2_bus"), REGULATOR_SUPPLY("vsupply", "mcde"), }; @@ -2736,6 +2734,7 @@ static struct regulator_consumer_supply db8500_esram12_consumers[] = { static struct regulator_consumer_supply db8500_esram34_consumers[] = { REGULATOR_SUPPLY("v-esram34", "mcde"), REGULATOR_SUPPLY("esram34", "cm_control"), + REGULATOR_SUPPLY("lcla_esram", "dma40.0"), }; static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { @@ -2792,7 +2791,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }, }, [DB8500_REGULATOR_SWITCH_SVAMMDSP] = { - .supply_regulator = "db8500-vape", + /* dependency to u8500-vape is handled outside regulator framework */ .constraints = { .name = "db8500-sva-mmdsp", .valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2808,7 +2807,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }, }, [DB8500_REGULATOR_SWITCH_SVAPIPE] = { - .supply_regulator = "db8500-vape", + /* dependency to u8500-vape is handled outside regulator framework */ .constraints = { .name = "db8500-sva-pipe", .valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2817,7 +2816,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers), }, [DB8500_REGULATOR_SWITCH_SIAMMDSP] = { - .supply_regulator = "db8500-vape", + /* dependency to u8500-vape is handled outside regulator framework */ .constraints = { .name = "db8500-sia-mmdsp", .valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2832,7 +2831,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }, }, [DB8500_REGULATOR_SWITCH_SIAPIPE] = { - .supply_regulator = "db8500-vape", + /* dependency to u8500-vape is handled outside regulator framework */ .constraints = { .name = "db8500-sia-pipe", .valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2860,7 +2859,10 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { .num_consumer_supplies = ARRAY_SIZE(db8500_b2r2_mcde_consumers), }, [DB8500_REGULATOR_SWITCH_ESRAM12] = { - .supply_regulator = "db8500-vape", + /* + * esram12 is set in retention and supplied by Vsafe when Vape is off, + * no need to hold Vape + */ .constraints = { .name = "db8500-esram12", .valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2875,7 +2877,10 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }, }, [DB8500_REGULATOR_SWITCH_ESRAM34] = { - .supply_regulator = "db8500-vape", + /* + * esram34 is set in retention and supplied by Vsafe when Vape is off, + * no need to hold Vape + */ .constraints = { .name = "db8500-esram34", .valid_ops_mask = REGULATOR_CHANGE_STATUS, -- cgit v1.1 From 4d64d2e34bc415b05eb77a2732a3164313cf6de3 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 13 Jan 2012 16:20:43 +0100 Subject: mfd: db8500 OPP and sleep handling update This updates the operating point handling code by: - Supporting the DDR OPP retention state. - Supporting another low operating point named APE_50_PARTLY_25_OPP - Adding an interface to figure out if the sleep state change was properly achieved. Signed-off-by: Shreshtha Kumar Sahu Signed-off-by: Rabin Vincent Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 73 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 1385639..8056968 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -343,11 +343,13 @@ static struct { * mb1_transfer - state needed for mailbox 1 communication. * @lock: The transaction lock. * @work: The transaction completion structure. + * @ape_opp: The current APE OPP. * @ack: Reply ("acknowledge") data. */ static struct { struct mutex lock; struct completion work; + u8 ape_opp; struct { u8 header; u8 arm_opp; @@ -816,6 +818,11 @@ int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) return 0; } +u8 db8500_prcmu_get_power_state_result(void) +{ + return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS); +} + /* This function should only be called while mb0_transfer.lock is held. */ static void config_wakeups(void) { @@ -965,6 +972,52 @@ int db8500_prcmu_set_ddr_opp(u8 opp) return 0; } +/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */ +static void request_even_slower_clocks(bool enable) +{ + void __iomem *clock_reg[] = { + PRCM_ACLK_MGT, + PRCM_DMACLK_MGT + }; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&clk_mgt_lock, flags); + + /* Grab the HW semaphore. */ + while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) + cpu_relax(); + + for (i = 0; i < ARRAY_SIZE(clock_reg); i++) { + u32 val; + u32 div; + + val = readl(clock_reg[i]); + div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK); + if (enable) { + if ((div <= 1) || (div > 15)) { + pr_err("prcmu: Bad clock divider %d in %s\n", + div, __func__); + goto unlock_and_return; + } + div <<= 1; + } else { + if (div <= 2) + goto unlock_and_return; + div >>= 1; + } + val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) | + (div & PRCM_CLK_MGT_CLKPLLDIV_MASK)); + writel(val, clock_reg[i]); + } + +unlock_and_return: + /* Release the HW semaphore. */ + writel(0, PRCM_SEM); + + spin_unlock_irqrestore(&clk_mgt_lock, flags); +} + /** * db8500_set_ape_opp - set the appropriate APE OPP * @opp: The new APE operating point to which transition is to be made @@ -976,14 +1029,24 @@ int db8500_prcmu_set_ape_opp(u8 opp) { int r = 0; + if (opp == mb1_transfer.ape_opp) + return 0; + mutex_lock(&mb1_transfer.lock); + if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP) + request_even_slower_clocks(false); + + if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP)) + goto skip_message; + 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)); + writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp), + (tcdm_base + PRCM_REQ_MB1_APE_OPP)); writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); wait_for_completion(&mb1_transfer.work); @@ -992,6 +1055,13 @@ int db8500_prcmu_set_ape_opp(u8 opp) (mb1_transfer.ack.ape_opp != opp)) r = -EIO; +skip_message: + if ((!r && (opp == APE_50_PARTLY_25_OPP)) || + (r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP))) + request_even_slower_clocks(true); + if (!r) + mb1_transfer.ape_opp = opp; + mutex_unlock(&mb1_transfer.lock); return r; @@ -2631,6 +2701,7 @@ void __init db8500_prcmu_early_init(void) init_completion(&mb0_transfer.ac_wake_work); mutex_init(&mb1_transfer.lock); init_completion(&mb1_transfer.work); + mb1_transfer.ape_opp = APE_NO_CHANGE; mutex_init(&mb2_transfer.lock); init_completion(&mb2_transfer.work); spin_lock_init(&mb2_transfer.auto_pm_lock); -- cgit v1.1 From 6f53d10dda1323c17fb09063c4df2c22754bf8aa Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Fri, 13 Jan 2012 16:20:51 +0100 Subject: mfd: Remove check for db8500 firmware bug In prcmu firmware version 3.4.4 the issue with longer intervalls than 131 s was fixed, we don't expect the issue to creep back up. Signed-off-by: Jonas Aaberg Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 8056968..0ee8015 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2184,16 +2184,8 @@ int db8500_prcmu_kick_a9wdog(u8 id) /* * timeout is 28 bit, in ms. */ -#define MAX_WATCHDOG_TIMEOUT 131000 int db8500_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) | /* -- cgit v1.1 From b4a6dbd5b7bad00ee4004443287468abddb96538 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 13 Jan 2012 16:21:00 +0100 Subject: mfd: Add initial db8500 prcmu register access api This patch adds an initial PRCMU register access API, which for now should only be used for a very limited set of registers. The idea about this API is that we split the PRCMU driver in one part that deals with interaction with the PRCMU firmware and one part that simply provide write accessors in the PRCMU register range. The latter are just a collection of registers exposed in the PRCMU register range for various purposes and not related to the PRCMU firmware. Currently we support some limited GPIO, SPI and UART settings through this API. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 0ee8015..128b5f4 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -417,8 +417,8 @@ static struct { static atomic_t ac_wake_req_state = ATOMIC_INIT(0); /* Spinlocks */ +static DEFINE_SPINLOCK(prcmu_lock); static DEFINE_SPINLOCK(clkout_lock); -static DEFINE_SPINLOCK(gpiocr_lock); /* Global var to runtime determine TCDM base for v2 or v1 */ static __iomem void *tcdm_base; @@ -639,32 +639,30 @@ int db8500_prcmu_set_display_clocks(void) return 0; } -/** - * prcmu_enable_spi2 - Enables pin muxing for SPI2 on OtherAlternateC1. - */ -void prcmu_enable_spi2(void) +u32 db8500_prcmu_read(unsigned int reg) +{ + return readl(_PRCMU_BASE + reg); +} + +void db8500_prcmu_write(unsigned int reg, u32 value) { - u32 reg; unsigned long flags; - spin_lock_irqsave(&gpiocr_lock, flags); - reg = readl(PRCM_GPIOCR); - writel(reg | PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR); - spin_unlock_irqrestore(&gpiocr_lock, flags); + spin_lock_irqsave(&prcmu_lock, flags); + writel(value, (_PRCMU_BASE + reg)); + spin_unlock_irqrestore(&prcmu_lock, flags); } -/** - * prcmu_disable_spi2 - Disables pin muxing for SPI2 on OtherAlternateC1. - */ -void prcmu_disable_spi2(void) +void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value) { - u32 reg; + u32 val; unsigned long flags; - spin_lock_irqsave(&gpiocr_lock, flags); - reg = readl(PRCM_GPIOCR); - writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR); - spin_unlock_irqrestore(&gpiocr_lock, flags); + spin_lock_irqsave(&prcmu_lock, flags); + val = readl(_PRCMU_BASE + reg); + val = ((val & ~mask) | (value & mask)); + writel(val, (_PRCMU_BASE + reg)); + spin_unlock_irqrestore(&prcmu_lock, flags); } struct prcmu_fw_version *prcmu_get_fw_version(void) -- cgit v1.1 From d2d5cb47e3e7e0f68d33d18ca15e4ac65ca5645b Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Mon, 20 Feb 2012 16:13:43 +0100 Subject: mfd: wm8350 variable dereferenced before check Remove "wm8350->irq_base = pdata->irq_base" to avoid null pointer exception and wm8350->irq_base got from irq_alloc_descs(). Signed-off-by: Jonghwan Choi Acked-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8350-irq.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c index 8a1fafd..9fd01bf 100644 --- a/drivers/mfd/wm8350-irq.c +++ b/drivers/mfd/wm8350-irq.c @@ -496,7 +496,6 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq, mutex_init(&wm8350->irq_lock); wm8350->chip_irq = irq; - wm8350->irq_base = pdata->irq_base; if (pdata && pdata->irq_base > 0) irq_base = pdata->irq_base; -- cgit v1.1 From c3ebb30aaeb2a30d69b9db564d93bf3486139c12 Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Mon, 16 Jan 2012 09:08:42 +0900 Subject: mfd: Fix possible s5m null pointer dereference This patch checks for pdata to using it. Signed-off-by: Jonghwan Choi Signed-off-by: Samuel Ortiz --- drivers/mfd/s5m-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index e075c11..caadabe 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -105,7 +105,7 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); i2c_set_clientdata(s5m87xx->rtc, s5m87xx); - if (pdata->cfg_pmic_irq) + if (pdata && pdata->cfg_pmic_irq) pdata->cfg_pmic_irq(); s5m_irq_init(s5m87xx); -- cgit v1.1 From c7a1fcf3b1660dbccdc2faf9e506e0264b17c4ce Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Mon, 20 Feb 2012 16:22:56 +0100 Subject: mfd: Fix s5m error handling for invalid device type If device type is not supported in driver, have to retun error. Signed-off-by: Jonghwan Choi Signed-off-by: Samuel Ortiz --- drivers/mfd/s5m-irq.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c index de76dfb..0236676 100644 --- a/drivers/mfd/s5m-irq.c +++ b/drivers/mfd/s5m-irq.c @@ -342,7 +342,10 @@ int s5m_irq_resume(struct s5m87xx_dev *s5m87xx) s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx); break; default: - break; + dev_err(s5m87xx->dev, + "Unknown device type %d\n", + s5m87xx->device_type); + return -EINVAL; } } @@ -444,7 +447,9 @@ int s5m_irq_init(struct s5m87xx_dev *s5m87xx) } break; default: - break; + dev_err(s5m87xx->dev, + "Unknown device type %d\n", s5m87xx->device_type); + return -EINVAL; } if (!s5m87xx->ono) @@ -467,12 +472,15 @@ int s5m_irq_init(struct s5m87xx_dev *s5m87xx) IRQF_ONESHOT, "s5m87xx-ono", s5m87xx); break; default: + ret = -EINVAL; break; } - if (ret) + if (ret) { dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n", s5m87xx->ono, ret); + return ret; + } return 0; } -- cgit v1.1 From 1773140feccf69528678348bb2d53b2c299ab4ee Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 18 Jan 2012 20:19:16 +0530 Subject: mfd: Initialize tps65910 irq platform data properly irq_base of the tps65910 irq platform data should be initialized with the board provided irq_base data. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 01cf501..4392f6b 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -168,7 +168,7 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, goto err; init_data->irq = pmic_plat_data->irq; - init_data->irq_base = pmic_plat_data->irq; + init_data->irq_base = pmic_plat_data->irq_base; tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base); -- cgit v1.1 From 621210e25d5de02dae111ad90548577dd64c7663 Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Fri, 20 Jan 2012 16:09:11 +0900 Subject: mfd: Convert s5m core driver to use devm_kzalloc() Convert s5m core driver to use devm_kzalloc(). Signed-off-by: Sangbeom Kim Signed-off-by: Samuel Ortiz --- drivers/mfd/s5m-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index caadabe..7585398 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -77,7 +77,8 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, int ret = 0; int error; - s5m87xx = kzalloc(sizeof(struct s5m87xx_dev), GFP_KERNEL); + s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev), + GFP_KERNEL); if (s5m87xx == NULL) return -ENOMEM; @@ -126,7 +127,6 @@ err: s5m_irq_exit(s5m87xx); i2c_unregister_device(s5m87xx->rtc); regmap_exit(s5m87xx->regmap); - kfree(s5m87xx); return ret; } @@ -138,7 +138,6 @@ static int s5m87xx_i2c_remove(struct i2c_client *i2c) s5m_irq_exit(s5m87xx); i2c_unregister_device(s5m87xx->rtc); regmap_exit(s5m87xx->regmap); - kfree(s5m87xx); return 0; } -- cgit v1.1 From 88a1cfd6f3b8ffc0c7c86471abce43a6e4f3e1c6 Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Fri, 20 Jan 2012 16:09:12 +0900 Subject: mfd: Add support for multiple s5m devices There are many samsung mfd devices. The s5m core driver try to support various samsung s5m series. It will cover S5M8767A and S5M8763 and S5M8751. This patch is enable to support various s5m series. Signed-off-by: Sangbeom Kim Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/s5m-core.c | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index 7585398..8b8cf85 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -26,7 +26,27 @@ #include #include -static struct mfd_cell s5m87xx_devs[] = { +static struct mfd_cell s5m8751_devs[] = { + { + .name = "s5m8751-pmic", + }, { + .name = "s5m-charger", + }, { + .name = "s5m8751-codec", + }, +}; + +static struct mfd_cell s5m8763_devs[] = { + { + .name = "s5m8763-pmic", + }, { + .name = "s5m-rtc", + }, { + .name = "s5m-charger", + }, +}; + +static struct mfd_cell s5m8767_devs[] = { { .name = "s5m8767-pmic", }, { @@ -113,9 +133,23 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, pm_runtime_set_active(s5m87xx->dev); - ret = mfd_add_devices(s5m87xx->dev, -1, - s5m87xx_devs, ARRAY_SIZE(s5m87xx_devs), - NULL, 0); + switch (s5m87xx->device_type) { + case S5M8751X: + ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs, + ARRAY_SIZE(s5m8751_devs), NULL, 0); + break; + case S5M8763X: + ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs, + ARRAY_SIZE(s5m8763_devs), NULL, 0); + break; + case S5M8767X: + ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs, + ARRAY_SIZE(s5m8767_devs), NULL, 0); + break; + default: + /* If this happens the probe function is problem */ + BUG(); + } if (ret < 0) goto err; -- cgit v1.1 From 0dc299a3c468b6a237146137c95eb53c7b5078e3 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 23 Jan 2012 13:16:15 +0530 Subject: mfd: Add itps65910 wakeup support Implementing irq_set_wake() so that device should able to wakeup the system through different interrupt provided by this device like gpios, onkey, rtc etc. Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910-irq.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c index 95c0d79..c9ed5c0 100644 --- a/drivers/mfd/tps65910-irq.c +++ b/drivers/mfd/tps65910-irq.c @@ -145,12 +145,23 @@ static void tps65910_irq_disable(struct irq_data *data) tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq)); } +#ifdef CONFIG_PM_SLEEP +static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable) +{ + struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); + return irq_set_irq_wake(tps65910->chip_irq, enable); +} +#else +#define tps65910_irq_set_wake NULL +#endif + static struct irq_chip tps65910_irq_chip = { .name = "tps65910", .irq_bus_lock = tps65910_irq_lock, .irq_bus_sync_unlock = tps65910_irq_sync_unlock, .irq_disable = tps65910_irq_disable, .irq_enable = tps65910_irq_enable, + .irq_set_wake = tps65910_irq_set_wake, }; int tps65910_irq_init(struct tps65910 *tps65910, int irq, -- cgit v1.1 From e31f9b826486c48f20e4f1066aa3e23e111c3a4e Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Thu, 26 Jan 2012 22:17:03 +0100 Subject: mfd: Add support for no-interrupt stmpe config Adds support for boards which have an STMPE device without the interrupt pin connected. Acked-by: Viresh Kumar Signed-off-by: Chris Blair Tested-by: Michel Jaouen Reviewed-by: Srinidhi Kasagar Signed-off-by: Linus Walleij Acked-by: Viresh Kumar Signed-off-by: Samuel Ortiz --- drivers/mfd/stmpe.c | 134 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 38 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index e07947e..2dd8d49 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -298,6 +298,11 @@ static struct mfd_cell stmpe_gpio_cell = { .num_resources = ARRAY_SIZE(stmpe_gpio_resources), }; +static struct mfd_cell stmpe_gpio_cell_noirq = { + .name = "stmpe-gpio", + /* gpio cell resources consist of an irq only so no resources here */ +}; + /* * Keypad (1601, 2401, 2403) */ @@ -346,6 +351,13 @@ static struct stmpe_variant_block stmpe801_blocks[] = { }, }; +static struct stmpe_variant_block stmpe801_blocks_noirq[] = { + { + .cell = &stmpe_gpio_cell_noirq, + .block = STMPE_BLOCK_GPIO, + }, +}; + static int stmpe801_enable(struct stmpe *stmpe, unsigned int blocks, bool enable) { @@ -367,6 +379,17 @@ static struct stmpe_variant_info stmpe801 = { .enable = stmpe801_enable, }; +static struct stmpe_variant_info stmpe801_noirq = { + .name = "stmpe801", + .id_val = STMPE801_ID, + .id_mask = 0xffff, + .num_gpios = 8, + .regs = stmpe801_regs, + .blocks = stmpe801_blocks_noirq, + .num_blocks = ARRAY_SIZE(stmpe801_blocks_noirq), + .enable = stmpe801_enable, +}; + /* * Touchscreen (STMPE811 or STMPE610) */ @@ -712,7 +735,7 @@ static struct stmpe_variant_info stmpe2403 = { .enable_autosleep = stmpe1601_autosleep, /* same as stmpe1601 */ }; -static struct stmpe_variant_info *stmpe_variant_info[] = { +static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = { [STMPE610] = &stmpe610, [STMPE801] = &stmpe801, [STMPE811] = &stmpe811, @@ -721,6 +744,16 @@ static struct stmpe_variant_info *stmpe_variant_info[] = { [STMPE2403] = &stmpe2403, }; +/* + * These devices can be connected in a 'no-irq' configuration - the irq pin + * is not used and the device cannot interrupt the CPU. Here we only list + * devices which support this configuration - the driver will fail probing + * for any devices not listed here which are configured in this way. + */ +static struct stmpe_variant_info *stmpe_noirq_variant_info[STMPE_NBR_PARTS] = { + [STMPE801] = &stmpe801_noirq, +}; + static irqreturn_t stmpe_irq(int irq, void *data) { struct stmpe *stmpe = data; @@ -864,7 +897,7 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe) unsigned int irq_trigger = stmpe->pdata->irq_trigger; int autosleep_timeout = stmpe->pdata->autosleep_timeout; struct stmpe_variant_info *variant = stmpe->variant; - u8 icr; + u8 icr = 0; unsigned int id; u8 data[2]; int ret; @@ -887,31 +920,33 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe) if (ret) return ret; - if (id == STMPE801_ID) - icr = STMPE801_REG_SYS_CTRL_INT_EN; - else - icr = STMPE_ICR_LSB_GIM; - - /* STMPE801 doesn't support Edge interrupts */ - if (id != STMPE801_ID) { - if (irq_trigger == IRQF_TRIGGER_FALLING || - irq_trigger == IRQF_TRIGGER_RISING) - icr |= STMPE_ICR_LSB_EDGE; - } - - if (irq_trigger == IRQF_TRIGGER_RISING || - irq_trigger == IRQF_TRIGGER_HIGH) { + if (stmpe->irq >= 0) { if (id == STMPE801_ID) - icr |= STMPE801_REG_SYS_CTRL_INT_HI; + icr = STMPE801_REG_SYS_CTRL_INT_EN; else - icr |= STMPE_ICR_LSB_HIGH; - } + icr = STMPE_ICR_LSB_GIM; - if (stmpe->pdata->irq_invert_polarity) { - if (id == STMPE801_ID) - icr ^= STMPE801_REG_SYS_CTRL_INT_HI; - else - icr ^= STMPE_ICR_LSB_HIGH; + /* STMPE801 doesn't support Edge interrupts */ + if (id != STMPE801_ID) { + if (irq_trigger == IRQF_TRIGGER_FALLING || + irq_trigger == IRQF_TRIGGER_RISING) + icr |= STMPE_ICR_LSB_EDGE; + } + + if (irq_trigger == IRQF_TRIGGER_RISING || + irq_trigger == IRQF_TRIGGER_HIGH) { + if (id == STMPE801_ID) + icr |= STMPE801_REG_SYS_CTRL_INT_HI; + else + icr |= STMPE_ICR_LSB_HIGH; + } + + if (stmpe->pdata->irq_invert_polarity) { + if (id == STMPE801_ID) + icr ^= STMPE801_REG_SYS_CTRL_INT_HI; + else + icr ^= STMPE_ICR_LSB_HIGH; + } } if (stmpe->pdata->autosleep) { @@ -1001,19 +1036,38 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) stmpe->irq = ci->irq; } + if (stmpe->irq < 0) { + /* use alternate variant info for no-irq mode, if supported */ + dev_info(stmpe->dev, + "%s configured in no-irq mode by platform data\n", + stmpe->variant->name); + if (!stmpe_noirq_variant_info[stmpe->partnum]) { + dev_err(stmpe->dev, + "%s does not support no-irq mode!\n", + stmpe->variant->name); + ret = -ENODEV; + goto free_gpio; + } + stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum]; + } + ret = stmpe_chip_init(stmpe); if (ret) goto free_gpio; - ret = stmpe_irq_init(stmpe); - if (ret) - goto free_gpio; + if (stmpe->irq >= 0) { + ret = stmpe_irq_init(stmpe); + if (ret) + goto free_gpio; - ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq, - pdata->irq_trigger | IRQF_ONESHOT, "stmpe", stmpe); - if (ret) { - dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret); - goto out_removeirq; + ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq, + pdata->irq_trigger | IRQF_ONESHOT, + "stmpe", stmpe); + if (ret) { + dev_err(stmpe->dev, "failed to request IRQ: %d\n", + ret); + goto out_removeirq; + } } ret = stmpe_devices_init(stmpe); @@ -1026,9 +1080,11 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) out_removedevs: mfd_remove_devices(stmpe->dev); - free_irq(stmpe->irq, stmpe); + if (stmpe->irq >= 0) + free_irq(stmpe->irq, stmpe); out_removeirq: - stmpe_irq_remove(stmpe); + if (stmpe->irq >= 0) + stmpe_irq_remove(stmpe); free_gpio: if (pdata->irq_over_gpio) gpio_free(pdata->irq_gpio); @@ -1041,8 +1097,10 @@ int stmpe_remove(struct stmpe *stmpe) { mfd_remove_devices(stmpe->dev); - free_irq(stmpe->irq, stmpe); - stmpe_irq_remove(stmpe); + if (stmpe->irq >= 0) { + free_irq(stmpe->irq, stmpe); + stmpe_irq_remove(stmpe); + } if (stmpe->pdata->irq_over_gpio) gpio_free(stmpe->pdata->irq_gpio); @@ -1057,7 +1115,7 @@ static int stmpe_suspend(struct device *dev) { struct stmpe *stmpe = dev_get_drvdata(dev); - if (device_may_wakeup(dev)) + if (stmpe->irq >= 0 && device_may_wakeup(dev)) enable_irq_wake(stmpe->irq); return 0; @@ -1067,7 +1125,7 @@ static int stmpe_resume(struct device *dev) { struct stmpe *stmpe = dev_get_drvdata(dev); - if (device_may_wakeup(dev)) + if (stmpe->irq >= 0 && device_may_wakeup(dev)) disable_irq_wake(stmpe->irq); return 0; -- cgit v1.1 From edcf3196e9e646acc88d53e3bf355a6975daf109 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 30 Jan 2012 22:08:44 +0100 Subject: mfd: Remove unneeded version.h include from ab5500 As 'make versioncheck' points out, there's no need for drivers/mfd/ab5500-core.c to #include , so this patch removes the unneeded include. Signed-off-by: Jesper Juhl Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab5500-core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c index bd56a76..54d0fe4 100644 --- a/drivers/mfd/ab5500-core.c +++ b/drivers/mfd/ab5500-core.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include "ab5500-core.h" -- cgit v1.1 From 450b1151065ae967583fe69c82c50140560a5681 Mon Sep 17 00:00:00 2001 From: Paul Parsons Date: Tue, 31 Jan 2012 01:18:35 +0000 Subject: mfd: Add to_irq() member to asic3 gpio_chip structure The gpio_to_irq() macro is now defined as __gpio_to_irq() instead of IRQ_GPIO(). The __gpio_to_irq() function returns -ENXIO if the referenced gpio_chip structure does not define a to_irq() member. This is true of the asic3 gpio_chip structure, and thus calls to gpio_to_irq() now fail (for example from the gpio-vbus module). This patch defines the to_irq() member in the asic3 gpio_chip structure. Signed-off-by: Paul Parsons Signed-off-by: Samuel Ortiz --- drivers/mfd/asic3.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index b85bbd7..1895cf9 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -525,6 +525,11 @@ static void asic3_gpio_set(struct gpio_chip *chip, return; } +static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return (offset < ASIC3_NUM_GPIOS) ? IRQ_BOARD_START + offset : -ENXIO; +} + static __init int asic3_gpio_probe(struct platform_device *pdev, u16 *gpio_config, int num) { @@ -976,6 +981,7 @@ static int __init asic3_probe(struct platform_device *pdev) asic->gpio.set = asic3_gpio_set; asic->gpio.direction_input = asic3_gpio_direction_input; asic->gpio.direction_output = asic3_gpio_direction_output; + asic->gpio.to_irq = asic3_gpio_to_irq; ret = asic3_gpio_probe(pdev, pdata->gpio_config, -- cgit v1.1 From cbcde05ec2c2b428a0fca1e01e944a24f89c5654 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Wed, 1 Feb 2012 03:02:48 +0200 Subject: mfd: Trivial twl4030 code-style fixes Signed-off-by: Felipe Contreras Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index b69bb51..b31f920 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -59,7 +59,6 @@ #define REG_PIH_ISR_P2 0x02 #define REG_PIH_SIR 0x03 /* for testing */ - /* Linux could (eventually) use either IRQ line */ static int irq_line; @@ -111,7 +110,8 @@ static int nr_sih_modules; #define TWL4030_MODULE_INT_PWR TWL4030_MODULE_INT -/* Order in this table matches order in PIH_ISR. That is, +/* + * Order in this table matches order in PIH_ISR. That is, * BIT(n) in PIH_ISR is sih_modules[n]. */ /* sih_modules_twl4030 is used both in twl4030 and twl5030 */ @@ -309,6 +309,7 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid) return IRQ_HANDLED; } + /*----------------------------------------------------------------------*/ /* @@ -337,7 +338,6 @@ static int twl4030_init_sih_modules(unsigned line) memset(buf, 0xff, sizeof buf); sih = sih_modules; for (i = 0; i < nr_sih_modules; i++, sih++) { - /* skip USB -- it's funky */ if (!sih->bytes_ixr) continue; @@ -352,7 +352,8 @@ static int twl4030_init_sih_modules(unsigned line) pr_err("twl4030: err %d initializing %s %s\n", status, sih->name, "IMR"); - /* Maybe disable "exclusive" mode; buffer second pending irq; + /* + * Maybe disable "exclusive" mode; buffer second pending irq; * set Clear-On-Read (COR) bit. * * NOTE that sometimes COR polarity is documented as being @@ -382,7 +383,8 @@ static int twl4030_init_sih_modules(unsigned line) if (sih->irq_lines <= line) continue; - /* Clear pending interrupt status. Either the read was + /* + * Clear pending interrupt status. Either the read was * enough, or we need to write those bits. Repeat, in * case an IRQ is pending (PENDDIS=0) ... that's not * uncommon with PWR_INT.PWRON. @@ -398,7 +400,8 @@ static int twl4030_init_sih_modules(unsigned line) status = twl_i2c_write(sih->module, buf, sih->mask[line].isr_offset, sih->bytes_ixr); - /* else COR=1 means read sufficed. + /* + * else COR=1 means read sufficed. * (for most SIH modules...) */ } @@ -410,7 +413,8 @@ static int twl4030_init_sih_modules(unsigned line) static inline void activate_irq(int irq) { #ifdef CONFIG_ARM - /* ARM requires an extra step to clear IRQ_NOREQUEST, which it + /* + * ARM requires an extra step to clear IRQ_NOREQUEST, which it * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. */ set_irq_flags(irq, IRQF_VALID); @@ -622,9 +626,7 @@ static irqreturn_t handle_twl4030_sih(int irq, void *data) static unsigned twl4030_irq_next; -/* returns the first IRQ used by this SIH bank, - * or negative errno - */ +/* returns the first IRQ used by this SIH bank, or negative errno */ int twl4030_sih_setup(int module) { int sih_mod; @@ -688,7 +690,6 @@ int twl4030_sih_setup(int module) /* FIXME need a call to reverse twl4030_sih_setup() ... */ - /*----------------------------------------------------------------------*/ /* FIXME pass in which interrupt line we'll use ... */ @@ -711,7 +712,8 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) twl4030_irq_base = irq_base; - /* install an irq handler for each of the SIH modules; + /* + * install an irq handler for each of the SIH modules; * clone dummy irq_chip since PIH can't *do* anything */ twl4030_irq_chip = dummy_irq_chip; -- cgit v1.1 From 9a022e5f0095a01456951714edaaa3994caf9ecc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 Feb 2012 16:50:03 +0000 Subject: mfd: wm8994: Mark MICBIAS register as readable Omitted in the regmap conversion. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-regmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index c598ae6..47743fa 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -968,6 +968,7 @@ static bool wm8994_readable_register(struct device *dev, unsigned int reg) { switch (reg) { case WM8994_DC_SERVO_READBACK: + case WM8994_MICBIAS: case WM8994_WRITE_SEQUENCER_CTRL_1: case WM8994_WRITE_SEQUENCER_CTRL_2: case WM8994_AIF1_ADC2_LEFT_VOLUME: -- cgit v1.1 From ee67b0cd5ed69953a6306efabd6e9e23bff09178 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 Feb 2012 18:47:37 +0000 Subject: mfd: wm8994: We don't need to runtime resume by default This is the default state that the runtime PM infrastructure expects so instead just kick the runtime PM core to suspend us if we're not doing anything (as is default). Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index f117e7f..c31e6a4 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -563,7 +563,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) } pm_runtime_enable(wm8994->dev); - pm_runtime_resume(wm8994->dev); + pm_runtime_idle(wm8994->dev); return 0; -- cgit v1.1 From d2cb87c23e8514ca49c85adc5924999927bb9494 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 20 Feb 2012 21:32:32 +0000 Subject: mfd: Test for jack detection when deciding if wm8994 should suspend The jack detection on WM1811 is often required during system suspend, add it as another check when deciding if we should suspend. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index c31e6a4..ecc3a42 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -256,6 +256,20 @@ static int wm8994_suspend(struct device *dev) break; } + switch (wm8994->type) { + case WM1811: + ret = wm8994_reg_read(wm8994, WM8994_ANTIPOP_2); + if (ret < 0) { + dev_err(dev, "Failed to read jackdet: %d\n", ret); + } else if (ret & WM1811_JACKDET_MODE_MASK) { + 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.1 From 6d95b7fdd0bd2e28ef651da6863d75edca4c2aca Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 20 Feb 2012 21:42:03 +0100 Subject: mfd: Fix ab8500 error path bug We were not freeing the irq properly in the error path in the AB8500 driver. Cc: Mark Brown Signed-off-by: Maxime Coquelin Signed-off-by: Alex Macro Signed-off-by: Michel Jaouen Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 53e2a80..d295941 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -956,11 +956,12 @@ int __devinit ab8500_init(struct ab8500 *ab8500) return ret; out_freeirq: - if (ab8500->irq_base) { + if (ab8500->irq_base) free_irq(ab8500->irq, ab8500); out_removeirq: + if (ab8500->irq_base) ab8500_irq_remove(ab8500); - } + return ret; } -- cgit v1.1 From 0f620837595145cd42be1c9dc6b619146fbeaf88 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 20 Feb 2012 21:42:10 +0100 Subject: mfd: Add ab8500 version detection and enforcing There are currently four different versions of the AB8500 around: AB8500, AB8505, AB9540 and AB8540. Unfortunately: - Some of the chips (AB8500, AB8505, AB9540) cannot read the AB8500_REV_REG register but return errors - Some of them have the same ID value in the hardware register AB8500_REV_REV, for example the first versions of AB8505 and AB9540 have 0xFF in this register - just like the AB8500. So we need to be able to enforce a certain version from the platform. We do this by using the id of the platform device that provides the read/write functions. Reviewed-by: Mark Brown Signed-off-by: Maxime Coquelin Signed-off-by: Alex Macro Signed-off-by: Michel Jaouen Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 63 +++++++++++++++++++++++++++++++---------------- drivers/mfd/ab8500-i2c.c | 15 +++++++++-- 2 files changed, 55 insertions(+), 23 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index d295941..3547eee 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -90,6 +90,7 @@ #define AB8500_IT_MASK24_REG 0x57 #define AB8500_REV_REG 0x80 +#define AB8500_IC_NAME_REG 0x82 #define AB8500_SWITCH_OFF_STATUS 0x00 #define AB8500_TURN_ON_STATUS 0x00 @@ -105,6 +106,13 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, }; +static const char ab8500_version_str[][7] = { + [AB8500_VERSION_AB8500] = "AB8500", + [AB8500_VERSION_AB8505] = "AB8505", + [AB8500_VERSION_AB9540] = "AB9540", + [AB8500_VERSION_AB8540] = "AB8540", +}; + static int ab8500_get_chip_id(struct device *dev) { struct ab8500 *ab8500; @@ -256,9 +264,12 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) if (new == old) continue; - /* Interrupt register 12 doesn't exist prior to version 2.0 */ - if (ab8500_irq_regoffset[i] == 11 && - ab8500->chip_id < AB8500_CUT2P0) + /* + * Interrupt register 12 doesn't exist prior to AB8500 version + * 2.0 + */ + if (ab8500->irq_reg_offset[i] == 11 && + is_ab8500_1p1_or_earlier(ab8500)) continue; ab8500->oldmask[i] = new; @@ -311,8 +322,11 @@ static irqreturn_t ab8500_irq(int irq, void *dev) int status; u8 value; - /* Interrupt register 12 doesn't exist prior to version 2.0 */ - if (regoffset == 11 && ab8500->chip_id < AB8500_CUT2P0) + /* + * Interrupt register 12 doesn't exist prior to AB8500 version + * 2.0 + */ + if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500)) continue; status = get_register_interruptible(ab8500, AB8500_INTERRUPT, @@ -857,7 +871,7 @@ static struct attribute_group ab8500_attr_group = { .attrs = ab8500_sysfs_entries, }; -int __devinit ab8500_init(struct ab8500 *ab8500) +int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) { struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); int ret; @@ -870,25 +884,29 @@ int __devinit ab8500_init(struct ab8500 *ab8500) mutex_init(&ab8500->lock); mutex_init(&ab8500->irq_lock); + if (version != AB8500_VERSION_UNDEFINED) + ab8500->version = version; + else { + ret = get_register_interruptible(ab8500, AB8500_MISC, + AB8500_IC_NAME_REG, &value); + if (ret < 0) + return ret; + + ab8500->version = value; + } + ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_REV_REG, &value); if (ret < 0) return ret; - switch (value) { - case AB8500_CUT1P0: - case AB8500_CUT1P1: - case AB8500_CUT2P0: - case AB8500_CUT3P0: - case AB8500_CUT3P3: - dev_info(ab8500->dev, "detected chip, revision: %#x\n", value); - break; - default: - dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value); - return -EINVAL; - } ab8500->chip_id = value; + dev_info(ab8500->dev, "detected chip, %s rev. %1x.%1x\n", + ab8500_version_str[ab8500->version], + ab8500->chip_id >> 4, + ab8500->chip_id & 0x0F); + /* * ab8500 has switched off due to (SWITCH_OFF_STATUS): * 0x01 Swoff bit programming @@ -912,9 +930,12 @@ int __devinit ab8500_init(struct ab8500 *ab8500) /* Clear and mask all interrupts */ for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { - /* Interrupt register 12 doesn't exist prior to version 2.0 */ - if (ab8500_irq_regoffset[i] == 11 && - ab8500->chip_id < AB8500_CUT2P0) + /* + * Interrupt register 12 doesn't exist prior to AB8500 version + * 2.0 + */ + if (ab8500->irq_reg_offset[i] == 11 && + is_ab8500_1p1_or_earlier(ab8500)) continue; get_register_interruptible(ab8500, AB8500_INTERRUPT, diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c index 087fecd..70a16ae 100644 --- a/drivers/mfd/ab8500-i2c.c +++ b/drivers/mfd/ab8500-i2c.c @@ -38,6 +38,7 @@ static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr) static int __devinit ab8500_i2c_probe(struct platform_device *plf) { + const struct platform_device_id *platid = platform_get_device_id(plf); struct ab8500 *ab8500; struct resource *resource; int ret; @@ -61,10 +62,11 @@ static int __devinit ab8500_i2c_probe(struct platform_device *plf) platform_set_drvdata(plf, ab8500); - ret = ab8500_init(ab8500); + ret = ab8500_init(ab8500, platid->driver_data); if (ret) kfree(ab8500); + return ret; } @@ -78,13 +80,22 @@ static int __devexit ab8500_i2c_remove(struct platform_device *plf) return 0; } +static const struct platform_device_id ab8500_id[] = { + { "ab8500-i2c", AB8500_VERSION_AB8500 }, + { "ab8505-i2c", AB8500_VERSION_AB8505 }, + { "ab9540-i2c", AB8500_VERSION_AB9540 }, + { "ab8540-i2c", AB8500_VERSION_AB8540 }, + { } +}; + static struct platform_driver ab8500_i2c_driver = { .driver = { .name = "ab8500-i2c", .owner = THIS_MODULE, }, .probe = ab8500_i2c_probe, - .remove = __devexit_p(ab8500_i2c_remove) + .remove = __devexit_p(ab8500_i2c_remove), + .id_table = ab8500_id, }; static int __init ab8500_i2c_init(void) -- cgit v1.1 From 2ced445e2ddf65f484a489161accddf475676965 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 20 Feb 2012 21:42:17 +0100 Subject: mfd: Parametrize ab8500 IRQ masks and registers This makes the AB8500 state struct contain the IRQ mask and register offsets previously hard-coded so as to make room for more AB8500 variants. Reviewed-by: Mark Brown Signed-off-by: Maxime Coquelin Signed-off-by: Alex Macro Signed-off-by: Michel Jaouen Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 50 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 3547eee..73907ad 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -97,11 +97,13 @@ /* * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt - * numbers are indexed into this array with (num / 8). + * numbers are indexed into this array with (num / 8). The interupts are + * defined in linux/mfd/ab8500.h * * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at * offset 0. */ +/* AB8500 support */ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, }; @@ -256,7 +258,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); int i; - for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { + for (i = 0; i < ab8500->mask_size; i++) { u8 old = ab8500->oldmask[i]; u8 new = ab8500->mask[i]; int reg; @@ -274,7 +276,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) ab8500->oldmask[i] = new; - reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i]; + reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i]; set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new); } @@ -317,8 +319,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev) dev_vdbg(ab8500->dev, "interrupt\n"); - for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { - int regoffset = ab8500_irq_regoffset[i]; + for (i = 0; i < ab8500->mask_size; i++) { + int regoffset = ab8500->irq_reg_offset[i]; int status; u8 value; @@ -350,8 +352,11 @@ static int ab8500_irq_init(struct ab8500 *ab8500) { int base = ab8500->irq_base; int irq; + int num_irqs; - for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { + num_irqs = AB8500_NR_IRQS; + + for (irq = base; irq < base + num_irqs; irq++) { irq_set_chip_data(irq, ab8500); irq_set_chip_and_handler(irq, &ab8500_irq_chip, handle_simple_irq); @@ -370,8 +375,11 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) { int base = ab8500->irq_base; int irq; + int num_irqs; + + num_irqs = AB8500_NR_IRQS; - for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { + for (irq = base; irq < base + num_irqs; irq++) { #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif @@ -907,6 +915,16 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) ab8500->chip_id >> 4, ab8500->chip_id & 0x0F); + ab8500->mask_size = AB8500_NUM_IRQ_REGS; + ab8500->irq_reg_offset = ab8500_irq_regoffset; + ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL); + if (!ab8500->mask) + return -ENOMEM; + ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL); + if (!ab8500->oldmask) { + ret = -ENOMEM; + goto out_freemask; + } /* * ab8500 has switched off due to (SWITCH_OFF_STATUS): * 0x01 Swoff bit programming @@ -929,7 +947,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) plat->init(ab8500); /* Clear and mask all interrupts */ - for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { + for (i = 0; i < ab8500->mask_size; i++) { /* * Interrupt register 12 doesn't exist prior to AB8500 version * 2.0 @@ -939,23 +957,23 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) continue; get_register_interruptible(ab8500, AB8500_INTERRUPT, - AB8500_IT_LATCH1_REG + ab8500_irq_regoffset[i], + AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i], &value); set_register_interruptible(ab8500, AB8500_INTERRUPT, - AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i], 0xff); + AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i], 0xff); } ret = abx500_register_ops(ab8500->dev, &ab8500_ops); if (ret) - return ret; + goto out_freeoldmask; - for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) + for (i = 0; i < ab8500->mask_size; i++) ab8500->mask[i] = ab8500->oldmask[i] = 0xff; if (ab8500->irq_base) { ret = ab8500_irq_init(ab8500); if (ret) - return ret; + goto out_freeoldmask; ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, IRQF_ONESHOT | IRQF_NO_SUSPEND, @@ -982,6 +1000,10 @@ out_freeirq: out_removeirq: if (ab8500->irq_base) ab8500_irq_remove(ab8500); +out_freeoldmask: + kfree(ab8500->oldmask); +out_freemask: + kfree(ab8500->mask); return ret; } @@ -994,6 +1016,8 @@ int __devexit ab8500_exit(struct ab8500 *ab8500) free_irq(ab8500->irq, ab8500); ab8500_irq_remove(ab8500); } + kfree(ab8500->oldmask); + kfree(ab8500->mask); return 0; } -- cgit v1.1 From d6255529b2639de542324f314b93939b7996a7c5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 20 Feb 2012 21:42:24 +0100 Subject: mfd: Support AB9540 ab8500 variant The AB9540 variant of the AB8500 is basically close enough to use the same driver. This adds the new registers and deviations for this new chip variant. Reviewed-by: Mark Brown Signed-off-by: Maxime Coquelin Signed-off-by: Alex Macro Signed-off-by: Michel Jaouen Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 191 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 170 insertions(+), 21 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 73907ad..15a18fe 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -32,6 +32,7 @@ #define AB8500_IT_SOURCE6_REG 0x05 #define AB8500_IT_SOURCE7_REG 0x06 #define AB8500_IT_SOURCE8_REG 0x07 +#define AB9540_IT_SOURCE13_REG 0x0C #define AB8500_IT_SOURCE19_REG 0x12 #define AB8500_IT_SOURCE20_REG 0x13 #define AB8500_IT_SOURCE21_REG 0x14 @@ -53,6 +54,7 @@ #define AB8500_IT_LATCH9_REG 0x28 #define AB8500_IT_LATCH10_REG 0x29 #define AB8500_IT_LATCH12_REG 0x2B +#define AB9540_IT_LATCH13_REG 0x2C #define AB8500_IT_LATCH19_REG 0x32 #define AB8500_IT_LATCH20_REG 0x33 #define AB8500_IT_LATCH21_REG 0x34 @@ -95,6 +97,9 @@ #define AB8500_TURN_ON_STATUS 0x00 +#define AB9540_MODEM_CTRL2_REG 0x23 +#define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT BIT(2) + /* * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt * numbers are indexed into this array with (num / 8). The interupts are @@ -108,6 +113,11 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, }; +/* AB9540 support */ +static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24, +}; + static const char ab8500_version_str[][7] = { [AB8500_VERSION_AB8500] = "AB8500", [AB8500_VERSION_AB8505] = "AB8505", @@ -354,7 +364,10 @@ static int ab8500_irq_init(struct ab8500 *ab8500) int irq; int num_irqs; - num_irqs = AB8500_NR_IRQS; + if (is_ab9540(ab8500)) + num_irqs = AB9540_NR_IRQS; + else + num_irqs = AB8500_NR_IRQS; for (irq = base; irq < base + num_irqs; irq++) { irq_set_chip_data(irq, ab8500); @@ -377,7 +390,10 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) int irq; int num_irqs; - num_irqs = AB8500_NR_IRQS; + if (is_ab9540(ab8500)) + num_irqs = AB9540_NR_IRQS; + else + num_irqs = AB8500_NR_IRQS; for (irq = base; irq < base + num_irqs; irq++) { #ifdef CONFIG_ARM @@ -388,6 +404,7 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) } } +/* AB8500 GPIO Resources */ static struct resource __devinitdata ab8500_gpio_resources[] = { { .name = "GPIO_INT6", @@ -397,6 +414,28 @@ static struct resource __devinitdata ab8500_gpio_resources[] = { } }; +/* AB9540 GPIO Resources */ +static struct resource __devinitdata ab9540_gpio_resources[] = { + { + .name = "GPIO_INT6", + .start = AB8500_INT_GPIO6R, + .end = AB8500_INT_GPIO41F, + .flags = IORESOURCE_IRQ, + }, + { + .name = "GPIO_INT14", + .start = AB9540_INT_GPIO50R, + .end = AB9540_INT_GPIO54R, + .flags = IORESOURCE_IRQ, + }, + { + .name = "GPIO_INT15", + .start = AB9540_INT_GPIO50F, + .end = AB9540_INT_GPIO54F, + .flags = IORESOURCE_IRQ, + } +}; + static struct resource __devinitdata ab8500_gpadc_resources[] = { { .name = "HW_CONV_END", @@ -713,7 +752,7 @@ static struct resource __devinitdata ab8500_temp_resources[] = { }, }; -static struct mfd_cell __devinitdata ab8500_devs[] = { +static struct mfd_cell __devinitdata abx500_common_devs[] = { #ifdef CONFIG_DEBUG_FS { .name = "ab8500-debug", @@ -728,11 +767,6 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { .name = "ab8500-regulator", }, { - .name = "ab8500-gpio", - .num_resources = ARRAY_SIZE(ab8500_gpio_resources), - .resources = ab8500_gpio_resources, - }, - { .name = "ab8500-gpadc", .num_resources = ARRAY_SIZE(ab8500_gpadc_resources), .resources = ab8500_gpadc_resources, @@ -770,11 +804,7 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { { .name = "ab8500-codec", }, - { - .name = "ab8500-usb", - .num_resources = ARRAY_SIZE(ab8500_usb_resources), - .resources = ab8500_usb_resources, - }, + { .name = "ab8500-poweron-key", .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), @@ -803,6 +833,32 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { }, }; +static struct mfd_cell __devinitdata ab8500_devs[] = { + { + .name = "ab8500-gpio", + .num_resources = ARRAY_SIZE(ab8500_gpio_resources), + .resources = ab8500_gpio_resources, + }, + { + .name = "ab8500-usb", + .num_resources = ARRAY_SIZE(ab8500_usb_resources), + .resources = ab8500_usb_resources, + }, +}; + +static struct mfd_cell __devinitdata ab9540_devs[] = { + { + .name = "ab8500-gpio", + .num_resources = ARRAY_SIZE(ab9540_gpio_resources), + .resources = ab9540_gpio_resources, + }, + { + .name = "ab9540-usb", + .num_resources = ARRAY_SIZE(ab8500_usb_resources), + .resources = ab8500_usb_resources, + }, +}; + static ssize_t show_chip_id(struct device *dev, struct device_attribute *attr, char *buf) { @@ -864,9 +920,64 @@ static ssize_t show_turn_on_status(struct device *dev, return sprintf(buf, "%#x\n", value); } +static ssize_t show_ab9540_dbbrstn(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ab8500 *ab8500; + int ret; + u8 value; + + ab8500 = dev_get_drvdata(dev); + + ret = get_register_interruptible(ab8500, AB8500_REGU_CTRL2, + AB9540_MODEM_CTRL2_REG, &value); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", + (value & AB9540_MODEM_CTRL2_SWDBBRSTN_BIT) ? 1 : 0); +} + +static ssize_t store_ab9540_dbbrstn(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ab8500 *ab8500; + int ret = count; + int err; + u8 bitvalues; + + ab8500 = dev_get_drvdata(dev); + + if (count > 0) { + switch (buf[0]) { + case '0': + bitvalues = 0; + break; + case '1': + bitvalues = AB9540_MODEM_CTRL2_SWDBBRSTN_BIT; + break; + default: + goto exit; + } + + err = mask_and_set_register_interruptible(ab8500, + AB8500_REGU_CTRL2, AB9540_MODEM_CTRL2_REG, + AB9540_MODEM_CTRL2_SWDBBRSTN_BIT, bitvalues); + if (err) + dev_info(ab8500->dev, + "Failed to set DBBRSTN %c, err %#x\n", + buf[0], err); + } + +exit: + return ret; +} + 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 DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR, + show_ab9540_dbbrstn, store_ab9540_dbbrstn); static struct attribute *ab8500_sysfs_entries[] = { &dev_attr_chip_id.attr, @@ -875,10 +986,22 @@ static struct attribute *ab8500_sysfs_entries[] = { NULL, }; +static struct attribute *ab9540_sysfs_entries[] = { + &dev_attr_chip_id.attr, + &dev_attr_switch_off_status.attr, + &dev_attr_turn_on_status.attr, + &dev_attr_dbbrstn.attr, + NULL, +}; + static struct attribute_group ab8500_attr_group = { .attrs = ab8500_sysfs_entries, }; +static struct attribute_group ab9540_attr_group = { + .attrs = ab9540_sysfs_entries, +}; + int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) { struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); @@ -915,8 +1038,14 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) ab8500->chip_id >> 4, ab8500->chip_id & 0x0F); - ab8500->mask_size = AB8500_NUM_IRQ_REGS; - ab8500->irq_reg_offset = ab8500_irq_regoffset; + /* Configure AB8500 or AB9540 IRQ */ + if (is_ab9540(ab8500)) { + ab8500->mask_size = AB9540_NUM_IRQ_REGS; + ab8500->irq_reg_offset = ab9540_irq_regoffset; + } else { + ab8500->mask_size = AB8500_NUM_IRQ_REGS; + ab8500->irq_reg_offset = ab8500_irq_regoffset; + } ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL); if (!ab8500->mask) return -ENOMEM; @@ -982,17 +1111,34 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) goto out_removeirq; } - ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, - ARRAY_SIZE(ab8500_devs), NULL, + ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, + ARRAY_SIZE(abx500_common_devs), NULL, + ab8500->irq_base); + + if (ret) + goto out_freeirq; + + if (is_ab9540(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, + ARRAY_SIZE(ab9540_devs), NULL, + ab8500->irq_base); + else + ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, + ARRAY_SIZE(ab9540_devs), NULL, ab8500->irq_base); if (ret) goto out_freeirq; - ret = sysfs_create_group(&ab8500->dev->kobj, &ab8500_attr_group); + if (is_ab9540(ab8500)) + ret = sysfs_create_group(&ab8500->dev->kobj, + &ab9540_attr_group); + else + ret = sysfs_create_group(&ab8500->dev->kobj, + &ab8500_attr_group); if (ret) dev_err(ab8500->dev, "error creating sysfs entries\n"); - - return ret; + else + return ret; out_freeirq: if (ab8500->irq_base) @@ -1010,7 +1156,10 @@ out_freemask: int __devexit ab8500_exit(struct ab8500 *ab8500) { - sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); + if (is_ab9540(ab8500)) + sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group); + else + sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); mfd_remove_devices(ab8500->dev); if (ab8500->irq_base) { free_irq(ab8500->irq, ab8500); -- cgit v1.1 From d1b5c5e2351c5d30327f77226daab21ce9ef427f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 12 Feb 2012 17:43:22 +0800 Subject: mfd: Fix kcalloc parameters swapped The first parameter should be "number of elements" and the second parameter should be "element size". Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 0f59228..9fc05b9 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -162,7 +162,7 @@ int mfd_add_devices(struct device *parent, int id, atomic_t *cnts; /* initialize reference counting for all cells */ - cnts = kcalloc(sizeof(*cnts), n_devs, GFP_KERNEL); + cnts = kcalloc(n_devs, sizeof(*cnts), GFP_KERNEL); if (!cnts) return -ENOMEM; -- cgit v1.1 From 113351b52b296afa91d829e880037ef91f0e96f5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 14 Feb 2012 11:08:07 +0800 Subject: mfd: Fix val_len parameters for s5m-core regmap_raw_write The val_len parameters for regmap_raw_write should be "count * sizeof(u8)" (or simply "count") instead of "count * sizeof(u16)". Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/s5m-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index 8b8cf85..578e12e 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -74,7 +74,7 @@ EXPORT_SYMBOL_GPL(s5m_reg_write); int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf) { - return regmap_raw_write(s5m87xx->regmap, reg, buf, count * sizeof(u16)); + return regmap_raw_write(s5m87xx->regmap, reg, buf, count); } EXPORT_SYMBOL_GPL(s5m_bulk_write); -- cgit v1.1 From 60b5c5a435007af5fe9f05483ff2a26aacb5fe78 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 14 Feb 2012 11:30:31 +0800 Subject: mfd: Make pcf50633_write_block() return 0 on success The callers of pcf50633_write_block assume pcf50633_write_block return 0 on success, thus make it return 0 instead of the number of registers written on success. Currently pcf50633_write_block is called in drivers/mfd/pcf50633-irq.c and drivers/rtc/rtc-pcf50633.c. Signed-off-by: Axel Lin Acked-by: Harald Welte Signed-off-by: Samuel Ortiz --- drivers/mfd/pcf50633-core.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index ff1a7e7..189c2f0 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -46,13 +46,7 @@ EXPORT_SYMBOL_GPL(pcf50633_read_block); int pcf50633_write_block(struct pcf50633 *pcf , u8 reg, int nr_regs, u8 *data) { - int ret; - - ret = regmap_raw_write(pcf->regmap, reg, data, nr_regs); - if (ret != 0) - return ret; - - return nr_regs; + return regmap_raw_write(pcf->regmap, reg, data, nr_regs); } EXPORT_SYMBOL_GPL(pcf50633_write_block); -- cgit v1.1 From 231dd9c8994430d4aaa75cdee95af13c154f489f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 15 Feb 2012 16:05:24 +0800 Subject: mfd: Fix typo for TPS65912 Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c559d35..d92bea2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -186,7 +186,7 @@ config MFD_TPS65912 depends on GPIOLIB config MFD_TPS65912_I2C - bool "TPS95612 Power Management chip with I2C" + bool "TPS65912 Power Management chip with I2C" select MFD_CORE select MFD_TPS65912 depends on I2C=y && GPIOLIB -- cgit v1.1 From cbb8c220e70d25d1108db39d7956b08a66492fad Mon Sep 17 00:00:00 2001 From: "Govindraj.R" Date: Wed, 15 Feb 2012 12:27:50 +0530 Subject: mfd: Remove omap-usb-host magic numbers for dev dma mask Remove the hardcoded magic values for dma mask and use the dma mask api/macro available. Signed-off-by: Govindraj.R Reviewed-by: Felipe Balbi Signed-off-by: Samuel Ortiz --- drivers/mfd/omap-usb-host.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 68ac2c5..6800411 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -170,7 +170,7 @@ struct usbhs_hcd_omap { /*-------------------------------------------------------------------------*/ const char usbhs_driver_name[] = USBHS_DRIVER_NAME; -static u64 usbhs_dmamask = ~(u32)0; +static u64 usbhs_dmamask = DMA_BIT_MASK(32); /*-------------------------------------------------------------------------*/ @@ -223,7 +223,7 @@ static struct platform_device *omap_usbhs_alloc_child(const char *name, } child->dev.dma_mask = &usbhs_dmamask; - child->dev.coherent_dma_mask = 0xffffffff; + dma_set_coherent_mask(&child->dev, DMA_BIT_MASK(32)); child->dev.parent = dev; ret = platform_device_add(child); -- cgit v1.1 From f0447a690e2cfe005a605253cf6bdfa9b9fd5d6e Mon Sep 17 00:00:00 2001 From: "Govindraj.R" Date: Wed, 15 Feb 2012 15:53:34 +0530 Subject: mfd: Move omap-usb-host usbhs init before allocing child dev There could be possible race condition where the host dev's are alloced and added to platform dev just before usbhs_init. Just move usbhs_init before adding child dev. CC: Alan Stern Signed-off-by: Govindraj.R Acked-by: Felipe Balbi Signed-off-by: Samuel Ortiz --- drivers/mfd/omap-usb-host.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 6800411..95a2e54 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -799,14 +799,13 @@ static int __devinit usbhs_omap_probe(struct platform_device *pdev) platform_set_drvdata(pdev, omap); + omap_usbhs_init(dev); ret = omap_usbhs_alloc_children(pdev); if (ret) { dev_err(dev, "omap_usbhs_alloc_children failed\n"); goto err_alloc; } - omap_usbhs_init(dev); - goto end_probe; err_alloc: -- cgit v1.1 From 1fa93bb2925781060971e984d11200c542a18fdb Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 16 Feb 2012 13:58:19 +0800 Subject: mfd: Show correct device id for wm8400 Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8400-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 2204893..e60d524 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -271,8 +271,7 @@ static int wm8400_init(struct wm8400 *wm8400, return -EIO; } if (i != reg_data[WM8400_RESET_ID].default_val) { - dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", - reg); + dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", i); return -ENODEV; } -- cgit v1.1 From ee42b3aa3791263a6cca36856b306cd1ae897ebf Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Feb 2012 15:58:03 -0800 Subject: mfd: wm8994: Add __devinit and __devexit annotations for probe and remove Fixes warnings and needed for correctness. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index ecc3a42..46d8671 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -613,8 +613,8 @@ static const struct of_device_id wm8994_of_match[] = { }; MODULE_DEVICE_TABLE(of, wm8994_of_match); -static int wm8994_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static __devinit int wm8994_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct wm8994 *wm8994; int ret; @@ -639,7 +639,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, return wm8994_device_init(wm8994, i2c->irq); } -static int wm8994_i2c_remove(struct i2c_client *i2c) +static __devexit int wm8994_i2c_remove(struct i2c_client *i2c) { struct wm8994 *wm8994 = i2c_get_clientdata(i2c); @@ -668,7 +668,7 @@ static struct i2c_driver wm8994_i2c_driver = { .of_match_table = wm8994_of_match, }, .probe = wm8994_i2c_probe, - .remove = wm8994_i2c_remove, + .remove = __devexit_p(wm8994_i2c_remove), .id_table = wm8994_i2c_id, }; -- cgit v1.1 From 855cc454341c6ca1212bf86939f2f2a51ab54e18 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 18 Feb 2012 17:54:23 +0100 Subject: mfd: Fix ACPI conflict check The code is currently always checking the first resource of every device only (several times.) This has been broken since the ACPI check was added in February 2010 in commit 91fedede0338eb6203cdd618d8ece873fdb7c22c. Fix the check to run on each resource individually, once. Signed-off-by: Jean Delvare Cc: stable@vger.kernel.org Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 9fc05b9..ffc3d48 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -123,7 +123,7 @@ static int mfd_add_device(struct device *parent, int id, } if (!cell->ignore_resource_conflicts) { - ret = acpi_check_resource_conflict(res); + ret = acpi_check_resource_conflict(&res[r]); if (ret) goto fail_res; } -- cgit v1.1 From 8dfc4705828178ae25f08ce774831e4c19de8e51 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 19 Feb 2012 15:50:11 +0800 Subject: mfd: Initialize tps65912 irq platform data properly irq_base of the tps65912 irq platform data should be initialized with the board provided irq_base data. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65912-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c index 5fec23a..74fd8cb 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -151,7 +151,7 @@ int tps65912_device_init(struct tps65912 *tps65912) goto err; init_data->irq = pmic_plat_data->irq; - init_data->irq_base = pmic_plat_data->irq; + init_data->irq_base = pmic_plat_data->irq_base; ret = tps65912_irq_init(tps65912, init_data->irq, init_data); if (ret < 0) goto err; -- cgit v1.1 From 485540dce01cf4b4d3629141399678e35e66b711 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 20 Feb 2012 12:30:26 +0100 Subject: mfd: Decouple/recouple gic from the ux500 PRCMU This patch allows to decouple and recouple the gic from the PRCMU. This is needed to put the A9 core in retention mode with the cpuidle driver. It is based on top of the "DB8500 PRCMU update" patchset. Signed-off-by: Daniel Lezcano Acked-by: Rickard Andersson Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 128b5f4..b320cc6 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -821,6 +821,38 @@ u8 db8500_prcmu_get_power_state_result(void) return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS); } +#define PRCMU_A9_MASK_REQ 0x00000328 +#define PRCMU_A9_MASK_REQ_MASK 0x00000001 +#define PRCMU_GIC_DELAY 1 + +/* This function decouple the gic from the prcmu */ +int db8500_prcmu_gic_decouple(void) +{ + u32 val = readl(_PRCMU_BASE + PRCMU_A9_MASK_REQ); + + /* Set bit 0 register value to 1 */ + writel(val | PRCMU_A9_MASK_REQ_MASK, _PRCMU_BASE + PRCMU_A9_MASK_REQ); + + /* Make sure the register is updated */ + readl(_PRCMU_BASE + PRCMU_A9_MASK_REQ); + + /* Wait a few cycles for the gic mask completion */ + udelay(PRCMU_GIC_DELAY); + + return 0; +} + +/* This function recouple the gic with the prcmu */ +int db8500_prcmu_gic_recouple(void) +{ + u32 val = readl(_PRCMU_BASE + PRCMU_A9_MASK_REQ); + + /* Set bit 0 register value to 0 */ + writel(val & ~PRCMU_A9_MASK_REQ_MASK, _PRCMU_BASE + PRCMU_A9_MASK_REQ); + + return 0; +} + /* This function should only be called while mb0_transfer.lock is held. */ static void config_wakeups(void) { -- cgit v1.1 From 91d6a9a6c0d98ef6daeaf229e5acada652b4f6f0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 22 Feb 2012 11:43:40 +0800 Subject: mfd: Remove unused io_lock mutex from da9052 da9052 has been converted to use regmap API, so we can remove the unused io_lock mutex. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/da9052-core.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 5ddde2a..7ff313f 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -647,8 +646,6 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) struct irq_desc *desc; int ret; - mutex_init(&da9052->io_lock); - if (pdata && pdata->init != NULL) pdata->init(da9052); -- cgit v1.1 From 3f8349e6e98ba0455437724589072523865eae5e Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 22 Feb 2012 20:03:45 -0600 Subject: mfd: Clear twl6030 IRQ status register only once TWL6030 family of PMIC use a shadow interrupt status register while kernel processes the current interrupt event. However, any write(0 or 1) to register INT_STS_A, INT_STS_B or INT_STS_C clears all 3 interrupt status registers. Since clear of the interrupt is done on 32k clk, depending on I2C bus speed, we could in-adverently clear the status of a interrupt status pending on shadow register in the current implementation. This is due to the fact that multi-byte i2c write operation into three seperate status register could result in multiple load and clear of status and result in lost interrupts. Instead, doing a single byte write to INT_STS_A register with 0x0 will clear all three interrupt status registers without the related risk. Acked-by: Santosh Shilimkar Signed-off-by: Nishanth Menon Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6030-irq.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index c6b456a..aa367a2 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -185,8 +185,17 @@ static int twl6030_irq_thread(void *data) } local_irq_enable(); } - ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes, - REG_INT_STS_A, 3); /* clear INT_STS_A */ + + /* + * NOTE: + * Simulation confirms that documentation is wrong w.r.t the + * interrupt status clear operation. A single *byte* write to + * any one of STS_A to STS_C register results in all three + * STS registers being reset. Since it does not matter which + * value is written, all three registers are cleared on a + * single byte write, so we just use 0x0 to clear. + */ + ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A); if (ret) pr_warning("twl6030: I2C error in clearing PIH ISR\n"); -- cgit v1.1 From b8b8d7932bb83300d0ae6553e320ab1aecb37990 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 22 Feb 2012 20:03:59 -0600 Subject: mfd: Make twl6030_irq_set_wake static twl6030_irq_set_wake is not used anywhere else in the kernel except as irq_chip.irq_set_wake. No reason for it to be exported. Also fixes build warning: drivers/mfd/twl6030-irq.c:230:5: warning: symbol 'twl6030_irq_set_wake' was not declared. Should it be static? Signed-off-by: Nishanth Menon Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6030-irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index aa367a2..3c2cc00 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -236,7 +236,7 @@ static inline void activate_irq(int irq) #endif } -int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) +static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) { if (on) atomic_inc(&twl6030_wakeirqs); -- cgit v1.1 From 2853378b6eafd8b9e2f0e39ab599c93ce518b04d Mon Sep 17 00:00:00 2001 From: "Jett.Zhou" Date: Mon, 27 Feb 2012 15:44:20 +0100 Subject: mfd: Add ability to wake the system for 88pm860x For 88pm860x pmic, it can wake the system from low power mode by irq, its sub-devs like RTC and onkey can be enabled for this usage. Signed-off-by: Jett.Zhou Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm860x-i2c.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/88pm860x-i2c.c b/drivers/mfd/88pm860x-i2c.c index f93dd95..b2cfdc4 100644 --- a/drivers/mfd/88pm860x-i2c.c +++ b/drivers/mfd/88pm860x-i2c.c @@ -334,10 +334,35 @@ static int __devexit pm860x_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int pm860x_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct pm860x_chip *chip = i2c_get_clientdata(client); + + if (device_may_wakeup(dev) && chip->wakeup_flag) + enable_irq_wake(chip->core_irq); + return 0; +} + +static int pm860x_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct pm860x_chip *chip = i2c_get_clientdata(client); + + if (device_may_wakeup(dev) && chip->wakeup_flag) + disable_irq_wake(chip->core_irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume); + static struct i2c_driver pm860x_driver = { .driver = { .name = "88PM860x", .owner = THIS_MODULE, + .pm = &pm860x_pm_ops, }, .probe = pm860x_probe, .remove = __devexit_p(pm860x_remove), -- cgit v1.1 From dc9913a050f1898c6a77f4f5606bc194d530aafd Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 21 Feb 2012 18:21:34 +0530 Subject: mfd: Use regmap for tps65910 register access. Using regmap apis for accessing the device registers and using RBTREE caching mechanims for caching registers. Enabling caching of the registers which is used for voltage controls. By doing this, the modify_bits operation is faster as it does not involve the i2c register read from device, just read from cache. This results faster set voltage operation. Signed-off-by: Laxman Dewangan Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/tps65910.c | 123 ++++++++++++++++++------------------------------- 2 files changed, 47 insertions(+), 77 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d92bea2..16178e1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -177,6 +177,7 @@ config MFD_TPS65910 depends on I2C=y && GPIOLIB select MFD_CORE select GPIO_TPS65910 + select REGMAP_I2C help if you say yes here you get support for the TPS65910 series of Power Management chips. diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 4392f6b..1c4f53e 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include #include +#include #include static struct mfd_cell tps65910s[] = { @@ -38,99 +40,56 @@ static struct mfd_cell tps65910s[] = { static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg, int bytes, void *dest) { - struct i2c_client *i2c = tps65910->i2c_client; - struct i2c_msg xfer[2]; - int ret; - - /* Write register */ - xfer[0].addr = i2c->addr; - xfer[0].flags = 0; - xfer[0].len = 1; - xfer[0].buf = ® - - /* Read data */ - xfer[1].addr = i2c->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = bytes; - xfer[1].buf = dest; - - ret = i2c_transfer(i2c->adapter, xfer, 2); - if (ret == 2) - ret = 0; - else if (ret >= 0) - ret = -EIO; - - return ret; + return regmap_bulk_read(tps65910->regmap, reg, dest, bytes); } static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg, - int bytes, void *src) + int bytes, void *src) { - struct i2c_client *i2c = tps65910->i2c_client; - /* we add 1 byte for device register */ - u8 msg[TPS65910_MAX_REGISTER + 1]; - int ret; - - if (bytes > TPS65910_MAX_REGISTER) - return -EINVAL; - - msg[0] = reg; - memcpy(&msg[1], src, bytes); - - ret = i2c_master_send(i2c, msg, bytes + 1); - if (ret < 0) - return ret; - if (ret != bytes + 1) - return -EIO; - return 0; + return regmap_bulk_write(tps65910->regmap, reg, src, bytes); } int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask) { - u8 data; - int err; - - mutex_lock(&tps65910->io_mutex); - err = tps65910_i2c_read(tps65910, reg, 1, &data); - if (err) { - dev_err(tps65910->dev, "read from reg %x failed\n", reg); - goto out; - } - - data |= mask; - err = tps65910_i2c_write(tps65910, reg, 1, &data); - if (err) - dev_err(tps65910->dev, "write to reg %x failed\n", reg); - -out: - mutex_unlock(&tps65910->io_mutex); - return err; + return regmap_update_bits(tps65910->regmap, reg, mask, mask); } EXPORT_SYMBOL_GPL(tps65910_set_bits); int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask) { - u8 data; - int err; - - mutex_lock(&tps65910->io_mutex); - err = tps65910_i2c_read(tps65910, reg, 1, &data); - if (err) { - dev_err(tps65910->dev, "read from reg %x failed\n", reg); - goto out; - } - - data &= ~mask; - err = tps65910_i2c_write(tps65910, reg, 1, &data); - if (err) - dev_err(tps65910->dev, "write to reg %x failed\n", reg); - -out: - mutex_unlock(&tps65910->io_mutex); - return err; + return regmap_update_bits(tps65910->regmap, reg, mask, 0); } EXPORT_SYMBOL_GPL(tps65910_clear_bits); +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ + struct tps65910 *tps65910 = dev_get_drvdata(dev); + + /* + * Caching all regulator registers. + * All regualator register address range is same for + * TPS65910 and TPS65911 + */ + if ((reg >= TPS65910_VIO) && (reg <= TPS65910_VDAC)) { + /* Check for non-existing register */ + if (tps65910_chip_id(tps65910) == TPS65910) + if ((reg == TPS65911_VDDCTRL_OP) || + (reg == TPS65911_VDDCTRL_SR)) + return true; + return false; + } + return true; +} + +static const struct regmap_config rc5t583_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = is_volatile_reg, + .max_register = TPS65910_MAX_REGISTER, + .num_reg_defaults_raw = TPS65910_MAX_REGISTER, + .cache_type = REGCACHE_RBTREE, +}; + static int tps65910_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -161,6 +120,13 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, tps65910->write = tps65910_i2c_write; mutex_init(&tps65910->io_mutex); + tps65910->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + if (IS_ERR(tps65910->regmap)) { + ret = PTR_ERR(tps65910->regmap); + dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); + goto regmap_err; + } + ret = mfd_add_devices(tps65910->dev, -1, tps65910s, ARRAY_SIZE(tps65910s), NULL, 0); @@ -178,6 +144,8 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, return ret; err: + regmap_exit(tps65910->regmap); +regmap_err: kfree(tps65910); kfree(init_data); return ret; @@ -189,6 +157,7 @@ static int tps65910_i2c_remove(struct i2c_client *i2c) tps65910_irq_exit(tps65910); mfd_remove_devices(tps65910->dev); + regmap_exit(tps65910->regmap); kfree(tps65910); return 0; -- cgit v1.1 From 378f64ec8ab0593ac5f73b44b5b9cffbe9f17fa3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 29 Feb 2012 15:37:59 +0000 Subject: mfd: Correct readability of WM8994 DC servo 4E register It should be marked as readable but wasn't, breaking DC servo operation. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-regmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index 47743fa..ba383f6 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -806,6 +806,7 @@ static bool wm1811_readable_register(struct device *dev, unsigned int reg) case WM8994_DC_SERVO_2: case WM8994_DC_SERVO_READBACK: case WM8994_DC_SERVO_4: + case WM8994_DC_SERVO_4E: case WM8994_ANALOGUE_HP_1: case WM8958_MIC_DETECT_1: case WM8958_MIC_DETECT_2: -- cgit v1.1 From 7f849a39eed81aef0ba36a7170a6c58056cf625a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 25 Feb 2012 11:36:54 +0800 Subject: mfd: Include linux/mfd/pcf50633/pmic.h in pcf50633-gpio.c Include linux/mfd/pcf50633/pmic.h instead of duplicating the same defines. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/pcf50633-gpio.c | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/pcf50633-gpio.c b/drivers/mfd/pcf50633-gpio.c index 9ab19a8..d02ddf2 100644 --- a/drivers/mfd/pcf50633-gpio.c +++ b/drivers/mfd/pcf50633-gpio.c @@ -19,32 +19,7 @@ #include #include - -enum pcf50633_regulator_id { - PCF50633_REGULATOR_AUTO, - PCF50633_REGULATOR_DOWN1, - PCF50633_REGULATOR_DOWN2, - PCF50633_REGULATOR_LDO1, - PCF50633_REGULATOR_LDO2, - PCF50633_REGULATOR_LDO3, - PCF50633_REGULATOR_LDO4, - PCF50633_REGULATOR_LDO5, - PCF50633_REGULATOR_LDO6, - PCF50633_REGULATOR_HCLDO, - PCF50633_REGULATOR_MEMLDO, -}; - -#define PCF50633_REG_AUTOOUT 0x1a -#define PCF50633_REG_DOWN1OUT 0x1e -#define PCF50633_REG_DOWN2OUT 0x22 -#define PCF50633_REG_MEMLDOOUT 0x26 -#define PCF50633_REG_LDO1OUT 0x2d -#define PCF50633_REG_LDO2OUT 0x2f -#define PCF50633_REG_LDO3OUT 0x31 -#define PCF50633_REG_LDO4OUT 0x33 -#define PCF50633_REG_LDO5OUT 0x35 -#define PCF50633_REG_LDO6OUT 0x37 -#define PCF50633_REG_HCLDOOUT 0x39 +#include static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = { [PCF50633_REGULATOR_AUTO] = PCF50633_REG_AUTOOUT, -- cgit v1.1 From 624e26e0209015718abbd63a402efba02628fec0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 25 Feb 2012 11:37:53 +0800 Subject: mfd: Include linux/mfd/pcf50633/mbc.h in pcf50633-irq.c Include linux/mfd/pcf50633/mbc.h instead of duplicating the same defines. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/pcf50633-irq.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c index 048a3b9..498286c 100644 --- a/drivers/mfd/pcf50633-irq.c +++ b/drivers/mfd/pcf50633-irq.c @@ -19,12 +19,7 @@ #include #include - -/* Two MBCS registers used during cold start */ -#define PCF50633_REG_MBCS1 0x4b -#define PCF50633_REG_MBCS2 0x4c -#define PCF50633_MBCS1_USBPRES 0x01 -#define PCF50633_MBCS1_ADAPTPRES 0x01 +#include int pcf50633_register_irq(struct pcf50633 *pcf, int irq, void (*handler) (int, void *), void *data) -- cgit v1.1 From fe2afaa5412126f7a41aec811228a1f439d232a0 Mon Sep 17 00:00:00 2001 From: Hai Dong Date: Fri, 24 Feb 2012 16:03:00 -0600 Subject: mfd: Fix wm831x-spi table id name typo in MODULE_DEVICE_TABLE macro Signed-off-by: Hai Dong Acked-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 62ef325..8ef7d4f 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -89,7 +89,7 @@ static const struct spi_device_id wm831x_spi_ids[] = { { "wm8326", WM8326 }, { }, }; -MODULE_DEVICE_TABLE(spi, wm831x_spi_id); +MODULE_DEVICE_TABLE(spi, wm831x_spi_ids); static struct spi_driver wm831x_spi_driver = { .driver = { -- cgit v1.1 From 23de435a59b37eda468472ac67179eee5ef10a07 Mon Sep 17 00:00:00 2001 From: "Jett.Zhou" Date: Thu, 1 Mar 2012 11:59:19 +0100 Subject: mfd: Add power control interface for pm8606 chip The reference group and internal oscillator are shared by sub-devs like led, backlight and vibrator in PM8606 chip. Now introduce a voting mechanism to enable/disable it. Add pm8606_osc_enable() and pm8606_osc_disable() interface and related defines to support this. This interface will be called by vibrator led and backlight driver.The refernce group and internal oscillator are enabled only when at least one of it's clients holds it on or disabled only all the clients don't use it any more based on the above mechanism. Signed-off-by: Jett.Zhou Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm860x-core.c | 95 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 17dfe9b..78c3a9e 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -503,6 +503,99 @@ static void device_irq_exit(struct pm860x_chip *chip) free_irq(chip->core_irq, chip); } +int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client) +{ + int ret = -EIO; + struct i2c_client *i2c = (chip->id == CHIP_PM8606) ? + chip->client : chip->companion; + + dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client); + dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n", + __func__, chip->osc_vote, + chip->osc_status); + + mutex_lock(&chip->osc_lock); + /* Update voting status */ + chip->osc_vote |= client; + /* If reference group is off - turn on*/ + if (chip->osc_status != PM8606_REF_GP_OSC_ON) { + chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN; + /* Enable Reference group Vsys */ + if (pm860x_set_bits(i2c, PM8606_VSYS, + PM8606_VSYS_EN, PM8606_VSYS_EN)) + goto out; + + /*Enable Internal Oscillator */ + if (pm860x_set_bits(i2c, PM8606_MISC, + PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN)) + goto out; + /* Update status (only if writes succeed) */ + chip->osc_status = PM8606_REF_GP_OSC_ON; + } + mutex_unlock(&chip->osc_lock); + + dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n", + __func__, chip->osc_vote, + chip->osc_status, ret); + return 0; +out: + mutex_unlock(&chip->osc_lock); + return ret; +} + +int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client) +{ + int ret = -EIO; + struct i2c_client *i2c = (chip->id == CHIP_PM8606) ? + chip->client : chip->companion; + + dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client); + dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n", + __func__, chip->osc_vote, + chip->osc_status); + + mutex_lock(&chip->osc_lock); + /*Update voting status */ + chip->osc_vote &= ~(client); + /* If reference group is off and this is the last client to release + * - turn off */ + if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) && + (chip->osc_vote == REF_GP_NO_CLIENTS)) { + chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN; + /* Disable Reference group Vsys */ + if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0)) + goto out; + /* Disable Internal Oscillator */ + if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0)) + goto out; + chip->osc_status = PM8606_REF_GP_OSC_OFF; + } + mutex_unlock(&chip->osc_lock); + + dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n", + __func__, chip->osc_vote, + chip->osc_status, ret); + return 0; +out: + mutex_unlock(&chip->osc_lock); + return ret; +} + +static void __devinit device_osc_init(struct i2c_client *i2c) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + + mutex_init(&chip->osc_lock); + /* init portofino reference group voting and status */ + /* Disable Reference group Vsys */ + pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0); + /* Disable Internal Oscillator */ + pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0); + + chip->osc_vote = REF_GP_NO_CLIENTS; + chip->osc_status = PM8606_REF_GP_OSC_OFF; +} + static void __devinit device_bk_init(struct pm860x_chip *chip, struct pm860x_platform_data *pdata) { @@ -774,6 +867,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, switch (chip->id) { case CHIP_PM8606: + device_osc_init(chip->client); device_bk_init(chip, pdata); device_led_init(chip, pdata); break; @@ -785,6 +879,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, if (chip->companion) { switch (chip->id) { case CHIP_PM8607: + device_osc_init(chip->companion); device_bk_init(chip, pdata); device_led_init(chip, pdata); break; -- cgit v1.1 From 78258064747a5d4570400955b1ad55d5d13909e4 Mon Sep 17 00:00:00 2001 From: "Jett.Zhou" Date: Tue, 28 Feb 2012 10:58:42 +0800 Subject: mfd: Code cleanup for pm8606 sub-dev initialization Collect all the sub-devices of pm8606 initialization into device_pm8606_init() for code clean up. Signed-off-by: Jett.Zhou Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm860x-core.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 78c3a9e..6ed8de7 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -860,6 +860,15 @@ out: return; } +static void __devinit device_8606_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + device_osc_init(i2c); + device_bk_init(chip, pdata); + device_led_init(chip, pdata); +} + int __devinit pm860x_device_init(struct pm860x_chip *chip, struct pm860x_platform_data *pdata) { @@ -867,9 +876,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, switch (chip->id) { case CHIP_PM8606: - device_osc_init(chip->client); - device_bk_init(chip, pdata); - device_led_init(chip, pdata); + device_8606_init(chip, chip->client, pdata); break; case CHIP_PM8607: device_8607_init(chip, chip->client, pdata); @@ -879,9 +886,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, if (chip->companion) { switch (chip->id) { case CHIP_PM8607: - device_osc_init(chip->companion); - device_bk_init(chip, pdata); - device_led_init(chip, pdata); + device_8606_init(chip, chip->companion, pdata); break; case CHIP_PM8606: device_8607_init(chip, chip->companion, pdata); -- cgit v1.1 From 61a2af301fa145490868fcf36b4bbea22398f760 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 28 Feb 2012 14:23:55 +0800 Subject: mfd: Return proper error if s5m-core regmap_init_i2c fails Also remove a redundant semicolon from return statement. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/s5m-core.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index 578e12e..48949d9 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(s5m_reg_read); int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf) { - return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);; + return regmap_bulk_read(s5m87xx->regmap, reg, buf, count); } EXPORT_SYMBOL_GPL(s5m_bulk_read); @@ -94,8 +94,7 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, { struct s5m_platform_data *pdata = i2c->dev.platform_data; struct s5m87xx_dev *s5m87xx; - int ret = 0; - int error; + int ret; s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev), GFP_KERNEL); @@ -117,9 +116,9 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config); if (IS_ERR(s5m87xx->regmap)) { - error = PTR_ERR(s5m87xx->regmap); + ret = PTR_ERR(s5m87xx->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", - error); + ret); goto err; } -- cgit v1.1 From 801448e0672008acec050fbef3b8a7df7c5edcc1 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 28 Feb 2012 22:46:05 +0100 Subject: mfd : Cleanup duplicate db8500 definitions I missed in my previous patch the A9_MASK_REQ[_MASK] were already defined. Let's remove the duplicate definitions. The PRCMU_GIC_DELAY macro could be removed as it is not really useful here. Signed-off-by: Daniel Lezcano Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index b320cc6..d2244dc 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -821,23 +821,20 @@ u8 db8500_prcmu_get_power_state_result(void) return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS); } -#define PRCMU_A9_MASK_REQ 0x00000328 -#define PRCMU_A9_MASK_REQ_MASK 0x00000001 -#define PRCMU_GIC_DELAY 1 - /* This function decouple the gic from the prcmu */ int db8500_prcmu_gic_decouple(void) { - u32 val = readl(_PRCMU_BASE + PRCMU_A9_MASK_REQ); + u32 val = readl(PRCM_A9_MASK_REQ); /* Set bit 0 register value to 1 */ - writel(val | PRCMU_A9_MASK_REQ_MASK, _PRCMU_BASE + PRCMU_A9_MASK_REQ); + writel(val | PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, + PRCM_A9_MASK_REQ); /* Make sure the register is updated */ - readl(_PRCMU_BASE + PRCMU_A9_MASK_REQ); + readl(PRCM_A9_MASK_REQ); /* Wait a few cycles for the gic mask completion */ - udelay(PRCMU_GIC_DELAY); + udelay(1); return 0; } @@ -845,10 +842,10 @@ int db8500_prcmu_gic_decouple(void) /* This function recouple the gic with the prcmu */ int db8500_prcmu_gic_recouple(void) { - u32 val = readl(_PRCMU_BASE + PRCMU_A9_MASK_REQ); + u32 val = readl(PRCM_A9_MASK_REQ); /* Set bit 0 register value to 0 */ - writel(val & ~PRCMU_A9_MASK_REQ_MASK, _PRCMU_BASE + PRCMU_A9_MASK_REQ); + writel(val & ~PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, PRCM_A9_MASK_REQ); return 0; } -- cgit v1.1 From cc9a0f68d1f8b9bfd9c0c2ada13db64d63f63db3 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 28 Feb 2012 22:46:06 +0100 Subject: mfd : Check if there are pending irq on the db8500 gic This patch introduces a routine to check if there are some irqs pending on the gic. Usually this check is not relevant because it appears racy (an irq can arrive right after this check), but in the ux500 it makes sense because the prcmu decouples the gic from the A9 cores. Signed-off-by: Daniel Lezcano Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index d2244dc..8346a0e 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -850,6 +851,38 @@ int db8500_prcmu_gic_recouple(void) return 0; } +#define PRCMU_GIC_NUMBER_REGS 5 + +/* + * This function checks if there are pending irq on the gic. It only + * makes sense if the gic has been decoupled before with the + * db8500_prcmu_gic_decouple function. Disabling an interrupt only + * disables the forwarding of the interrupt to any CPU interface. It + * does not prevent the interrupt from changing state, for example + * becoming pending, or active and pending if it is already + * active. Hence, we have to check the interrupt is pending *and* is + * active. + */ +bool db8500_prcmu_gic_pending_irq(void) +{ + u32 pr; /* Pending register */ + u32 er; /* Enable register */ + void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE); + int i; + + /* 5 registers. STI & PPI not skipped */ + for (i = 0; i < PRCMU_GIC_NUMBER_REGS; i++) { + + pr = readl_relaxed(dist_base + GIC_DIST_PENDING_SET + i * 4); + er = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); + + if (pr & er) + return true; /* There is a pending interrupt */ + } + + return false; +} + /* This function should only be called while mb0_transfer.lock is held. */ static void config_wakeups(void) { -- cgit v1.1 From 9f60d33e1811e0aa696a3152050d6e3e4c3195aa Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 28 Feb 2012 22:46:07 +0100 Subject: mfd : Copy the db8500 gic setting to the prcmu In the case we go to the retention mode, we decoupled the gic in order to have the A9 core to reach a stable WFI state. But we want the prcmu to wake up the A9 when the gic has a pending irq which is done by copying the gic settings to the to the prcmu. Signed-off-by: Daniel Lezcano Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 8346a0e..97341aa 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -883,6 +883,26 @@ bool db8500_prcmu_gic_pending_irq(void) return false; } +/* + * This function copies the gic SPI settings to the prcmu in order to + * monitor them and abort/finish the retention/off sequence or state. + */ +int db8500_prcmu_copy_gic_settings(void) +{ + u32 er; /* Enable register */ + void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE); + int i; + + /* We skip the STI and PPI */ + for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) { + er = readl_relaxed(dist_base + + GIC_DIST_ENABLE_SET + (i + 1) * 4); + writel(er, PRCM_ARMITMSK31TO0 + i * 4); + } + + return 0; +} + /* This function should only be called while mb0_transfer.lock is held. */ static void config_wakeups(void) { -- cgit v1.1 From 9ab492e12d588af7b05892c3744e8bdc2eace6d0 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 28 Feb 2012 22:46:08 +0100 Subject: mfd : Check if the db8500 prcmu has pending irq This patch allows to check if there are some pending irqs on the prcmu. Signed-off-by: Daniel Lezcano Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 97341aa..4e27db8 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -884,6 +884,26 @@ bool db8500_prcmu_gic_pending_irq(void) } /* + * This function checks if there are pending interrupt on the + * prcmu which has been delegated to monitor the irqs with the + * db8500_prcmu_copy_gic_settings function. + */ +bool db8500_prcmu_pending_irq(void) +{ + u32 it, im; + int i; + + for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) { + it = readl(PRCM_ARMITVAL31TO0 + i * 4); + im = readl(PRCM_ARMITMSK31TO0 + i * 4); + if (it & im) + return true; /* There is a pending interrupt */ + } + + return false; +} + +/* * This function copies the gic SPI settings to the prcmu in order to * monitor them and abort/finish the retention/off sequence or state. */ -- cgit v1.1 From 34fe6f107eab096ac2f70a51763e9978b4abbeb6 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 28 Feb 2012 22:46:09 +0100 Subject: mfd : Check if the other db8500 core is in WFI This patch allows to check if the other core is in WFI mode. It is the last check the idle routine has to do before entering into the retention state. Signed-off-by: Daniel Lezcano Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 12 ++++++++++++ drivers/mfd/dbx500-prcmu-regs.h | 2 ++ 2 files changed, 14 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 4e27db8..a1b3464 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -904,6 +904,18 @@ bool db8500_prcmu_pending_irq(void) } /* + * This function checks if the specified cpu is in in WFI. It's usage + * makes sense only if the gic is decoupled with the db8500_prcmu_gic_decouple + * function. Of course passing smp_processor_id() to this function will + * always return false... + */ +bool db8500_prcmu_is_cpu_in_wfi(int cpu) +{ + return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : + PRCM_ARM_WFI_STANDBY_WFI0; +} + +/* * This function copies the gic SPI settings to the prcmu in order to * monitor them and abort/finish the retention/off sequence or state. */ diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index b9ab4ce..3a0bf91 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -79,6 +79,8 @@ /* ARM WFI Standby signal register */ #define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) +#define PRCM_ARM_WFI_STANDBY_WFI0 0x08 +#define PRCM_ARM_WFI_STANDBY_WFI1 0x10 #define PRCM_IOCR (_PRCMU_BASE + 0x310) #define PRCM_IOCR_IOFORCE 0x1 -- cgit v1.1 From 5a924d13ecdd433fd2c7eb268b72a2770ef2204a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 1 Mar 2012 09:31:25 +0800 Subject: mfd: Fix a typo in MFD_MAX8997 Kconfig entry Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 16178e1..82da448 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -415,7 +415,7 @@ config MFD_MAX8997 depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE help - Say yes here to support for Maxim Semiconductor MAX8998/8966. + Say yes here to support for Maxim Semiconductor MAX8997/8966. This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, MUIC controls on chip. This driver provides common support for accessing the device; -- cgit v1.1 From 2f5f89be6f2216f93d44f2609b182df932a929b4 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 6 Mar 2012 08:06:14 +0100 Subject: mfd: Fix pm8606 build failure Reported by Andrew Morton , with i386 allmodconfig: ERROR: "pm8606_osc_disable" [drivers/video/backlight/88pm860x_bl.ko] undefined! ERROR: "pm8606_osc_enable" [drivers/video/backlight/88pm860x_bl.ko] undefined! ERROR: "pm8606_osc_disable" [drivers/leds/leds-88pm860x.ko] undefined! ERROR: "pm8606_osc_enable" [drivers/leds/leds-88pm860x.ko] undefined! Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm860x-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 6ed8de7..87bd5ba 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -542,6 +542,7 @@ out: mutex_unlock(&chip->osc_lock); return ret; } +EXPORT_SYMBOL(pm8606_osc_enable); int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client) { @@ -580,6 +581,7 @@ out: mutex_unlock(&chip->osc_lock); return ret; } +EXPORT_SYMBOL(pm8606_osc_disable); static void __devinit device_osc_init(struct i2c_client *i2c) { -- cgit v1.1 From 9f9efcd526ccb23813e28762afb7d4512e5e5707 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Mar 2012 23:40:14 +0000 Subject: mfd: wm8994: Remove defaults for volatile registers Some volatile registers accidentally had defaults added, though there were no harmful side effects other than increased memory consumption. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-regmap.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index ba383f6..bd50d80 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -19,7 +19,6 @@ #include "wm8994.h" static struct reg_default wm1811_defaults[] = { - { 0x0000, 0x1811 }, /* R0 - Software Reset */ { 0x0001, 0x0000 }, /* R1 - Power Management (1) */ { 0x0002, 0x6000 }, /* R2 - Power Management (2) */ { 0x0003, 0x0000 }, /* R3 - Power Management (3) */ @@ -68,16 +67,12 @@ static struct reg_default wm1811_defaults[] = { { 0x004C, 0x1F25 }, /* R76 - Charge Pump (1) */ { 0x004D, 0xAB19 }, /* R77 - Charge Pump (2) */ { 0x0051, 0x0004 }, /* R81 - Class W (1) */ - { 0x0054, 0x0000 }, /* R84 - DC Servo (1) */ { 0x0055, 0x054A }, /* R85 - DC Servo (2) */ - { 0x0058, 0x0000 }, /* R88 - DC Servo Readback */ { 0x0059, 0x0000 }, /* R89 - DC Servo (4) */ { 0x0060, 0x0000 }, /* R96 - Analogue HP (1) */ { 0x00C5, 0x0000 }, /* R197 - Class D Test (5) */ { 0x00D0, 0x7600 }, /* R208 - Mic Detect 1 */ { 0x00D1, 0x007F }, /* R209 - Mic Detect 2 */ - { 0x00D2, 0x0000 }, /* R210 - Mic Detect 3 */ - { 0x0100, 0x0100 }, /* R256 - Chip Revision */ { 0x0101, 0x8004 }, /* R257 - Control Interface */ { 0x0200, 0x0000 }, /* R512 - AIF1 Clocking (1) */ { 0x0201, 0x0000 }, /* R513 - AIF1 Clocking (2) */ @@ -87,7 +82,6 @@ static struct reg_default wm1811_defaults[] = { { 0x0209, 0x0000 }, /* R521 - Clocking (2) */ { 0x0210, 0x0083 }, /* R528 - AIF1 Rate */ { 0x0211, 0x0083 }, /* R529 - AIF2 Rate */ - { 0x0212, 0x0000 }, /* R530 - Rate Status */ { 0x0220, 0x0000 }, /* R544 - FLL1 Control (1) */ { 0x0221, 0x0000 }, /* R545 - FLL1 Control (2) */ { 0x0222, 0x0000 }, /* R546 - FLL1 Control (3) */ @@ -217,8 +211,6 @@ static struct reg_default wm1811_defaults[] = { { 0x070A, 0xA101 }, /* R1802 - GPIO 11 */ { 0x0720, 0x0000 }, /* R1824 - Pull Control (1) */ { 0x0721, 0x0156 }, /* R1825 - Pull Control (2) */ - { 0x0730, 0x0000 }, /* R1840 - Interrupt Status 1 */ - { 0x0731, 0x0000 }, /* R1841 - Interrupt Status 2 */ { 0x0732, 0x0000 }, /* R1842 - Interrupt Raw Status 2 */ { 0x0738, 0x07FF }, /* R1848 - Interrupt Status 1 Mask */ { 0x0739, 0xDFEF }, /* R1849 - Interrupt Status 2 Mask */ @@ -227,7 +219,6 @@ static struct reg_default wm1811_defaults[] = { }; static struct reg_default wm8994_defaults[] = { - { 0x0000, 0x8994 }, /* R0 - Software Reset */ { 0x0001, 0x0000 }, /* R1 - Power Management (1) */ { 0x0002, 0x6000 }, /* R2 - Power Management (2) */ { 0x0003, 0x0000 }, /* R3 - Power Management (3) */ @@ -274,12 +265,9 @@ static struct reg_default wm8994_defaults[] = { { 0x003C, 0x0003 }, /* R60 - LDO 2 */ { 0x004C, 0x1F25 }, /* R76 - Charge Pump (1) */ { 0x0051, 0x0004 }, /* R81 - Class W (1) */ - { 0x0054, 0x0000 }, /* R84 - DC Servo (1) */ { 0x0055, 0x054A }, /* R85 - DC Servo (2) */ { 0x0057, 0x0000 }, /* R87 - DC Servo (4) */ - { 0x0058, 0x0000 }, /* R88 - DC Servo Readback */ { 0x0060, 0x0000 }, /* R96 - Analogue HP (1) */ - { 0x0100, 0x0003 }, /* R256 - Chip Revision */ { 0x0101, 0x8004 }, /* R257 - Control Interface */ { 0x0110, 0x0000 }, /* R272 - Write Sequencer Ctrl (1) */ { 0x0111, 0x0000 }, /* R273 - Write Sequencer Ctrl (2) */ @@ -291,7 +279,6 @@ static struct reg_default wm8994_defaults[] = { { 0x0209, 0x0000 }, /* R521 - Clocking (2) */ { 0x0210, 0x0083 }, /* R528 - AIF1 Rate */ { 0x0211, 0x0083 }, /* R529 - AIF2 Rate */ - { 0x0212, 0x0000 }, /* R530 - Rate Status */ { 0x0220, 0x0000 }, /* R544 - FLL1 Control (1) */ { 0x0221, 0x0000 }, /* R545 - FLL1 Control (2) */ { 0x0222, 0x0000 }, /* R546 - FLL1 Control (3) */ @@ -444,9 +431,6 @@ static struct reg_default wm8994_defaults[] = { { 0x070A, 0xA101 }, /* R1802 - GPIO 11 */ { 0x0720, 0x0000 }, /* R1824 - Pull Control (1) */ { 0x0721, 0x0156 }, /* R1825 - Pull Control (2) */ - { 0x0730, 0x0000 }, /* R1840 - Interrupt Status 1 */ - { 0x0731, 0x0000 }, /* R1841 - Interrupt Status 2 */ - { 0x0732, 0x0000 }, /* R1842 - Interrupt Raw Status 2 */ { 0x0738, 0x07FF }, /* R1848 - Interrupt Status 1 Mask */ { 0x0739, 0xFFFF }, /* R1849 - Interrupt Status 2 Mask */ { 0x0740, 0x0000 }, /* R1856 - Interrupt Control */ @@ -454,7 +438,6 @@ static struct reg_default wm8994_defaults[] = { }; static struct reg_default wm8958_defaults[] = { - { 0x0000, 0x8958 }, /* R0 - Software Reset */ { 0x0001, 0x0000 }, /* R1 - Power Management (1) */ { 0x0002, 0x6000 }, /* R2 - Power Management (2) */ { 0x0003, 0x0000 }, /* R3 - Power Management (3) */ -- cgit v1.1 From 618dd15d216915df04fe3baa93123179b1c887c5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Mar 2012 23:42:06 +0000 Subject: mfd: wm8994: Fix register default for WM1811 AntiPOP2 Should be all bits clear. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index bd50d80..f00427d 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -59,7 +59,7 @@ static struct reg_default wm1811_defaults[] = { { 0x0036, 0x0000 }, /* R54 - Speaker Mixer */ { 0x0037, 0x0000 }, /* R55 - Additional Control */ { 0x0038, 0x0000 }, /* R56 - AntiPOP (1) */ - { 0x0039, 0x0180 }, /* R57 - AntiPOP (2) */ + { 0x0039, 0x0000 }, /* R57 - AntiPOP (2) */ { 0x003B, 0x000D }, /* R59 - LDO 1 */ { 0x003C, 0x0003 }, /* R60 - LDO 2 */ { 0x003D, 0x0039 }, /* R61 - MICBIAS1 */ -- cgit v1.1 From 39ecb0376508b5cd20a951388d10aed2d719a77f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 7 Mar 2012 18:46:05 +0530 Subject: mfd: Use correct variable name for tps65910 regmap config This was the copy-paste issue in reg cache support code where variable name for regmap config was not really starting from the device name, it was starting from some other device name. Fixing this so that variable name contains actual device name. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 1c4f53e..bf2b25e 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -81,7 +81,7 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg) return true; } -static const struct regmap_config rc5t583_regmap_config = { +static const struct regmap_config tps65910_regmap_config = { .reg_bits = 8, .val_bits = 8, .volatile_reg = is_volatile_reg, @@ -120,7 +120,7 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, tps65910->write = tps65910_i2c_write; mutex_init(&tps65910->io_mutex); - tps65910->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config); if (IS_ERR(tps65910->regmap)) { ret = PTR_ERR(tps65910->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); -- cgit v1.1 From 392cbd1e608ba79bd2da652eb3a28d841e51eaee Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Thu, 8 Mar 2012 14:01:46 +0100 Subject: mfd: Don't use mutex_lock_interruptible in ab8500-core This disadvantage of having it interruptible is that it would appear to work OK in most situations, but in the rare case that this call does fail, it can lead to misconfiguration of the AB, such as a regulator not being turned off and leading to a platform power consumption increase. There's no real benefit to having this interruptible. Make it a plain mutex_lock. The non-interruptability matches other low-level I/O functions such as SPI and I2C. Signed-off-by: Rabin Vincent Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 15a18fe..f134a6c 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -147,9 +147,7 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank, dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data); - ret = mutex_lock_interruptible(&ab8500->lock); - if (ret) - return ret; + mutex_lock(&ab8500->lock); ret = ab8500->write(ab8500, addr, data); if (ret < 0) @@ -176,9 +174,7 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank, * bank on higher 8 bits and reg in lower */ u16 addr = ((u16)bank) << 8 | reg; - ret = mutex_lock_interruptible(&ab8500->lock); - if (ret) - return ret; + mutex_lock(&ab8500->lock); ret = ab8500->read(ab8500, addr); if (ret < 0) @@ -210,9 +206,7 @@ static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank, * bank on higher 8 bits and reg in lower */ u16 addr = ((u16)bank) << 8 | reg; - ret = mutex_lock_interruptible(&ab8500->lock); - if (ret) - return ret; + mutex_lock(&ab8500->lock); ret = ab8500->read(ab8500, addr); if (ret < 0) { -- cgit v1.1 From a982362c1723464fec0414f6460684844f2638f3 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Thu, 8 Mar 2012 14:01:57 +0100 Subject: mfd: Support for the AB8500 AB8505 variant This builds upon the changes done to support AB9540 so as also to support the AB8505 derivative of the AB8500 circuit. Signed-off-by: Bengt Jonsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index f134a6c..c637c8d 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -360,6 +360,8 @@ static int ab8500_irq_init(struct ab8500 *ab8500) if (is_ab9540(ab8500)) num_irqs = AB9540_NR_IRQS; + else if (is_ab8505(ab8500)) + num_irqs = AB8505_NR_IRQS; else num_irqs = AB8500_NR_IRQS; @@ -386,6 +388,8 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) if (is_ab9540(ab8500)) num_irqs = AB9540_NR_IRQS; + else if (is_ab8505(ab8500)) + num_irqs = AB8505_NR_IRQS; else num_irqs = AB8500_NR_IRQS; @@ -546,12 +550,6 @@ static struct resource __devinitdata ab8500_charger_resources[] = { .flags = IORESOURCE_IRQ, }, { - .name = "USB_CHARGE_DET_DONE", - .start = AB8500_INT_USB_CHG_DET_DONE, - .end = AB8500_INT_USB_CHG_DET_DONE, - .flags = IORESOURCE_IRQ, - }, - { .name = "VBUS_OVV", .start = AB8500_INT_VBUS_OVV, .end = AB8500_INT_VBUS_OVV, @@ -589,14 +587,8 @@ static struct resource __devinitdata ab8500_charger_resources[] = { }, { .name = "USB_CHARGER_NOT_OKR", - .start = AB8500_INT_USB_CHARGER_NOT_OK, - .end = AB8500_INT_USB_CHARGER_NOT_OK, - .flags = IORESOURCE_IRQ, - }, - { - .name = "USB_CHARGER_NOT_OKF", - .start = AB8500_INT_USB_CHARGER_NOT_OKF, - .end = AB8500_INT_USB_CHARGER_NOT_OKF, + .start = AB8500_INT_USB_CHARGER_NOT_OKR, + .end = AB8500_INT_USB_CHARGER_NOT_OKR, .flags = IORESOURCE_IRQ, }, { @@ -671,6 +663,12 @@ static struct resource __devinitdata ab8500_fg_resources[] = { .end = AB8500_INT_CC_INT_CALIB, .flags = IORESOURCE_IRQ, }, + { + .name = "CCEOC", + .start = AB8500_INT_CCEOC, + .end = AB8500_INT_CCEOC, + .flags = IORESOURCE_IRQ, + }, }; static struct resource __devinitdata ab8500_chargalg_resources[] = {}; @@ -685,8 +683,8 @@ static struct resource __devinitdata ab8500_debug_resources[] = { }, { .name = "IRQ_LAST", - .start = AB8500_INT_USB_CHARGER_NOT_OKF, - .end = AB8500_INT_USB_CHARGER_NOT_OKF, + .start = AB8500_INT_XTAL32K_KO, + .end = AB8500_INT_XTAL32K_KO, .flags = IORESOURCE_IRQ, }, }; @@ -1033,7 +1031,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) ab8500->chip_id & 0x0F); /* Configure AB8500 or AB9540 IRQ */ - if (is_ab9540(ab8500)) { + if (is_ab9540(ab8500) || is_ab8505(ab8500)) { ab8500->mask_size = AB9540_NUM_IRQ_REGS; ab8500->irq_reg_offset = ab9540_irq_regoffset; } else { -- cgit v1.1 From 3c3e489831b601e566f6bc47e711f5847fb93dff Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Thu, 8 Mar 2012 14:02:05 +0100 Subject: mfd: Add a prcmu_abb_write_masked routine to db8500-prcmu This patch adds driver support for the I2C read-modify-write service in the U8500 PRCMU firmware. Signed-off-by: Mattias Nilsson Reviewed-by: Jonas ABERG Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index a1b3464..6e5a0a0 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2332,6 +2332,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) cpu_relax(); + writeb(0, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5)); writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP)); writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS)); writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG)); @@ -2357,16 +2358,19 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) } /** - * prcmu_abb_write() - Write register value(s) to the ABB. + * prcmu_abb_write_masked() - Write masked register value(s) to the ABB. * @slave: The I2C slave address. * @reg: The (start) register address. * @value: The value(s) to write. + * @mask: The mask(s) to use. * @size: The number of registers to write. * - * Reads register value(s) from the ABB. + * Writes masked register value(s) to the ABB. + * For each @value, only the bits set to 1 in the corresponding @mask + * will be written. The other bits are not changed. * @size has to be 1 for the current firmware version. */ -int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) +int prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size) { int r; @@ -2378,6 +2382,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) cpu_relax(); + writeb(~*mask, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5)); writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP)); writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS)); writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG)); @@ -2400,6 +2405,23 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) } /** + * prcmu_abb_write() - Write register value(s) to the ABB. + * @slave: The I2C slave address. + * @reg: The (start) register address. + * @value: The value(s) to write. + * @size: The number of registers to write. + * + * Writes register value(s) to the ABB. + * @size has to be 1 for the current firmware version. + */ +int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) +{ + u8 mask = ~0; + + return prcmu_abb_write_masked(slave, reg, value, &mask, size); +} + +/** * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem */ void prcmu_ac_wake_req(void) -- cgit v1.1 From bc628fd19d2d1d053b88fa225bb599be026c048b Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Thu, 8 Mar 2012 14:02:20 +0100 Subject: mfd: Make use of the ab8500 firmware read-modify-write service This patch updates the AB8500 driver to make use of the I2C read-modify-write service in the PRCMU firmware. Signed-off-by: Mattias Nilsson Reviewed-by: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 37 +++++++++++++++++++++++-------------- drivers/mfd/ab8500-i2c.c | 15 ++++++++++++++- 2 files changed, 37 insertions(+), 15 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index c637c8d..1f08704 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -201,29 +201,38 @@ static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { int ret; - u8 data; /* put the u8 bank and u8 reg together into a an u16. * bank on higher 8 bits and reg in lower */ u16 addr = ((u16)bank) << 8 | reg; mutex_lock(&ab8500->lock); - ret = ab8500->read(ab8500, addr); - if (ret < 0) { - dev_err(ab8500->dev, "failed to read reg %#x: %d\n", - addr, ret); - goto out; - } + if (ab8500->write_masked == NULL) { + u8 data; - data = (u8)ret; - data = (~bitmask & data) | (bitmask & bitvalues); + ret = ab8500->read(ab8500, addr); + if (ret < 0) { + dev_err(ab8500->dev, "failed to read reg %#x: %d\n", + addr, ret); + goto out; + } - ret = ab8500->write(ab8500, addr, data); - if (ret < 0) - dev_err(ab8500->dev, "failed to write reg %#x: %d\n", - addr, ret); + data = (u8)ret; + data = (~bitmask & data) | (bitmask & bitvalues); + + ret = ab8500->write(ab8500, addr, data); + if (ret < 0) + dev_err(ab8500->dev, "failed to write reg %#x: %d\n", + addr, ret); - dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, data); + dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, + data); + goto out; + } + ret = ab8500->write_masked(ab8500, addr, bitmask, bitvalues); + if (ret < 0) + dev_err(ab8500->dev, "failed to modify reg %#x: %d\n", addr, + ret); out: mutex_unlock(&ab8500->lock); return ret; diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c index 70a16ae..b83045f 100644 --- a/drivers/mfd/ab8500-i2c.c +++ b/drivers/mfd/ab8500-i2c.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data) { @@ -23,6 +23,18 @@ static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data) return ret; } +static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, + u8 data) +{ + int ret; + + ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data, + &mask, 1); + if (ret < 0) + dev_err(ab8500->dev, "prcmu i2c error %d\n", ret); + return ret; +} + static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr) { int ret; @@ -59,6 +71,7 @@ static int __devinit ab8500_i2c_probe(struct platform_device *plf) ab8500->read = ab8500_i2c_read; ab8500->write = ab8500_i2c_write; + ab8500->write_masked = ab8500_i2c_write_masked; platform_set_drvdata(plf, ab8500); -- cgit v1.1 From 1b1247dd75aa5cf5fae54a3bec7280046e9c7957 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 28 Feb 2012 18:35:17 +0530 Subject: mfd: Add support for RICOH PMIC RC5T583 Ricoh power management IC RC5T583 contains is multi functional device having multiple sub devices inside this. This device has multiple dcdc/ldo regulators, gpios, interrupt controllers, on-key, RTCs, ADCs. This device have 4 DCDCs, 8 LDOs, 8 GPIOs, 6 ADCs, 3 RTCs etc. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/rc5t583-irq.c | 408 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/rc5t583.c | 386 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 809 insertions(+) create mode 100644 drivers/mfd/rc5t583-irq.c create mode 100644 drivers/mfd/rc5t583.c (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 82da448..0f59396 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -846,6 +846,20 @@ config MFD_INTEL_MSIC Passage) chip. This chip embeds audio, battery, GPIO, etc. devices used in Intel Medfield platforms. +config MFD_RC5T583 + bool "Ricoh RC5T583 Power Management system device" + depends on I2C && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + help + Select this option to get support for the RICOH583 Power + Management system device. + This driver provides common support for accessing the device + through i2c interface. The device supports multiple sub-devices + like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey. + Additional drivers must be enabled in order to use the + different functionality of the device. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 27430d3..ea0bb80 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -112,4 +112,5 @@ 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 +obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c new file mode 100644 index 0000000..fa6f80f --- /dev/null +++ b/drivers/mfd/rc5t583-irq.c @@ -0,0 +1,408 @@ +/* + * Interrupt driver for RICOH583 power management chip. + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan + * + * based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + * + */ +#include +#include +#include +#include +#include + +enum int_type { + SYS_INT = 0x1, + DCDC_INT = 0x2, + RTC_INT = 0x4, + ADC_INT = 0x8, + GPIO_INT = 0x10, +}; + +static int gpedge_add[] = { + RC5T583_GPIO_GPEDGE2, + RC5T583_GPIO_GPEDGE2 +}; + +static int irq_en_add[] = { + RC5T583_INT_EN_SYS1, + RC5T583_INT_EN_SYS2, + RC5T583_INT_EN_DCDC, + RC5T583_INT_EN_RTC, + RC5T583_INT_EN_ADC1, + RC5T583_INT_EN_ADC2, + RC5T583_INT_EN_ADC3, + RC5T583_GPIO_EN_INT +}; + +static int irq_mon_add[] = { + RC5T583_INT_MON_SYS1, + RC5T583_INT_MON_SYS2, + RC5T583_INT_MON_DCDC, + RC5T583_INT_MON_RTC, + RC5T583_INT_IR_ADCL, + RC5T583_INT_IR_ADCH, + RC5T583_INT_IR_ADCEND, + RC5T583_INT_IR_GPIOF, + RC5T583_INT_IR_GPIOR +}; + +static int irq_clr_add[] = { + RC5T583_INT_IR_SYS1, + RC5T583_INT_IR_SYS2, + RC5T583_INT_IR_DCDC, + RC5T583_INT_IR_RTC, + RC5T583_INT_IR_ADCL, + RC5T583_INT_IR_ADCH, + RC5T583_INT_IR_ADCEND, + RC5T583_INT_IR_GPIOF, + RC5T583_INT_IR_GPIOR +}; + +static int main_int_type[] = { + SYS_INT, + SYS_INT, + DCDC_INT, + RTC_INT, + ADC_INT, + ADC_INT, + ADC_INT, + GPIO_INT, + GPIO_INT, +}; + +struct rc5t583_irq_data { + u8 int_type; + u8 master_bit; + u8 int_en_bit; + u8 mask_reg_index; + int grp_index; +}; + +#define RC5T583_IRQ(_int_type, _master_bit, _grp_index, \ + _int_bit, _mask_ind) \ + { \ + .int_type = _int_type, \ + .master_bit = _master_bit, \ + .grp_index = _grp_index, \ + .int_en_bit = _int_bit, \ + .mask_reg_index = _mask_ind, \ + } + +static const struct rc5t583_irq_data rc5t583_irqs[RC5T583_MAX_IRQS] = { + [RC5T583_IRQ_ONKEY] = RC5T583_IRQ(SYS_INT, 0, 0, 0, 0), + [RC5T583_IRQ_ACOK] = RC5T583_IRQ(SYS_INT, 0, 1, 1, 0), + [RC5T583_IRQ_LIDOPEN] = RC5T583_IRQ(SYS_INT, 0, 2, 2, 0), + [RC5T583_IRQ_PREOT] = RC5T583_IRQ(SYS_INT, 0, 3, 3, 0), + [RC5T583_IRQ_CLKSTP] = RC5T583_IRQ(SYS_INT, 0, 4, 4, 0), + [RC5T583_IRQ_ONKEY_OFF] = RC5T583_IRQ(SYS_INT, 0, 5, 5, 0), + [RC5T583_IRQ_WD] = RC5T583_IRQ(SYS_INT, 0, 7, 7, 0), + [RC5T583_IRQ_EN_PWRREQ1] = RC5T583_IRQ(SYS_INT, 0, 8, 0, 1), + [RC5T583_IRQ_EN_PWRREQ2] = RC5T583_IRQ(SYS_INT, 0, 9, 1, 1), + [RC5T583_IRQ_PRE_VINDET] = RC5T583_IRQ(SYS_INT, 0, 10, 2, 1), + + [RC5T583_IRQ_DC0LIM] = RC5T583_IRQ(DCDC_INT, 1, 0, 0, 2), + [RC5T583_IRQ_DC1LIM] = RC5T583_IRQ(DCDC_INT, 1, 1, 1, 2), + [RC5T583_IRQ_DC2LIM] = RC5T583_IRQ(DCDC_INT, 1, 2, 2, 2), + [RC5T583_IRQ_DC3LIM] = RC5T583_IRQ(DCDC_INT, 1, 3, 3, 2), + + [RC5T583_IRQ_CTC] = RC5T583_IRQ(RTC_INT, 2, 0, 0, 3), + [RC5T583_IRQ_YALE] = RC5T583_IRQ(RTC_INT, 2, 5, 5, 3), + [RC5T583_IRQ_DALE] = RC5T583_IRQ(RTC_INT, 2, 6, 6, 3), + [RC5T583_IRQ_WALE] = RC5T583_IRQ(RTC_INT, 2, 7, 7, 3), + + [RC5T583_IRQ_AIN1L] = RC5T583_IRQ(ADC_INT, 3, 0, 0, 4), + [RC5T583_IRQ_AIN2L] = RC5T583_IRQ(ADC_INT, 3, 1, 1, 4), + [RC5T583_IRQ_AIN3L] = RC5T583_IRQ(ADC_INT, 3, 2, 2, 4), + [RC5T583_IRQ_VBATL] = RC5T583_IRQ(ADC_INT, 3, 3, 3, 4), + [RC5T583_IRQ_VIN3L] = RC5T583_IRQ(ADC_INT, 3, 4, 4, 4), + [RC5T583_IRQ_VIN8L] = RC5T583_IRQ(ADC_INT, 3, 5, 5, 4), + [RC5T583_IRQ_AIN1H] = RC5T583_IRQ(ADC_INT, 3, 6, 0, 5), + [RC5T583_IRQ_AIN2H] = RC5T583_IRQ(ADC_INT, 3, 7, 1, 5), + [RC5T583_IRQ_AIN3H] = RC5T583_IRQ(ADC_INT, 3, 8, 2, 5), + [RC5T583_IRQ_VBATH] = RC5T583_IRQ(ADC_INT, 3, 9, 3, 5), + [RC5T583_IRQ_VIN3H] = RC5T583_IRQ(ADC_INT, 3, 10, 4, 5), + [RC5T583_IRQ_VIN8H] = RC5T583_IRQ(ADC_INT, 3, 11, 5, 5), + [RC5T583_IRQ_ADCEND] = RC5T583_IRQ(ADC_INT, 3, 12, 0, 6), + + [RC5T583_IRQ_GPIO0] = RC5T583_IRQ(GPIO_INT, 4, 0, 0, 7), + [RC5T583_IRQ_GPIO1] = RC5T583_IRQ(GPIO_INT, 4, 1, 1, 7), + [RC5T583_IRQ_GPIO2] = RC5T583_IRQ(GPIO_INT, 4, 2, 2, 7), + [RC5T583_IRQ_GPIO3] = RC5T583_IRQ(GPIO_INT, 4, 3, 3, 7), + [RC5T583_IRQ_GPIO4] = RC5T583_IRQ(GPIO_INT, 4, 4, 4, 7), + [RC5T583_IRQ_GPIO5] = RC5T583_IRQ(GPIO_INT, 4, 5, 5, 7), + [RC5T583_IRQ_GPIO6] = RC5T583_IRQ(GPIO_INT, 4, 6, 6, 7), + [RC5T583_IRQ_GPIO7] = RC5T583_IRQ(GPIO_INT, 4, 7, 7, 7), +}; + +static void rc5t583_irq_lock(struct irq_data *irq_data) +{ + struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); + mutex_lock(&rc5t583->irq_lock); +} + +static void rc5t583_irq_unmask(struct irq_data *irq_data) +{ + struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - rc5t583->irq_base; + const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq]; + + rc5t583->group_irq_en[data->grp_index] |= 1 << data->grp_index; + rc5t583->intc_inten_reg |= 1 << data->master_bit; + rc5t583->irq_en_reg[data->mask_reg_index] |= 1 << data->int_en_bit; +} + +static void rc5t583_irq_mask(struct irq_data *irq_data) +{ + struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - rc5t583->irq_base; + const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq]; + + rc5t583->group_irq_en[data->grp_index] &= ~(1 << data->grp_index); + if (!rc5t583->group_irq_en[data->grp_index]) + rc5t583->intc_inten_reg &= ~(1 << data->master_bit); + + rc5t583->irq_en_reg[data->mask_reg_index] &= ~(1 << data->int_en_bit); +} + +static int rc5t583_irq_set_type(struct irq_data *irq_data, unsigned int type) +{ + struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - rc5t583->irq_base; + const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq]; + int val = 0; + int gpedge_index; + int gpedge_bit_pos; + + /* Supporting only trigger level inetrrupt */ + if ((data->int_type & GPIO_INT) && (type & IRQ_TYPE_EDGE_BOTH)) { + gpedge_index = data->int_en_bit / 4; + gpedge_bit_pos = data->int_en_bit % 4; + + if (type & IRQ_TYPE_EDGE_FALLING) + val |= 0x2; + + if (type & IRQ_TYPE_EDGE_RISING) + val |= 0x1; + + rc5t583->gpedge_reg[gpedge_index] &= ~(3 << gpedge_bit_pos); + rc5t583->gpedge_reg[gpedge_index] |= (val << gpedge_bit_pos); + rc5t583_irq_unmask(irq_data); + return 0; + } + return -EINVAL; +} + +static void rc5t583_irq_sync_unlock(struct irq_data *irq_data) +{ + struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); + int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(rc5t583->gpedge_reg); i++) { + ret = rc5t583_write(rc5t583->dev, gpedge_add[i], + rc5t583->gpedge_reg[i]); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in writing reg 0x%02x error: %d\n", + gpedge_add[i], ret); + } + + for (i = 0; i < ARRAY_SIZE(rc5t583->irq_en_reg); i++) { + ret = rc5t583_write(rc5t583->dev, irq_en_add[i], + rc5t583->irq_en_reg[i]); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in writing reg 0x%02x error: %d\n", + irq_en_add[i], ret); + } + + ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN, + rc5t583->intc_inten_reg); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in writing reg 0x%02x error: %d\n", + RC5T583_INTC_INTEN, ret); + + mutex_unlock(&rc5t583->irq_lock); +} +#ifdef CONFIG_PM_SLEEP +static int rc5t583_irq_set_wake(struct irq_data *irq_data, unsigned int on) +{ + struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); + return irq_set_irq_wake(rc5t583->chip_irq, on); +} +#else +#define rc5t583_irq_set_wake NULL +#endif + +static irqreturn_t rc5t583_irq(int irq, void *data) +{ + struct rc5t583 *rc5t583 = data; + uint8_t int_sts[RC5T583_MAX_INTERRUPT_MASK_REGS]; + uint8_t master_int; + int i; + int ret; + unsigned int rtc_int_sts = 0; + + /* Clear the status */ + for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) + int_sts[i] = 0; + + ret = rc5t583_read(rc5t583->dev, RC5T583_INTC_INTMON, &master_int); + if (ret < 0) { + dev_err(rc5t583->dev, + "Error in reading reg 0x%02x error: %d\n", + RC5T583_INTC_INTMON, ret); + return IRQ_HANDLED; + } + + for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; ++i) { + if (!(master_int & main_int_type[i])) + continue; + + ret = rc5t583_read(rc5t583->dev, irq_mon_add[i], &int_sts[i]); + if (ret < 0) { + dev_warn(rc5t583->dev, + "Error in reading reg 0x%02x error: %d\n", + irq_mon_add[i], ret); + int_sts[i] = 0; + continue; + } + + if (main_int_type[i] & RTC_INT) { + rtc_int_sts = 0; + if (int_sts[i] & 0x1) + rtc_int_sts |= BIT(6); + if (int_sts[i] & 0x2) + rtc_int_sts |= BIT(7); + if (int_sts[i] & 0x4) + rtc_int_sts |= BIT(0); + if (int_sts[i] & 0x8) + rtc_int_sts |= BIT(5); + } + + ret = rc5t583_write(rc5t583->dev, irq_clr_add[i], + ~int_sts[i]); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in reading reg 0x%02x error: %d\n", + irq_clr_add[i], ret); + + if (main_int_type[i] & RTC_INT) + int_sts[i] = rtc_int_sts; + } + + /* Merge gpio interrupts for rising and falling case*/ + int_sts[7] |= int_sts[8]; + + /* Call interrupt handler if enabled */ + for (i = 0; i < RC5T583_MAX_IRQS; ++i) { + const struct rc5t583_irq_data *data = &rc5t583_irqs[i]; + if ((int_sts[data->mask_reg_index] & (1 << data->int_en_bit)) && + (rc5t583->group_irq_en[data->master_bit] & + (1 << data->grp_index))) + handle_nested_irq(rc5t583->irq_base + i); + } + + return IRQ_HANDLED; +} + +static struct irq_chip rc5t583_irq_chip = { + .name = "rc5t583-irq", + .irq_mask = rc5t583_irq_mask, + .irq_unmask = rc5t583_irq_unmask, + .irq_bus_lock = rc5t583_irq_lock, + .irq_bus_sync_unlock = rc5t583_irq_sync_unlock, + .irq_set_type = rc5t583_irq_set_type, + .irq_set_wake = rc5t583_irq_set_wake, +}; + +int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base) +{ + int i, ret; + + if (!irq_base) { + dev_warn(rc5t583->dev, "No interrupt support on IRQ base\n"); + return -EINVAL; + } + + mutex_init(&rc5t583->irq_lock); + + /* Initailize all int register to 0 */ + for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) { + ret = rc5t583_write(rc5t583->dev, irq_en_add[i], + rc5t583->irq_en_reg[i]); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in writing reg 0x%02x error: %d\n", + irq_en_add[i], ret); + } + + for (i = 0; i < RC5T583_MAX_GPEDGE_REG; i++) { + ret = rc5t583_write(rc5t583->dev, gpedge_add[i], + rc5t583->gpedge_reg[i]); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in writing reg 0x%02x error: %d\n", + gpedge_add[i], ret); + } + + ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN, 0x0); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in writing reg 0x%02x error: %d\n", + RC5T583_INTC_INTEN, ret); + + /* Clear all interrupts in case they woke up active. */ + for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) { + ret = rc5t583_write(rc5t583->dev, irq_clr_add[i], 0); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in writing reg 0x%02x error: %d\n", + irq_clr_add[i], ret); + } + + rc5t583->irq_base = irq_base; + rc5t583->chip_irq = irq; + + for (i = 0; i < RC5T583_MAX_IRQS; i++) { + int __irq = i + rc5t583->irq_base; + irq_set_chip_data(__irq, rc5t583); + irq_set_chip_and_handler(__irq, &rc5t583_irq_chip, + handle_simple_irq); + irq_set_nested_thread(__irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(__irq, IRQF_VALID); +#endif + } + + ret = request_threaded_irq(irq, NULL, rc5t583_irq, IRQF_ONESHOT, + "rc5t583", rc5t583); + if (ret < 0) + dev_err(rc5t583->dev, + "Error in registering interrupt error: %d\n", ret); + return ret; +} + +int rc5t583_irq_exit(struct rc5t583 *rc5t583) +{ + if (rc5t583->chip_irq) + free_irq(rc5t583->chip_irq, rc5t583); + return 0; +} diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c new file mode 100644 index 0000000..99ef944 --- /dev/null +++ b/drivers/mfd/rc5t583.c @@ -0,0 +1,386 @@ +/* + * Core driver access RC5T583 power management chip. + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan + * + * Based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RICOH_ONOFFSEL_REG 0x10 +#define RICOH_SWCTL_REG 0x5E + +struct deepsleep_control_data { + u8 reg_add; + u8 ds_pos_bit; +}; + +#define DEEPSLEEP_INIT(_id, _reg, _pos) \ + { \ + .reg_add = RC5T583_##_reg, \ + .ds_pos_bit = _pos, \ + } + +static struct deepsleep_control_data deepsleep_data[] = { + DEEPSLEEP_INIT(DC0, SLPSEQ1, 0), + DEEPSLEEP_INIT(DC1, SLPSEQ1, 4), + DEEPSLEEP_INIT(DC2, SLPSEQ2, 0), + DEEPSLEEP_INIT(DC3, SLPSEQ2, 4), + DEEPSLEEP_INIT(LDO0, SLPSEQ3, 0), + DEEPSLEEP_INIT(LDO1, SLPSEQ3, 4), + DEEPSLEEP_INIT(LDO2, SLPSEQ4, 0), + DEEPSLEEP_INIT(LDO3, SLPSEQ4, 4), + DEEPSLEEP_INIT(LDO4, SLPSEQ5, 0), + DEEPSLEEP_INIT(LDO5, SLPSEQ5, 4), + DEEPSLEEP_INIT(LDO6, SLPSEQ6, 0), + DEEPSLEEP_INIT(LDO7, SLPSEQ6, 4), + DEEPSLEEP_INIT(LDO8, SLPSEQ7, 0), + DEEPSLEEP_INIT(LDO9, SLPSEQ7, 4), + DEEPSLEEP_INIT(PSO0, SLPSEQ8, 0), + DEEPSLEEP_INIT(PSO1, SLPSEQ8, 4), + DEEPSLEEP_INIT(PSO2, SLPSEQ9, 0), + DEEPSLEEP_INIT(PSO3, SLPSEQ9, 4), + DEEPSLEEP_INIT(PSO4, SLPSEQ10, 0), + DEEPSLEEP_INIT(PSO5, SLPSEQ10, 4), + DEEPSLEEP_INIT(PSO6, SLPSEQ11, 0), + DEEPSLEEP_INIT(PSO7, SLPSEQ11, 4), +}; + +#define EXT_PWR_REQ \ + (RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL) + +static struct mfd_cell rc5t583_subdevs[] = { + {.name = "rc5t583-regulator",}, + {.name = "rc5t583-rtc", }, + {.name = "rc5t583-key", } +}; + +int rc5t583_write(struct device *dev, uint8_t reg, uint8_t val) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + return regmap_write(rc5t583->regmap, reg, val); +} + +int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + unsigned int ival; + int ret; + ret = regmap_read(rc5t583->regmap, reg, &ival); + if (!ret) + *val = (uint8_t)ival; + return ret; +} + +int rc5t583_set_bits(struct device *dev, unsigned int reg, + unsigned int bit_mask) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + return regmap_update_bits(rc5t583->regmap, reg, bit_mask, bit_mask); +} + +int rc5t583_clear_bits(struct device *dev, unsigned int reg, + unsigned int bit_mask) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + return regmap_update_bits(rc5t583->regmap, reg, bit_mask, 0); +} + +int rc5t583_update(struct device *dev, unsigned int reg, + unsigned int val, unsigned int mask) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev); + return regmap_update_bits(rc5t583->regmap, reg, mask, val); +} + +static int __rc5t583_set_ext_pwrreq1_control(struct device *dev, + int id, int ext_pwr, int slots) +{ + int ret; + uint8_t sleepseq_val; + unsigned int en_bit; + unsigned int slot_bit; + + if (id == RC5T583_DS_DC0) { + dev_err(dev, "PWRREQ1 is invalid control for rail %d\n", id); + return -EINVAL; + } + + en_bit = deepsleep_data[id].ds_pos_bit; + slot_bit = en_bit + 1; + ret = rc5t583_read(dev, deepsleep_data[id].reg_add, &sleepseq_val); + if (ret < 0) { + dev_err(dev, "Error in reading reg 0x%x\n", + deepsleep_data[id].reg_add); + return ret; + } + + sleepseq_val &= ~(0xF << en_bit); + sleepseq_val |= BIT(en_bit); + sleepseq_val |= ((slots & 0x7) << slot_bit); + ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(1)); + if (ret < 0) { + dev_err(dev, "Error in updating the 0x%02x register\n", + RICOH_ONOFFSEL_REG); + return ret; + } + + ret = rc5t583_write(dev, deepsleep_data[id].reg_add, sleepseq_val); + if (ret < 0) { + dev_err(dev, "Error in writing reg 0x%x\n", + deepsleep_data[id].reg_add); + return ret; + } + + if (id == RC5T583_DS_LDO4) { + ret = rc5t583_write(dev, RICOH_SWCTL_REG, 0x1); + if (ret < 0) + dev_err(dev, "Error in writing reg 0x%x\n", + RICOH_SWCTL_REG); + } + return ret; +} + +static int __rc5t583_set_ext_pwrreq2_control(struct device *dev, + int id, int ext_pwr) +{ + int ret; + + if (id != RC5T583_DS_DC0) { + dev_err(dev, "PWRREQ2 is invalid control for rail %d\n", id); + return -EINVAL; + } + + ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(2)); + if (ret < 0) + dev_err(dev, "Error in updating the ONOFFSEL 0x10 register\n"); + return ret; +} + +int rc5t583_ext_power_req_config(struct device *dev, int ds_id, + int ext_pwr_req, int deepsleep_slot_nr) +{ + if ((ext_pwr_req & EXT_PWR_REQ) == EXT_PWR_REQ) + return -EINVAL; + + if (ext_pwr_req & RC5T583_EXT_PWRREQ1_CONTROL) + return __rc5t583_set_ext_pwrreq1_control(dev, ds_id, + ext_pwr_req, deepsleep_slot_nr); + + if (ext_pwr_req & RC5T583_EXT_PWRREQ2_CONTROL) + return __rc5t583_set_ext_pwrreq2_control(dev, + ds_id, ext_pwr_req); + return 0; +} + +static int rc5t583_clear_ext_power_req(struct rc5t583 *rc5t583, + struct rc5t583_platform_data *pdata) +{ + int ret; + int i; + uint8_t on_off_val = 0; + + /* Clear ONOFFSEL register */ + if (pdata->enable_shutdown) + on_off_val = 0x1; + + ret = rc5t583_write(rc5t583->dev, RICOH_ONOFFSEL_REG, on_off_val); + if (ret < 0) + dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n", + RICOH_ONOFFSEL_REG, ret); + + ret = rc5t583_write(rc5t583->dev, RICOH_SWCTL_REG, 0x0); + if (ret < 0) + dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n", + RICOH_SWCTL_REG, ret); + + /* Clear sleep sequence register */ + for (i = RC5T583_SLPSEQ1; i <= RC5T583_SLPSEQ11; ++i) { + ret = rc5t583_write(rc5t583->dev, i, 0x0); + if (ret < 0) + dev_warn(rc5t583->dev, + "Error in writing reg 0x%02x error: %d\n", + i, ret); + } + return 0; +} + +static bool volatile_reg(struct device *dev, unsigned int reg) +{ + /* Enable caching in interrupt registers */ + switch (reg) { + case RC5T583_INT_EN_SYS1: + case RC5T583_INT_EN_SYS2: + case RC5T583_INT_EN_DCDC: + case RC5T583_INT_EN_RTC: + case RC5T583_INT_EN_ADC1: + case RC5T583_INT_EN_ADC2: + case RC5T583_INT_EN_ADC3: + case RC5T583_GPIO_GPEDGE1: + case RC5T583_GPIO_GPEDGE2: + case RC5T583_GPIO_EN_INT: + return false; + + case RC5T583_GPIO_MON_IOIN: + /* This is gpio input register */ + return true; + + default: + /* Enable caching in gpio registers */ + if ((reg >= RC5T583_GPIO_IOSEL) && + (reg <= RC5T583_GPIO_GPOFUNC)) + return false; + + /* Enable caching in sleep seq registers */ + if ((reg >= RC5T583_SLPSEQ1) && (reg <= RC5T583_SLPSEQ11)) + return false; + + /* Enable caching of regulator registers */ + if ((reg >= RC5T583_REG_DC0CTL) && (reg <= RC5T583_REG_SR3CTL)) + return false; + if ((reg >= RC5T583_REG_LDOEN1) && + (reg <= RC5T583_REG_LDO9DAC_DS)) + return false; + + break; + } + + return true; +} + +static const struct regmap_config rc5t583_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = volatile_reg, + .max_register = RC5T583_MAX_REGS, + .num_reg_defaults_raw = RC5T583_MAX_REGS, + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rc5t583 *rc5t583; + struct rc5t583_platform_data *pdata = i2c->dev.platform_data; + int ret; + bool irq_init_success = false; + + if (!pdata) { + dev_err(&i2c->dev, "Err: Platform data not found\n"); + return -EINVAL; + } + + rc5t583 = devm_kzalloc(&i2c->dev, sizeof(struct rc5t583), GFP_KERNEL); + if (!rc5t583) { + dev_err(&i2c->dev, "Memory allocation failed\n"); + return -ENOMEM; + } + + rc5t583->dev = &i2c->dev; + i2c_set_clientdata(i2c, rc5t583); + + rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + if (IS_ERR(rc5t583->regmap)) { + ret = PTR_ERR(rc5t583->regmap); + dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); + return ret; + } + + ret = rc5t583_clear_ext_power_req(rc5t583, pdata); + if (ret < 0) + goto err_irq_init; + + if (i2c->irq) { + ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base); + /* Still continue with waring if irq init fails */ + if (ret) + dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret); + else + irq_init_success = true; + } + + ret = mfd_add_devices(rc5t583->dev, -1, rc5t583_subdevs, + ARRAY_SIZE(rc5t583_subdevs), NULL, 0); + if (ret) { + dev_err(&i2c->dev, "add mfd devices failed: %d\n", ret); + goto err_add_devs; + } + + return 0; + +err_add_devs: + if (irq_init_success) + rc5t583_irq_exit(rc5t583); +err_irq_init: + regmap_exit(rc5t583->regmap); + return ret; +} + +static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c) +{ + struct rc5t583 *rc5t583 = i2c_get_clientdata(i2c); + + mfd_remove_devices(rc5t583->dev); + rc5t583_irq_exit(rc5t583); + regmap_exit(rc5t583->regmap); + return 0; +} + +static const struct i2c_device_id rc5t583_i2c_id[] = { + {.name = "rc5t583", .driver_data = 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, rc5t583_i2c_id); + +static struct i2c_driver rc5t583_i2c_driver = { + .driver = { + .name = "rc5t583", + .owner = THIS_MODULE, + }, + .probe = rc5t583_i2c_probe, + .remove = __devexit_p(rc5t583_i2c_remove), + .id_table = rc5t583_i2c_id, +}; + +static int __init rc5t583_i2c_init(void) +{ + return i2c_add_driver(&rc5t583_i2c_driver); +} +subsys_initcall(rc5t583_i2c_init); + +static void __exit rc5t583_i2c_exit(void) +{ + i2c_del_driver(&rc5t583_i2c_driver); +} + +module_exit(rc5t583_i2c_exit); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("RICOH RC5T583 power management system device driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 829ecbcb14edea378a1409203ef0d4a68bbf795a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 12 Mar 2012 10:12:34 +0800 Subject: mfd: Use DIV_ROUND_CLOSEST for sm501 clock Use DIV_ROUND_CLOSEST to replace sm501fb_round_div function. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/sm501.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index f4d8611..d927dd4 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -387,14 +387,6 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) EXPORT_SYMBOL_GPL(sm501_unit_power); - -/* Perform a rounded division. */ -static long sm501fb_round_div(long num, long denom) -{ - /* n / d + 1 / 2 = (2n + d) / 2d */ - return (2 * num + denom) / (2 * denom); -} - /* clock value structure. */ struct sm501_clock { unsigned long mclk; @@ -428,7 +420,7 @@ static int sm501_calc_clock(unsigned long freq, /* try all 8 shift values.*/ for (shift = 0; shift < 8; shift++) { /* Calculate difference to requested clock */ - diff = sm501fb_round_div(mclk, divider << shift) - freq; + diff = DIV_ROUND_CLOSEST(mclk, divider << shift) - freq; if (diff < 0) diff = -diff; -- cgit v1.1 From d902d0d18c50fe195c66e60c615cfa0b81169454 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Thu, 15 Mar 2012 19:50:26 +0100 Subject: mfd: Remove obsolete hwacc implementation for db8500-prmcu This patch removes the obsolete hwacc implementation in the DB8500 PRCMU driver. Signed-off-by: Mattias Nilsson Reviewed-by: Jonas Aberg Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 155 --------------------------------------------- 1 file changed, 155 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 6e5a0a0..09fd6a3 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -516,35 +516,6 @@ static struct dsiescclk dsiescclk[3] = { } }; -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 */ @@ -1295,132 +1266,6 @@ static int request_pll(u8 clock, bool enable) } /** - * 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 * @epod_state: The new EPOD state -- cgit v1.1 From 5f96a1a6d5d82f79015e5e480e4ac8772607f69b Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Thu, 15 Mar 2012 19:50:40 +0100 Subject: mfd: Add 8520 PRCMU variant to db8500-prcmu Signed-off-by: Bengt Jonsson Reviewed-by: Mattias Nilssson Reviewed-by: Jonas Aberg Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 09fd6a3..06a3c32 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2634,6 +2634,8 @@ static char *fw_project_name(u8 project) return "U9500"; case PRCMU_FW_PROJECT_U9500_C2: return "U9500 C2"; + case PRCMU_FW_PROJECT_U8520: + return "U8520"; default: return "Unknown"; } -- cgit v1.1 From 1927ddf66805fca2af010c3e9d0b29216aed0fae Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Thu, 15 Mar 2012 19:50:51 +0100 Subject: mfd: Add 8420 variant to db8500-prcmu Signed-off-by: Bengt Jonsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 06a3c32..ebc1e86 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2636,6 +2636,8 @@ static char *fw_project_name(u8 project) return "U9500 C2"; case PRCMU_FW_PROJECT_U8520: return "U8520"; + case PRCMU_FW_PROJECT_U8420: + return "U8420"; default: return "Unknown"; } -- cgit v1.1 From 75060a1d9dc016fb25524e65afba7ec86778084f Mon Sep 17 00:00:00 2001 From: "Ying-Chun Liu (PaulLiu)" Date: Fri, 16 Mar 2012 21:12:32 +0100 Subject: mfd: Add anatop mfd driver Signed-off-by: Ying-Chun Liu (PaulLiu) Acked-by: Shawn Guo Reviewed-by: Arnd Bergmann Reviewed-by: Mark Brown Cc: Venu Byravarasu Cc: Peter Korsgaard Cc: Rob Lee Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 8 +++ drivers/mfd/Makefile | 1 + drivers/mfd/anatop-mfd.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 drivers/mfd/anatop-mfd.c (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0f59396..a5f4176 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -860,6 +860,14 @@ config MFD_RC5T583 Additional drivers must be enabled in order to use the different functionality of the device. +config MFD_ANATOP + bool "Support for Freescale i.MX on-chip ANATOP controller" + depends on SOC_IMX6Q + help + Select this option to enable Freescale i.MX on-chip ANATOP + MFD controller. This controller embeds regulator and + thermal devices for Freescale i.MX platforms. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index ea0bb80..67f7b0e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -114,3 +114,4 @@ obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o +obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o diff --git a/drivers/mfd/anatop-mfd.c b/drivers/mfd/anatop-mfd.c new file mode 100644 index 0000000..2af4248 --- /dev/null +++ b/drivers/mfd/anatop-mfd.c @@ -0,0 +1,137 @@ +/* + * Anatop MFD driver + * + * Copyright (C) 2012 Ying-Chun Liu (PaulLiu) + * Copyright (C) 2012 Linaro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +u32 anatop_get_bits(struct anatop *adata, u32 addr, int bit_shift, + int bit_width) +{ + u32 val, mask; + + if (bit_width == 32) + mask = ~0; + else + mask = (1 << bit_width) - 1; + + val = readl(adata->ioreg + addr); + val = (val >> bit_shift) & mask; + + return val; +} +EXPORT_SYMBOL_GPL(anatop_get_bits); + +void anatop_set_bits(struct anatop *adata, u32 addr, int bit_shift, + int bit_width, u32 data) +{ + u32 val, mask; + + if (bit_width == 32) + mask = ~0; + else + mask = (1 << bit_width) - 1; + + spin_lock(&adata->reglock); + val = readl(adata->ioreg + addr) & ~(mask << bit_shift); + writel((data << bit_shift) | val, adata->ioreg + addr); + spin_unlock(&adata->reglock); +} +EXPORT_SYMBOL_GPL(anatop_set_bits); + +static const struct of_device_id of_anatop_match[] = { + { .compatible = "fsl,imx6q-anatop", }, + { }, +}; + +static int __devinit of_anatop_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void *ioreg; + struct anatop *drvdata; + + ioreg = of_iomap(np, 0); + if (!ioreg) + return -EADDRNOTAVAIL; + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + drvdata->ioreg = ioreg; + spin_lock_init(&drvdata->reglock); + platform_set_drvdata(pdev, drvdata); + of_platform_populate(np, of_anatop_match, NULL, dev); + + return 0; +} + +static int __devexit of_anatop_remove(struct platform_device *pdev) +{ + struct anatop *drvdata; + drvdata = platform_get_drvdata(pdev); + iounmap(drvdata->ioreg); + + return 0; +} + +static struct platform_driver anatop_of_driver = { + .driver = { + .name = "anatop-mfd", + .owner = THIS_MODULE, + .of_match_table = of_anatop_match, + }, + .probe = of_anatop_probe, + .remove = of_anatop_remove, +}; + +static int __init anatop_init(void) +{ + return platform_driver_register(&anatop_of_driver); +} +postcore_initcall(anatop_init); + +static void __exit anatop_exit(void) +{ + platform_driver_unregister(&anatop_of_driver); +} +module_exit(anatop_exit); + +MODULE_AUTHOR("Ying-Chun Liu (PaulLiu) "); +MODULE_DESCRIPTION("ANATOP MFD driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 5364d0b8640dd15e5c0b3ba40d0e874764b1bc88 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 19 Mar 2012 11:44:55 +0530 Subject: mfd: Build rtc5t583 only if I2C config is selected to y. Fixing build error reported by Stephen Rothwell: drivers/built-in.o: In function `rc5t583_i2c_init': rc5t583.c:(.init.text+0xb3db): undefined reference to `i2c_register_driver' drivers/built-in.o: In function `rc5t583_i2c_probe': rc5t583.c:(.devinit.text+0x8fa0): undefined reference to `regmap_init_i2c' drivers/built-in.o: In function `rc5t583_i2c_exit': rc5t583.c:(.exit.text+0x708): undefined reference to `i2c_del_driver' Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index a5f4176..ef22292 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -848,7 +848,7 @@ config MFD_INTEL_MSIC config MFD_RC5T583 bool "Ricoh RC5T583 Power Management system device" - depends on I2C && GENERIC_HARDIRQS + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C help -- cgit v1.1 From bcc2d6d6fcbee3c07515837b522f6c242f3f99e4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 17 Mar 2012 16:01:09 -0700 Subject: mfd: Add some da9052-i2c section annotations da9052_i2c_remove() can and should be marked as __devexit. Signed-off-by: Dmitry Torokhov Signed-off-by: Samuel Ortiz --- drivers/mfd/da9052-i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index fdc2c6a..36b88e3 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -89,7 +89,7 @@ err: return ret; } -static int da9052_i2c_remove(struct i2c_client *client) +static int __devexit da9052_i2c_remove(struct i2c_client *client) { struct da9052 *da9052 = i2c_get_clientdata(client); @@ -110,7 +110,7 @@ static struct i2c_device_id da9052_i2c_id[] = { static struct i2c_driver da9052_i2c_driver = { .probe = da9052_i2c_probe, - .remove = da9052_i2c_remove, + .remove = __devexit_p(da9052_i2c_remove), .id_table = da9052_i2c_id, .driver = { .name = "da9052", -- cgit v1.1 From 3c33be06f9aa0949ad24e67dfcae1f2a3006f4e1 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Fri, 16 Mar 2012 11:10:19 +0530 Subject: mfd: Add support for TPS65090 TPS65090 is a Texas Instrument PMIC. It contains 3 Step-Down converters, 2 always on LDO's and 7 current limited load switches. Signed-off-by: Venu Byravarasu Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 12 ++ drivers/mfd/Makefile | 1 + drivers/mfd/tps65090.c | 387 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/mfd/tps65090.c (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ef22292..f67d20e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -827,6 +827,18 @@ config MFD_PM8XXX_IRQ config TPS65911_COMPARATOR tristate +config MFD_TPS65090 + bool "TPS65090 Power Management chips" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + help + If you say yes here you get support for the TPS65090 series of + Power Management chips. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + config MFD_AAT2870_CORE bool "Support for the AnalogicTech AAT2870" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 67f7b0e..05fa538 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o 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_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c new file mode 100644 index 0000000..a66d4df --- /dev/null +++ b/drivers/mfd/tps65090.c @@ -0,0 +1,387 @@ +/* + * Core driver for TI TPS65090 PMIC family + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + + * This program is distributed in the hope 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_INT_REG 2 +#define TOTAL_NUM_REG 0x18 + +/* interrupt status registers */ +#define TPS65090_INT_STS 0x0 +#define TPS65090_INT_STS2 0x1 + +/* interrupt mask registers */ +#define TPS65090_INT_MSK 0x2 +#define TPS65090_INT_MSK2 0x3 + +struct tps65090_irq_data { + u8 mask_reg; + u8 mask_pos; +}; + +#define TPS65090_IRQ(_reg, _mask_pos) \ + { \ + .mask_reg = (_reg), \ + .mask_pos = (_mask_pos), \ + } + +static const struct tps65090_irq_data tps65090_irqs[] = { + [0] = TPS65090_IRQ(0, 0), + [1] = TPS65090_IRQ(0, 1), + [2] = TPS65090_IRQ(0, 2), + [3] = TPS65090_IRQ(0, 3), + [4] = TPS65090_IRQ(0, 4), + [5] = TPS65090_IRQ(0, 5), + [6] = TPS65090_IRQ(0, 6), + [7] = TPS65090_IRQ(0, 7), + [8] = TPS65090_IRQ(1, 0), + [9] = TPS65090_IRQ(1, 1), + [10] = TPS65090_IRQ(1, 2), + [11] = TPS65090_IRQ(1, 3), + [12] = TPS65090_IRQ(1, 4), + [13] = TPS65090_IRQ(1, 5), + [14] = TPS65090_IRQ(1, 6), + [15] = TPS65090_IRQ(1, 7), +}; + +static struct mfd_cell tps65090s[] = { + { + .name = "tps65910-pmic", + }, + { + .name = "tps65910-regulator", + }, +}; + +struct tps65090 { + struct mutex lock; + struct device *dev; + struct i2c_client *client; + struct regmap *rmap; + struct irq_chip irq_chip; + struct mutex irq_lock; + int irq_base; + unsigned int id; +}; + +int tps65090_write(struct device *dev, int reg, uint8_t val) +{ + struct tps65090 *tps = dev_get_drvdata(dev); + return regmap_write(tps->rmap, reg, val); +} +EXPORT_SYMBOL_GPL(tps65090_write); + +int tps65090_read(struct device *dev, int reg, uint8_t *val) +{ + struct tps65090 *tps = dev_get_drvdata(dev); + unsigned int temp_val; + int ret; + ret = regmap_read(tps->rmap, reg, &temp_val); + if (!ret) + *val = temp_val; + return ret; +} +EXPORT_SYMBOL_GPL(tps65090_read); + +int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num) +{ + struct tps65090 *tps = dev_get_drvdata(dev); + return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u); +} +EXPORT_SYMBOL_GPL(tps65090_set_bits); + +int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num) +{ + struct tps65090 *tps = dev_get_drvdata(dev); + return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u); +} +EXPORT_SYMBOL_GPL(tps65090_clr_bits); + +static void tps65090_irq_lock(struct irq_data *data) +{ + struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); + + mutex_lock(&tps65090->irq_lock); +} + +static void tps65090_irq_mask(struct irq_data *irq_data) +{ + struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->hwirq; + const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; + + tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), + data->mask_pos); +} + +static void tps65090_irq_unmask(struct irq_data *irq_data) +{ + struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - tps65090->irq_base; + const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; + + tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), + data->mask_pos); +} + +static void tps65090_irq_sync_unlock(struct irq_data *data) +{ + struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); + + mutex_unlock(&tps65090->irq_lock); +} + +static irqreturn_t tps65090_irq(int irq, void *data) +{ + struct tps65090 *tps65090 = data; + int ret = 0; + u8 status, mask; + unsigned long int acks = 0; + int i; + + for (i = 0; i < NUM_INT_REG; i++) { + ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask); + if (ret < 0) { + dev_err(tps65090->dev, + "failed to read mask reg [addr:%d]\n", + TPS65090_INT_MSK + i); + return IRQ_NONE; + } + ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i, + &status); + if (ret < 0) { + dev_err(tps65090->dev, + "failed to read status reg [addr:%d]\n", + TPS65090_INT_STS + i); + return IRQ_NONE; + } + if (status) { + /* Ack only those interrupts which are not masked */ + status &= (~mask); + ret = tps65090_write(tps65090->dev, + TPS65090_INT_STS + i, status); + if (ret < 0) { + dev_err(tps65090->dev, + "failed to write interrupt status\n"); + return IRQ_NONE; + } + acks |= (status << (i * 8)); + } + } + + for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs)) + handle_nested_irq(tps65090->irq_base + i); + return acks ? IRQ_HANDLED : IRQ_NONE; +} + +static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq, + int irq_base) +{ + int i, ret; + + if (!irq_base) { + dev_err(tps65090->dev, "IRQ base not set\n"); + return -EINVAL; + } + + mutex_init(&tps65090->irq_lock); + + for (i = 0; i < NUM_INT_REG; i++) + tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF); + + for (i = 0; i < NUM_INT_REG; i++) + tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff); + + tps65090->irq_base = irq_base; + tps65090->irq_chip.name = "tps65090"; + tps65090->irq_chip.irq_mask = tps65090_irq_mask; + tps65090->irq_chip.irq_unmask = tps65090_irq_unmask; + tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock; + tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock; + + for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) { + int __irq = i + tps65090->irq_base; + irq_set_chip_data(__irq, tps65090); + irq_set_chip_and_handler(__irq, &tps65090->irq_chip, + handle_simple_irq); + irq_set_nested_thread(__irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(__irq, IRQF_VALID); +#endif + } + + ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT, + "tps65090", tps65090); + if (!ret) { + device_init_wakeup(tps65090->dev, 1); + enable_irq_wake(irq); + } + + return ret; +} + +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ + if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS)) + return true; + else + return false; +} + +static const struct regmap_config tps65090_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TOTAL_NUM_REG, + .num_reg_defaults_raw = TOTAL_NUM_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = is_volatile_reg, +}; + +static int __devinit tps65090_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps65090_platform_data *pdata = client->dev.platform_data; + struct tps65090 *tps65090; + int ret; + + if (!pdata) { + dev_err(&client->dev, "tps65090 requires platform data\n"); + return -EINVAL; + } + + tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090), + GFP_KERNEL); + if (tps65090 == NULL) + return -ENOMEM; + + tps65090->client = client; + tps65090->dev = &client->dev; + i2c_set_clientdata(client, tps65090); + + mutex_init(&tps65090->lock); + + if (client->irq) { + ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base); + if (ret) { + dev_err(&client->dev, "IRQ init failed with err: %d\n", + ret); + goto err_exit; + } + } + + tps65090->rmap = regmap_init_i2c(tps65090->client, + &tps65090_regmap_config); + if (IS_ERR(tps65090->rmap)) { + dev_err(&client->dev, "regmap_init failed with err: %ld\n", + PTR_ERR(tps65090->rmap)); + goto err_irq_exit; + }; + + ret = mfd_add_devices(tps65090->dev, -1, tps65090s, + ARRAY_SIZE(tps65090s), NULL, 0); + if (ret) { + dev_err(&client->dev, "add mfd devices failed with err: %d\n", + ret); + goto err_regmap_exit; + } + + return 0; + +err_regmap_exit: + regmap_exit(tps65090->rmap); + +err_irq_exit: + if (client->irq) + free_irq(client->irq, tps65090); +err_exit: + return ret; +} + +static int __devexit tps65090_i2c_remove(struct i2c_client *client) +{ + struct tps65090 *tps65090 = i2c_get_clientdata(client); + + mfd_remove_devices(tps65090->dev); + regmap_exit(tps65090->rmap); + if (client->irq) + free_irq(client->irq, tps65090); + + return 0; +} + +#ifdef CONFIG_PM +static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state) +{ + if (client->irq) + disable_irq(client->irq); + return 0; +} + +static int tps65090_i2c_resume(struct i2c_client *client) +{ + if (client->irq) + enable_irq(client->irq); + return 0; +} +#endif + +static const struct i2c_device_id tps65090_id_table[] = { + { "tps65090", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tps65090_id_table); + +static struct i2c_driver tps65090_driver = { + .driver = { + .name = "tps65090", + .owner = THIS_MODULE, + }, + .probe = tps65090_i2c_probe, + .remove = __devexit_p(tps65090_i2c_remove), +#ifdef CONFIG_PM + .suspend = tps65090_i2c_suspend, + .resume = tps65090_i2c_resume, +#endif + .id_table = tps65090_id_table, +}; + +static int __init tps65090_init(void) +{ + return i2c_add_driver(&tps65090_driver); +} +subsys_initcall(tps65090_init); + +static void __exit tps65090_exit(void) +{ + i2c_del_driver(&tps65090_driver); +} +module_exit(tps65090_exit); + +MODULE_DESCRIPTION("TPS65090 core driver"); +MODULE_AUTHOR("Venu Byravarasu "); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From bdd61bc667401dc8054b69dddb0cf32ca2aab57b Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 2 Mar 2012 16:15:22 +0100 Subject: mfd: Return twl6030_mmc_card_detect IRQ for board setup Card detect IRQ from the TWL6030 used to be provided to the MMC controller code using a statically allocated IRQ scheme: card_detect_irq = TWL6030_IRQ_BASE + MMCDETECT_INTR_OFFSET; This is no longer valid in a SPARSE_IRQ context since there is no more pre-defined TWL6030_IRQ_BASE. Return the proper card detect IRQ value in the twl6030_mmc_card_detect_config that will be called from the MMC controller. Signed-off-by: Benoit Cousson Cc: Felipe Balbi Cc: Tony Lindgren Cc: Rajendra Nayak Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6030-irq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 3c2cc00..bb3d762 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -320,7 +320,8 @@ int twl6030_mmc_card_detect_config(void) ret); return ret; } - return 0; + + return twl6030_irq_base + MMCDETECT_INTR_OFFSET; } EXPORT_SYMBOL(twl6030_mmc_card_detect_config); -- cgit v1.1 From 9e1786202704f199941f7c2f5c9fe12879de7917 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 22 Feb 2012 14:32:16 +0200 Subject: mfd: Make twl-core not depend on pdata->irq_base/end With sparse IRQs the driver shouldn't depend at all on any IRQ values coming from board-file. Remove every occurences of pdata->irq_base/end. Signed-off-by: Felipe Balbi Signed-off-by: Benoit Cousson Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 61 ++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index c1e4f1a..a0297f7 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -652,7 +652,8 @@ add_regulator(int num, struct regulator_init_data *pdata, */ static int -add_children(struct twl4030_platform_data *pdata, unsigned long features) +add_children(struct twl4030_platform_data *pdata, unsigned irq_base, + unsigned long features) { struct device *child; unsigned sub_chip_id; @@ -660,7 +661,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (twl_has_gpio() && pdata->gpio) { child = add_child(SUB_CHIP_ID1, "twl4030_gpio", pdata->gpio, sizeof(*pdata->gpio), - false, pdata->irq_base + GPIO_INTR_OFFSET, 0); + false, irq_base + GPIO_INTR_OFFSET, 0); if (IS_ERR(child)) return PTR_ERR(child); } @@ -668,7 +669,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (twl_has_keypad() && pdata->keypad) { child = add_child(SUB_CHIP_ID2, "twl4030_keypad", pdata->keypad, sizeof(*pdata->keypad), - true, pdata->irq_base + KEYPAD_INTR_OFFSET, 0); + true, irq_base + KEYPAD_INTR_OFFSET, 0); if (IS_ERR(child)) return PTR_ERR(child); } @@ -676,7 +677,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (twl_has_madc() && pdata->madc) { child = add_child(2, "twl4030_madc", pdata->madc, sizeof(*pdata->madc), - true, pdata->irq_base + MADC_INTR_OFFSET, 0); + true, irq_base + MADC_INTR_OFFSET, 0); if (IS_ERR(child)) return PTR_ERR(child); } @@ -692,7 +693,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) sub_chip_id = twl_map[TWL_MODULE_RTC].sid; child = add_child(sub_chip_id, "twl_rtc", NULL, 0, - true, pdata->irq_base + RTC_INTR_OFFSET, 0); + true, irq_base + RTC_INTR_OFFSET, 0); if (IS_ERR(child)) return PTR_ERR(child); } @@ -745,8 +746,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) pdata->usb, sizeof(*pdata->usb), true, /* irq0 = USB_PRES, irq1 = USB */ - pdata->irq_base + USB_PRES_INTR_OFFSET, - pdata->irq_base + USB_INTR_OFFSET); + irq_base + USB_PRES_INTR_OFFSET, + irq_base + USB_INTR_OFFSET); if (IS_ERR(child)) return PTR_ERR(child); @@ -794,8 +795,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) pdata->usb, sizeof(*pdata->usb), true, /* irq1 = VBUS_PRES, irq0 = USB ID */ - pdata->irq_base + USBOTG_INTR_OFFSET, - pdata->irq_base + USB_PRES_INTR_OFFSET); + irq_base + USBOTG_INTR_OFFSET, + irq_base + USB_PRES_INTR_OFFSET); if (IS_ERR(child)) return PTR_ERR(child); @@ -822,7 +823,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (twl_has_pwrbutton() && twl_class_is_4030()) { child = add_child(1, "twl4030_pwrbutton", - NULL, 0, true, pdata->irq_base + 8 + 0, 0); + NULL, 0, true, irq_base + 8 + 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } @@ -1056,8 +1057,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) child = add_child(3, "twl4030_bci", pdata->bci, sizeof(*pdata->bci), false, /* irq0 = CHG_PRES, irq1 = BCI */ - pdata->irq_base + BCI_PRES_INTR_OFFSET, - pdata->irq_base + BCI_INTR_OFFSET); + irq_base + BCI_PRES_INTR_OFFSET, + irq_base + BCI_INTR_OFFSET); if (IS_ERR(child)) return PTR_ERR(child); } @@ -1191,6 +1192,8 @@ static int twl_remove(struct i2c_client *client) static int __devinit twl_probe(struct i2c_client *client, const struct i2c_device_id *id) { + int irq_base; + int irq_end; int status; unsigned i; struct twl4030_platform_data *pdata = client->dev.platform_data; @@ -1219,22 +1222,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return -EINVAL; } - status = irq_alloc_descs(-1, pdata->irq_base, nr_irqs, 0); + status = irq_alloc_descs(-1, 0, nr_irqs, 0); if (IS_ERR_VALUE(status)) { dev_err(&client->dev, "Fail to allocate IRQ descs\n"); return status; } - pdata->irq_base = status; - pdata->irq_end = pdata->irq_base + nr_irqs; - -#ifdef CONFIG_IRQ_DOMAIN - domain.irq_base = pdata->irq_base; - domain.nr_irq = nr_irqs; - domain.of_node = of_node_get(node); - domain.ops = &irq_domain_simple_ops; - irq_domain_add(&domain); -#endif + irq_base = status; + irq_end = irq_base + nr_irqs; + irq_domain_add_legacy(node, nr_irqs, irq_base, 0, + &irq_domain_simple_ops, NULL); if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { dev_dbg(&client->dev, "can't talk I2C?\n"); @@ -1287,16 +1284,14 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) twl4030_power_init(pdata->power); /* Maybe init the T2 Interrupt subsystem */ - if (client->irq - && pdata->irq_base - && pdata->irq_end > pdata->irq_base) { + if (client->irq) { if (twl_class_is_4030()) { twl4030_init_chip_irq(id->name); - status = twl4030_init_irq(client->irq, pdata->irq_base, - pdata->irq_end); + status = twl4030_init_irq(client->irq, irq_base, + irq_end); } else { - status = twl6030_init_irq(client->irq, pdata->irq_base, - pdata->irq_end); + status = twl6030_init_irq(client->irq, irq_base, + irq_end); } if (status < 0) @@ -1315,12 +1310,10 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1); } -#ifdef CONFIG_OF_DEVICE if (node) status = of_platform_populate(node, NULL, NULL, &client->dev); - else -#endif - status = add_children(pdata, id->driver_data); + if (status) + status = add_children(pdata, irq_base, id->driver_data); fail: if (status < 0) -- cgit v1.1 From 5fd32d6a3a0812ef730b7b83031fc00fa4c4075f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 22 Feb 2012 14:39:09 +0200 Subject: mfd: Remove unneeded header from twl-core This driver doesn't really need , so remove it. Signed-off-by: Felipe Balbi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index a0297f7..140340f 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -45,10 +45,6 @@ #include #include -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) -#include -#endif - /* * The TWL4030 "Triton 2" is one of a family of a multi-function "Power * Management and System Companion Device" chips originally designed for -- cgit v1.1 From 1b8f333ff49f778a1215f65754c31c02408d1d08 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Wed, 29 Feb 2012 19:28:14 +0100 Subject: mfd: Remove references already defineid in header file from twl-core The twl-core exported functions are already declared in twl-core.h Include the header file instead or re-declaring the functions. Signed-off-by: Benoit Cousson Acked-by: Felipe Balbi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 140340f..712e262 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -45,6 +45,8 @@ #include #include +#include "twl-core.h" + /* * The TWL4030 "Triton 2" is one of a family of a multi-function "Power * Management and System Companion Device" chips originally designed for @@ -1154,11 +1156,6 @@ static void clocks_init(struct device *dev, /*----------------------------------------------------------------------*/ -int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); -int twl4030_exit_irq(void); -int twl4030_init_chip_irq(const char *chip); -int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); -int twl6030_exit_irq(void); static int twl_remove(struct i2c_client *client) { -- cgit v1.1 From 78518ffa08fceee42d61359303c58bdd0a82033f Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Wed, 29 Feb 2012 19:40:31 +0100 Subject: mfd: Move twl-core IRQ allocation into twl[4030|6030]-irq files During DT adaptation, the irq_alloc_desc was added into twl-core, but due to the rather different and weird IRQ management required by the twl4030, it is much better to have a different approach for it. The issue is that twl4030 uses a two level IRQ mechanism but handles all the PWR interrupts as part of the twl-core interrupt range. It ends up with a range of 16 interrupts total for CORE and PWR. The other twl4030 functionalities already have a dedicated driver and thus their IRQs and irqdomain can and should be defined localy. twl6030 is using a single level IRQ controller and thus does not require any trick. Move the irq_alloc_desc and irq_domain_add_legacy in twl4030-irq and twl6030-irq. Allocate together CORE and PWR IRQs for twl4030-irq. Conflicts: drivers/mfd/twl-core.c Signed-off-by: Benoit Cousson Acked-by: Felipe Balbi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 29 +++++------------------------ drivers/mfd/twl-core.h | 4 ++-- drivers/mfd/twl4030-irq.c | 29 +++++++++++++++++++++++++++-- drivers/mfd/twl6030-irq.c | 31 ++++++++++++++++++++++--------- 4 files changed, 56 insertions(+), 37 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 712e262..764c5b5 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -147,9 +147,6 @@ #define TWL_MODULE_LAST TWL4030_MODULE_LAST -#define TWL4030_NR_IRQS 8 -#define TWL6030_NR_IRQS 20 - /* Base Address defns for twl4030_map[] */ /* subchip/slave 0 - USB ID */ @@ -1186,17 +1183,12 @@ static int __devinit twl_probe(struct i2c_client *client, const struct i2c_device_id *id) { int irq_base; - int irq_end; int status; unsigned i; struct twl4030_platform_data *pdata = client->dev.platform_data; struct device_node *node = client->dev.of_node; u8 temp; int ret = 0; - int nr_irqs = TWL4030_NR_IRQS; - - if ((id->driver_data) & TWL6030_CLASS) - nr_irqs = TWL6030_NR_IRQS; if (node && !pdata) { /* @@ -1215,17 +1207,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return -EINVAL; } - status = irq_alloc_descs(-1, 0, nr_irqs, 0); - if (IS_ERR_VALUE(status)) { - dev_err(&client->dev, "Fail to allocate IRQ descs\n"); - return status; - } - - irq_base = status; - irq_end = irq_base + nr_irqs; - irq_domain_add_legacy(node, nr_irqs, irq_base, 0, - &irq_domain_simple_ops, NULL); - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { dev_dbg(&client->dev, "can't talk I2C?\n"); return -EIO; @@ -1280,15 +1261,15 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) if (client->irq) { if (twl_class_is_4030()) { twl4030_init_chip_irq(id->name); - status = twl4030_init_irq(client->irq, irq_base, - irq_end); + irq_base = twl4030_init_irq(&client->dev, client->irq); } else { - status = twl6030_init_irq(client->irq, irq_base, - irq_end); + irq_base = twl6030_init_irq(&client->dev, client->irq); } - if (status < 0) + if (irq_base < 0) { + status = irq_base; goto fail; + } } /* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface. diff --git a/drivers/mfd/twl-core.h b/drivers/mfd/twl-core.h index 8c50a55..6ff99dc 100644 --- a/drivers/mfd/twl-core.h +++ b/drivers/mfd/twl-core.h @@ -1,9 +1,9 @@ #ifndef __TWL_CORE_H__ #define __TWL_CORE_H__ -extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); +extern int twl6030_init_irq(struct device *dev, int irq_num); extern int twl6030_exit_irq(void); -extern int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); +extern int twl4030_init_irq(struct device *dev, int irq_num); extern int twl4030_exit_irq(void); extern int twl4030_init_chip_irq(const char *chip); diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index b31f920..a3dc1d9 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -28,10 +28,13 @@ */ #include +#include #include #include #include +#include +#include #include #include "twl-core.h" @@ -53,6 +56,8 @@ * base + 8 .. base + 15 SIH for PWR_INT * base + 16 .. base + 33 SIH for GPIO */ +#define TWL4030_CORE_NR_IRQS 8 +#define TWL4030_PWR_NR_IRQS 8 /* PIH register offsets */ #define REG_PIH_ISR_P1 0x01 @@ -695,14 +700,34 @@ int twl4030_sih_setup(int module) /* FIXME pass in which interrupt line we'll use ... */ #define twl_irq_line 0 -int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) +int twl4030_init_irq(struct device *dev, int irq_num) { static struct irq_chip twl4030_irq_chip; + int irq_base, irq_end, nr_irqs; + struct device_node *node = dev->of_node; int status; int i; /* + * TWL core and pwr interrupts must be contiguous because + * the hwirqs numbers are defined contiguously from 1 to 15. + * Create only one domain for both. + */ + nr_irqs = TWL4030_PWR_NR_IRQS + TWL4030_CORE_NR_IRQS; + + irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); + if (IS_ERR_VALUE(irq_base)) { + dev_err(dev, "Fail to allocate IRQ descs\n"); + return irq_base; + } + + irq_domain_add_legacy(node, nr_irqs, irq_base, 0, + &irq_domain_simple_ops, NULL); + + irq_end = irq_base + TWL4030_CORE_NR_IRQS; + + /* * Mask and clear all TWL4030 interrupts since initially we do * not have any TWL4030 module interrupt handlers present */ @@ -747,7 +772,7 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) goto fail_rqirq; } - return status; + return irq_base; fail_rqirq: /* clean up twl4030_sih_setup */ fail: diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index bb3d762..86c4082 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include "twl-core.h" @@ -53,6 +55,7 @@ * specifies mapping between interrupt number and the associated module. * */ +#define TWL6030_NR_IRQS 20 static int twl6030_interrupt_mapping[24] = { PWR_INTR_OFFSET, /* Bit 0 PWRON */ @@ -246,11 +249,6 @@ static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) return 0; } -/*----------------------------------------------------------------------*/ - -static unsigned twl6030_irq_next; - -/*----------------------------------------------------------------------*/ int twl6030_interrupt_unmask(u8 bit_mask, u8 offset) { int ret; @@ -350,8 +348,10 @@ int twl6030_mmc_card_detect(struct device *dev, int slot) } EXPORT_SYMBOL(twl6030_mmc_card_detect); -int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) +int twl6030_init_irq(struct device *dev, int irq_num) { + struct device_node *node = dev->of_node; + int nr_irqs, irq_base, irq_end; int status = 0; int i; @@ -360,6 +360,20 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) u8 mask[4]; static struct irq_chip twl6030_irq_chip; + + nr_irqs = TWL6030_NR_IRQS; + + irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); + if (IS_ERR_VALUE(irq_base)) { + dev_err(dev, "Fail to allocate IRQ descs\n"); + return irq_base; + } + + irq_domain_add_legacy(node, nr_irqs, irq_base, 0, + &irq_domain_simple_ops, NULL); + + irq_end = irq_base + nr_irqs; + mask[1] = 0xFF; mask[2] = 0xFF; mask[3] = 0xFF; @@ -387,9 +401,8 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) activate_irq(i); } - twl6030_irq_next = i; pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", - irq_num, irq_base, twl6030_irq_next - 1); + irq_num, irq_base, irq_end); /* install an irq handler to demultiplex the TWL6030 interrupt */ init_completion(&irq_event); @@ -410,7 +423,7 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) twl_irq = irq_num; register_pm_notifier(&twl6030_irq_pm_notifier_block); - return status; + return irq_base; fail_kthread: free_irq(irq_num, &irq_event); -- cgit v1.1 From f01b1f90bf46ddaf2a68215a9489364c974e5689 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Wed, 29 Feb 2012 22:38:06 +0100 Subject: mfd: Make twl4030 SIH SPARSE_IRQ capable twl4030 is using a two level irq controllers infrastruture. So far, only the first level was using dynamic irq_desc allocation to be able to have irq_domain support for device tree. There is a need to allocate separate irq_descs for the SIH too to avoid hacking the first level with interrupts from the second level. Add an irq_base parameter to allow the caller to provide the base from pdata or from dynamic allocation. Affect TWL4030_NR_IRQS to the twl-core IRQs only. Moreover that will allow the extraction of the of_node pointer for further Device Tree conversion. Signed-off-by: Felipe Balbi Signed-off-by: Benoit Cousson Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index a3dc1d9..d6f3a5e 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -629,28 +629,21 @@ static irqreturn_t handle_twl4030_sih(int irq, void *data) return IRQ_HANDLED; } -static unsigned twl4030_irq_next; - /* returns the first IRQ used by this SIH bank, or negative errno */ -int twl4030_sih_setup(int module) +int twl4030_sih_setup(struct device *dev, int module, int irq_base) { int sih_mod; const struct sih *sih = NULL; struct sih_agent *agent; int i, irq; int status = -EINVAL; - unsigned irq_base = twl4030_irq_next; /* only support modules with standard clear-on-read for now */ for (sih_mod = 0, sih = sih_modules; sih_mod < nr_sih_modules; sih_mod++, sih++) { if (sih->module == module && sih->set_cor) { - if (!WARN((irq_base + sih->bits) > NR_IRQS, - "irq %d for %s too big\n", - irq_base + sih->bits, - sih->name)) - status = 0; + status = 0; break; } } @@ -661,8 +654,6 @@ int twl4030_sih_setup(int module) if (!agent) return -ENOMEM; - status = 0; - agent->irq_base = irq_base; agent->sih = sih; agent->imr = ~0; @@ -678,8 +669,6 @@ int twl4030_sih_setup(int module) activate_irq(irq); } - twl4030_irq_next += i; - /* replace generic PIH handler (handle_simple_irq) */ irq = sih_mod + twl4030_irq_base; irq_set_handler_data(irq, agent); @@ -688,7 +677,7 @@ int twl4030_sih_setup(int module) agent->irq_name ?: sih->name, NULL); pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", sih->name, - irq, irq_base, twl4030_irq_next - 1); + irq, irq_base, irq_base + i - 1); return status < 0 ? status : irq_base; } @@ -752,12 +741,12 @@ int twl4030_init_irq(struct device *dev, int irq_num) irq_set_nested_thread(i, 1); activate_irq(i); } - twl4030_irq_next = i; + pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", - irq_num, irq_base, twl4030_irq_next - 1); + irq_num, irq_base, irq_end); /* ... and the PWR_INT module ... */ - status = twl4030_sih_setup(TWL4030_MODULE_INT); + status = twl4030_sih_setup(dev, TWL4030_MODULE_INT, irq_end); if (status < 0) { pr_err("twl4030: sih_setup PWR INT --> %d\n", status); goto fail; -- cgit v1.1 From 5a903090e7aa561901b7f052eb744b480d6126d4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 22 Feb 2012 14:53:59 +0200 Subject: mfd: Micro-optimization on twl4030 IRQ handler __ffs() will be far faster than the for loop used. Signed-off-by: Felipe Balbi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-irq.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index d6f3a5e..3b748b7 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -293,7 +293,6 @@ static unsigned twl4030_irq_base; */ static irqreturn_t handle_twl4030_pih(int irq, void *devid) { - int module_irq; irqreturn_t ret; u8 pih_isr; @@ -304,12 +303,13 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid) 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) - handle_nested_irq(module_irq); + while (pih_isr) { + unsigned long pending = __ffs(pih_isr); + unsigned int irq; + + pih_isr &= ~BIT(pending); + irq = pending + twl4030_irq_base; + handle_nested_irq(irq); } return IRQ_HANDLED; -- cgit v1.1 From ec1a07b3440cc28946a77a974c21570bbef6ffa1 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 2 Mar 2012 11:11:26 +0100 Subject: mfd: Replace twl-* pr_ macros by the dev_ equivalent and do various cleanups Since a structure device is available now, use the dev_ macros instead of the pr_ ones. Clean some badly formatted comments. Remove some unused variables. Move some variable to the place they belong. Clean some badly wrapped lines. Align variable definition Add missing braces in if-then-else block. Add blank line for better readability. Move stuff here and there... Conflicts: drivers/mfd/twl-core.c Signed-off-by: Benoit Cousson Cc: Felipe Balbi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 33 +++++++++++++++++++-------------- drivers/mfd/twl4030-irq.c | 19 ++++++++----------- drivers/mfd/twl6030-irq.c | 39 +++++++++++++++++++-------------------- 3 files changed, 46 insertions(+), 45 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 764c5b5..eb9bd20 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1178,17 +1178,15 @@ static int twl_remove(struct i2c_client *client) return 0; } -/* NOTE: this driver only handles a single twl4030/tps659x0 chip */ +/* NOTE: This driver only handles a single twl4030/tps659x0 chip */ static int __devinit twl_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int irq_base; - int status; - unsigned i; struct twl4030_platform_data *pdata = client->dev.platform_data; struct device_node *node = client->dev.of_node; - u8 temp; - int ret = 0; + int irq_base = 0; + int status; + unsigned i; if (node && !pdata) { /* @@ -1218,12 +1216,12 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) } for (i = 0; i < TWL_NUM_SLAVES; i++) { - struct twl_client *twl = &twl_modules[i]; + struct twl_client *twl = &twl_modules[i]; twl->address = client->addr + i; - if (i == 0) + if (i == 0) { twl->client = client; - else { + } else { twl->client = i2c_new_dummy(client->adapter, twl->address); if (!twl->client) { @@ -1235,7 +1233,9 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) } mutex_init(&twl->xfer_lock); } + inuse = true; + if ((id->driver_data) & TWL6030_CLASS) { twl_id = TWL6030_CLASS_ID; twl_map = &twl6030_map[0]; @@ -1249,8 +1249,8 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) /* read TWL IDCODE Register */ if (twl_id == TWL4030_CLASS_ID) { - ret = twl_read_idcode_register(); - WARN(ret < 0, "Error: reading twl_idcode register value\n"); + status = twl_read_idcode_register(); + WARN(status < 0, "Error: reading twl_idcode register value\n"); } /* load power event scripts */ @@ -1272,18 +1272,22 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) } } - /* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface. + /* + * Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface. * Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0, * SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0. */ - if (twl_class_is_4030()) { + u8 temp; + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &temp, REG_GPPUPDCTR1); temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \ - I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU); + I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU); twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1); } + status = -ENODEV; + if (node) status = of_platform_populate(node, NULL, NULL, &client->dev); if (status) @@ -1292,6 +1296,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) fail: if (status < 0) twl_remove(client); + return status; } diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 3b748b7..5d656e8 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -32,7 +32,6 @@ #include #include #include - #include #include #include @@ -639,14 +638,14 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base) int status = -EINVAL; /* only support modules with standard clear-on-read for now */ - for (sih_mod = 0, sih = sih_modules; - sih_mod < nr_sih_modules; + for (sih_mod = 0, sih = sih_modules; sih_mod < nr_sih_modules; sih_mod++, sih++) { if (sih->module == module && sih->set_cor) { status = 0; break; } } + if (status < 0) return status; @@ -676,7 +675,7 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base) status = request_threaded_irq(irq, NULL, handle_twl4030_sih, 0, agent->irq_name ?: sih->name, NULL); - pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", sih->name, + dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name, irq, irq_base, irq_base + i - 1); return status < 0 ? status : irq_base; @@ -692,12 +691,10 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base) int twl4030_init_irq(struct device *dev, int irq_num) { static struct irq_chip twl4030_irq_chip; + int status, i; int irq_base, irq_end, nr_irqs; struct device_node *node = dev->of_node; - int status; - int i; - /* * TWL core and pwr interrupts must be contiguous because * the hwirqs numbers are defined contiguously from 1 to 15. @@ -727,7 +724,7 @@ int twl4030_init_irq(struct device *dev, int irq_num) twl4030_irq_base = irq_base; /* - * install an irq handler for each of the SIH modules; + * Install an irq handler for each of the SIH modules; * clone dummy irq_chip since PIH can't *do* anything */ twl4030_irq_chip = dummy_irq_chip; @@ -742,13 +739,13 @@ int twl4030_init_irq(struct device *dev, int irq_num) activate_irq(i); } - pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", + dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", "PIH", irq_num, irq_base, irq_end); /* ... and the PWR_INT module ... */ status = twl4030_sih_setup(dev, TWL4030_MODULE_INT, irq_end); if (status < 0) { - pr_err("twl4030: sih_setup PWR INT --> %d\n", status); + dev_err(dev, "sih_setup PWR INT --> %d\n", status); goto fail; } @@ -757,7 +754,7 @@ int twl4030_init_irq(struct device *dev, int irq_num) IRQF_ONESHOT, "TWL4030-PIH", NULL); if (status < 0) { - pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status); + dev_err(dev, "could not claim irq%d: %d\n", irq_num, status); goto fail_rqirq; } diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 86c4082..b76902f 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -53,7 +53,6 @@ * * We set up IRQs starting at a platform-specified base. An interrupt map table, * specifies mapping between interrupt number and the associated module. - * */ #define TWL6030_NR_IRQS 20 @@ -352,14 +351,11 @@ int twl6030_init_irq(struct device *dev, int irq_num) { struct device_node *node = dev->of_node; int nr_irqs, irq_base, irq_end; - - int status = 0; - int i; struct task_struct *task; - int ret; - u8 mask[4]; - - static struct irq_chip twl6030_irq_chip; + static struct irq_chip twl6030_irq_chip; + int status = 0; + int i; + u8 mask[4]; nr_irqs = TWL6030_NR_IRQS; @@ -377,16 +373,18 @@ int twl6030_init_irq(struct device *dev, int irq_num) mask[1] = 0xFF; mask[2] = 0xFF; mask[3] = 0xFF; - ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], - REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */ - ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], - REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */ - ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], - REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */ + + /* mask all int lines */ + twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3); + /* mask all int sts */ + twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3); + /* clear INT_STS_A,B,C */ + twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3); twl6030_irq_base = irq_base; - /* install an irq handler for each of the modules; + /* + * install an irq handler for each of the modules; * clone dummy irq_chip since PIH can't *do* anything */ twl6030_irq_chip = dummy_irq_chip; @@ -401,22 +399,22 @@ int twl6030_init_irq(struct device *dev, int irq_num) activate_irq(i); } - pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", + dev_info(dev, "PIH (irq %d) chaining IRQs %d..%d\n", irq_num, irq_base, irq_end); /* install an irq handler to demultiplex the TWL6030 interrupt */ init_completion(&irq_event); - status = request_irq(irq_num, handle_twl6030_pih, 0, - "TWL6030-PIH", &irq_event); + 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); + dev_err(dev, "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); + dev_err(dev, "could not create irq %d thread!\n", irq_num); status = PTR_ERR(task); goto fail_kthread; } @@ -431,6 +429,7 @@ fail_kthread: fail_irq: for (i = irq_base; i < irq_end; i++) irq_set_chip_and_handler(i, NULL, NULL); + return status; } -- cgit v1.1 From 364cedb2f97063d649b2950c099883b89b60c000 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 4 Jan 2012 14:58:33 +0200 Subject: mfd: Detach twl6040 from the pmic mfd driver On OMAP4 platform audio has separate IC, it is no longer part of the pmic chip. Prevent twl-core to claim the 0x4b address, which belongs to the twl6040 audio IC. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Signed-off-by: Benoit Cousson Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 59 ++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 26 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index eb9bd20..8bff968 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -113,8 +113,8 @@ #define twl_has_watchdog() false #endif -#if defined(CONFIG_MFD_TWL4030_AUDIO) || defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) ||\ - defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE) +#if defined(CONFIG_MFD_TWL4030_AUDIO) || \ + defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) #define twl_has_codec() true #else #define twl_has_codec() false @@ -144,6 +144,7 @@ #define SUB_CHIP_ID1 1 #define SUB_CHIP_ID2 2 #define SUB_CHIP_ID3 3 +#define SUB_CHIP_ID_INVAL 0xff #define TWL_MODULE_LAST TWL4030_MODULE_LAST @@ -312,7 +313,7 @@ static struct twl_mapping twl6030_map[] = { * so they continue to match the order in this table. */ { SUB_CHIP_ID1, TWL6030_BASEADD_USB }, - { SUB_CHIP_ID3, TWL6030_BASEADD_AUDIO }, + { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO }, { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID }, { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, { SUB_CHIP_ID1, TWL6030_BASEADD_PIH }, @@ -374,6 +375,11 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) return -EPERM; } sid = twl_map[mod_no].sid; + if (unlikely(sid == SUB_CHIP_ID_INVAL)) { + pr_err("%s: module %d is not part of the pmic\n", + DRIVER_NAME, mod_no); + return -EINVAL; + } twl = &twl_modules[sid]; mutex_lock(&twl->xfer_lock); @@ -431,6 +437,11 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) return -EPERM; } sid = twl_map[mod_no].sid; + if (unlikely(sid == SUB_CHIP_ID_INVAL)) { + pr_err("%s: module %d is not part of the pmic\n", + DRIVER_NAME, mod_no); + return -EINVAL; + } twl = &twl_modules[sid]; mutex_lock(&twl->xfer_lock); @@ -832,15 +843,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, return PTR_ERR(child); } - if (twl_has_codec() && pdata->audio && twl_class_is_6030()) { - sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl6040", - pdata->audio, sizeof(*pdata->audio), - false, 0, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - /* twl4030 regulators */ if (twl_has_regulator() && twl_class_is_4030()) { child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1, @@ -1156,18 +1158,21 @@ static void clocks_init(struct device *dev, static int twl_remove(struct i2c_client *client) { - unsigned i; + unsigned i, num_slaves; int status; - if (twl_class_is_4030()) + if (twl_class_is_4030()) { status = twl4030_exit_irq(); - else + num_slaves = TWL_NUM_SLAVES; + } else { status = twl6030_exit_irq(); + num_slaves = TWL_NUM_SLAVES - 1; + } if (status < 0) return status; - for (i = 0; i < TWL_NUM_SLAVES; i++) { + for (i = 0; i < num_slaves; i++) { struct twl_client *twl = &twl_modules[i]; if (twl->client && twl->client != client) @@ -1186,7 +1191,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) struct device_node *node = client->dev.of_node; int irq_base = 0; int status; - unsigned i; + unsigned i, num_slaves; if (node && !pdata) { /* @@ -1215,7 +1220,17 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return -EBUSY; } - for (i = 0; i < TWL_NUM_SLAVES; i++) { + if ((id->driver_data) & TWL6030_CLASS) { + twl_id = TWL6030_CLASS_ID; + twl_map = &twl6030_map[0]; + num_slaves = TWL_NUM_SLAVES - 1; + } else { + twl_id = TWL4030_CLASS_ID; + twl_map = &twl4030_map[0]; + num_slaves = TWL_NUM_SLAVES; + } + + for (i = 0; i < num_slaves; i++) { struct twl_client *twl = &twl_modules[i]; twl->address = client->addr + i; @@ -1236,14 +1251,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) inuse = true; - if ((id->driver_data) & TWL6030_CLASS) { - twl_id = TWL6030_CLASS_ID; - twl_map = &twl6030_map[0]; - } else { - twl_id = TWL4030_CLASS_ID; - twl_map = &twl4030_map[0]; - } - /* setup clock framework */ clocks_init(&client->dev, pdata->clock); -- cgit v1.1