Merge branch 'sched-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / drivers / mfd / wm831x-core.c
index cc1040c9d46cc431b6818c8909dba5c0c9b6dbb0..49b7885c2702ce5ae286b8e267ff75f4ac698c6a 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
 #include <linux/mfd/core.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/regulator.h>
+
+/* Current settings - values are 2*2^(reg_val/4) microamps.  These are
+ * exported since they are used by multiple drivers.
+ */
+int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL] = {
+       2,
+       2,
+       3,
+       3,
+       4,
+       5,
+       6,
+       7,
+       8,
+       10,
+       11,
+       13,
+       16,
+       19,
+       23,
+       27,
+       32,
+       38,
+       45,
+       54,
+       64,
+       76,
+       91,
+       108,
+       128,
+       152,
+       181,
+       215,
+       256,
+       304,
+       362,
+       431,
+       512,
+       609,
+       724,
+       861,
+       1024,
+       1218,
+       1448,
+       1722,
+       2048,
+       2435,
+       2896,
+       3444,
+       4096,
+       4871,
+       5793,
+       6889,
+       8192,
+       9742,
+       11585,
+       13777,
+       16384,
+       19484,
+       23170,
+       27554,
+};
+EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
 
 enum wm831x_parent {
        WM8310 = 0,
@@ -243,6 +312,103 @@ out:
 }
 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 tries = 10;
+       int ret, src;
+
+       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;
+       }
+
+       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;
+       }
+
+       do {
+               msleep(1);
+
+               ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
+               if (ret < 0)
+                       ret = WM831X_AUX_CVT_ENA;
+       } while ((ret & WM831X_AUX_CVT_ENA) && --tries);
+
+       if (ret & WM831X_AUX_CVT_ENA) {
+               dev_err(wm831x->dev, "Timed out reading 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);
+
+/**
+ * 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,
@@ -1071,6 +1237,12 @@ static struct mfd_cell wm8312_devs[] = {
        },
 };
 
+static struct mfd_cell backlight_devs[] = {
+       {
+               .name = "wm831x-backlight",
+       },
+};
+
 /*
  * Instantiate the generic non-control parts of the device.
  */
@@ -1083,6 +1255,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
 
        mutex_init(&wm831x->io_lock);
        mutex_init(&wm831x->key_lock);
+       mutex_init(&wm831x->auxadc_lock);
        dev_set_drvdata(wm831x->dev, wm831x);
 
        ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
@@ -1189,6 +1362,10 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
                }
        }
 
+       ret = wm831x_irq_init(wm831x, irq);
+       if (ret != 0)
+               goto err;
+
        /* The core device is up, instantiate the subdevices. */
        switch (parent) {
        case WM8310:
@@ -1216,19 +1393,32 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
 
        if (ret != 0) {
                dev_err(wm831x->dev, "Failed to add children\n");
-               goto err;
+               goto err_irq;
        }
 
+       if (pdata && pdata->backlight) {
+               /* Treat errors as non-critical */
+               ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
+                                     ARRAY_SIZE(backlight_devs), NULL, 0);
+               if (ret < 0)
+                       dev_err(wm831x->dev, "Failed to add backlight: %d\n",
+                               ret);
+       }
+
+       wm831x_otp_init(wm831x);
+
        if (pdata && pdata->post_init) {
                ret = pdata->post_init(wm831x);
                if (ret != 0) {
                        dev_err(wm831x->dev, "post_init() failed: %d\n", ret);
-                       goto err;
+                       goto err_irq;
                }
        }
 
        return 0;
 
+err_irq:
+       wm831x_irq_exit(wm831x);
 err:
        mfd_remove_devices(wm831x->dev);
        kfree(wm831x);
@@ -1237,7 +1427,9 @@ err:
 
 static void wm831x_device_exit(struct wm831x *wm831x)
 {
+       wm831x_otp_exit(wm831x);
        mfd_remove_devices(wm831x->dev);
+       wm831x_irq_exit(wm831x);
        kfree(wm831x);
 }