diff options
Diffstat (limited to 'drivers/mfd/wm831x-core.c')
| -rw-r--r-- | drivers/mfd/wm831x-core.c | 779 |
1 files changed, 501 insertions, 278 deletions
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 7d2563fc15c..28366a90e1a 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -18,12 +18,14 @@ #include <linux/delay.h> #include <linux/mfd/core.h> #include <linux/slab.h> +#include <linux/err.h> #include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/pdata.h> #include <linux/mfd/wm831x/irq.h> #include <linux/mfd/wm831x/auxadc.h> #include <linux/mfd/wm831x/otp.h> +#include <linux/mfd/wm831x/pmu.h> #include <linux/mfd/wm831x/regulator.h> /* Current settings - values are 2*2^(reg_val/4) microamps. These are @@ -160,27 +162,350 @@ 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) +static bool wm831x_reg_readable(struct device *dev, unsigned int reg) { - int ret, i; - u16 *buf = dest; - - BUG_ON(bytes % 2); - BUG_ON(bytes <= 0); + 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; + } +} - ret = wm831x->read_dev(wm831x, reg, bytes, dest); - if (ret < 0) - return ret; +static bool wm831x_reg_writeable(struct device *dev, unsigned int reg) +{ + struct wm831x *wm831x = dev_get_drvdata(dev); - for (i = 0; i < bytes / 2; i++) { - buf[i] = be16_to_cpu(buf[i]); + if (wm831x_reg_locked(wm831x, reg)) + return false; - dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n", - buf[i], reg + i, reg + i); + 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; } +} - return 0; +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; + } } /** @@ -191,14 +516,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 +539,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 +547,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 +558,12 @@ 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]); + if (ret != 0) + return ret; } - return wm831x->write_dev(wm831x, reg, bytes, src); + return 0; } /** @@ -286,171 +600,25 @@ 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; - - 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; } EXPORT_SYMBOL_GPL(wm831x_set_bits); -/** - * wm831x_auxadc_read: Read a value from the WM831x AUXADC - * - * @wm831x: Device to read from. - * @input: AUXADC input to read. - */ -int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) -{ - int ret, src, irq_masked, timeout; - - /* Are we using the interrupt? */ - irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK); - irq_masked &= WM831X_AUXADC_DATA_EINT; - - mutex_lock(&wm831x->auxadc_lock); - - ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, - WM831X_AUX_ENA, WM831X_AUX_ENA); - if (ret < 0) { - dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret); - goto out; - } - - /* We force a single source at present */ - src = input; - ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE, - 1 << src); - if (ret < 0) { - dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret); - goto out; - } - - /* Clear any notification from a very late arriving interrupt */ - try_wait_for_completion(&wm831x->auxadc_done); - - ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, - WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA); - if (ret < 0) { - dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret); - goto disable; - } - - if (irq_masked) { - /* If we're not using interrupts then poll the - * interrupt status register */ - timeout = 5; - while (timeout) { - msleep(1); - - ret = wm831x_reg_read(wm831x, - WM831X_INTERRUPT_STATUS_1); - if (ret < 0) { - dev_err(wm831x->dev, - "ISR 1 read failed: %d\n", ret); - goto disable; - } - - /* Did it complete? */ - if (ret & WM831X_AUXADC_DATA_EINT) { - wm831x_reg_write(wm831x, - WM831X_INTERRUPT_STATUS_1, - WM831X_AUXADC_DATA_EINT); - break; - } else { - dev_err(wm831x->dev, - "AUXADC conversion timeout\n"); - ret = -EBUSY; - goto disable; - } - } - } else { - /* If we are using interrupts then wait for the - * interrupt to complete. Use an extremely long - * timeout to handle situations with heavy load where - * the notification of the interrupt may be delayed by - * threaded IRQ handling. */ - if (!wait_for_completion_timeout(&wm831x->auxadc_done, - msecs_to_jiffies(500))) { - dev_err(wm831x->dev, "Timed out waiting for AUXADC\n"); - ret = -EBUSY; - goto disable; - } - } - - ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); - if (ret < 0) { - dev_err(wm831x->dev, "Failed to read AUXADC data: %d\n", ret); - } else { - src = ((ret & WM831X_AUX_DATA_SRC_MASK) - >> WM831X_AUX_DATA_SRC_SHIFT) - 1; - - if (src == 14) - src = WM831X_AUX_CAL; - - if (src != input) { - dev_err(wm831x->dev, "Data from source %d not %d\n", - src, input); - ret = -EINVAL; - } else { - ret &= WM831X_AUX_DATA_MASK; - } - } - -disable: - wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0); -out: - mutex_unlock(&wm831x->auxadc_lock); - return ret; -} -EXPORT_SYMBOL_GPL(wm831x_auxadc_read); - -static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) -{ - struct wm831x *wm831x = irq_data; - - complete(&wm831x->auxadc_done); - - return IRQ_HANDLED; -} - -/** - * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC - * - * @wm831x: Device to read from. - * @input: AUXADC input to read. - */ -int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input) -{ - int ret; - - ret = wm831x_auxadc_read(wm831x, input); - if (ret < 0) - return ret; - - ret *= 1465; - - return ret; -} -EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv); - static struct resource wm831x_dcdc1_resources[] = { { .start = WM831X_DC1_CONTROL_1, .end = WM831X_DC1_DVS_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -471,7 +639,7 @@ static struct resource wm831x_dcdc2_resources[] = { { .start = WM831X_DC2_CONTROL_1, .end = WM831X_DC2_DVS_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -491,7 +659,7 @@ static struct resource wm831x_dcdc3_resources[] = { { .start = WM831X_DC3_CONTROL_1, .end = WM831X_DC3_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -505,7 +673,7 @@ static struct resource wm831x_dcdc4_resources[] = { { .start = WM831X_DC4_CONTROL, .end = WM831X_DC4_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -519,7 +687,7 @@ static struct resource wm8320_dcdc4_buck_resources[] = { { .start = WM831X_DC4_CONTROL, .end = WM832X_DC4_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -541,7 +709,7 @@ static struct resource wm831x_isink1_resources[] = { { .start = WM831X_CURRENT_SINK_1, .end = WM831X_CURRENT_SINK_1, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .start = WM831X_IRQ_CS1, @@ -554,7 +722,7 @@ static struct resource wm831x_isink2_resources[] = { { .start = WM831X_CURRENT_SINK_2, .end = WM831X_CURRENT_SINK_2, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .start = WM831X_IRQ_CS2, @@ -567,7 +735,7 @@ static struct resource wm831x_ldo1_resources[] = { { .start = WM831X_LDO1_CONTROL, .end = WM831X_LDO1_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -581,7 +749,7 @@ static struct resource wm831x_ldo2_resources[] = { { .start = WM831X_LDO2_CONTROL, .end = WM831X_LDO2_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -595,7 +763,7 @@ static struct resource wm831x_ldo3_resources[] = { { .start = WM831X_LDO3_CONTROL, .end = WM831X_LDO3_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -609,7 +777,7 @@ static struct resource wm831x_ldo4_resources[] = { { .start = WM831X_LDO4_CONTROL, .end = WM831X_LDO4_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -623,7 +791,7 @@ static struct resource wm831x_ldo5_resources[] = { { .start = WM831X_LDO5_CONTROL, .end = WM831X_LDO5_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -637,7 +805,7 @@ static struct resource wm831x_ldo6_resources[] = { { .start = WM831X_LDO6_CONTROL, .end = WM831X_LDO6_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -651,7 +819,7 @@ static struct resource wm831x_ldo7_resources[] = { { .start = WM831X_LDO7_CONTROL, .end = WM831X_LDO7_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -665,7 +833,7 @@ static struct resource wm831x_ldo8_resources[] = { { .start = WM831X_LDO8_CONTROL, .end = WM831X_LDO8_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -679,7 +847,7 @@ static struct resource wm831x_ldo9_resources[] = { { .start = WM831X_LDO9_CONTROL, .end = WM831X_LDO9_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -693,7 +861,7 @@ static struct resource wm831x_ldo10_resources[] = { { .start = WM831X_LDO10_CONTROL, .end = WM831X_LDO10_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, { .name = "UV", @@ -707,7 +875,7 @@ static struct resource wm831x_ldo11_resources[] = { { .start = WM831X_LDO11_ON_CONTROL, .end = WM831X_LDO11_SLEEP_CONTROL, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, }; @@ -808,7 +976,7 @@ static struct resource wm831x_status1_resources[] = { { .start = WM831X_STATUS_LED_1, .end = WM831X_STATUS_LED_1, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, }; @@ -816,7 +984,7 @@ static struct resource wm831x_status2_resources[] = { { .start = WM831X_STATUS_LED_2, .end = WM831X_STATUS_LED_2, - .flags = IORESOURCE_IO, + .flags = IORESOURCE_REG, }, }; @@ -843,7 +1011,7 @@ static struct resource wm831x_wdt_resources[] = { }, }; -static struct mfd_cell wm8310_devs[] = { +static const struct mfd_cell wm8310_devs[] = { { .name = "wm831x-backup", }, @@ -872,6 +1040,9 @@ static struct mfd_cell wm8310_devs[] = { .resources = wm831x_dcdc4_resources, }, { + .name = "wm831x-clk", + }, + { .name = "wm831x-epe", .id = 1, }, @@ -976,11 +1147,6 @@ static struct mfd_cell wm8310_devs[] = { .resources = wm831x_power_resources, }, { - .name = "wm831x-rtc", - .num_resources = ARRAY_SIZE(wm831x_rtc_resources), - .resources = wm831x_rtc_resources, - }, - { .name = "wm831x-status", .id = 1, .num_resources = ARRAY_SIZE(wm831x_status1_resources), @@ -999,7 +1165,7 @@ static struct mfd_cell wm8310_devs[] = { }, }; -static struct mfd_cell wm8311_devs[] = { +static const struct mfd_cell wm8311_devs[] = { { .name = "wm831x-backup", }, @@ -1028,6 +1194,9 @@ static struct mfd_cell wm8311_devs[] = { .resources = wm831x_dcdc4_resources, }, { + .name = "wm831x-clk", + }, + { .name = "wm831x-epe", .id = 1, }, @@ -1108,11 +1277,6 @@ static struct mfd_cell wm8311_devs[] = { .resources = wm831x_power_resources, }, { - .name = "wm831x-rtc", - .num_resources = ARRAY_SIZE(wm831x_rtc_resources), - .resources = wm831x_rtc_resources, - }, - { .name = "wm831x-status", .id = 1, .num_resources = ARRAY_SIZE(wm831x_status1_resources), @@ -1125,18 +1289,13 @@ static struct mfd_cell wm8311_devs[] = { .resources = wm831x_status2_resources, }, { - .name = "wm831x-touch", - .num_resources = ARRAY_SIZE(wm831x_touch_resources), - .resources = wm831x_touch_resources, - }, - { .name = "wm831x-watchdog", .num_resources = ARRAY_SIZE(wm831x_wdt_resources), .resources = wm831x_wdt_resources, }, }; -static struct mfd_cell wm8312_devs[] = { +static const struct mfd_cell wm8312_devs[] = { { .name = "wm831x-backup", }, @@ -1165,6 +1324,9 @@ static struct mfd_cell wm8312_devs[] = { .resources = wm831x_dcdc4_resources, }, { + .name = "wm831x-clk", + }, + { .name = "wm831x-epe", .id = 1, }, @@ -1269,11 +1431,6 @@ static struct mfd_cell wm8312_devs[] = { .resources = wm831x_power_resources, }, { - .name = "wm831x-rtc", - .num_resources = ARRAY_SIZE(wm831x_rtc_resources), - .resources = wm831x_rtc_resources, - }, - { .name = "wm831x-status", .id = 1, .num_resources = ARRAY_SIZE(wm831x_status1_resources), @@ -1286,18 +1443,13 @@ static struct mfd_cell wm8312_devs[] = { .resources = wm831x_status2_resources, }, { - .name = "wm831x-touch", - .num_resources = ARRAY_SIZE(wm831x_touch_resources), - .resources = wm831x_touch_resources, - }, - { .name = "wm831x-watchdog", .num_resources = ARRAY_SIZE(wm831x_wdt_resources), .resources = wm831x_wdt_resources, }, }; -static struct mfd_cell wm8320_devs[] = { +static const struct mfd_cell wm8320_devs[] = { { .name = "wm831x-backup", }, @@ -1326,6 +1478,9 @@ static struct mfd_cell wm8320_devs[] = { .resources = wm8320_dcdc4_buck_resources, }, { + .name = "wm831x-clk", + }, + { .name = "wm831x-gpio", .num_resources = ARRAY_SIZE(wm831x_gpio_resources), .resources = wm831x_gpio_resources, @@ -1405,11 +1560,6 @@ static struct mfd_cell wm8320_devs[] = { .resources = wm831x_on_resources, }, { - .name = "wm831x-rtc", - .num_resources = ARRAY_SIZE(wm831x_rtc_resources), - .resources = wm831x_rtc_resources, - }, - { .name = "wm831x-status", .id = 1, .num_resources = ARRAY_SIZE(wm831x_status1_resources), @@ -1428,34 +1578,66 @@ static struct mfd_cell wm8320_devs[] = { }, }; -static struct mfd_cell backlight_devs[] = { +static const struct mfd_cell touch_devs[] = { + { + .name = "wm831x-touch", + .num_resources = ARRAY_SIZE(wm831x_touch_resources), + .resources = wm831x_touch_resources, + }, +}; + +static const struct mfd_cell rtc_devs[] = { + { + .name = "wm831x-rtc", + .num_resources = ARRAY_SIZE(wm831x_rtc_resources), + .resources = wm831x_rtc_resources, + }, +}; + +static const struct mfd_cell backlight_devs[] = { { .name = "wm831x-backlight", }, }; +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, + .volatile_reg = wm831x_reg_volatile, +}; +EXPORT_SYMBOL_GPL(wm831x_regmap_config); + /* * Instantiate the generic non-control parts of the device. */ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) { - struct wm831x_pdata *pdata = wm831x->dev->platform_data; - int rev; + struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); + int rev, wm831x_num; enum wm831x_parent parent; - int ret; + int ret, i; mutex_init(&wm831x->io_lock); mutex_init(&wm831x->key_lock); - mutex_init(&wm831x->auxadc_lock); - init_completion(&wm831x->auxadc_done); dev_set_drvdata(wm831x->dev, wm831x); + wm831x->soft_shutdown = pdata->soft_shutdown; 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; } - if (ret != 0x6204) { + switch (ret) { + case 0x6204: + case 0x6246: + break; + default: dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret); ret = -EINVAL; goto err; @@ -1537,6 +1719,12 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev); break; + case WM8326: + parent = WM8326; + wm831x->num_gpio = 12; + dev_info(wm831x->dev, "WM8326 revision %c\n", 'A' + rev); + break; + default: dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret); ret = -EINVAL; @@ -1571,56 +1759,64 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) } } + if (pdata) { + for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) { + if (!pdata->gpio_defaults[i]) + continue; + + wm831x_reg_write(wm831x, + WM831X_GPIO1_CONTROL + i, + pdata->gpio_defaults[i] & 0xffff); + } + } + + /* Multiply by 10 as we have many subdevices of the same type */ + if (pdata && pdata->wm831x_num) + wm831x_num = pdata->wm831x_num * 10; + else + wm831x_num = -1; + ret = wm831x_irq_init(wm831x, irq); if (ret != 0) goto err; - if (wm831x->irq_base) { - ret = request_threaded_irq(wm831x->irq_base + - WM831X_IRQ_AUXADC_DATA, - NULL, wm831x_auxadc_irq, 0, - "auxadc", wm831x); - if (ret < 0) - dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n", - ret); - } + wm831x_auxadc_init(wm831x); /* The core device is up, instantiate the subdevices. */ switch (parent) { case WM8310: - ret = mfd_add_devices(wm831x->dev, -1, + ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8310_devs, ARRAY_SIZE(wm8310_devs), - NULL, wm831x->irq_base); + NULL, 0, NULL); break; case WM8311: - ret = mfd_add_devices(wm831x->dev, -1, + ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8311_devs, ARRAY_SIZE(wm8311_devs), - NULL, wm831x->irq_base); + NULL, 0, NULL); + if (!pdata || !pdata->disable_touch) + mfd_add_devices(wm831x->dev, wm831x_num, + touch_devs, ARRAY_SIZE(touch_devs), + NULL, 0, NULL); break; case WM8312: - ret = mfd_add_devices(wm831x->dev, -1, + ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8312_devs, ARRAY_SIZE(wm8312_devs), - NULL, wm831x->irq_base); + NULL, 0, NULL); + if (!pdata || !pdata->disable_touch) + mfd_add_devices(wm831x->dev, wm831x_num, + touch_devs, ARRAY_SIZE(touch_devs), + NULL, 0, NULL); break; case WM8320: - ret = mfd_add_devices(wm831x->dev, -1, - wm8320_devs, ARRAY_SIZE(wm8320_devs), - NULL, 0); - break; - case WM8321: - ret = mfd_add_devices(wm831x->dev, -1, - wm8320_devs, ARRAY_SIZE(wm8320_devs), - NULL, 0); - break; - case WM8325: - ret = mfd_add_devices(wm831x->dev, -1, + case WM8326: + ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8320_devs, ARRAY_SIZE(wm8320_devs), - NULL, 0); + NULL, 0, NULL); break; default: @@ -1633,11 +1829,32 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) goto err_irq; } + /* The RTC can only be used if the 32.768kHz crystal is + * enabled; this can't be controlled by software at runtime. + */ + ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to read clock status: %d\n", ret); + goto err_irq; + } + + if (ret & WM831X_XTAL_ENA) { + ret = mfd_add_devices(wm831x->dev, wm831x_num, + rtc_devs, ARRAY_SIZE(rtc_devs), + NULL, 0, NULL); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret); + goto err_irq; + } + } else { + dev_info(wm831x->dev, "32.768kHz clock disabled, no RTC\n"); + } + if (pdata && pdata->backlight) { /* Treat errors as non-critical */ - ret = mfd_add_devices(wm831x->dev, -1, backlight_devs, + ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs, ARRAY_SIZE(backlight_devs), NULL, - wm831x->irq_base); + 0, NULL); if (ret < 0) dev_err(wm831x->dev, "Failed to add backlight: %d\n", ret); @@ -1659,7 +1876,6 @@ err_irq: wm831x_irq_exit(wm831x); err: mfd_remove_devices(wm831x->dev); - kfree(wm831x); return ret; } @@ -1667,10 +1883,8 @@ void wm831x_device_exit(struct wm831x *wm831x) { wm831x_otp_exit(wm831x); mfd_remove_devices(wm831x->dev); - if (wm831x->irq_base) - free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x); + free_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), wm831x); wm831x_irq_exit(wm831x); - kfree(wm831x); } int wm831x_device_suspend(struct wm831x *wm831x) @@ -1709,6 +1923,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"); |
