summaryrefslogtreecommitdiffstats
path: root/drivers/mfd/arizona-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/arizona-core.c')
-rw-r--r--drivers/mfd/arizona-core.c261
1 files changed, 228 insertions, 33 deletions
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 6ab03043fd6..89a115301a0 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -16,9 +16,13 @@
#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,79 @@ 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 },
+ { .compatible = "wlf,wm8997", .data = (void *)WM8997 },
+ {},
+};
+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" },
};
@@ -451,6 +587,15 @@ static struct mfd_cell wm5110_devs[] = {
{ .name = "wm5110-codec" },
};
+static struct mfd_cell wm8997_devs[] = {
+ { .name = "arizona-micsupp" },
+ { .name = "arizona-extcon" },
+ { .name = "arizona-gpio" },
+ { .name = "arizona-haptics" },
+ { .name = "arizona-pwm" },
+ { .name = "wm8997-codec" },
+};
+
int arizona_dev_init(struct arizona *arizona)
{
struct device *dev = arizona->dev;
@@ -462,6 +607,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));
@@ -471,6 +618,7 @@ int arizona_dev_init(struct arizona *arizona)
switch (arizona->type) {
case WM5102:
case WM5110:
+ case WM8997:
for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
arizona->core_supplies[i].supply
= wm5102_core_supplies[i];
@@ -536,51 +684,23 @@ 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;
+ case 0x8997:
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 +720,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 +741,63 @@ 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
+#ifdef CONFIG_MFD_WM8997
+ case 0x8997:
+ type_name = "WM8997";
+ if (arizona->type != WM8997) {
+ dev_err(arizona->dev, "WM8997 registered as %d\n",
+ arizona->type);
+ arizona->type = WM8997;
+ }
+ apply_patch = wm8997_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 +829,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 +883,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)
@@ -771,6 +957,10 @@ int arizona_dev_init(struct arizona *arizona)
ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
ARRAY_SIZE(wm5110_devs), NULL, 0, NULL);
break;
+ case WM8997:
+ ret = mfd_add_devices(arizona->dev, -1, wm8997_devs,
+ ARRAY_SIZE(wm8997_devs), NULL, 0, NULL);
+ break;
}
if (ret != 0) {
@@ -809,6 +999,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);