|
@@ -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,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, ®);
|
|
|
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, ®);
|
|
|
+ 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);
|