aboutsummaryrefslogtreecommitdiff
path: root/drivers/mfd/wm831x-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/wm831x-core.c')
-rw-r--r--drivers/mfd/wm831x-core.c779
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");