mfd: Add WM8994 interrupt controller support
[safe/jmp/linux-2.6] / drivers / mfd / wm831x-core.c
index 42bef1d..07101e9 100644 (file)
 #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 + 1] = {
+       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,
-       WM8311 = 1,
-       WM8312 = 2,
+       WM8310 = 0x8310,
+       WM8311 = 0x8311,
+       WM8312 = 0x8312,
+       WM8320 = 0x8320,
 };
 
 static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
@@ -255,7 +321,6 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits);
  */
 int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
 {
-       int tries = 10;
        int ret, src;
 
        mutex_lock(&wm831x->auxadc_lock);
@@ -283,13 +348,14 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
                goto disable;
        }
 
-       do {
-               msleep(1);
+       /* Ignore the result to allow us to soldier on without IRQ hookup */
+       wait_for_completion_timeout(&wm831x->auxadc_done, msecs_to_jiffies(5));
 
-               ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
-               if (ret < 0)
-                       ret = WM831X_AUX_CVT_ENA;
-       } while ((ret & WM831X_AUX_CVT_ENA) && --tries);
+       ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "AUXADC status read failed: %d\n", ret);
+               goto disable;
+       }
 
        if (ret & WM831X_AUX_CVT_ENA) {
                dev_err(wm831x->dev, "Timed out reading AUXADC\n");
@@ -324,6 +390,15 @@ out:
 }
 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
  *
@@ -413,6 +488,20 @@ static struct resource wm831x_dcdc4_resources[] = {
        },
 };
 
+static struct resource wm8320_dcdc4_buck_resources[] = {
+       {
+               .start = WM831X_DC4_CONTROL,
+               .end   = WM832X_DC4_SLEEP_CONTROL,
+               .flags = IORESOURCE_IO,
+       },
+       {
+               .name  = "UV",
+               .start = WM831X_IRQ_UV_DC4,
+               .end   = WM831X_IRQ_UV_DC4,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
 static struct resource wm831x_gpio_resources[] = {
        {
                .start = WM831X_IRQ_GPIO_1,
@@ -729,6 +818,9 @@ static struct resource wm831x_wdt_resources[] = {
 
 static struct mfd_cell wm8310_devs[] = {
        {
+               .name = "wm831x-backup",
+       },
+       {
                .name = "wm831x-buckv",
                .id = 1,
                .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
@@ -882,6 +974,9 @@ static struct mfd_cell wm8310_devs[] = {
 
 static struct mfd_cell wm8311_devs[] = {
        {
+               .name = "wm831x-backup",
+       },
+       {
                .name = "wm831x-buckv",
                .id = 1,
                .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
@@ -1016,6 +1111,9 @@ static struct mfd_cell wm8311_devs[] = {
 
 static struct mfd_cell wm8312_devs[] = {
        {
+               .name = "wm831x-backup",
+       },
+       {
                .name = "wm831x-buckv",
                .id = 1,
                .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
@@ -1172,6 +1270,143 @@ static struct mfd_cell wm8312_devs[] = {
        },
 };
 
+static struct mfd_cell wm8320_devs[] = {
+       {
+               .name = "wm831x-backup",
+       },
+       {
+               .name = "wm831x-buckv",
+               .id = 1,
+               .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+               .resources = wm831x_dcdc1_resources,
+       },
+       {
+               .name = "wm831x-buckv",
+               .id = 2,
+               .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+               .resources = wm831x_dcdc2_resources,
+       },
+       {
+               .name = "wm831x-buckp",
+               .id = 3,
+               .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+               .resources = wm831x_dcdc3_resources,
+       },
+       {
+               .name = "wm831x-buckp",
+               .id = 4,
+               .num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
+               .resources = wm8320_dcdc4_buck_resources,
+       },
+       {
+               .name = "wm831x-gpio",
+               .num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+               .resources = wm831x_gpio_resources,
+       },
+       {
+               .name = "wm831x-hwmon",
+       },
+       {
+               .name = "wm831x-ldo",
+               .id = 1,
+               .num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+               .resources = wm831x_ldo1_resources,
+       },
+       {
+               .name = "wm831x-ldo",
+               .id = 2,
+               .num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+               .resources = wm831x_ldo2_resources,
+       },
+       {
+               .name = "wm831x-ldo",
+               .id = 3,
+               .num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+               .resources = wm831x_ldo3_resources,
+       },
+       {
+               .name = "wm831x-ldo",
+               .id = 4,
+               .num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+               .resources = wm831x_ldo4_resources,
+       },
+       {
+               .name = "wm831x-ldo",
+               .id = 5,
+               .num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+               .resources = wm831x_ldo5_resources,
+       },
+       {
+               .name = "wm831x-ldo",
+               .id = 6,
+               .num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
+               .resources = wm831x_ldo6_resources,
+       },
+       {
+               .name = "wm831x-aldo",
+               .id = 7,
+               .num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+               .resources = wm831x_ldo7_resources,
+       },
+       {
+               .name = "wm831x-aldo",
+               .id = 8,
+               .num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
+               .resources = wm831x_ldo8_resources,
+       },
+       {
+               .name = "wm831x-aldo",
+               .id = 9,
+               .num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
+               .resources = wm831x_ldo9_resources,
+       },
+       {
+               .name = "wm831x-aldo",
+               .id = 10,
+               .num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
+               .resources = wm831x_ldo10_resources,
+       },
+       {
+               .name = "wm831x-alive-ldo",
+               .id = 11,
+               .num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+               .resources = wm831x_ldo11_resources,
+       },
+       {
+               .name = "wm831x-on",
+               .num_resources = ARRAY_SIZE(wm831x_on_resources),
+               .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),
+               .resources = wm831x_status1_resources,
+       },
+       {
+               .name = "wm831x-status",
+               .id = 2,
+               .num_resources = ARRAY_SIZE(wm831x_status2_resources),
+               .resources = wm831x_status2_resources,
+       },
+       {
+               .name = "wm831x-watchdog",
+               .num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+               .resources = wm831x_wdt_resources,
+       },
+};
+
+static struct mfd_cell backlight_devs[] = {
+       {
+               .name = "wm831x-backlight",
+       },
+};
+
 /*
  * Instantiate the generic non-control parts of the device.
  */
@@ -1185,6 +1420,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);
+       init_completion(&wm831x->auxadc_done);
        dev_set_drvdata(wm831x->dev, wm831x);
 
        ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
@@ -1211,50 +1447,52 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
                goto err;
        }
 
+       /* Some engineering samples do not have the ID set, rely on
+        * the device being registered correctly.
+        */
+       if (ret == 0) {
+               dev_info(wm831x->dev, "Device is an engineering sample\n");
+               ret = id;
+       }
+
        switch (ret) {
-       case 0x8310:
+       case WM8310:
                parent = WM8310;
-               switch (rev) {
-               case 0:
-                       dev_info(wm831x->dev, "WM8310 revision %c\n",
-                                'A' + rev);
-                       break;
+               wm831x->num_gpio = 16;
+               if (rev > 0) {
+                       wm831x->has_gpio_ena = 1;
+                       wm831x->has_cs_sts = 1;
                }
+
+               dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
                break;
 
-       case 0x8311:
+       case WM8311:
                parent = WM8311;
-               switch (rev) {
-               case 0:
-                       dev_info(wm831x->dev, "WM8311 revision %c\n",
-                                'A' + rev);
-                       break;
+               wm831x->num_gpio = 16;
+               if (rev > 0) {
+                       wm831x->has_gpio_ena = 1;
+                       wm831x->has_cs_sts = 1;
                }
+
+               dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev);
                break;
 
-       case 0x8312:
+       case WM8312:
                parent = WM8312;
-               switch (rev) {
-               case 0:
-                       dev_info(wm831x->dev, "WM8312 revision %c\n",
-                                'A' + rev);
-                       break;
+               wm831x->num_gpio = 16;
+               if (rev > 0) {
+                       wm831x->has_gpio_ena = 1;
+                       wm831x->has_cs_sts = 1;
                }
+
+               dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
                break;
 
-       case 0:
-               /* Some engineering samples do not have the ID set,
-                * rely on the device being registered correctly.
-                * This will need revisiting for future devices with
-                * multiple dies.
-                */
-               parent = id;
-               switch (rev) {
-               case 0:
-                       dev_info(wm831x->dev, "WM831%d ES revision %c\n",
-                                parent, 'A' + rev);
-                       break;
-               }
+       case WM8320:
+               parent = WM8320;
+               wm831x->num_gpio = 12;
+               dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
                break;
 
        default:
@@ -1267,7 +1505,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
         * current parts.
         */
        if (parent != id)
-               dev_warn(wm831x->dev, "Device was registered as a WM831%lu\n",
+               dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
                         id);
 
        /* Bootstrap the user key */
@@ -1295,23 +1533,39 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int 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);
+       }
+
        /* The core device is up, instantiate the subdevices. */
        switch (parent) {
        case WM8310:
                ret = mfd_add_devices(wm831x->dev, -1,
                                      wm8310_devs, ARRAY_SIZE(wm8310_devs),
-                                     NULL, 0);
+                                     NULL, wm831x->irq_base);
                break;
 
        case WM8311:
                ret = mfd_add_devices(wm831x->dev, -1,
                                      wm8311_devs, ARRAY_SIZE(wm8311_devs),
-                                     NULL, 0);
+                                     NULL, wm831x->irq_base);
                break;
 
        case WM8312:
                ret = mfd_add_devices(wm831x->dev, -1,
                                      wm8312_devs, ARRAY_SIZE(wm8312_devs),
+                                     NULL, wm831x->irq_base);
+               break;
+
+       case WM8320:
+               ret = mfd_add_devices(wm831x->dev, -1,
+                                     wm8320_devs, ARRAY_SIZE(wm8320_devs),
                                      NULL, 0);
                break;
 
@@ -1325,6 +1579,18 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
                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,
+                                     wm831x->irq_base);
+               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) {
@@ -1345,7 +1611,10 @@ err:
 
 static 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);
        wm831x_irq_exit(wm831x);
        kfree(wm831x);
 }
@@ -1428,6 +1697,7 @@ static const struct i2c_device_id wm831x_i2c_id[] = {
        { "wm8310", WM8310 },
        { "wm8311", WM8311 },
        { "wm8312", WM8312 },
+       { "wm8320", WM8320 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);