From 1df5981b82d9eabdd6e66d1d9514164c02329345 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 Jun 2011 19:28:10 +0100 Subject: mfd: Convert WM831x to use regmap API Factor out the register read/write code to use the register map API. We still need some wm831x specific code and locking in place to check that the user key is handled correctly but only on the write side, reads are not affected by the key. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/Kconfig | 2 ++ drivers/mfd/wm831x-core.c | 89 +++++++++++++++-------------------------------- drivers/mfd/wm831x-i2c.c | 68 +++++++----------------------------- drivers/mfd/wm831x-spi.c | 60 +++++++------------------------- 4 files changed, 54 insertions(+), 165 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 21574bdf485f..cfae5c594d24 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -404,6 +404,7 @@ config MFD_WM831X_I2C bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C" select MFD_CORE select MFD_WM831X + select REGMAP_I2C depends on I2C=y && GENERIC_HARDIRQS help Support for the Wolfson Microelecronics WM831x and WM832x PMICs @@ -415,6 +416,7 @@ config MFD_WM831X_SPI bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI" select MFD_CORE select MFD_WM831X + select REGMAP_SPI depends on SPI_MASTER && GENERIC_HARDIRQS help Support for the Wolfson Microelecronics WM831x and WM832x PMICs diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 282e76ab678f..578e0c2ad82b 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -160,29 +161,6 @@ int wm831x_reg_unlock(struct wm831x *wm831x) } EXPORT_SYMBOL_GPL(wm831x_reg_unlock); -static int wm831x_read(struct wm831x *wm831x, unsigned short reg, - int bytes, void *dest) -{ - int ret, i; - u16 *buf = dest; - - BUG_ON(bytes % 2); - BUG_ON(bytes <= 0); - - ret = wm831x->read_dev(wm831x, reg, bytes, dest); - if (ret < 0) - return ret; - - for (i = 0; i < bytes / 2; i++) { - buf[i] = be16_to_cpu(buf[i]); - - dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n", - buf[i], reg + i, reg + i); - } - - return 0; -} - /** * wm831x_reg_read: Read a single WM831x register. * @@ -191,14 +169,10 @@ static int wm831x_read(struct wm831x *wm831x, unsigned short reg, */ int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg) { - unsigned short val; + unsigned int val; int ret; - mutex_lock(&wm831x->io_lock); - - ret = wm831x_read(wm831x, reg, 2, &val); - - mutex_unlock(&wm831x->io_lock); + ret = regmap_read(wm831x->regmap, reg, &val); if (ret < 0) return ret; @@ -218,15 +192,7 @@ EXPORT_SYMBOL_GPL(wm831x_reg_read); int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg, int count, u16 *buf) { - int ret; - - mutex_lock(&wm831x->io_lock); - - ret = wm831x_read(wm831x, reg, count * 2, buf); - - mutex_unlock(&wm831x->io_lock); - - return ret; + return regmap_bulk_read(wm831x->regmap, reg, buf, count); } EXPORT_SYMBOL_GPL(wm831x_bulk_read); @@ -234,7 +200,7 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg, int bytes, void *src) { u16 *buf = src; - int i; + int i, ret; BUG_ON(bytes % 2); BUG_ON(bytes <= 0); @@ -245,11 +211,10 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg, dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n", buf[i], reg + i, reg + i); - - buf[i] = cpu_to_be16(buf[i]); + ret = regmap_write(wm831x->regmap, reg + i, buf[i]); } - return wm831x->write_dev(wm831x, reg, bytes, src); + return 0; } /** @@ -286,20 +251,14 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg, unsigned short mask, unsigned short val) { int ret; - u16 r; mutex_lock(&wm831x->io_lock); - ret = wm831x_read(wm831x, reg, 2, &r); - if (ret < 0) - goto out; - - r &= ~mask; - r |= val & mask; - - ret = wm831x_write(wm831x, reg, 2, &r); + if (!wm831x_reg_locked(wm831x, reg)) + ret = regmap_update_bits(wm831x->regmap, reg, mask, val); + else + ret = -EPERM; -out: mutex_unlock(&wm831x->io_lock); return ret; @@ -1292,6 +1251,12 @@ static struct mfd_cell backlight_devs[] = { }, }; +struct regmap_config wm831x_regmap_config = { + .reg_bits = 16, + .val_bits = 16, +}; +EXPORT_SYMBOL_GPL(wm831x_regmap_config); + /* * Instantiate the generic non-control parts of the device. */ @@ -1309,7 +1274,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); if (ret < 0) { dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret); - goto err; + goto err_regmap; } switch (ret) { case 0x6204: @@ -1318,20 +1283,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) default: dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret); ret = -EINVAL; - goto err; + goto err_regmap; } ret = wm831x_reg_read(wm831x, WM831X_REVISION); if (ret < 0) { dev_err(wm831x->dev, "Failed to read revision: %d\n", ret); - goto err; + goto err_regmap; } rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT; ret = wm831x_reg_read(wm831x, WM831X_RESET_ID); if (ret < 0) { dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret); - goto err; + goto err_regmap; } /* Some engineering samples do not have the ID set, rely on @@ -1406,7 +1371,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) default: dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret); ret = -EINVAL; - goto err; + goto err_regmap; } /* This will need revisiting in future but is OK for all @@ -1420,7 +1385,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY); if (ret < 0) { dev_err(wm831x->dev, "Failed to read security key: %d\n", ret); - goto err; + goto err_regmap; } if (ret != 0) { dev_warn(wm831x->dev, "Security key had non-zero value %x\n", @@ -1433,7 +1398,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = pdata->pre_init(wm831x); if (ret != 0) { dev_err(wm831x->dev, "pre_init() failed: %d\n", ret); - goto err; + goto err_regmap; } } @@ -1456,7 +1421,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) ret = wm831x_irq_init(wm831x, irq); if (ret != 0) - goto err; + goto err_regmap; wm831x_auxadc_init(wm831x); @@ -1552,8 +1517,9 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) err_irq: wm831x_irq_exit(wm831x); -err: +err_regmap: mfd_remove_devices(wm831x->dev); + regmap_exit(wm831x->regmap); kfree(wm831x); return ret; } @@ -1565,6 +1531,7 @@ void wm831x_device_exit(struct wm831x *wm831x) if (wm831x->irq_base) free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x); wm831x_irq_exit(wm831x); + regmap_exit(wm831x->regmap); kfree(wm831x); } diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index a06cbc739716..3ec6085d5fc0 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -18,67 +18,17 @@ #include #include #include +#include +#include #include #include -static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg, - int bytes, void *dest) -{ - struct i2c_client *i2c = wm831x->control_data; - int ret; - u16 r = cpu_to_be16(reg); - - ret = i2c_master_send(i2c, (unsigned char *)&r, 2); - if (ret < 0) - return ret; - if (ret != 2) - return -EIO; - - ret = i2c_master_recv(i2c, dest, bytes); - if (ret < 0) - return ret; - if (ret != bytes) - return -EIO; - return 0; -} - -/* Currently we allocate the write buffer on the stack; this is OK for - * small writes - if we need to do large writes this will need to be - * revised. - */ -static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg, - int bytes, void *src) -{ - struct i2c_client *i2c = wm831x->control_data; - struct i2c_msg xfer[2]; - int ret; - - reg = cpu_to_be16(reg); - - xfer[0].addr = i2c->addr; - xfer[0].flags = 0; - xfer[0].len = 2; - xfer[0].buf = (char *)® - - xfer[1].addr = i2c->addr; - xfer[1].flags = I2C_M_NOSTART; - xfer[1].len = bytes; - xfer[1].buf = (char *)src; - - ret = i2c_transfer(i2c->adapter, xfer, 2); - if (ret < 0) - return ret; - if (ret != 2) - return -EIO; - - return 0; -} - static int wm831x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm831x *wm831x; + int ret; wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); if (wm831x == NULL) @@ -86,9 +36,15 @@ static int wm831x_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm831x); wm831x->dev = &i2c->dev; - wm831x->control_data = i2c; - wm831x->read_dev = wm831x_i2c_read_device; - wm831x->write_dev = wm831x_i2c_write_device; + + wm831x->regmap = regmap_init_i2c(i2c, &wm831x_regmap_config); + if (IS_ERR(wm831x->regmap)) { + ret = PTR_ERR(wm831x->regmap); + dev_err(wm831x->dev, "Failed to allocate register map: %d\n", + ret); + kfree(wm831x); + return ret; + } return wm831x_device_init(wm831x, id->driver_data, i2c->irq); } diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index eed8e4f7a5a1..dbe11472afce 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -16,58 +16,16 @@ #include #include #include +#include +#include #include -static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg, - int bytes, void *dest) -{ - u16 tx_val; - u16 *d = dest; - int r, ret; - - /* Go register at a time */ - for (r = reg; r < reg + (bytes / 2); r++) { - tx_val = r | 0x8000; - - ret = spi_write_then_read(wm831x->control_data, - (u8 *)&tx_val, 2, (u8 *)d, 2); - if (ret != 0) - return ret; - - *d = be16_to_cpu(*d); - - d++; - } - - return 0; -} - -static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg, - int bytes, void *src) -{ - struct spi_device *spi = wm831x->control_data; - u16 *s = src; - u16 data[2]; - int ret, r; - - /* Go register at a time */ - for (r = reg; r < reg + (bytes / 2); r++) { - data[0] = r; - data[1] = *s++; - - ret = spi_write(spi, (char *)&data, sizeof(data)); - if (ret != 0) - return ret; - } - - return 0; -} - static int __devinit wm831x_spi_probe(struct spi_device *spi) { struct wm831x *wm831x; enum wm831x_parent type; + int ret; /* Currently SPI support for ID tables is unmerged, we're faking it */ if (strcmp(spi->modalias, "wm8310") == 0) @@ -98,9 +56,15 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, wm831x); wm831x->dev = &spi->dev; - wm831x->control_data = spi; - wm831x->read_dev = wm831x_spi_read_device; - wm831x->write_dev = wm831x_spi_write_device; + + wm831x->regmap = regmap_init_spi(wm831x->dev, &wm831x_regmap_config); + if (IS_ERR(wm831x->regmap)) { + ret = PTR_ERR(wm831x->regmap); + dev_err(wm831x->dev, "Failed to allocate register map: %d\n", + ret); + kfree(wm831x); + return ret; + } return wm831x_device_init(wm831x, type, spi->irq); } -- cgit v1.2.3 From 2e47fff113b7468be2d905e381408c05df72f1ae Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Jul 2011 17:30:08 +0100 Subject: mfd: Provide regmap register access info from wm831x driver Lets us see the register map in debugfs and will enable us to push access checking down into the regmap API. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 578e0c2ad82b..9338f8dcbb83 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -161,6 +161,352 @@ int wm831x_reg_unlock(struct wm831x *wm831x) } EXPORT_SYMBOL_GPL(wm831x_reg_unlock); +static bool wm831x_reg_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM831X_RESET_ID: + case WM831X_REVISION: + case WM831X_PARENT_ID: + case WM831X_SYSVDD_CONTROL: + case WM831X_THERMAL_MONITORING: + case WM831X_POWER_STATE: + case WM831X_WATCHDOG: + case WM831X_ON_PIN_CONTROL: + case WM831X_RESET_CONTROL: + case WM831X_CONTROL_INTERFACE: + case WM831X_SECURITY_KEY: + case WM831X_SOFTWARE_SCRATCH: + case WM831X_OTP_CONTROL: + case WM831X_GPIO_LEVEL: + case WM831X_SYSTEM_STATUS: + case WM831X_ON_SOURCE: + case WM831X_OFF_SOURCE: + case WM831X_SYSTEM_INTERRUPTS: + case WM831X_INTERRUPT_STATUS_1: + case WM831X_INTERRUPT_STATUS_2: + case WM831X_INTERRUPT_STATUS_3: + case WM831X_INTERRUPT_STATUS_4: + case WM831X_INTERRUPT_STATUS_5: + case WM831X_IRQ_CONFIG: + case WM831X_SYSTEM_INTERRUPTS_MASK: + case WM831X_INTERRUPT_STATUS_1_MASK: + case WM831X_INTERRUPT_STATUS_2_MASK: + case WM831X_INTERRUPT_STATUS_3_MASK: + case WM831X_INTERRUPT_STATUS_4_MASK: + case WM831X_INTERRUPT_STATUS_5_MASK: + case WM831X_RTC_WRITE_COUNTER: + case WM831X_RTC_TIME_1: + case WM831X_RTC_TIME_2: + case WM831X_RTC_ALARM_1: + case WM831X_RTC_ALARM_2: + case WM831X_RTC_CONTROL: + case WM831X_RTC_TRIM: + case WM831X_TOUCH_CONTROL_1: + case WM831X_TOUCH_CONTROL_2: + case WM831X_TOUCH_DATA_X: + case WM831X_TOUCH_DATA_Y: + case WM831X_TOUCH_DATA_Z: + case WM831X_AUXADC_DATA: + case WM831X_AUXADC_CONTROL: + case WM831X_AUXADC_SOURCE: + case WM831X_COMPARATOR_CONTROL: + case WM831X_COMPARATOR_1: + case WM831X_COMPARATOR_2: + case WM831X_COMPARATOR_3: + case WM831X_COMPARATOR_4: + case WM831X_GPIO1_CONTROL: + case WM831X_GPIO2_CONTROL: + case WM831X_GPIO3_CONTROL: + case WM831X_GPIO4_CONTROL: + case WM831X_GPIO5_CONTROL: + case WM831X_GPIO6_CONTROL: + case WM831X_GPIO7_CONTROL: + case WM831X_GPIO8_CONTROL: + case WM831X_GPIO9_CONTROL: + case WM831X_GPIO10_CONTROL: + case WM831X_GPIO11_CONTROL: + case WM831X_GPIO12_CONTROL: + case WM831X_GPIO13_CONTROL: + case WM831X_GPIO14_CONTROL: + case WM831X_GPIO15_CONTROL: + case WM831X_GPIO16_CONTROL: + case WM831X_CHARGER_CONTROL_1: + case WM831X_CHARGER_CONTROL_2: + case WM831X_CHARGER_STATUS: + case WM831X_BACKUP_CHARGER_CONTROL: + case WM831X_STATUS_LED_1: + case WM831X_STATUS_LED_2: + case WM831X_CURRENT_SINK_1: + case WM831X_CURRENT_SINK_2: + case WM831X_DCDC_ENABLE: + case WM831X_LDO_ENABLE: + case WM831X_DCDC_STATUS: + case WM831X_LDO_STATUS: + case WM831X_DCDC_UV_STATUS: + case WM831X_LDO_UV_STATUS: + case WM831X_DC1_CONTROL_1: + case WM831X_DC1_CONTROL_2: + case WM831X_DC1_ON_CONFIG: + case WM831X_DC1_SLEEP_CONTROL: + case WM831X_DC1_DVS_CONTROL: + case WM831X_DC2_CONTROL_1: + case WM831X_DC2_CONTROL_2: + case WM831X_DC2_ON_CONFIG: + case WM831X_DC2_SLEEP_CONTROL: + case WM831X_DC2_DVS_CONTROL: + case WM831X_DC3_CONTROL_1: + case WM831X_DC3_CONTROL_2: + case WM831X_DC3_ON_CONFIG: + case WM831X_DC3_SLEEP_CONTROL: + case WM831X_DC4_CONTROL: + case WM831X_DC4_SLEEP_CONTROL: + case WM831X_EPE1_CONTROL: + case WM831X_EPE2_CONTROL: + case WM831X_LDO1_CONTROL: + case WM831X_LDO1_ON_CONTROL: + case WM831X_LDO1_SLEEP_CONTROL: + case WM831X_LDO2_CONTROL: + case WM831X_LDO2_ON_CONTROL: + case WM831X_LDO2_SLEEP_CONTROL: + case WM831X_LDO3_CONTROL: + case WM831X_LDO3_ON_CONTROL: + case WM831X_LDO3_SLEEP_CONTROL: + case WM831X_LDO4_CONTROL: + case WM831X_LDO4_ON_CONTROL: + case WM831X_LDO4_SLEEP_CONTROL: + case WM831X_LDO5_CONTROL: + case WM831X_LDO5_ON_CONTROL: + case WM831X_LDO5_SLEEP_CONTROL: + case WM831X_LDO6_CONTROL: + case WM831X_LDO6_ON_CONTROL: + case WM831X_LDO6_SLEEP_CONTROL: + case WM831X_LDO7_CONTROL: + case WM831X_LDO7_ON_CONTROL: + case WM831X_LDO7_SLEEP_CONTROL: + case WM831X_LDO8_CONTROL: + case WM831X_LDO8_ON_CONTROL: + case WM831X_LDO8_SLEEP_CONTROL: + case WM831X_LDO9_CONTROL: + case WM831X_LDO9_ON_CONTROL: + case WM831X_LDO9_SLEEP_CONTROL: + case WM831X_LDO10_CONTROL: + case WM831X_LDO10_ON_CONTROL: + case WM831X_LDO10_SLEEP_CONTROL: + case WM831X_LDO11_ON_CONTROL: + case WM831X_LDO11_SLEEP_CONTROL: + case WM831X_POWER_GOOD_SOURCE_1: + case WM831X_POWER_GOOD_SOURCE_2: + case WM831X_CLOCK_CONTROL_1: + case WM831X_CLOCK_CONTROL_2: + case WM831X_FLL_CONTROL_1: + case WM831X_FLL_CONTROL_2: + case WM831X_FLL_CONTROL_3: + case WM831X_FLL_CONTROL_4: + case WM831X_FLL_CONTROL_5: + case WM831X_UNIQUE_ID_1: + case WM831X_UNIQUE_ID_2: + case WM831X_UNIQUE_ID_3: + case WM831X_UNIQUE_ID_4: + case WM831X_UNIQUE_ID_5: + case WM831X_UNIQUE_ID_6: + case WM831X_UNIQUE_ID_7: + case WM831X_UNIQUE_ID_8: + case WM831X_FACTORY_OTP_ID: + case WM831X_FACTORY_OTP_1: + case WM831X_FACTORY_OTP_2: + case WM831X_FACTORY_OTP_3: + case WM831X_FACTORY_OTP_4: + case WM831X_FACTORY_OTP_5: + case WM831X_CUSTOMER_OTP_ID: + case WM831X_DC1_OTP_CONTROL: + case WM831X_DC2_OTP_CONTROL: + case WM831X_DC3_OTP_CONTROL: + case WM831X_LDO1_2_OTP_CONTROL: + case WM831X_LDO3_4_OTP_CONTROL: + case WM831X_LDO5_6_OTP_CONTROL: + case WM831X_LDO7_8_OTP_CONTROL: + case WM831X_LDO9_10_OTP_CONTROL: + case WM831X_LDO11_EPE_CONTROL: + case WM831X_GPIO1_OTP_CONTROL: + case WM831X_GPIO2_OTP_CONTROL: + case WM831X_GPIO3_OTP_CONTROL: + case WM831X_GPIO4_OTP_CONTROL: + case WM831X_GPIO5_OTP_CONTROL: + case WM831X_GPIO6_OTP_CONTROL: + case WM831X_DBE_CHECK_DATA: + return true; + default: + return false; + } +} + +static bool wm831x_reg_writeable(struct device *dev, unsigned int reg) +{ + struct wm831x *wm831x = dev_get_drvdata(dev); + + if (wm831x_reg_locked(wm831x, reg)) + return false; + + switch (reg) { + case WM831X_SYSVDD_CONTROL: + case WM831X_THERMAL_MONITORING: + case WM831X_POWER_STATE: + case WM831X_WATCHDOG: + case WM831X_ON_PIN_CONTROL: + case WM831X_RESET_CONTROL: + case WM831X_CONTROL_INTERFACE: + case WM831X_SECURITY_KEY: + case WM831X_SOFTWARE_SCRATCH: + case WM831X_OTP_CONTROL: + case WM831X_GPIO_LEVEL: + case WM831X_INTERRUPT_STATUS_1: + case WM831X_INTERRUPT_STATUS_2: + case WM831X_INTERRUPT_STATUS_3: + case WM831X_INTERRUPT_STATUS_4: + case WM831X_INTERRUPT_STATUS_5: + case WM831X_IRQ_CONFIG: + case WM831X_SYSTEM_INTERRUPTS_MASK: + case WM831X_INTERRUPT_STATUS_1_MASK: + case WM831X_INTERRUPT_STATUS_2_MASK: + case WM831X_INTERRUPT_STATUS_3_MASK: + case WM831X_INTERRUPT_STATUS_4_MASK: + case WM831X_INTERRUPT_STATUS_5_MASK: + case WM831X_RTC_TIME_1: + case WM831X_RTC_TIME_2: + case WM831X_RTC_ALARM_1: + case WM831X_RTC_ALARM_2: + case WM831X_RTC_CONTROL: + case WM831X_RTC_TRIM: + case WM831X_TOUCH_CONTROL_1: + case WM831X_TOUCH_CONTROL_2: + case WM831X_AUXADC_CONTROL: + case WM831X_AUXADC_SOURCE: + case WM831X_COMPARATOR_CONTROL: + case WM831X_COMPARATOR_1: + case WM831X_COMPARATOR_2: + case WM831X_COMPARATOR_3: + case WM831X_COMPARATOR_4: + case WM831X_GPIO1_CONTROL: + case WM831X_GPIO2_CONTROL: + case WM831X_GPIO3_CONTROL: + case WM831X_GPIO4_CONTROL: + case WM831X_GPIO5_CONTROL: + case WM831X_GPIO6_CONTROL: + case WM831X_GPIO7_CONTROL: + case WM831X_GPIO8_CONTROL: + case WM831X_GPIO9_CONTROL: + case WM831X_GPIO10_CONTROL: + case WM831X_GPIO11_CONTROL: + case WM831X_GPIO12_CONTROL: + case WM831X_GPIO13_CONTROL: + case WM831X_GPIO14_CONTROL: + case WM831X_GPIO15_CONTROL: + case WM831X_GPIO16_CONTROL: + case WM831X_CHARGER_CONTROL_1: + case WM831X_CHARGER_CONTROL_2: + case WM831X_CHARGER_STATUS: + case WM831X_BACKUP_CHARGER_CONTROL: + case WM831X_STATUS_LED_1: + case WM831X_STATUS_LED_2: + case WM831X_CURRENT_SINK_1: + case WM831X_CURRENT_SINK_2: + case WM831X_DCDC_ENABLE: + case WM831X_LDO_ENABLE: + case WM831X_DC1_CONTROL_1: + case WM831X_DC1_CONTROL_2: + case WM831X_DC1_ON_CONFIG: + case WM831X_DC1_SLEEP_CONTROL: + case WM831X_DC1_DVS_CONTROL: + case WM831X_DC2_CONTROL_1: + case WM831X_DC2_CONTROL_2: + case WM831X_DC2_ON_CONFIG: + case WM831X_DC2_SLEEP_CONTROL: + case WM831X_DC2_DVS_CONTROL: + case WM831X_DC3_CONTROL_1: + case WM831X_DC3_CONTROL_2: + case WM831X_DC3_ON_CONFIG: + case WM831X_DC3_SLEEP_CONTROL: + case WM831X_DC4_CONTROL: + case WM831X_DC4_SLEEP_CONTROL: + case WM831X_EPE1_CONTROL: + case WM831X_EPE2_CONTROL: + case WM831X_LDO1_CONTROL: + case WM831X_LDO1_ON_CONTROL: + case WM831X_LDO1_SLEEP_CONTROL: + case WM831X_LDO2_CONTROL: + case WM831X_LDO2_ON_CONTROL: + case WM831X_LDO2_SLEEP_CONTROL: + case WM831X_LDO3_CONTROL: + case WM831X_LDO3_ON_CONTROL: + case WM831X_LDO3_SLEEP_CONTROL: + case WM831X_LDO4_CONTROL: + case WM831X_LDO4_ON_CONTROL: + case WM831X_LDO4_SLEEP_CONTROL: + case WM831X_LDO5_CONTROL: + case WM831X_LDO5_ON_CONTROL: + case WM831X_LDO5_SLEEP_CONTROL: + case WM831X_LDO6_CONTROL: + case WM831X_LDO6_ON_CONTROL: + case WM831X_LDO6_SLEEP_CONTROL: + case WM831X_LDO7_CONTROL: + case WM831X_LDO7_ON_CONTROL: + case WM831X_LDO7_SLEEP_CONTROL: + case WM831X_LDO8_CONTROL: + case WM831X_LDO8_ON_CONTROL: + case WM831X_LDO8_SLEEP_CONTROL: + case WM831X_LDO9_CONTROL: + case WM831X_LDO9_ON_CONTROL: + case WM831X_LDO9_SLEEP_CONTROL: + case WM831X_LDO10_CONTROL: + case WM831X_LDO10_ON_CONTROL: + case WM831X_LDO10_SLEEP_CONTROL: + case WM831X_LDO11_ON_CONTROL: + case WM831X_LDO11_SLEEP_CONTROL: + case WM831X_POWER_GOOD_SOURCE_1: + case WM831X_POWER_GOOD_SOURCE_2: + case WM831X_CLOCK_CONTROL_1: + case WM831X_CLOCK_CONTROL_2: + case WM831X_FLL_CONTROL_1: + case WM831X_FLL_CONTROL_2: + case WM831X_FLL_CONTROL_3: + case WM831X_FLL_CONTROL_4: + case WM831X_FLL_CONTROL_5: + return true; + default: + return false; + } +} + +static bool wm831x_reg_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM831X_SYSTEM_STATUS: + case WM831X_ON_SOURCE: + case WM831X_OFF_SOURCE: + case WM831X_GPIO_LEVEL: + case WM831X_SYSTEM_INTERRUPTS: + case WM831X_INTERRUPT_STATUS_1: + case WM831X_INTERRUPT_STATUS_2: + case WM831X_INTERRUPT_STATUS_3: + case WM831X_INTERRUPT_STATUS_4: + case WM831X_INTERRUPT_STATUS_5: + case WM831X_RTC_TIME_1: + case WM831X_RTC_TIME_2: + case WM831X_TOUCH_DATA_X: + case WM831X_TOUCH_DATA_Y: + case WM831X_TOUCH_DATA_Z: + case WM831X_AUXADC_DATA: + case WM831X_CHARGER_STATUS: + case WM831X_DCDC_STATUS: + case WM831X_LDO_STATUS: + case WM831X_DCDC_UV_STATUS: + case WM831X_LDO_UV_STATUS: + return true; + default: + return false; + } +} + /** * wm831x_reg_read: Read a single WM831x register. * @@ -1254,6 +1600,11 @@ static struct mfd_cell backlight_devs[] = { struct regmap_config wm831x_regmap_config = { .reg_bits = 16, .val_bits = 16, + + .max_register = WM831X_DBE_CHECK_DATA, + .readable_reg = wm831x_reg_readable, + .writeable_reg = wm831x_reg_writeable, + .volatile_reg = wm831x_reg_volatile, }; EXPORT_SYMBOL_GPL(wm831x_regmap_config); -- cgit v1.2.3 From 5570c2f709bdc455a2e8907919c1214ca8a21859 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 24 Jul 2011 11:44:19 +0100 Subject: mfd: Use device ID matching for WM831x SPI driver Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm831x-spi.c | 138 +++++++---------------------------------------- 1 file changed, 19 insertions(+), 119 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index dbe11472afce..5ea60cd860fc 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -23,29 +23,12 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi) { + const struct spi_device_id *id = spi_get_device_id(spi); struct wm831x *wm831x; enum wm831x_parent type; int ret; - /* Currently SPI support for ID tables is unmerged, we're faking it */ - if (strcmp(spi->modalias, "wm8310") == 0) - type = WM8310; - else if (strcmp(spi->modalias, "wm8311") == 0) - type = WM8311; - else if (strcmp(spi->modalias, "wm8312") == 0) - type = WM8312; - else if (strcmp(spi->modalias, "wm8320") == 0) - type = WM8320; - else if (strcmp(spi->modalias, "wm8321") == 0) - type = WM8321; - else if (strcmp(spi->modalias, "wm8325") == 0) - type = WM8325; - else if (strcmp(spi->modalias, "wm8326") == 0) - type = WM8326; - else { - dev_err(&spi->dev, "Unknown device type\n"); - return -EINVAL; - } + type = (enum wm831x_parent)id->driver_data; wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); if (wm831x == NULL) @@ -57,7 +40,7 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, wm831x); wm831x->dev = &spi->dev; - wm831x->regmap = regmap_init_spi(wm831x->dev, &wm831x_regmap_config); + wm831x->regmap = regmap_init_spi(spi, &wm831x_regmap_config); if (IS_ERR(wm831x->regmap)) { ret = PTR_ERR(wm831x->regmap); dev_err(wm831x->dev, "Failed to allocate register map: %d\n", @@ -90,79 +73,26 @@ static const struct dev_pm_ops wm831x_spi_pm = { .suspend = wm831x_spi_suspend, }; -static struct spi_driver wm8310_spi_driver = { - .driver = { - .name = "wm8310", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - .pm = &wm831x_spi_pm, - }, - .probe = wm831x_spi_probe, - .remove = __devexit_p(wm831x_spi_remove), -}; - -static struct spi_driver wm8311_spi_driver = { - .driver = { - .name = "wm8311", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - .pm = &wm831x_spi_pm, - }, - .probe = wm831x_spi_probe, - .remove = __devexit_p(wm831x_spi_remove), +static const struct spi_device_id wm831x_spi_ids[] = { + { "wm8310", WM8310 }, + { "wm8311", WM8311 }, + { "wm8312", WM8312 }, + { "wm8320", WM8320 }, + { "wm8321", WM8321 }, + { "wm8325", WM8325 }, + { "wm8326", WM8326 }, + { }, }; +MODULE_DEVICE_TABLE(spi, wm831x_spi_id); -static struct spi_driver wm8312_spi_driver = { +static struct spi_driver wm831x_spi_driver = { .driver = { - .name = "wm8312", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - .pm = &wm831x_spi_pm, - }, - .probe = wm831x_spi_probe, - .remove = __devexit_p(wm831x_spi_remove), -}; - -static struct spi_driver wm8320_spi_driver = { - .driver = { - .name = "wm8320", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - .pm = &wm831x_spi_pm, - }, - .probe = wm831x_spi_probe, - .remove = __devexit_p(wm831x_spi_remove), -}; - -static struct spi_driver wm8321_spi_driver = { - .driver = { - .name = "wm8321", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - .pm = &wm831x_spi_pm, - }, - .probe = wm831x_spi_probe, - .remove = __devexit_p(wm831x_spi_remove), -}; - -static struct spi_driver wm8325_spi_driver = { - .driver = { - .name = "wm8325", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - .pm = &wm831x_spi_pm, - }, - .probe = wm831x_spi_probe, - .remove = __devexit_p(wm831x_spi_remove), -}; - -static struct spi_driver wm8326_spi_driver = { - .driver = { - .name = "wm8326", + .name = "wm831x", .bus = &spi_bus_type, .owner = THIS_MODULE, .pm = &wm831x_spi_pm, }, + .id_table = wm831x_spi_ids, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), }; @@ -171,33 +101,9 @@ static int __init wm831x_spi_init(void) { int ret; - ret = spi_register_driver(&wm8310_spi_driver); - if (ret != 0) - pr_err("Failed to register WM8310 SPI driver: %d\n", ret); - - ret = spi_register_driver(&wm8311_spi_driver); - if (ret != 0) - pr_err("Failed to register WM8311 SPI driver: %d\n", ret); - - ret = spi_register_driver(&wm8312_spi_driver); - if (ret != 0) - pr_err("Failed to register WM8312 SPI driver: %d\n", ret); - - ret = spi_register_driver(&wm8320_spi_driver); - if (ret != 0) - pr_err("Failed to register WM8320 SPI driver: %d\n", ret); - - ret = spi_register_driver(&wm8321_spi_driver); - if (ret != 0) - pr_err("Failed to register WM8321 SPI driver: %d\n", ret); - - ret = spi_register_driver(&wm8325_spi_driver); - if (ret != 0) - pr_err("Failed to register WM8325 SPI driver: %d\n", ret); - - ret = spi_register_driver(&wm8326_spi_driver); + ret = spi_register_driver(&wm831x_spi_driver); if (ret != 0) - pr_err("Failed to register WM8326 SPI driver: %d\n", ret); + pr_err("Failed to register WM831x SPI driver: %d\n", ret); return 0; } @@ -205,13 +111,7 @@ subsys_initcall(wm831x_spi_init); static void __exit wm831x_spi_exit(void) { - spi_unregister_driver(&wm8326_spi_driver); - spi_unregister_driver(&wm8325_spi_driver); - spi_unregister_driver(&wm8321_spi_driver); - spi_unregister_driver(&wm8320_spi_driver); - spi_unregister_driver(&wm8312_spi_driver); - spi_unregister_driver(&wm8311_spi_driver); - spi_unregister_driver(&wm8310_spi_driver); + spi_unregister_driver(&wm831x_spi_driver); } module_exit(wm831x_spi_exit); -- cgit v1.2.3 From d6c645fc00777a6f8a7df1f580065ec30c71be7b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 Jun 2011 13:02:27 +0100 Subject: mfd: Convert WM8994 to use new register map API Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/wm8994-core.c | 178 ++++++++-------------------------------- include/linux/mfd/wm8994/core.h | 9 +- 3 files changed, 36 insertions(+), 152 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cfae5c594d24..a67adcbd0fa1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -490,6 +490,7 @@ config MFD_WM8350_I2C config MFD_WM8994 bool "Support Wolfson Microelectronics WM8994" select MFD_CORE + select REGMAP_I2C depends on I2C=y && GENERIC_HARDIRQS help The WM8994 is a highly integrated hi-fi CODEC designed for diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 96479c9b1728..bfde4e8ec638 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -16,9 +16,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -29,22 +31,7 @@ static int wm8994_read(struct wm8994 *wm8994, unsigned short reg, int bytes, void *dest) { - int ret, i; - u16 *buf = dest; - - BUG_ON(bytes % 2); - BUG_ON(bytes <= 0); - - ret = wm8994->read_dev(wm8994, reg, bytes, dest); - if (ret < 0) - return ret; - - for (i = 0; i < bytes / 2; i++) { - dev_vdbg(wm8994->dev, "Read %04x from R%d(0x%x)\n", - be16_to_cpu(buf[i]), reg + i, reg + i); - } - - return 0; + return regmap_raw_read(wm8994->regmap, reg, dest, bytes); } /** @@ -55,19 +42,15 @@ static int wm8994_read(struct wm8994 *wm8994, unsigned short reg, */ int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg) { - unsigned short val; + unsigned int val; int ret; - mutex_lock(&wm8994->io_lock); - - ret = wm8994_read(wm8994, reg, 2, &val); - - mutex_unlock(&wm8994->io_lock); + ret = regmap_read(wm8994->regmap, reg, &val); if (ret < 0) return ret; else - return be16_to_cpu(val); + return val; } EXPORT_SYMBOL_GPL(wm8994_reg_read); @@ -82,33 +65,13 @@ EXPORT_SYMBOL_GPL(wm8994_reg_read); int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg, int count, u16 *buf) { - int ret; - - mutex_lock(&wm8994->io_lock); - - ret = wm8994_read(wm8994, reg, count * 2, buf); - - mutex_unlock(&wm8994->io_lock); - - return ret; + return regmap_bulk_read(wm8994->regmap, reg, buf, count); } -EXPORT_SYMBOL_GPL(wm8994_bulk_read); static int wm8994_write(struct wm8994 *wm8994, unsigned short reg, int bytes, const void *src) { - const u16 *buf = src; - int i; - - BUG_ON(bytes % 2); - BUG_ON(bytes <= 0); - - for (i = 0; i < bytes / 2; i++) { - dev_vdbg(wm8994->dev, "Write %04x to R%d(0x%x)\n", - be16_to_cpu(buf[i]), reg + i, reg + i); - } - - return wm8994->write_dev(wm8994, reg, bytes, src); + return regmap_raw_write(wm8994->regmap, reg, src, bytes); } /** @@ -121,17 +84,7 @@ static int wm8994_write(struct wm8994 *wm8994, unsigned short reg, int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg, unsigned short val) { - int ret; - - val = cpu_to_be16(val); - - mutex_lock(&wm8994->io_lock); - - ret = wm8994_write(wm8994, reg, 2, &val); - - mutex_unlock(&wm8994->io_lock); - - return ret; + return regmap_write(wm8994->regmap, reg, val); } EXPORT_SYMBOL_GPL(wm8994_reg_write); @@ -146,15 +99,7 @@ EXPORT_SYMBOL_GPL(wm8994_reg_write); int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg, int count, const u16 *buf) { - int ret; - - mutex_lock(&wm8994->io_lock); - - ret = wm8994_write(wm8994, reg, count * 2, buf); - - mutex_unlock(&wm8994->io_lock); - - return ret; + return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16)); } EXPORT_SYMBOL_GPL(wm8994_bulk_write); @@ -169,28 +114,7 @@ EXPORT_SYMBOL_GPL(wm8994_bulk_write); int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg, unsigned short mask, unsigned short val) { - int ret; - u16 r; - - mutex_lock(&wm8994->io_lock); - - ret = wm8994_read(wm8994, reg, 2, &r); - if (ret < 0) - goto out; - - r = be16_to_cpu(r); - - r &= ~mask; - r |= val; - - r = cpu_to_be16(r); - - ret = wm8994_write(wm8994, reg, 2, &r); - -out: - mutex_unlock(&wm8994->io_lock); - - return ret; + return regmap_update_bits(wm8994->regmap, reg, mask, val); } EXPORT_SYMBOL_GPL(wm8994_set_bits); @@ -378,6 +302,11 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo) } #endif +static struct regmap_config wm8994_regmap_config = { + .reg_bits = 16, + .val_bits = 16, +}; + /* * Instantiate the generic non-control parts of the device. */ @@ -387,7 +316,6 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) const char *devname; int ret, i; - mutex_init(&wm8994->io_lock); dev_set_drvdata(wm8994->dev, wm8994); /* Add the on-chip regulators first for bootstrapping */ @@ -397,7 +325,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) NULL, 0); if (ret != 0) { dev_err(wm8994->dev, "Failed to add children: %d\n", ret); - goto err; + goto err_regmap; } switch (wm8994->type) { @@ -409,7 +337,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) break; default: BUG(); - goto err; + goto err_regmap; } wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) * @@ -417,7 +345,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) GFP_KERNEL); if (!wm8994->supplies) { ret = -ENOMEM; - goto err; + goto err_regmap; } switch (wm8994->type) { @@ -431,7 +359,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) break; default: BUG(); - goto err; + goto err_regmap; } ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies, @@ -554,7 +482,8 @@ err_get: regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); err_supplies: kfree(wm8994->supplies); -err: +err_regmap: + regmap_exit(wm8994->regmap); mfd_remove_devices(wm8994->dev); kfree(wm8994); return ret; @@ -569,62 +498,15 @@ static void wm8994_device_exit(struct wm8994 *wm8994) wm8994->supplies); regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); kfree(wm8994->supplies); + regmap_exit(wm8994->regmap); kfree(wm8994); } -static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg, - int bytes, void *dest) -{ - struct i2c_client *i2c = wm8994->control_data; - int ret; - u16 r = cpu_to_be16(reg); - - ret = i2c_master_send(i2c, (unsigned char *)&r, 2); - if (ret < 0) - return ret; - if (ret != 2) - return -EIO; - - ret = i2c_master_recv(i2c, dest, bytes); - if (ret < 0) - return ret; - if (ret != bytes) - return -EIO; - return 0; -} - -static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg, - int bytes, const void *src) -{ - struct i2c_client *i2c = wm8994->control_data; - struct i2c_msg xfer[2]; - int ret; - - reg = cpu_to_be16(reg); - - xfer[0].addr = i2c->addr; - xfer[0].flags = 0; - xfer[0].len = 2; - xfer[0].buf = (char *)® - - xfer[1].addr = i2c->addr; - xfer[1].flags = I2C_M_NOSTART; - xfer[1].len = bytes; - xfer[1].buf = (char *)src; - - ret = i2c_transfer(i2c->adapter, xfer, 2); - if (ret < 0) - return ret; - if (ret != 2) - return -EIO; - - return 0; -} - static int wm8994_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8994 *wm8994; + int ret; wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL); if (wm8994 == NULL) @@ -632,12 +514,18 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8994); wm8994->dev = &i2c->dev; - wm8994->control_data = i2c; - wm8994->read_dev = wm8994_i2c_read_device; - wm8994->write_dev = wm8994_i2c_write_device; wm8994->irq = i2c->irq; wm8994->type = id->driver_data; + wm8994->regmap = regmap_init_i2c(i2c, &wm8994_regmap_config); + if (IS_ERR(wm8994->regmap)) { + ret = PTR_ERR(wm8994->regmap); + dev_err(wm8994->dev, "Failed to allocate register map: %d\n", + ret); + kfree(wm8994); + return ret; + } + return wm8994_device_init(wm8994, i2c->irq); } diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index f0b69cdae41c..45df450d869f 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -24,6 +24,7 @@ enum wm8994_type { struct regulator_dev; struct regulator_bulk_data; +struct regmap; #define WM8994_NUM_GPIO_REGS 11 #define WM8994_NUM_LDO_REGS 2 @@ -50,18 +51,12 @@ struct regulator_bulk_data; #define WM8994_IRQ_GPIO(x) (x + WM8994_IRQ_TEMP_WARN) struct wm8994 { - struct mutex io_lock; struct mutex irq_lock; enum wm8994_type type; struct device *dev; - int (*read_dev)(struct wm8994 *wm8994, unsigned short reg, - int bytes, void *dest); - int (*write_dev)(struct wm8994 *wm8994, unsigned short reg, - int bytes, const void *src); - - void *control_data; + struct regmap *regmap; int gpio_base; int irq_base; -- cgit v1.2.3 From 50eeef5d3cea5afcced17a0410e8b0bf88997845 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 9 Aug 2011 16:34:29 +0900 Subject: mfd: Convert WM8400 to regmap API Signed-off-by: Mark Brown --- drivers/mfd/wm8400-core.c | 106 ++++++++++--------------------------- include/linux/mfd/wm8400-private.h | 7 ++- 2 files changed, 32 insertions(+), 81 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 597f82edacaa..e06ba9440cdb 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -13,11 +13,13 @@ */ #include +#include #include #include #include #include #include +#include #include static struct { @@ -123,14 +125,9 @@ static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest) /* If there are any volatile reads then read back the entire block */ for (i = reg; i < reg + num_regs; i++) if (reg_data[i].vol) { - ret = wm8400->read_dev(wm8400->io_data, reg, - num_regs, dest); - if (ret != 0) - return ret; - for (i = 0; i < num_regs; i++) - dest[i] = be16_to_cpu(dest[i]); - - return 0; + ret = regmap_bulk_read(wm8400->regmap, reg, dest, + num_regs); + return ret; } /* Otherwise use the cache */ @@ -149,14 +146,11 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs, for (i = 0; i < num_regs; i++) { BUG_ON(!reg_data[reg + i].writable); wm8400->reg_cache[reg + i] = src[i]; - src[i] = cpu_to_be16(src[i]); + ret = regmap_write(wm8400->regmap, reg, src[i]); + if (ret != 0) + return ret; } - /* Do the actual I/O */ - ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src); - if (ret != 0) - return -EIO; - return 0; } @@ -270,14 +264,14 @@ static int wm8400_init(struct wm8400 *wm8400, dev_set_drvdata(wm8400->dev, wm8400); /* Check that this is actually a WM8400 */ - ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, ®); + ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i); if (ret != 0) { dev_err(wm8400->dev, "Chip ID register read failed\n"); return -EIO; } - if (be16_to_cpu(reg) != reg_data[WM8400_RESET_ID].default_val) { + if (i != reg_data[WM8400_RESET_ID].default_val) { dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", - be16_to_cpu(reg)); + reg); return -ENODEV; } @@ -285,9 +279,8 @@ static int wm8400_init(struct wm8400 *wm8400, * is a PMIC we can't reset it safely so initialise the register * cache from the hardware. */ - ret = wm8400->read_dev(wm8400->io_data, 0, - ARRAY_SIZE(wm8400->reg_cache), - wm8400->reg_cache); + ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache, + ARRAY_SIZE(wm8400->reg_cache)); if (ret != 0) { dev_err(wm8400->dev, "Register cache read failed\n"); return -EIO; @@ -337,60 +330,13 @@ static void wm8400_release(struct wm8400 *wm8400) mfd_remove_devices(wm8400->dev); } -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -static int wm8400_i2c_read(void *io_data, char reg, int count, u16 *dest) -{ - struct i2c_client *i2c = io_data; - 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 = count * sizeof(u16); - xfer[1].buf = (u8 *)dest; - - ret = i2c_transfer(i2c->adapter, xfer, 2); - if (ret == 2) - ret = 0; - else if (ret >= 0) - ret = -EIO; - - return ret; -} - -static int wm8400_i2c_write(void *io_data, char reg, int count, const u16 *src) -{ - struct i2c_client *i2c = io_data; - u8 *msg; - int ret; - - /* We add 1 byte for device register - ideally I2C would gather. */ - msg = kmalloc((count * sizeof(u16)) + 1, GFP_KERNEL); - if (msg == NULL) - return -ENOMEM; - - msg[0] = reg; - memcpy(&msg[1], src, count * sizeof(u16)); - - ret = i2c_master_send(i2c, msg, (count * sizeof(u16)) + 1); - - if (ret == (count * 2) + 1) - ret = 0; - else if (ret >= 0) - ret = -EIO; - - kfree(msg); - - return ret; -} +static const struct regmap_config wm8400_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = WM8400_REGISTER_COUNT - 1, +}; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static int wm8400_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -403,18 +349,23 @@ static int wm8400_i2c_probe(struct i2c_client *i2c, goto err; } - wm8400->io_data = i2c; - wm8400->read_dev = wm8400_i2c_read; - wm8400->write_dev = wm8400_i2c_write; + wm8400->regmap = regmap_init_i2c(i2c, &wm8400_regmap_config); + if (IS_ERR(wm8400->regmap)) { + ret = PTR_ERR(wm8400->regmap); + goto struct_err; + } + wm8400->dev = &i2c->dev; i2c_set_clientdata(i2c, wm8400); ret = wm8400_init(wm8400, i2c->dev.platform_data); if (ret != 0) - goto struct_err; + goto map_err; return 0; +map_err: + regmap_exit(wm8400->regmap); struct_err: kfree(wm8400); err: @@ -426,6 +377,7 @@ static int wm8400_i2c_remove(struct i2c_client *i2c) struct wm8400 *wm8400 = i2c_get_clientdata(i2c); wm8400_release(wm8400); + regmap_exit(wm8400->regmap); kfree(wm8400); return 0; diff --git a/include/linux/mfd/wm8400-private.h b/include/linux/mfd/wm8400-private.h index 2aab4e93a5c9..0147b6968510 100644 --- a/include/linux/mfd/wm8400-private.h +++ b/include/linux/mfd/wm8400-private.h @@ -25,16 +25,15 @@ #include #include +struct regmap; + #define WM8400_REGISTER_COUNT 0x55 struct wm8400 { struct device *dev; - int (*read_dev)(void *data, char reg, int count, u16 *dst); - int (*write_dev)(void *data, char reg, int count, const u16 *src); - struct mutex io_lock; - void *io_data; + struct regmap *regmap; u16 reg_cache[WM8400_REGISTER_COUNT]; -- cgit v1.2.3 From 523d9cfbb2094e095ff08a01c4eac10cc7d287c3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 15 Sep 2011 18:54:53 +0200 Subject: mfd: Support software initiated shutdown of WM831x PMICs In systems where there is no hardware signal from the processor to the PMIC to initiate the final power off sequence we must initiate the shutdown with a register write to the PMIC. Support such systems in the driver. Since this may prevent a full shutdown of the system platform data is used to enable the feature. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 11 +++++++++++ drivers/mfd/wm831x-i2c.c | 8 ++++++++ drivers/mfd/wm831x-spi.c | 8 ++++++++ include/linux/mfd/wm831x/core.h | 3 +++ include/linux/mfd/wm831x/pdata.h | 3 +++ 5 files changed, 33 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 9338f8dcbb83..e758c89ac5bb 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -25,6 +25,7 @@ #include #include #include +#include #include /* Current settings - values are 2*2^(reg_val/4) microamps. These are @@ -1621,6 +1622,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) mutex_init(&wm831x->io_lock); mutex_init(&wm831x->key_lock); dev_set_drvdata(wm831x->dev, wm831x); + wm831x->soft_shutdown = pdata->soft_shutdown; ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); if (ret < 0) { @@ -1922,6 +1924,15 @@ int wm831x_device_suspend(struct wm831x *wm831x) return 0; } +void wm831x_device_shutdown(struct wm831x *wm831x) +{ + if (wm831x->soft_shutdown) { + dev_info(wm831x->dev, "Initiating shutdown...\n"); + wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON, 0); + } +} +EXPORT_SYMBOL_GPL(wm831x_device_shutdown); + MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Brown"); diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index 3ec6085d5fc0..ac8da1d439da 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -65,6 +65,13 @@ static int wm831x_i2c_suspend(struct device *dev) return wm831x_device_suspend(wm831x); } +static void wm831x_i2c_shutdown(struct i2c_client *i2c) +{ + struct wm831x *wm831x = i2c_get_clientdata(i2c); + + wm831x_device_shutdown(wm831x); +} + static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8310", WM8310 }, { "wm8311", WM8311 }, @@ -89,6 +96,7 @@ static struct i2c_driver wm831x_i2c_driver = { }, .probe = wm831x_i2c_probe, .remove = wm831x_i2c_remove, + .shutdown = wm831x_i2c_shutdown, .id_table = wm831x_i2c_id, }; diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 5ea60cd860fc..8d6a9a969dbc 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -68,6 +68,13 @@ static int wm831x_spi_suspend(struct device *dev) return wm831x_device_suspend(wm831x); } +static void wm831x_spi_shutdown(struct spi_device *spi) +{ + struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + + wm831x_device_shutdown(wm831x); +} + static const struct dev_pm_ops wm831x_spi_pm = { .freeze = wm831x_spi_suspend, .suspend = wm831x_spi_suspend, @@ -95,6 +102,7 @@ static struct spi_driver wm831x_spi_driver = { .id_table = wm831x_spi_ids, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), + .shutdown = wm831x_spi_shutdown, }; static int __init wm831x_spi_init(void) diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 44acdb25681b..ed8fe0d04097 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -371,6 +371,8 @@ struct wm831x { int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */ int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */ + bool soft_shutdown; + /* Chip revision based flags */ unsigned has_gpio_ena:1; /* Has GPIO enable bit */ unsigned has_cs_sts:1; /* Has current sink status bit */ @@ -409,6 +411,7 @@ int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg, int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq); void wm831x_device_exit(struct wm831x *wm831x); int wm831x_device_suspend(struct wm831x *wm831x); +void wm831x_device_shutdown(struct wm831x *wm831x); int wm831x_irq_init(struct wm831x *wm831x, int irq); void wm831x_irq_exit(struct wm831x *wm831x); void wm831x_auxadc_init(struct wm831x *wm831x); diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h index 0ba24599fe51..1d7a3f7b3b5d 100644 --- a/include/linux/mfd/wm831x/pdata.h +++ b/include/linux/mfd/wm831x/pdata.h @@ -123,6 +123,9 @@ struct wm831x_pdata { /** Disable the touchscreen */ bool disable_touch; + /** The driver should initiate a power off sequence during shutdown */ + bool soft_shutdown; + int irq_base; int gpio_base; int gpio_defaults[WM831X_GPIO_NUM]; -- cgit v1.2.3 From 7cccbdc84487616c3dbe493b04bfa1f362f4bc56 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 9 Oct 2011 13:38:06 +0100 Subject: mfd: Enable rbtree cache for wm831x devices Most useful with the regulators where we're doing a lot of read/modify/write updates in potentially performance critical paths. Providing some defaults would make this slightly better but this is a win right now. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index e758c89ac5bb..0a2b8d41a702 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -1602,6 +1602,8 @@ struct regmap_config wm831x_regmap_config = { .reg_bits = 16, .val_bits = 16, + .cache_type = REGCACHE_RBTREE, + .max_register = WM831X_DBE_CHECK_DATA, .readable_reg = wm831x_reg_readable, .writeable_reg = wm831x_reg_writeable, -- cgit v1.2.3