Merge tag 'mfd-arizona-v3.10-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Sat, 25 May 2013 14:31:46 +0000 (10:31 -0400)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Sat, 25 May 2013 14:31:46 +0000 (10:31 -0400)
mfd: arizona: Updates for v3.10

A bunch of enhancements and fixes for the arizona devices, adding a few
new features (the main one being device tree) and improving robustness.

Documentation/devicetree/bindings/mfd/arizona.txt [new file with mode: 0644]
drivers/mfd/arizona-core.c
drivers/mfd/arizona-i2c.c
drivers/mfd/arizona-spi.c
drivers/mfd/arizona.h
drivers/mfd/wm5110-tables.c
include/linux/mfd/arizona/core.h
include/linux/mfd/arizona/pdata.h
include/linux/mfd/arizona/registers.h

diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt
new file mode 100644 (file)
index 0000000..0e295c9
--- /dev/null
@@ -0,0 +1,62 @@
+Wolfson Arizona class audio SoCs
+
+These devices are audio SoCs with extensive digital capabilites and a range
+of analogue I/O.
+
+Required properties:
+
+  - compatible : one of the following chip-specific strings:
+       "wlf,wm5102"
+       "wlf,wm5110"
+  - reg : I2C slave address when connected using I2C, chip select number when
+    using SPI.
+
+  - interrupts : The interrupt line the /IRQ signal for the device is
+    connected to.
+  - interrupt-controller : Arizona class devices contain interrupt controllers
+    and may provide interrupt services to other devices.
+  - interrupt-parent : The parent interrupt controller.
+  - #interrupt-cells: the number of cells to describe an IRQ, this should be 2.
+    The first cell is the IRQ number.
+    The second cell is the flags, encoded as the trigger masks from
+    Documentation/devicetree/bindings/interrupts.txt
+
+  - gpio-controller : Indicates this device is a GPIO controller.
+  - #gpio-cells : Must be 2. The first cell is the pin number and the
+    second cell is used to specify optional parameters (currently unused).
+
+  - AVDD1-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply, CPVDD-supply,
+    SPKVDDL-supply, SPKVDDR-supply : power supplies for the device, as covered
+    in Documentation/devicetree/bindings/regulator/regulator.txt
+
+Optional properties:
+
+  - wlf,reset : GPIO specifier for the GPIO controlling /RESET
+  - wlf,ldoena : GPIO specifier for the GPIO controlling LDOENA
+
+  - wlf,gpio-defaults : A list of GPIO configuration register values. If
+    absent, no configuration of these registers is performed. If any
+    entry has a value that is out of range for a 16 bit register then
+    the chip default will be used.  If present exactly five values must
+    be specified.
+
+Example:
+
+codec: wm5102@1a {
+       compatible = "wlf,wm5102";
+       reg = <0x1a>;
+       interrupts = <347>;
+       #interrupt-cells = <2>;
+        interrupt-parent = <&gic>;
+
+       gpio-controller;
+       #gpio-cells = <2>;
+
+       wlf,gpio-defaults = <
+               0x00000000, /* AIF1TXLRCLK */
+               0xffffffff,
+               0xffffffff,
+               0xffffffff,
+               0xffffffff,
+       >;
+};
index 6ab03043fd609a23cf95f4061b6a1a1fca799b34..74b4481754fdcb2679513dde2046266a481e040e 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/mfd/core.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
 #include <linux/slab.h>
 
 #include <linux/mfd/arizona/core.h>
@@ -344,6 +348,17 @@ static int arizona_runtime_resume(struct device *dev)
 
        switch (arizona->type) {
        case WM5102:
+               if (arizona->external_dcvdd) {
+                       ret = regmap_update_bits(arizona->regmap,
+                                                ARIZONA_ISOLATION_CONTROL,
+                                                ARIZONA_ISOLATE_DCVDD1, 0);
+                       if (ret != 0) {
+                               dev_err(arizona->dev,
+                                       "Failed to connect DCVDD: %d\n", ret);
+                               goto err;
+                       }
+               }
+
                ret = wm5102_patch(arizona);
                if (ret != 0) {
                        dev_err(arizona->dev, "Failed to apply patch: %d\n",
@@ -365,6 +380,28 @@ static int arizona_runtime_resume(struct device *dev)
                        goto err;
                }
 
+               if (arizona->external_dcvdd) {
+                       ret = regmap_update_bits(arizona->regmap,
+                                                ARIZONA_ISOLATION_CONTROL,
+                                                ARIZONA_ISOLATE_DCVDD1, 0);
+                       if (ret != 0) {
+                               dev_err(arizona->dev,
+                                       "Failed to connect DCVDD: %d\n", ret);
+                               goto err;
+                       }
+               }
+               break;
+       }
+
+       switch (arizona->type) {
+       case WM5102:
+               ret = wm5102_patch(arizona);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to apply patch: %d\n",
+                               ret);
+                       goto err;
+               }
+       default:
                break;
        }
 
@@ -385,9 +422,22 @@ err:
 static int arizona_runtime_suspend(struct device *dev)
 {
        struct arizona *arizona = dev_get_drvdata(dev);
+       int ret;
 
        dev_dbg(arizona->dev, "Entering AoD mode\n");
 
+       if (arizona->external_dcvdd) {
+               ret = regmap_update_bits(arizona->regmap,
+                                        ARIZONA_ISOLATION_CONTROL,
+                                        ARIZONA_ISOLATE_DCVDD1,
+                                        ARIZONA_ISOLATE_DCVDD1);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
        regulator_disable(arizona->dcvdd);
        regcache_cache_only(arizona->regmap, true);
        regcache_mark_dirty(arizona->regmap);
@@ -397,6 +447,26 @@ static int arizona_runtime_suspend(struct device *dev)
 #endif
 
 #ifdef CONFIG_PM_SLEEP
+static int arizona_suspend(struct device *dev)
+{
+       struct arizona *arizona = dev_get_drvdata(dev);
+
+       dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
+       disable_irq(arizona->irq);
+
+       return 0;
+}
+
+static int arizona_suspend_late(struct device *dev)
+{
+       struct arizona *arizona = dev_get_drvdata(dev);
+
+       dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
+       enable_irq(arizona->irq);
+
+       return 0;
+}
+
 static int arizona_resume_noirq(struct device *dev)
 {
        struct arizona *arizona = dev_get_drvdata(dev);
@@ -422,13 +492,78 @@ const struct dev_pm_ops arizona_pm_ops = {
        SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
                           arizona_runtime_resume,
                           NULL)
-       SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume)
+       SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
 #ifdef CONFIG_PM_SLEEP
+       .suspend_late = arizona_suspend_late,
        .resume_noirq = arizona_resume_noirq,
 #endif
 };
 EXPORT_SYMBOL_GPL(arizona_pm_ops);
 
+#ifdef CONFIG_OF
+int arizona_of_get_type(struct device *dev)
+{
+       const struct of_device_id *id = of_match_device(arizona_of_match, dev);
+
+       if (id)
+               return (int)id->data;
+       else
+               return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_of_get_type);
+
+static int arizona_of_get_core_pdata(struct arizona *arizona)
+{
+       int ret, i;
+
+       arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node,
+                                                "wlf,reset", 0);
+       if (arizona->pdata.reset < 0)
+               arizona->pdata.reset = 0;
+
+       arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node,
+                                                 "wlf,ldoena", 0);
+       if (arizona->pdata.ldoena < 0)
+               arizona->pdata.ldoena = 0;
+
+       ret = of_property_read_u32_array(arizona->dev->of_node,
+                                        "wlf,gpio-defaults",
+                                        arizona->pdata.gpio_defaults,
+                                        ARRAY_SIZE(arizona->pdata.gpio_defaults));
+       if (ret >= 0) {
+               /*
+                * All values are literal except out of range values
+                * which are chip default, translate into platform
+                * data which uses 0 as chip default and out of range
+                * as zero.
+                */
+               for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
+                       if (arizona->pdata.gpio_defaults[i] > 0xffff)
+                               arizona->pdata.gpio_defaults[i] = 0;
+                       if (arizona->pdata.gpio_defaults[i] == 0)
+                               arizona->pdata.gpio_defaults[i] = 0x10000;
+               }
+       } else {
+               dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n",
+                       ret);
+       }
+
+       return 0;
+}
+
+const struct of_device_id arizona_of_match[] = {
+       { .compatible = "wlf,wm5102", .data = (void *)WM5102 },
+       { .compatible = "wlf,wm5110", .data = (void *)WM5110 },
+       {},
+};
+EXPORT_SYMBOL_GPL(arizona_of_match);
+#else
+static inline int arizona_of_get_core_pdata(struct arizona *arizona)
+{
+       return 0;
+}
+#endif
+
 static struct mfd_cell early_devs[] = {
        { .name = "arizona-ldo1" },
 };
@@ -462,6 +597,8 @@ int arizona_dev_init(struct arizona *arizona)
        dev_set_drvdata(arizona->dev, arizona);
        mutex_init(&arizona->clk_lock);
 
+       arizona_of_get_core_pdata(arizona);
+
        if (dev_get_platdata(arizona->dev))
                memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
                       sizeof(arizona->pdata));
@@ -536,51 +673,22 @@ int arizona_dev_init(struct arizona *arizona)
 
        regcache_cache_only(arizona->regmap, false);
 
+       /* Verify that this is a chip we know about */
        ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
        if (ret != 0) {
                dev_err(dev, "Failed to read ID register: %d\n", ret);
                goto err_reset;
        }
 
-       ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
-                         &arizona->rev);
-       if (ret != 0) {
-               dev_err(dev, "Failed to read revision register: %d\n", ret);
-               goto err_reset;
-       }
-       arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
-
        switch (reg) {
-#ifdef CONFIG_MFD_WM5102
        case 0x5102:
-               type_name = "WM5102";
-               if (arizona->type != WM5102) {
-                       dev_err(arizona->dev, "WM5102 registered as %d\n",
-                               arizona->type);
-                       arizona->type = WM5102;
-               }
-               apply_patch = wm5102_patch;
-               arizona->rev &= 0x7;
-               break;
-#endif
-#ifdef CONFIG_MFD_WM5110
        case 0x5110:
-               type_name = "WM5110";
-               if (arizona->type != WM5110) {
-                       dev_err(arizona->dev, "WM5110 registered as %d\n",
-                               arizona->type);
-                       arizona->type = WM5110;
-               }
-               apply_patch = wm5110_patch;
                break;
-#endif
        default:
-               dev_err(arizona->dev, "Unknown device ID %x\n", reg);
+               dev_err(arizona->dev, "Unknown device ID: %x\n", reg);
                goto err_reset;
        }
 
-       dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
-
        /* If we have a /RESET GPIO we'll already be reset */
        if (!arizona->pdata.reset) {
                regcache_mark_dirty(arizona->regmap);
@@ -600,6 +708,7 @@ int arizona_dev_init(struct arizona *arizona)
                }
        }
 
+       /* Ensure device startup is complete */
        switch (arizona->type) {
        case WM5102:
                ret = regmap_read(arizona->regmap, 0x19, &val);
@@ -620,6 +729,52 @@ int arizona_dev_init(struct arizona *arizona)
                break;
        }
 
+       /* Read the device ID information & do device specific stuff */
+       ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, &reg);
+       if (ret != 0) {
+               dev_err(dev, "Failed to read ID register: %d\n", ret);
+               goto err_reset;
+       }
+
+       ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
+                         &arizona->rev);
+       if (ret != 0) {
+               dev_err(dev, "Failed to read revision register: %d\n", ret);
+               goto err_reset;
+       }
+       arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
+
+       switch (reg) {
+#ifdef CONFIG_MFD_WM5102
+       case 0x5102:
+               type_name = "WM5102";
+               if (arizona->type != WM5102) {
+                       dev_err(arizona->dev, "WM5102 registered as %d\n",
+                               arizona->type);
+                       arizona->type = WM5102;
+               }
+               apply_patch = wm5102_patch;
+               arizona->rev &= 0x7;
+               break;
+#endif
+#ifdef CONFIG_MFD_WM5110
+       case 0x5110:
+               type_name = "WM5110";
+               if (arizona->type != WM5110) {
+                       dev_err(arizona->dev, "WM5110 registered as %d\n",
+                               arizona->type);
+                       arizona->type = WM5110;
+               }
+               apply_patch = wm5110_patch;
+               break;
+#endif
+       default:
+               dev_err(arizona->dev, "Unknown device ID %x\n", reg);
+               goto err_reset;
+       }
+
+       dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
+
        if (apply_patch) {
                ret = apply_patch(arizona);
                if (ret != 0) {
@@ -651,6 +806,14 @@ int arizona_dev_init(struct arizona *arizona)
                             arizona->pdata.gpio_defaults[i]);
        }
 
+       /*
+        * LDO1 can only be used to supply DCVDD so if it has no
+        * consumers then DCVDD is supplied externally.
+        */
+       if (arizona->pdata.ldo1 &&
+           arizona->pdata.ldo1->num_consumer_supplies == 0)
+               arizona->external_dcvdd = true;
+
        pm_runtime_set_autosuspend_delay(arizona->dev, 100);
        pm_runtime_use_autosuspend(arizona->dev);
        pm_runtime_enable(arizona->dev);
@@ -697,7 +860,7 @@ int arizona_dev_init(struct arizona *arizona)
                if (arizona->pdata.micbias[i].discharge)
                        val |= ARIZONA_MICB1_DISCH;
 
-               if (arizona->pdata.micbias[i].fast_start)
+               if (arizona->pdata.micbias[i].soft_start)
                        val |= ARIZONA_MICB1_RATE;
 
                if (arizona->pdata.micbias[i].bypass)
@@ -809,6 +972,11 @@ int arizona_dev_exit(struct arizona *arizona)
        arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
        pm_runtime_disable(arizona->dev);
        arizona_irq_exit(arizona);
+       if (arizona->pdata.reset)
+               gpio_set_value_cansleep(arizona->pdata.reset, 0);
+       regulator_disable(arizona->dcvdd);
+       regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies),
+                              arizona->core_supplies);
        return 0;
 }
 EXPORT_SYMBOL_GPL(arizona_dev_exit);
index 44a1bb96984185e3757f31330a37d50266fdbbed..deb267ebf84e3cdff73619d1f8dbe1c6f44b4e8a 100644 (file)
@@ -27,9 +27,14 @@ static int arizona_i2c_probe(struct i2c_client *i2c,
 {
        struct arizona *arizona;
        const struct regmap_config *regmap_config;
-       int ret;
+       int ret, type;
 
-       switch (id->driver_data) {
+       if (i2c->dev.of_node)
+               type = arizona_of_get_type(&i2c->dev);
+       else
+               type = id->driver_data;
+
+       switch (type) {
 #ifdef CONFIG_MFD_WM5102
        case WM5102:
                regmap_config = &wm5102_i2c_regmap;
@@ -84,6 +89,7 @@ static struct i2c_driver arizona_i2c_driver = {
                .name   = "arizona",
                .owner  = THIS_MODULE,
                .pm     = &arizona_pm_ops,
+               .of_match_table = of_match_ptr(arizona_of_match),
        },
        .probe          = arizona_i2c_probe,
        .remove         = arizona_i2c_remove,
index b57e642d2b4a3eedaad99e65822e728ab6ff0ae6..47be7b35b5c5b09c9e17c4ef0cb7dae32b0c2855 100644 (file)
@@ -27,9 +27,14 @@ static int arizona_spi_probe(struct spi_device *spi)
        const struct spi_device_id *id = spi_get_device_id(spi);
        struct arizona *arizona;
        const struct regmap_config *regmap_config;
-       int ret;
+       int ret, type;
 
-       switch (id->driver_data) {
+       if (spi->dev.of_node)
+               type = arizona_of_get_type(&spi->dev);
+       else
+               type = id->driver_data;
+
+       switch (type) {
 #ifdef CONFIG_MFD_WM5102
        case WM5102:
                regmap_config = &wm5102_spi_regmap;
@@ -84,6 +89,7 @@ static struct spi_driver arizona_spi_driver = {
                .name   = "arizona",
                .owner  = THIS_MODULE,
                .pm     = &arizona_pm_ops,
+               .of_match_table = of_match_ptr(arizona_of_match),
        },
        .probe          = arizona_spi_probe,
        .remove         = arizona_spi_remove,
index 9798ae5da67be2b72f2d52101266c825d0e47a19..db55d9854a5587cb1d87fd46bb3d1eee6c085c00 100644 (file)
@@ -13,6 +13,7 @@
 #ifndef _WM5102_H
 #define _WM5102_H
 
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <linux/pm.h>
 
@@ -26,6 +27,8 @@ extern const struct regmap_config wm5110_spi_regmap;
 
 extern const struct dev_pm_ops arizona_pm_ops;
 
+extern const struct of_device_id arizona_of_match[];
+
 extern const struct regmap_irq_chip wm5102_aod;
 extern const struct regmap_irq_chip wm5102_irq;
 
@@ -37,4 +40,13 @@ int arizona_dev_exit(struct arizona *arizona);
 int arizona_irq_init(struct arizona *arizona);
 int arizona_irq_exit(struct arizona *arizona);
 
+#ifdef CONFIG_OF
+int arizona_of_get_type(struct device *dev);
+#else
+static inline int arizona_of_get_type(struct device *dev)
+{
+       return 0;
+}
+#endif
+
 #endif
index c41599815299ddb043654f1008864329c1d21ca0..2a7972349159fb91f27e802addf7790be81c341a 100644 (file)
@@ -2273,18 +2273,22 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg)
        case ARIZONA_DSP1_CLOCKING_1:
        case ARIZONA_DSP1_STATUS_1:
        case ARIZONA_DSP1_STATUS_2:
+       case ARIZONA_DSP1_STATUS_3:
        case ARIZONA_DSP2_CONTROL_1:
        case ARIZONA_DSP2_CLOCKING_1:
        case ARIZONA_DSP2_STATUS_1:
        case ARIZONA_DSP2_STATUS_2:
+       case ARIZONA_DSP2_STATUS_3:
        case ARIZONA_DSP3_CONTROL_1:
        case ARIZONA_DSP3_CLOCKING_1:
        case ARIZONA_DSP3_STATUS_1:
        case ARIZONA_DSP3_STATUS_2:
+       case ARIZONA_DSP3_STATUS_3:
        case ARIZONA_DSP4_CONTROL_1:
        case ARIZONA_DSP4_CLOCKING_1:
        case ARIZONA_DSP4_STATUS_1:
        case ARIZONA_DSP4_STATUS_2:
+       case ARIZONA_DSP4_STATUS_3:
                return true;
        default:
                return false;
@@ -2334,12 +2338,16 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg)
        case ARIZONA_DSP1_CLOCKING_1:
        case ARIZONA_DSP1_STATUS_1:
        case ARIZONA_DSP1_STATUS_2:
+       case ARIZONA_DSP1_STATUS_3:
        case ARIZONA_DSP2_STATUS_1:
        case ARIZONA_DSP2_STATUS_2:
+       case ARIZONA_DSP2_STATUS_3:
        case ARIZONA_DSP3_STATUS_1:
        case ARIZONA_DSP3_STATUS_2:
+       case ARIZONA_DSP3_STATUS_3:
        case ARIZONA_DSP4_STATUS_1:
        case ARIZONA_DSP4_STATUS_2:
+       case ARIZONA_DSP4_STATUS_3:
                return true;
        default:
                return false;
index cc281368dc555f17d486b84fe1a3087f328bb880..f797bb9b8b56838458fe108c343bd4c5d6bde7f5 100644 (file)
@@ -95,6 +95,8 @@ struct arizona {
 
        struct arizona_pdata pdata;
 
+       unsigned int external_dcvdd:1;
+
        int irq;
        struct irq_domain *virq;
        struct regmap_irq_chip_data *aod_irq_chip;
index 80dead1f710014564bf07319945025e40faca17e..12a5c135c746d9c69be4199a022f4d3519920f08 100644 (file)
@@ -77,7 +77,7 @@ struct arizona_micbias {
        int mV;                    /** Regulated voltage */
        unsigned int ext_cap:1;    /** External capacitor fitted */
        unsigned int discharge:1;  /** Actively discharge */
-       unsigned int fast_start:1; /** Enable aggressive startup ramp rate */
+       unsigned int soft_start:1; /** Disable aggressive startup ramp rate */
        unsigned int bypass:1;     /** Use bypass mode */
 };
 
index 715b6ba3d52a2c949a359f0a440a5defa7bc3b59..4730b5c576e27607d14aebe5a74b9a1fd3958199 100644 (file)
 #define ARIZONA_DSP2_CLOCKING_1                  0x1201
 #define ARIZONA_DSP2_STATUS_1                    0x1204
 #define ARIZONA_DSP2_STATUS_2                    0x1205
+#define ARIZONA_DSP2_STATUS_3                    0x1206
 #define ARIZONA_DSP2_SCRATCH_0                   0x1240
 #define ARIZONA_DSP2_SCRATCH_1                   0x1241
 #define ARIZONA_DSP2_SCRATCH_2                   0x1242
 #define ARIZONA_DSP3_CLOCKING_1                  0x1301
 #define ARIZONA_DSP3_STATUS_1                    0x1304
 #define ARIZONA_DSP3_STATUS_2                    0x1305
+#define ARIZONA_DSP3_STATUS_3                    0x1306
 #define ARIZONA_DSP3_SCRATCH_0                   0x1340
 #define ARIZONA_DSP3_SCRATCH_1                   0x1341
 #define ARIZONA_DSP3_SCRATCH_2                   0x1342
 #define ARIZONA_DSP4_CLOCKING_1                  0x1401
 #define ARIZONA_DSP4_STATUS_1                    0x1404
 #define ARIZONA_DSP4_STATUS_2                    0x1405
+#define ARIZONA_DSP4_STATUS_3                    0x1406
 #define ARIZONA_DSP4_SCRATCH_0                   0x1440
 #define ARIZONA_DSP4_SCRATCH_1                   0x1441
 #define ARIZONA_DSP4_SCRATCH_2                   0x1442