From: S Twiss <[email protected]>
This patch set adds support for the Dialog DA9062 Power Management IC.
In this patch set the following is provided:
- [PATCH V1 1/6]: MFD core support
- [PATCH V1 2/6]: BUCK and LDO regulator driver
- [PATCH V1 3/6]: RTC driver
- [PATCH V1 4/6]: OnKey driver
- [PATCH V1 5/6]: Watchdog driver
- [PATCH V1 6/6]: Add bindings for all DA9062 components
This patch applies against linux-next and v4.0
Thank you,
Steve Twiss, Dialog Semiconductor Ltd.
S Twiss (6):
mfd: da9062: DA9062 MFD core driver
regulator: da9062: DA9062 regulator driver
rtc: da9062: DA9062 RTC driver
input: misc: onkey: da9062: DA9062 OnKey driver
watchdog: da9062: DA9062 watchdog driver
devicetree: da9062: Add bindings for DA9062 driver
Documentation/devicetree/bindings/mfd/da9062.txt | 107 +++
drivers/input/misc/Kconfig | 10 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/da9062-onkey.c | 223 +++++
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 3 +-
drivers/mfd/da9062-core.c | 611 ++++++++++++
drivers/regulator/Kconfig | 10 +
drivers/regulator/Makefile | 1 +
drivers/regulator/da9062-regulator.c | 968 +++++++++++++++++++
drivers/rtc/Kconfig | 10 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-da9062.c | 367 +++++++
drivers/watchdog/Kconfig | 9 +
drivers/watchdog/Makefile | 1 +
drivers/watchdog/da9062_wdt.c | 249 +++++
include/linux/mfd/da9062/core.h | 61 ++
include/linux/mfd/da9062/registers.h | 1108 ++++++++++++++++++++++
18 files changed, 3751 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/mfd/da9062.txt
create mode 100644 drivers/input/misc/da9062-onkey.c
create mode 100644 drivers/mfd/da9062-core.c
create mode 100644 drivers/regulator/da9062-regulator.c
create mode 100644 drivers/rtc/rtc-da9062.c
create mode 100644 drivers/watchdog/da9062_wdt.c
create mode 100644 include/linux/mfd/da9062/core.h
create mode 100644 include/linux/mfd/da9062/registers.h
--
end-of-patch for PATCH V1
From: S Twiss <[email protected]>
Add MFD core driver support for DA9062
Signed-off-by: Steve Twiss <[email protected]>
---
This patch applies against linux-next and v4.0
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 3 +-
drivers/mfd/da9062-core.c | 611 +++++++++++++++++++
include/linux/mfd/da9062/core.h | 61 ++
include/linux/mfd/da9062/registers.h | 1108 ++++++++++++++++++++++++++++++++++
5 files changed, 1794 insertions(+), 1 deletion(-)
create mode 100644 drivers/mfd/da9062-core.c
create mode 100644 include/linux/mfd/da9062/core.h
create mode 100644 include/linux/mfd/da9062/registers.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 38356e3..37ff888 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -183,6 +183,18 @@ config MFD_DA9055
This driver can be built as a module. If built as a module it will be
called "da9055"
+config MFD_DA9062
+ bool "Dialog Semiconductor DA9062 PMIC Support"
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ depends on I2C=y
+ help
+ Say yes here for support for the Dialog Semiconductor DA9062 PMIC.
+ This includes the I2C driver and core APIs.
+ Additional drivers must be enabled in order to use the functionality
+ of the device.
+
config MFD_DA9063
bool "Dialog Semiconductor DA9063 PMIC Support"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 19f3d74..f20a727 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -110,10 +110,11 @@ obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o
da9055-objs := da9055-core.o da9055-i2c.o
obj-$(CONFIG_MFD_DA9055) += da9055.o
-
+obj-$(CONFIG_MFD_DA9062) += da9062-core.o
da9063-objs := da9063-core.o da9063-irq.o da9063-i2c.o
obj-$(CONFIG_MFD_DA9063) += da9063.o
obj-$(CONFIG_MFD_DA9150) += da9150-core.o
+
obj-$(CONFIG_MFD_MAX14577) += max14577.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o
diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c
new file mode 100644
index 0000000..dd99e0f
--- /dev/null
+++ b/drivers/mfd/da9062-core.c
@@ -0,0 +1,611 @@
+/*
+ * da9062-core.c - CORE device driver for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <linux/proc_fs.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+
+/* IRQ device driver for DA9062 */
+
+#define DA9062_REG_EVENT_A_OFFSET 0
+#define DA9062_REG_EVENT_B_OFFSET 1
+#define DA9062_REG_EVENT_C_OFFSET 2
+
+static struct regmap_irq da9062_irqs[] = {
+ /* EVENT A */
+ [DA9062_IRQ_ONKEY] = {
+ .reg_offset = DA9062_REG_EVENT_A_OFFSET,
+ .mask = DA9062AA_M_NONKEY_MASK,
+ },
+ [DA9062_IRQ_ALARM] = {
+ .reg_offset = DA9062_REG_EVENT_A_OFFSET,
+ .mask = DA9062AA_M_ALARM_MASK,
+ },
+ [DA9062_IRQ_TICK] = {
+ .reg_offset = DA9062_REG_EVENT_A_OFFSET,
+ .mask = DA9062AA_M_TICK_MASK,
+ },
+ [DA9062_IRQ_WDG_WARN] = {
+ .reg_offset = DA9062_REG_EVENT_A_OFFSET,
+ .mask = DA9062AA_M_WDG_WARN_MASK,
+ },
+ [DA9062_IRQ_SEQ_RDY] = {
+ .reg_offset = DA9062_REG_EVENT_A_OFFSET,
+ .mask = DA9062AA_M_SEQ_RDY_MASK,
+ },
+ /* EVENT B */
+ [DA9062_IRQ_TEMP] = {
+ .reg_offset = DA9062_REG_EVENT_B_OFFSET,
+ .mask = DA9062AA_M_TEMP_MASK,
+ },
+ [DA9062_IRQ_LDO_LIM] = {
+ .reg_offset = DA9062_REG_EVENT_B_OFFSET,
+ .mask = DA9062AA_M_LDO_LIM_MASK,
+ },
+ [DA9062_IRQ_DVC_RDY] = {
+ .reg_offset = DA9062_REG_EVENT_B_OFFSET,
+ .mask = DA9062AA_M_DVC_RDY_MASK,
+ },
+ [DA9062_IRQ_VDD_WARN] = {
+ .reg_offset = DA9062_REG_EVENT_B_OFFSET,
+ .mask = DA9062AA_M_VDD_WARN_MASK,
+ },
+ /* EVENT C */
+ [DA9062_IRQ_GPI0] = {
+ .reg_offset = DA9062_REG_EVENT_C_OFFSET,
+ .mask = DA9062AA_M_GPI0_MASK,
+ },
+ [DA9062_IRQ_GPI1] = {
+ .reg_offset = DA9062_REG_EVENT_C_OFFSET,
+ .mask = DA9062AA_M_GPI1_MASK,
+ },
+ [DA9062_IRQ_GPI2] = {
+ .reg_offset = DA9062_REG_EVENT_C_OFFSET,
+ .mask = DA9062AA_M_GPI2_MASK,
+ },
+ [DA9062_IRQ_GPI3] = {
+ .reg_offset = DA9062_REG_EVENT_C_OFFSET,
+ .mask = DA9062AA_M_GPI3_MASK,
+ },
+ [DA9062_IRQ_GPI4] = {
+ .reg_offset = DA9062_REG_EVENT_C_OFFSET,
+ .mask = DA9062AA_M_GPI4_MASK,
+ },
+};
+
+static struct regmap_irq_chip da9062_irq_chip = {
+ /* IRQ */
+ .name = "da9062-irq",
+ .irqs = da9062_irqs,
+ .num_irqs = DA9062_NUM_IRQ,
+ /* STATUS and EVENT */
+ .num_regs = 3,
+ .status_base = DA9062AA_EVENT_A,
+ .mask_base = DA9062AA_IRQ_MASK_A,
+ .ack_base = DA9062AA_EVENT_A,
+};
+
+int da9062_irq_init(struct da9062 *chip)
+{
+ int ret;
+
+ if (!chip->chip_irq) {
+ dev_err(chip->dev, "No IRQ configured\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_add_irq_chip(chip->regmap, chip->chip_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
+ chip->irq_base, &da9062_irq_chip,
+ &chip->regmap_irq);
+ if (ret) {
+ dev_err(chip->dev, "Failed to request IRQ %d: %d\n",
+ chip->chip_irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void da9062_irq_exit(struct da9062 *chip)
+{
+ regmap_del_irq_chip(chip->chip_irq, chip->regmap_irq);
+}
+
+/* CORE device driver for DA9062 */
+
+static struct resource da9062_regulators_resources[] = {
+ {
+ .name = "LDO_LIM",
+ .start = DA9062_IRQ_LDO_LIM,
+ .end = DA9062_IRQ_LDO_LIM,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "VDD_WARN",
+ .start = DA9062_IRQ_VDD_WARN,
+ .end = DA9062_IRQ_VDD_WARN,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource da9062_thermal_resources[] = {
+ {
+ .name = "THERMAL",
+ .start = DA9062_IRQ_TEMP,
+ .end = DA9062_IRQ_TEMP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource da9062_wdt_resources[] = {
+ {
+ .name = "WDG_WARN",
+ .start = DA9062_IRQ_WDG_WARN,
+ .end = DA9062_IRQ_WDG_WARN,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource da9062_rtc_resources[] = {
+ {
+ .name = "ALARM",
+ .start = DA9062_IRQ_ALARM,
+ .end = DA9062_IRQ_ALARM,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "TICK",
+ .start = DA9062_IRQ_TICK,
+ .end = DA9062_IRQ_TICK,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource da9062_onkey_resources[] = {
+ {
+ .name = "ONKEY",
+ .start = DA9062_IRQ_ONKEY,
+ .end = DA9062_IRQ_ONKEY,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct mfd_cell da9062_devs[] = {
+ {
+ .name = "da9062-regulators",
+ .num_resources = ARRAY_SIZE(da9062_regulators_resources),
+ .resources = da9062_regulators_resources,
+ },
+ {
+ .name = "da9062-watchdog",
+ .num_resources = ARRAY_SIZE(da9062_wdt_resources),
+ .resources = da9062_wdt_resources,
+ .of_compatible = "dlg,da9062-wdt",
+ },
+ {
+ .name = "da9062-onkey",
+ .num_resources = ARRAY_SIZE(da9062_onkey_resources),
+ .resources = da9062_onkey_resources,
+ .of_compatible = "dlg,da9062-onkey",
+ },
+ {
+ .name = "da9062-thermal",
+ .num_resources = ARRAY_SIZE(da9062_thermal_resources),
+ .resources = da9062_thermal_resources,
+ .of_compatible = "dlg,da9062-thermal",
+ },
+ {
+ .name = "da9062-rtc",
+ .num_resources = ARRAY_SIZE(da9062_rtc_resources),
+ .resources = da9062_rtc_resources,
+ .of_compatible = "dlg,da9062-rtc",
+ },
+};
+
+static int da9062_clear_fault_log(struct da9062 *chip)
+{
+ int ret = 0;
+ int fault_log = 0;
+
+ ret = regmap_read(chip->regmap, DA9062AA_FAULT_LOG, &fault_log);
+ if (ret < 0) {
+ dev_err(chip->dev, "Cannot read FAULT_LOG.\n");
+ ret = -EIO;
+ } else {
+ if (fault_log) {
+ if (fault_log & DA9062AA_TWD_ERROR_MASK)
+ dev_dbg(chip->dev, "Fault log entry detected: TWD_ERROR\n");
+ if (fault_log & DA9062AA_POR_MASK)
+ dev_dbg(chip->dev, "Fault log entry detected: POR\n");
+ if (fault_log & DA9062AA_VDD_FAULT_MASK)
+ dev_dbg(chip->dev, "Fault log entry detected: VDD_FAULT\n");
+ if (fault_log & DA9062AA_VDD_START_MASK)
+ dev_dbg(chip->dev, "Fault log entry detected: VDD_START\n");
+ if (fault_log & DA9062AA_TEMP_CRIT_MASK)
+ dev_dbg(chip->dev, "Fault log entry detected: TEMP_CRIT\n");
+ if (fault_log & DA9062AA_KEY_RESET_MASK)
+ dev_dbg(chip->dev, "Fault log entry detected: KEY_RESET\n");
+ if (fault_log & DA9062AA_NSHUTDOWN_MASK)
+ dev_dbg(chip->dev, "Fault log entry detected: NSHUTDOWN\n");
+ if (fault_log & DA9062AA_WAIT_SHUT_MASK)
+ dev_dbg(chip->dev, "Fault log entry detected: WAIT_SHUT\n");
+ }
+
+ ret = regmap_write(chip->regmap, DA9062AA_FAULT_LOG,
+ fault_log);
+ if (ret < 0)
+ dev_err(chip->dev,
+ "Cannot reset FAULT_LOG values %d\n", ret);
+ }
+
+ return ret;
+}
+
+int da9062_device_init(struct da9062 *chip, unsigned int irq)
+{
+ int device_id, variant_id, variant_mrc;
+ int ret;
+
+ ret = da9062_clear_fault_log(chip);
+ if (ret < 0)
+ dev_err(chip->dev, "Cannot clear fault log\n");
+
+ chip->irq_base = -1;
+ chip->chip_irq = irq;
+
+ ret = regmap_read(chip->regmap, DA9062AA_DEVICE_ID, &device_id);
+ if (ret < 0) {
+ dev_err(chip->dev, "Cannot read chip ID.\n");
+ return -EIO;
+ }
+ if (device_id != DA9062_PMIC_DEVICE_ID) {
+ dev_err(chip->dev, "Invalid device ID: 0x%02x\n", device_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(chip->regmap, DA9062AA_VARIANT_ID, &variant_id);
+ if (ret < 0) {
+ dev_err(chip->dev, "Cannot read chip variant id.\n");
+ return -EIO;
+ }
+
+ dev_info(chip->dev,
+ "Device detected (device-ID: 0x%02X, var-ID: 0x%02X)\n",
+ device_id, variant_id);
+
+ variant_mrc = (variant_id & DA9062AA_MRC_MASK) >> DA9062AA_MRC_SHIFT;
+
+ if (variant_mrc < DA9062_PMIC_VARIANT_MRC_AA) {
+ dev_err(chip->dev,
+ "Cannot support variant MRC: 0x%02X\n", variant_mrc);
+ return -ENODEV;
+ }
+
+ chip->device_id = device_id;
+ chip->variant_mrc = variant_mrc;
+
+ ret = da9062_irq_init(chip);
+ if (ret) {
+ dev_err(chip->dev, "Cannot initialize interrupts.\n");
+ return ret;
+ }
+
+ chip->irq_base = regmap_irq_chip_get_base(chip->regmap_irq);
+
+ ret = mfd_add_devices(chip->dev, -1, da9062_devs,
+ ARRAY_SIZE(da9062_devs), NULL, chip->irq_base,
+ NULL);
+ if (ret) {
+ dev_err(chip->dev, "Cannot add MFD cells\n");
+ da9062_irq_exit(chip);
+ return ret;
+ }
+
+ return ret;
+}
+
+void da9062_device_exit(struct da9062 *chip)
+{
+ mfd_remove_devices(chip->dev);
+ da9062_irq_exit(chip);
+}
+
+/* I2C device driver for DA9062 */
+
+static const struct regmap_range da9062_aa_readable_ranges[] = {
+ {
+ .range_min = DA9062AA_PAGE_CON,
+ .range_max = DA9062AA_STATUS_B,
+ }, {
+ .range_min = DA9062AA_STATUS_D,
+ .range_max = DA9062AA_EVENT_C,
+ }, {
+ .range_min = DA9062AA_IRQ_MASK_A,
+ .range_max = DA9062AA_IRQ_MASK_C,
+ }, {
+ .range_min = DA9062AA_CONTROL_A,
+ .range_max = DA9062AA_GPIO_4,
+ }, {
+ .range_min = DA9062AA_GPIO_WKUP_MODE,
+ .range_max = DA9062AA_BUCK4_CONT,
+ }, {
+ .range_min = DA9062AA_BUCK3_CONT,
+ .range_max = DA9062AA_BUCK3_CONT,
+ }, {
+ .range_min = DA9062AA_LDO1_CONT,
+ .range_max = DA9062AA_LDO4_CONT,
+ }, {
+ .range_min = DA9062AA_DVC_1,
+ .range_max = DA9062AA_DVC_1,
+ }, {
+ .range_min = DA9062AA_COUNT_S,
+ .range_max = DA9062AA_SECOND_D,
+ }, {
+ .range_min = DA9062AA_SEQ,
+ .range_max = DA9062AA_ID_4_3,
+ }, {
+ .range_min = DA9062AA_ID_12_11,
+ .range_max = DA9062AA_ID_16_15,
+ }, {
+ .range_min = DA9062AA_ID_22_21,
+ .range_max = DA9062AA_ID_32_31,
+ }, {
+ .range_min = DA9062AA_SEQ_A,
+ .range_max = DA9062AA_BUCK3_CFG,
+ }, {
+ .range_min = DA9062AA_VBUCK2_A,
+ .range_max = DA9062AA_VBUCK4_A,
+ }, {
+ .range_min = DA9062AA_VBUCK3_A,
+ .range_max = DA9062AA_VBUCK3_A,
+ }, {
+ .range_min = DA9062AA_VLDO1_A,
+ .range_max = DA9062AA_VLDO4_A,
+ }, {
+ .range_min = DA9062AA_VBUCK2_B,
+ .range_max = DA9062AA_VBUCK4_B,
+ }, {
+ .range_min = DA9062AA_VBUCK3_B,
+ .range_max = DA9062AA_VBUCK3_B,
+ }, {
+ .range_min = DA9062AA_VLDO1_B,
+ .range_max = DA9062AA_VLDO4_B,
+ }, {
+ .range_min = DA9062AA_BBAT_CONT,
+ .range_max = DA9062AA_BBAT_CONT,
+ }, {
+ .range_min = DA9062AA_INTERFACE,
+ .range_max = DA9062AA_CONFIG_E,
+ }, {
+ .range_min = DA9062AA_CONFIG_G,
+ .range_max = DA9062AA_CONFIG_K,
+ }, {
+ .range_min = DA9062AA_CONFIG_M,
+ .range_max = DA9062AA_CONFIG_M,
+ }, {
+ .range_min = DA9062AA_TRIM_CLDR,
+ .range_max = DA9062AA_GP_ID_19,
+ }, {
+ .range_min = DA9062AA_DEVICE_ID,
+ .range_max = DA9062AA_CONFIG_ID,
+ },
+};
+
+static const struct regmap_range da9062_aa_writeable_ranges[] = {
+ {
+ .range_min = DA9062AA_PAGE_CON,
+ .range_max = DA9062AA_PAGE_CON,
+ }, {
+ .range_min = DA9062AA_FAULT_LOG,
+ .range_max = DA9062AA_EVENT_C,
+ }, {
+ .range_min = DA9062AA_IRQ_MASK_A,
+ .range_max = DA9062AA_IRQ_MASK_C,
+ }, {
+ .range_min = DA9062AA_CONTROL_A,
+ .range_max = DA9062AA_GPIO_4,
+ }, {
+ .range_min = DA9062AA_GPIO_WKUP_MODE,
+ .range_max = DA9062AA_BUCK4_CONT,
+ }, {
+ .range_min = DA9062AA_BUCK3_CONT,
+ .range_max = DA9062AA_BUCK3_CONT,
+ }, {
+ .range_min = DA9062AA_LDO1_CONT,
+ .range_max = DA9062AA_LDO4_CONT,
+ }, {
+ .range_min = DA9062AA_DVC_1,
+ .range_max = DA9062AA_DVC_1,
+ }, {
+ .range_min = DA9062AA_COUNT_S,
+ .range_max = DA9062AA_ALARM_Y,
+ }, {
+ .range_min = DA9062AA_SEQ,
+ .range_max = DA9062AA_ID_4_3,
+ }, {
+ .range_min = DA9062AA_ID_12_11,
+ .range_max = DA9062AA_ID_16_15,
+ }, {
+ .range_min = DA9062AA_ID_22_21,
+ .range_max = DA9062AA_ID_32_31,
+ }, {
+ .range_min = DA9062AA_SEQ_A,
+ .range_max = DA9062AA_BUCK3_CFG,
+ }, {
+ .range_min = DA9062AA_VBUCK2_A,
+ .range_max = DA9062AA_VBUCK4_A,
+ }, {
+ .range_min = DA9062AA_VBUCK3_A,
+ .range_max = DA9062AA_VBUCK3_A,
+ }, {
+ .range_min = DA9062AA_VLDO1_A,
+ .range_max = DA9062AA_VLDO4_A,
+ }, {
+ .range_min = DA9062AA_VBUCK2_B,
+ .range_max = DA9062AA_VBUCK4_B,
+ }, {
+ .range_min = DA9062AA_VBUCK3_B,
+ .range_max = DA9062AA_VBUCK3_B,
+ }, {
+ .range_min = DA9062AA_VLDO1_B,
+ .range_max = DA9062AA_VLDO4_B,
+ }, {
+ .range_min = DA9062AA_BBAT_CONT,
+ .range_max = DA9062AA_BBAT_CONT,
+ }, {
+ .range_min = DA9062AA_GP_ID_0,
+ .range_max = DA9062AA_GP_ID_19,
+ },
+};
+
+static const struct regmap_range da9062_aa_volatile_ranges[] = {
+ {
+ .range_min = DA9062AA_PAGE_CON,
+ .range_max = DA9062AA_STATUS_B,
+ }, {
+ .range_min = DA9062AA_STATUS_D,
+ .range_max = DA9062AA_EVENT_C,
+ }, {
+ .range_min = DA9062AA_CONTROL_F,
+ .range_max = DA9062AA_CONTROL_F,
+ }, {
+ .range_min = DA9062AA_COUNT_S,
+ .range_max = DA9062AA_SECOND_D,
+ },
+};
+
+static const struct regmap_access_table da9062_aa_readable_table = {
+ .yes_ranges = da9062_aa_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9062_aa_readable_ranges),
+};
+
+static const struct regmap_access_table da9062_aa_writeable_table = {
+ .yes_ranges = da9062_aa_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9062_aa_writeable_ranges),
+};
+
+static const struct regmap_access_table da9062_aa_volatile_table = {
+ .yes_ranges = da9062_aa_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9062_aa_volatile_ranges),
+};
+
+static const struct regmap_range_cfg da9062_range_cfg[] = {
+ {
+ .range_min = DA9062AA_PAGE_CON,
+ .range_max = DA9062AA_CONFIG_ID,
+ .selector_reg = DA9062AA_PAGE_CON,
+ .selector_mask = 1 << DA9062_I2C_PAGE_SEL_SHIFT,
+ .selector_shift = DA9062_I2C_PAGE_SEL_SHIFT,
+ .window_start = 0,
+ .window_len = 256,
+ }
+};
+
+static struct regmap_config da9062_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .ranges = da9062_range_cfg,
+ .num_ranges = ARRAY_SIZE(da9062_range_cfg),
+ .max_register = DA9062AA_CONFIG_ID,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct of_device_id da9062_dt_ids[] = {
+ { .compatible = "dlg,da9062", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da9062_dt_ids);
+
+static int da9062_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da9062 *chip;
+ int ret;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct da9062), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, chip);
+ chip->dev = &i2c->dev;
+ chip->chip_irq = i2c->irq;
+
+ da9062_regmap_config.rd_table = &da9062_aa_readable_table;
+ da9062_regmap_config.wr_table = &da9062_aa_writeable_table;
+ da9062_regmap_config.volatile_table = &da9062_aa_volatile_table;
+
+ chip->regmap = devm_regmap_init_i2c(i2c, &da9062_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ return da9062_device_init(chip, i2c->irq);
+}
+
+static int da9062_i2c_remove(struct i2c_client *i2c)
+{
+ struct da9062 *chip = i2c_get_clientdata(i2c);
+
+ da9062_device_exit(chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id da9062_i2c_id[] = {
+ {"da9062", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, da9062_i2c_id);
+
+static struct i2c_driver da9062_i2c_driver = {
+ .driver = {
+ .name = "da9062",
+ .of_match_table = of_match_ptr(da9062_dt_ids),
+ },
+ .probe = da9062_i2c_probe,
+ .remove = da9062_i2c_remove,
+ .id_table = da9062_i2c_id,
+};
+
+module_i2c_driver(da9062_i2c_driver);
+
+
+MODULE_DESCRIPTION("CORE device driver for Dialog DA9062");
+MODULE_AUTHOR("S Twiss <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/da9062/core.h b/include/linux/mfd/da9062/core.h
new file mode 100644
index 0000000..3b4e36e
--- /dev/null
+++ b/include/linux/mfd/da9062/core.h
@@ -0,0 +1,61 @@
+/*
+ * core.h - CORE H for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MFD_DA9062_CORE_H__
+#define __MFD_DA9062_CORE_H__
+
+#include <linux/interrupt.h>
+#include <linux/mfd/da9062/registers.h>
+
+/* Interrupts */
+enum da9062_irqs {
+ /* IRQ A */
+ DA9062_IRQ_ONKEY,
+ DA9062_IRQ_ALARM,
+ DA9062_IRQ_TICK,
+ DA9062_IRQ_WDG_WARN,
+ DA9062_IRQ_SEQ_RDY,
+ /* IRQ B*/
+ DA9062_IRQ_TEMP,
+ DA9062_IRQ_LDO_LIM,
+ DA9062_IRQ_DVC_RDY,
+ DA9062_IRQ_VDD_WARN,
+ /* IRQ C */
+ DA9062_IRQ_GPI0,
+ DA9062_IRQ_GPI1,
+ DA9062_IRQ_GPI2,
+ DA9062_IRQ_GPI3,
+ DA9062_IRQ_GPI4,
+
+ DA9062_NUM_IRQ,
+};
+
+struct da9062 {
+ struct device *dev;
+ unsigned char device_id;
+ unsigned char variant_mrc;
+ struct regmap *regmap;
+ int chip_irq;
+ unsigned int irq_base;
+ struct regmap_irq_chip_data *regmap_irq;
+};
+
+int da9062_device_init(struct da9062 *, unsigned int);
+int da9062_irq_init(struct da9062 *);
+
+void da9062_device_exit(struct da9062 *);
+void da9062_irq_exit(struct da9062 *);
+
+#endif /* __MFD_DA9062_CORE_H__ */
diff --git a/include/linux/mfd/da9062/registers.h b/include/linux/mfd/da9062/registers.h
new file mode 100644
index 0000000..d07c2bc
--- /dev/null
+++ b/include/linux/mfd/da9062/registers.h
@@ -0,0 +1,1108 @@
+/*
+ * registers.h - REGISTERS H for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DA9062_H__
+#define __DA9062_H__
+
+#define DA9062_PMIC_DEVICE_ID 0x62
+#define DA9062_PMIC_VARIANT_MRC_AA 0x01
+
+#define DA9062_I2C_PAGE_SEL_SHIFT 1
+
+/*
+ * Registers
+ */
+
+#define DA9062AA_PAGE_CON 0x000
+#define DA9062AA_STATUS_A 0x001
+#define DA9062AA_STATUS_B 0x002
+#define DA9062AA_STATUS_D 0x004
+#define DA9062AA_FAULT_LOG 0x005
+#define DA9062AA_EVENT_A 0x006
+#define DA9062AA_EVENT_B 0x007
+#define DA9062AA_EVENT_C 0x008
+#define DA9062AA_IRQ_MASK_A 0x00A
+#define DA9062AA_IRQ_MASK_B 0x00B
+#define DA9062AA_IRQ_MASK_C 0x00C
+#define DA9062AA_CONTROL_A 0x00E
+#define DA9062AA_CONTROL_B 0x00F
+#define DA9062AA_CONTROL_C 0x010
+#define DA9062AA_CONTROL_D 0x011
+#define DA9062AA_CONTROL_E 0x012
+#define DA9062AA_CONTROL_F 0x013
+#define DA9062AA_PD_DIS 0x014
+#define DA9062AA_GPIO_0_1 0x015
+#define DA9062AA_GPIO_2_3 0x016
+#define DA9062AA_GPIO_4 0x017
+#define DA9062AA_GPIO_WKUP_MODE 0x01C
+#define DA9062AA_GPIO_MODE0_4 0x01D
+#define DA9062AA_GPIO_OUT0_2 0x01E
+#define DA9062AA_GPIO_OUT3_4 0x01F
+#define DA9062AA_BUCK2_CONT 0x020
+#define DA9062AA_BUCK1_CONT 0x021
+#define DA9062AA_BUCK4_CONT 0x022
+#define DA9062AA_BUCK3_CONT 0x024
+#define DA9062AA_LDO1_CONT 0x026
+#define DA9062AA_LDO2_CONT 0x027
+#define DA9062AA_LDO3_CONT 0x028
+#define DA9062AA_LDO4_CONT 0x029
+#define DA9062AA_DVC_1 0x032
+#define DA9062AA_COUNT_S 0x040
+#define DA9062AA_COUNT_MI 0x041
+#define DA9062AA_COUNT_H 0x042
+#define DA9062AA_COUNT_D 0x043
+#define DA9062AA_COUNT_MO 0x044
+#define DA9062AA_COUNT_Y 0x045
+#define DA9062AA_ALARM_S 0x046
+#define DA9062AA_ALARM_MI 0x047
+#define DA9062AA_ALARM_H 0x048
+#define DA9062AA_ALARM_D 0x049
+#define DA9062AA_ALARM_MO 0x04A
+#define DA9062AA_ALARM_Y 0x04B
+#define DA9062AA_SECOND_A 0x04C
+#define DA9062AA_SECOND_B 0x04D
+#define DA9062AA_SECOND_C 0x04E
+#define DA9062AA_SECOND_D 0x04F
+#define DA9062AA_SEQ 0x081
+#define DA9062AA_SEQ_TIMER 0x082
+#define DA9062AA_ID_2_1 0x083
+#define DA9062AA_ID_4_3 0x084
+#define DA9062AA_ID_12_11 0x088
+#define DA9062AA_ID_14_13 0x089
+#define DA9062AA_ID_16_15 0x08A
+#define DA9062AA_ID_22_21 0x08D
+#define DA9062AA_ID_24_23 0x08E
+#define DA9062AA_ID_26_25 0x08F
+#define DA9062AA_ID_28_27 0x090
+#define DA9062AA_ID_30_29 0x091
+#define DA9062AA_ID_32_31 0x092
+#define DA9062AA_SEQ_A 0x095
+#define DA9062AA_SEQ_B 0x096
+#define DA9062AA_WAIT 0x097
+#define DA9062AA_EN_32K 0x098
+#define DA9062AA_RESET 0x099
+#define DA9062AA_BUCK_ILIM_A 0x09A
+#define DA9062AA_BUCK_ILIM_B 0x09B
+#define DA9062AA_BUCK_ILIM_C 0x09C
+#define DA9062AA_BUCK2_CFG 0x09D
+#define DA9062AA_BUCK1_CFG 0x09E
+#define DA9062AA_BUCK4_CFG 0x09F
+#define DA9062AA_BUCK3_CFG 0x0A0
+#define DA9062AA_VBUCK2_A 0x0A3
+#define DA9062AA_VBUCK1_A 0x0A4
+#define DA9062AA_VBUCK4_A 0x0A5
+#define DA9062AA_VBUCK3_A 0x0A7
+#define DA9062AA_VLDO1_A 0x0A9
+#define DA9062AA_VLDO2_A 0x0AA
+#define DA9062AA_VLDO3_A 0x0AB
+#define DA9062AA_VLDO4_A 0x0AC
+#define DA9062AA_VBUCK2_B 0x0B4
+#define DA9062AA_VBUCK1_B 0x0B5
+#define DA9062AA_VBUCK4_B 0x0B6
+#define DA9062AA_VBUCK3_B 0x0B8
+#define DA9062AA_VLDO1_B 0x0BA
+#define DA9062AA_VLDO2_B 0x0BB
+#define DA9062AA_VLDO3_B 0x0BC
+#define DA9062AA_VLDO4_B 0x0BD
+#define DA9062AA_BBAT_CONT 0x0C5
+#define DA9062AA_INTERFACE 0x105
+#define DA9062AA_CONFIG_A 0x106
+#define DA9062AA_CONFIG_B 0x107
+#define DA9062AA_CONFIG_C 0x108
+#define DA9062AA_CONFIG_D 0x109
+#define DA9062AA_CONFIG_E 0x10A
+#define DA9062AA_CONFIG_G 0x10C
+#define DA9062AA_CONFIG_H 0x10D
+#define DA9062AA_CONFIG_I 0x10E
+#define DA9062AA_CONFIG_J 0x10F
+#define DA9062AA_CONFIG_K 0x110
+#define DA9062AA_CONFIG_M 0x112
+#define DA9062AA_TRIM_CLDR 0x120
+#define DA9062AA_GP_ID_0 0x121
+#define DA9062AA_GP_ID_1 0x122
+#define DA9062AA_GP_ID_2 0x123
+#define DA9062AA_GP_ID_3 0x124
+#define DA9062AA_GP_ID_4 0x125
+#define DA9062AA_GP_ID_5 0x126
+#define DA9062AA_GP_ID_6 0x127
+#define DA9062AA_GP_ID_7 0x128
+#define DA9062AA_GP_ID_8 0x129
+#define DA9062AA_GP_ID_9 0x12A
+#define DA9062AA_GP_ID_10 0x12B
+#define DA9062AA_GP_ID_11 0x12C
+#define DA9062AA_GP_ID_12 0x12D
+#define DA9062AA_GP_ID_13 0x12E
+#define DA9062AA_GP_ID_14 0x12F
+#define DA9062AA_GP_ID_15 0x130
+#define DA9062AA_GP_ID_16 0x131
+#define DA9062AA_GP_ID_17 0x132
+#define DA9062AA_GP_ID_18 0x133
+#define DA9062AA_GP_ID_19 0x134
+#define DA9062AA_DEVICE_ID 0x181
+#define DA9062AA_VARIANT_ID 0x182
+#define DA9062AA_CUSTOMER_ID 0x183
+#define DA9062AA_CONFIG_ID 0x184
+
+/*
+ * Bit fields
+ */
+
+/* DA9062AA_PAGE_CON = 0x000 */
+#define DA9062AA_PAGE_SHIFT 0
+#define DA9062AA_PAGE_MASK (0x3f << 0)
+#define DA9062AA_WRITE_MODE_SHIFT 6
+#define DA9062AA_WRITE_MODE_MASK (0x01 << 6)
+#define DA9062AA_REVERT_SHIFT 7
+#define DA9062AA_REVERT_MASK (0x01 << 7)
+
+/* DA9062AA_STATUS_A = 0x001 */
+#define DA9062AA_NONKEY_SHIFT 0
+#define DA9062AA_NONKEY_MASK (0x01 << 0)
+#define DA9062AA_DVC_BUSY_SHIFT 2
+#define DA9062AA_DVC_BUSY_MASK (0x01 << 2)
+
+/* DA9062AA_STATUS_B = 0x002 */
+#define DA9062AA_GPI0_SHIFT 0
+#define DA9062AA_GPI0_MASK (0x01 << 0)
+#define DA9062AA_GPI1_SHIFT 1
+#define DA9062AA_GPI1_MASK (0x01 << 1)
+#define DA9062AA_GPI2_SHIFT 2
+#define DA9062AA_GPI2_MASK (0x01 << 2)
+#define DA9062AA_GPI3_SHIFT 3
+#define DA9062AA_GPI3_MASK (0x01 << 3)
+#define DA9062AA_GPI4_SHIFT 4
+#define DA9062AA_GPI4_MASK (0x01 << 4)
+
+/* DA9062AA_STATUS_D = 0x004 */
+#define DA9062AA_LDO1_ILIM_SHIFT 0
+#define DA9062AA_LDO1_ILIM_MASK (0x01 << 0)
+#define DA9062AA_LDO2_ILIM_SHIFT 1
+#define DA9062AA_LDO2_ILIM_MASK (0x01 << 1)
+#define DA9062AA_LDO3_ILIM_SHIFT 2
+#define DA9062AA_LDO3_ILIM_MASK (0x01 << 2)
+#define DA9062AA_LDO4_ILIM_SHIFT 3
+#define DA9062AA_LDO4_ILIM_MASK (0x01 << 3)
+
+/* DA9062AA_FAULT_LOG = 0x005 */
+#define DA9062AA_TWD_ERROR_SHIFT 0
+#define DA9062AA_TWD_ERROR_MASK (0x01 << 0)
+#define DA9062AA_POR_SHIFT 1
+#define DA9062AA_POR_MASK (0x01 << 1)
+#define DA9062AA_VDD_FAULT_SHIFT 2
+#define DA9062AA_VDD_FAULT_MASK (0x01 << 2)
+#define DA9062AA_VDD_START_SHIFT 3
+#define DA9062AA_VDD_START_MASK (0x01 << 3)
+#define DA9062AA_TEMP_CRIT_SHIFT 4
+#define DA9062AA_TEMP_CRIT_MASK (0x01 << 4)
+#define DA9062AA_KEY_RESET_SHIFT 5
+#define DA9062AA_KEY_RESET_MASK (0x01 << 5)
+#define DA9062AA_NSHUTDOWN_SHIFT 6
+#define DA9062AA_NSHUTDOWN_MASK (0x01 << 6)
+#define DA9062AA_WAIT_SHUT_SHIFT 7
+#define DA9062AA_WAIT_SHUT_MASK (0x01 << 7)
+
+/* DA9062AA_EVENT_A = 0x006 */
+#define DA9062AA_E_NONKEY_SHIFT 0
+#define DA9062AA_E_NONKEY_MASK (0x01 << 0)
+#define DA9062AA_E_ALARM_SHIFT 1
+#define DA9062AA_E_ALARM_MASK (0x01 << 1)
+#define DA9062AA_E_TICK_SHIFT 2
+#define DA9062AA_E_TICK_MASK (0x01 << 2)
+#define DA9062AA_E_WDG_WARN_SHIFT 3
+#define DA9062AA_E_WDG_WARN_MASK (0x01 << 3)
+#define DA9062AA_E_SEQ_RDY_SHIFT 4
+#define DA9062AA_E_SEQ_RDY_MASK (0x01 << 4)
+#define DA9062AA_EVENTS_B_SHIFT 5
+#define DA9062AA_EVENTS_B_MASK (0x01 << 5)
+#define DA9062AA_EVENTS_C_SHIFT 6
+#define DA9062AA_EVENTS_C_MASK (0x01 << 6)
+
+/* DA9062AA_EVENT_B = 0x007 */
+#define DA9062AA_E_TEMP_SHIFT 1
+#define DA9062AA_E_TEMP_MASK (0x01 << 1)
+#define DA9062AA_E_LDO_LIM_SHIFT 3
+#define DA9062AA_E_LDO_LIM_MASK (0x01 << 3)
+#define DA9062AA_E_DVC_RDY_SHIFT 5
+#define DA9062AA_E_DVC_RDY_MASK (0x01 << 5)
+#define DA9062AA_E_VDD_WARN_SHIFT 7
+#define DA9062AA_E_VDD_WARN_MASK (0x01 << 7)
+
+/* DA9062AA_EVENT_C = 0x008 */
+#define DA9062AA_E_GPI0_SHIFT 0
+#define DA9062AA_E_GPI0_MASK (0x01 << 0)
+#define DA9062AA_E_GPI1_SHIFT 1
+#define DA9062AA_E_GPI1_MASK (0x01 << 1)
+#define DA9062AA_E_GPI2_SHIFT 2
+#define DA9062AA_E_GPI2_MASK (0x01 << 2)
+#define DA9062AA_E_GPI3_SHIFT 3
+#define DA9062AA_E_GPI3_MASK (0x01 << 3)
+#define DA9062AA_E_GPI4_SHIFT 4
+#define DA9062AA_E_GPI4_MASK (0x01 << 4)
+
+/* DA9062AA_IRQ_MASK_A = 0x00A */
+#define DA9062AA_M_NONKEY_SHIFT 0
+#define DA9062AA_M_NONKEY_MASK (0x01 << 0)
+#define DA9062AA_M_ALARM_SHIFT 1
+#define DA9062AA_M_ALARM_MASK (0x01 << 1)
+#define DA9062AA_M_TICK_SHIFT 2
+#define DA9062AA_M_TICK_MASK (0x01 << 2)
+#define DA9062AA_M_WDG_WARN_SHIFT 3
+#define DA9062AA_M_WDG_WARN_MASK (0x01 << 3)
+#define DA9062AA_M_SEQ_RDY_SHIFT 4
+#define DA9062AA_M_SEQ_RDY_MASK (0x01 << 4)
+
+/* DA9062AA_IRQ_MASK_B = 0x00B */
+#define DA9062AA_M_TEMP_SHIFT 1
+#define DA9062AA_M_TEMP_MASK (0x01 << 1)
+#define DA9062AA_M_LDO_LIM_SHIFT 3
+#define DA9062AA_M_LDO_LIM_MASK (0x01 << 3)
+#define DA9062AA_M_DVC_RDY_SHIFT 5
+#define DA9062AA_M_DVC_RDY_MASK (0x01 << 5)
+#define DA9062AA_M_VDD_WARN_SHIFT 7
+#define DA9062AA_M_VDD_WARN_MASK (0x01 << 7)
+
+/* DA9062AA_IRQ_MASK_C = 0x00C */
+#define DA9062AA_M_GPI0_SHIFT 0
+#define DA9062AA_M_GPI0_MASK (0x01 << 0)
+#define DA9062AA_M_GPI1_SHIFT 1
+#define DA9062AA_M_GPI1_MASK (0x01 << 1)
+#define DA9062AA_M_GPI2_SHIFT 2
+#define DA9062AA_M_GPI2_MASK (0x01 << 2)
+#define DA9062AA_M_GPI3_SHIFT 3
+#define DA9062AA_M_GPI3_MASK (0x01 << 3)
+#define DA9062AA_M_GPI4_SHIFT 4
+#define DA9062AA_M_GPI4_MASK (0x01 << 4)
+
+/* DA9062AA_CONTROL_A = 0x00E */
+#define DA9062AA_SYSTEM_EN_SHIFT 0
+#define DA9062AA_SYSTEM_EN_MASK (0x01 << 0)
+#define DA9062AA_POWER_EN_SHIFT 1
+#define DA9062AA_POWER_EN_MASK (0x01 << 1)
+#define DA9062AA_POWER1_EN_SHIFT 2
+#define DA9062AA_POWER1_EN_MASK (0x01 << 2)
+#define DA9062AA_STANDBY_SHIFT 3
+#define DA9062AA_STANDBY_MASK (0x01 << 3)
+#define DA9062AA_M_SYSTEM_EN_SHIFT 4
+#define DA9062AA_M_SYSTEM_EN_MASK (0x01 << 4)
+#define DA9062AA_M_POWER_EN_SHIFT 5
+#define DA9062AA_M_POWER_EN_MASK (0x01 << 5)
+#define DA9062AA_M_POWER1_EN_SHIFT 6
+#define DA9062AA_M_POWER1_EN_MASK (0x01 << 6)
+
+/* DA9062AA_CONTROL_B = 0x00F */
+#define DA9062AA_WATCHDOG_PD_SHIFT 1
+#define DA9062AA_WATCHDOG_PD_MASK (0x01 << 1)
+#define DA9062AA_FREEZE_EN_SHIFT 2
+#define DA9062AA_FREEZE_EN_MASK (0x01 << 2)
+#define DA9062AA_NRES_MODE_SHIFT 3
+#define DA9062AA_NRES_MODE_MASK (0x01 << 3)
+#define DA9062AA_NONKEY_LOCK_SHIFT 4
+#define DA9062AA_NONKEY_LOCK_MASK (0x01 << 4)
+#define DA9062AA_NFREEZE_SHIFT 5
+#define DA9062AA_NFREEZE_MASK (0x03 << 5)
+#define DA9062AA_BUCK_SLOWSTART_SHIFT 7
+#define DA9062AA_BUCK_SLOWSTART_MASK (0x01 << 7)
+
+/* DA9062AA_CONTROL_C = 0x010 */
+#define DA9062AA_DEBOUNCING_SHIFT 0
+#define DA9062AA_DEBOUNCING_MASK (0x07 << 0)
+#define DA9062AA_AUTO_BOOT_SHIFT 3
+#define DA9062AA_AUTO_BOOT_MASK (0x01 << 3)
+#define DA9062AA_OTPREAD_EN_SHIFT 4
+#define DA9062AA_OTPREAD_EN_MASK (0x01 << 4)
+#define DA9062AA_SLEW_RATE_SHIFT 5
+#define DA9062AA_SLEW_RATE_MASK (0x03 << 5)
+#define DA9062AA_DEF_SUPPLY_SHIFT 7
+#define DA9062AA_DEF_SUPPLY_MASK (0x01 << 7)
+
+/* DA9062AA_CONTROL_D = 0x011 */
+#define DA9062AA_TWDSCALE_SHIFT 0
+#define DA9062AA_TWDSCALE_MASK (0x07 << 0)
+
+/* DA9062AA_CONTROL_E = 0x012 */
+#define DA9062AA_RTC_MODE_PD_SHIFT 0
+#define DA9062AA_RTC_MODE_PD_MASK (0x01 << 0)
+#define DA9062AA_RTC_MODE_SD_SHIFT 1
+#define DA9062AA_RTC_MODE_SD_MASK (0x01 << 1)
+#define DA9062AA_RTC_EN_SHIFT 2
+#define DA9062AA_RTC_EN_MASK (0x01 << 2)
+#define DA9062AA_V_LOCK_SHIFT 7
+#define DA9062AA_V_LOCK_MASK (0x01 << 7)
+
+/* DA9062AA_CONTROL_F = 0x013 */
+#define DA9062AA_WATCHDOG_SHIFT 0
+#define DA9062AA_WATCHDOG_MASK (0x01 << 0)
+#define DA9062AA_SHUTDOWN_SHIFT 1
+#define DA9062AA_SHUTDOWN_MASK (0x01 << 1)
+#define DA9062AA_WAKE_UP_SHIFT 2
+#define DA9062AA_WAKE_UP_MASK (0x01 << 2)
+
+/* DA9062AA_PD_DIS = 0x014 */
+#define DA9062AA_GPI_DIS_SHIFT 0
+#define DA9062AA_GPI_DIS_MASK (0x01 << 0)
+#define DA9062AA_PMIF_DIS_SHIFT 2
+#define DA9062AA_PMIF_DIS_MASK (0x01 << 2)
+#define DA9062AA_CLDR_PAUSE_SHIFT 4
+#define DA9062AA_CLDR_PAUSE_MASK (0x01 << 4)
+#define DA9062AA_BBAT_DIS_SHIFT 5
+#define DA9062AA_BBAT_DIS_MASK (0x01 << 5)
+#define DA9062AA_OUT32K_PAUSE_SHIFT 6
+#define DA9062AA_OUT32K_PAUSE_MASK (0x01 << 6)
+#define DA9062AA_PMCONT_DIS_SHIFT 7
+#define DA9062AA_PMCONT_DIS_MASK (0x01 << 7)
+
+/* DA9062AA_GPIO_0_1 = 0x015 */
+#define DA9062AA_GPIO0_PIN_SHIFT 0
+#define DA9062AA_GPIO0_PIN_MASK (0x03 << 0)
+#define DA9062AA_GPIO0_TYPE_SHIFT 2
+#define DA9062AA_GPIO0_TYPE_MASK (0x01 << 2)
+#define DA9062AA_GPIO0_WEN_SHIFT 3
+#define DA9062AA_GPIO0_WEN_MASK (0x01 << 3)
+#define DA9062AA_GPIO1_PIN_SHIFT 4
+#define DA9062AA_GPIO1_PIN_MASK (0x03 << 4)
+#define DA9062AA_GPIO1_TYPE_SHIFT 6
+#define DA9062AA_GPIO1_TYPE_MASK (0x01 << 6)
+#define DA9062AA_GPIO1_WEN_SHIFT 7
+#define DA9062AA_GPIO1_WEN_MASK (0x01 << 7)
+
+/* DA9062AA_GPIO_2_3 = 0x016 */
+#define DA9062AA_GPIO2_PIN_SHIFT 0
+#define DA9062AA_GPIO2_PIN_MASK (0x03 << 0)
+#define DA9062AA_GPIO2_TYPE_SHIFT 2
+#define DA9062AA_GPIO2_TYPE_MASK (0x01 << 2)
+#define DA9062AA_GPIO2_WEN_SHIFT 3
+#define DA9062AA_GPIO2_WEN_MASK (0x01 << 3)
+#define DA9062AA_GPIO3_PIN_SHIFT 4
+#define DA9062AA_GPIO3_PIN_MASK (0x03 << 4)
+#define DA9062AA_GPIO3_TYPE_SHIFT 6
+#define DA9062AA_GPIO3_TYPE_MASK (0x01 << 6)
+#define DA9062AA_GPIO3_WEN_SHIFT 7
+#define DA9062AA_GPIO3_WEN_MASK (0x01 << 7)
+
+/* DA9062AA_GPIO_4 = 0x017 */
+#define DA9062AA_GPIO4_PIN_SHIFT 0
+#define DA9062AA_GPIO4_PIN_MASK (0x03 << 0)
+#define DA9062AA_GPIO4_TYPE_SHIFT 2
+#define DA9062AA_GPIO4_TYPE_MASK (0x01 << 2)
+#define DA9062AA_GPIO4_WEN_SHIFT 3
+#define DA9062AA_GPIO4_WEN_MASK (0x01 << 3)
+
+/* DA9062AA_GPIO_WKUP_MODE = 0x01C */
+#define DA9062AA_GPIO0_WKUP_MODE_SHIFT 0
+#define DA9062AA_GPIO0_WKUP_MODE_MASK (0x01 << 0)
+#define DA9062AA_GPIO1_WKUP_MODE_SHIFT 1
+#define DA9062AA_GPIO1_WKUP_MODE_MASK (0x01 << 1)
+#define DA9062AA_GPIO2_WKUP_MODE_SHIFT 2
+#define DA9062AA_GPIO2_WKUP_MODE_MASK (0x01 << 2)
+#define DA9062AA_GPIO3_WKUP_MODE_SHIFT 3
+#define DA9062AA_GPIO3_WKUP_MODE_MASK (0x01 << 3)
+#define DA9062AA_GPIO4_WKUP_MODE_SHIFT 4
+#define DA9062AA_GPIO4_WKUP_MODE_MASK (0x01 << 4)
+
+/* DA9062AA_GPIO_MODE0_4 = 0x01D */
+#define DA9062AA_GPIO0_MODE_SHIFT 0
+#define DA9062AA_GPIO0_MODE_MASK (0x01 << 0)
+#define DA9062AA_GPIO1_MODE_SHIFT 1
+#define DA9062AA_GPIO1_MODE_MASK (0x01 << 1)
+#define DA9062AA_GPIO2_MODE_SHIFT 2
+#define DA9062AA_GPIO2_MODE_MASK (0x01 << 2)
+#define DA9062AA_GPIO3_MODE_SHIFT 3
+#define DA9062AA_GPIO3_MODE_MASK (0x01 << 3)
+#define DA9062AA_GPIO4_MODE_SHIFT 4
+#define DA9062AA_GPIO4_MODE_MASK (0x01 << 4)
+
+/* DA9062AA_GPIO_OUT0_2 = 0x01E */
+#define DA9062AA_GPIO0_OUT_SHIFT 0
+#define DA9062AA_GPIO0_OUT_MASK (0x07 << 0)
+#define DA9062AA_GPIO1_OUT_SHIFT 3
+#define DA9062AA_GPIO1_OUT_MASK (0x07 << 3)
+#define DA9062AA_GPIO2_OUT_SHIFT 6
+#define DA9062AA_GPIO2_OUT_MASK (0x03 << 6)
+
+/* DA9062AA_GPIO_OUT3_4 = 0x01F */
+#define DA9062AA_GPIO3_OUT_SHIFT 0
+#define DA9062AA_GPIO3_OUT_MASK (0x07 << 0)
+#define DA9062AA_GPIO4_OUT_SHIFT 3
+#define DA9062AA_GPIO4_OUT_MASK (0x03 << 3)
+
+/* DA9062AA_BUCK2_CONT = 0x020 */
+#define DA9062AA_BUCK2_EN_SHIFT 0
+#define DA9062AA_BUCK2_EN_MASK (0x01 << 0)
+#define DA9062AA_BUCK2_GPI_SHIFT 1
+#define DA9062AA_BUCK2_GPI_MASK (0x03 << 1)
+#define DA9062AA_BUCK2_CONF_SHIFT 3
+#define DA9062AA_BUCK2_CONF_MASK (0x01 << 3)
+#define DA9062AA_VBUCK2_GPI_SHIFT 5
+#define DA9062AA_VBUCK2_GPI_MASK (0x03 << 5)
+
+/* DA9062AA_BUCK1_CONT = 0x021 */
+#define DA9062AA_BUCK1_EN_SHIFT 0
+#define DA9062AA_BUCK1_EN_MASK (0x01 << 0)
+#define DA9062AA_BUCK1_GPI_SHIFT 1
+#define DA9062AA_BUCK1_GPI_MASK (0x03 << 1)
+#define DA9062AA_BUCK1_CONF_SHIFT 3
+#define DA9062AA_BUCK1_CONF_MASK (0x01 << 3)
+#define DA9062AA_VBUCK1_GPI_SHIFT 5
+#define DA9062AA_VBUCK1_GPI_MASK (0x03 << 5)
+
+/* DA9062AA_BUCK4_CONT = 0x022 */
+#define DA9062AA_BUCK4_EN_SHIFT 0
+#define DA9062AA_BUCK4_EN_MASK (0x01 << 0)
+#define DA9062AA_BUCK4_GPI_SHIFT 1
+#define DA9062AA_BUCK4_GPI_MASK (0x03 << 1)
+#define DA9062AA_BUCK4_CONF_SHIFT 3
+#define DA9062AA_BUCK4_CONF_MASK (0x01 << 3)
+#define DA9062AA_VBUCK4_GPI_SHIFT 5
+#define DA9062AA_VBUCK4_GPI_MASK (0x03 << 5)
+
+/* DA9062AA_BUCK3_CONT = 0x024 */
+#define DA9062AA_BUCK3_EN_SHIFT 0
+#define DA9062AA_BUCK3_EN_MASK (0x01 << 0)
+#define DA9062AA_BUCK3_GPI_SHIFT 1
+#define DA9062AA_BUCK3_GPI_MASK (0x03 << 1)
+#define DA9062AA_BUCK3_CONF_SHIFT 3
+#define DA9062AA_BUCK3_CONF_MASK (0x01 << 3)
+#define DA9062AA_VBUCK3_GPI_SHIFT 5
+#define DA9062AA_VBUCK3_GPI_MASK (0x03 << 5)
+
+/* DA9062AA_LDO1_CONT = 0x026 */
+#define DA9062AA_LDO1_EN_SHIFT 0
+#define DA9062AA_LDO1_EN_MASK (0x01 << 0)
+#define DA9062AA_LDO1_GPI_SHIFT 1
+#define DA9062AA_LDO1_GPI_MASK (0x03 << 1)
+#define DA9062AA_LDO1_PD_DIS_SHIFT 3
+#define DA9062AA_LDO1_PD_DIS_MASK (0x01 << 3)
+#define DA9062AA_VLDO1_GPI_SHIFT 5
+#define DA9062AA_VLDO1_GPI_MASK (0x03 << 5)
+#define DA9062AA_LDO1_CONF_SHIFT 7
+#define DA9062AA_LDO1_CONF_MASK (0x01 << 7)
+
+/* DA9062AA_LDO2_CONT = 0x027 */
+#define DA9062AA_LDO2_EN_SHIFT 0
+#define DA9062AA_LDO2_EN_MASK (0x01 << 0)
+#define DA9062AA_LDO2_GPI_SHIFT 1
+#define DA9062AA_LDO2_GPI_MASK (0x03 << 1)
+#define DA9062AA_LDO2_PD_DIS_SHIFT 3
+#define DA9062AA_LDO2_PD_DIS_MASK (0x01 << 3)
+#define DA9062AA_VLDO2_GPI_SHIFT 5
+#define DA9062AA_VLDO2_GPI_MASK (0x03 << 5)
+#define DA9062AA_LDO2_CONF_SHIFT 7
+#define DA9062AA_LDO2_CONF_MASK (0x01 << 7)
+
+/* DA9062AA_LDO3_CONT = 0x028 */
+#define DA9062AA_LDO3_EN_SHIFT 0
+#define DA9062AA_LDO3_EN_MASK (0x01 << 0)
+#define DA9062AA_LDO3_GPI_SHIFT 1
+#define DA9062AA_LDO3_GPI_MASK (0x03 << 1)
+#define DA9062AA_LDO3_PD_DIS_SHIFT 3
+#define DA9062AA_LDO3_PD_DIS_MASK (0x01 << 3)
+#define DA9062AA_VLDO3_GPI_SHIFT 5
+#define DA9062AA_VLDO3_GPI_MASK (0x03 << 5)
+#define DA9062AA_LDO3_CONF_SHIFT 7
+#define DA9062AA_LDO3_CONF_MASK (0x01 << 7)
+
+/* DA9062AA_LDO4_CONT = 0x029 */
+#define DA9062AA_LDO4_EN_SHIFT 0
+#define DA9062AA_LDO4_EN_MASK (0x01 << 0)
+#define DA9062AA_LDO4_GPI_SHIFT 1
+#define DA9062AA_LDO4_GPI_MASK (0x03 << 1)
+#define DA9062AA_LDO4_PD_DIS_SHIFT 3
+#define DA9062AA_LDO4_PD_DIS_MASK (0x01 << 3)
+#define DA9062AA_VLDO4_GPI_SHIFT 5
+#define DA9062AA_VLDO4_GPI_MASK (0x03 << 5)
+#define DA9062AA_LDO4_CONF_SHIFT 7
+#define DA9062AA_LDO4_CONF_MASK (0x01 << 7)
+
+/* DA9062AA_DVC_1 = 0x032 */
+#define DA9062AA_VBUCK1_SEL_SHIFT 0
+#define DA9062AA_VBUCK1_SEL_MASK (0x01 << 0)
+#define DA9062AA_VBUCK2_SEL_SHIFT 1
+#define DA9062AA_VBUCK2_SEL_MASK (0x01 << 1)
+#define DA9062AA_VBUCK4_SEL_SHIFT 2
+#define DA9062AA_VBUCK4_SEL_MASK (0x01 << 2)
+#define DA9062AA_VBUCK3_SEL_SHIFT 3
+#define DA9062AA_VBUCK3_SEL_MASK (0x01 << 3)
+#define DA9062AA_VLDO1_SEL_SHIFT 4
+#define DA9062AA_VLDO1_SEL_MASK (0x01 << 4)
+#define DA9062AA_VLDO2_SEL_SHIFT 5
+#define DA9062AA_VLDO2_SEL_MASK (0x01 << 5)
+#define DA9062AA_VLDO3_SEL_SHIFT 6
+#define DA9062AA_VLDO3_SEL_MASK (0x01 << 6)
+#define DA9062AA_VLDO4_SEL_SHIFT 7
+#define DA9062AA_VLDO4_SEL_MASK (0x01 << 7)
+
+/* DA9062AA_COUNT_S = 0x040 */
+#define DA9062AA_COUNT_SEC_SHIFT 0
+#define DA9062AA_COUNT_SEC_MASK (0x3f << 0)
+#define DA9062AA_RTC_READ_SHIFT 7
+#define DA9062AA_RTC_READ_MASK (0x01 << 7)
+
+/* DA9062AA_COUNT_MI = 0x041 */
+#define DA9062AA_COUNT_MIN_SHIFT 0
+#define DA9062AA_COUNT_MIN_MASK (0x3f << 0)
+
+/* DA9062AA_COUNT_H = 0x042 */
+#define DA9062AA_COUNT_HOUR_SHIFT 0
+#define DA9062AA_COUNT_HOUR_MASK (0x1f << 0)
+
+/* DA9062AA_COUNT_D = 0x043 */
+#define DA9062AA_COUNT_DAY_SHIFT 0
+#define DA9062AA_COUNT_DAY_MASK (0x1f << 0)
+
+/* DA9062AA_COUNT_MO = 0x044 */
+#define DA9062AA_COUNT_MONTH_SHIFT 0
+#define DA9062AA_COUNT_MONTH_MASK (0x0f << 0)
+
+/* DA9062AA_COUNT_Y = 0x045 */
+#define DA9062AA_COUNT_YEAR_SHIFT 0
+#define DA9062AA_COUNT_YEAR_MASK (0x3f << 0)
+#define DA9062AA_MONITOR_SHIFT 6
+#define DA9062AA_MONITOR_MASK (0x01 << 6)
+
+/* DA9062AA_ALARM_S = 0x046 */
+#define DA9062AA_ALARM_SEC_SHIFT 0
+#define DA9062AA_ALARM_SEC_MASK (0x3f << 0)
+#define DA9062AA_ALARM_STATUS_SHIFT 6
+#define DA9062AA_ALARM_STATUS_MASK (0x03 << 6)
+
+/* DA9062AA_ALARM_MI = 0x047 */
+#define DA9062AA_ALARM_MIN_SHIFT 0
+#define DA9062AA_ALARM_MIN_MASK (0x3f << 0)
+
+/* DA9062AA_ALARM_H = 0x048 */
+#define DA9062AA_ALARM_HOUR_SHIFT 0
+#define DA9062AA_ALARM_HOUR_MASK (0x1f << 0)
+
+/* DA9062AA_ALARM_D = 0x049 */
+#define DA9062AA_ALARM_DAY_SHIFT 0
+#define DA9062AA_ALARM_DAY_MASK (0x1f << 0)
+
+/* DA9062AA_ALARM_MO = 0x04A */
+#define DA9062AA_ALARM_MONTH_SHIFT 0
+#define DA9062AA_ALARM_MONTH_MASK (0x0f << 0)
+#define DA9062AA_TICK_TYPE_SHIFT 4
+#define DA9062AA_TICK_TYPE_MASK (0x01 << 4)
+#define DA9062AA_TICK_WAKE_SHIFT 5
+#define DA9062AA_TICK_WAKE_MASK (0x01 << 5)
+
+/* DA9062AA_ALARM_Y = 0x04B */
+#define DA9062AA_ALARM_YEAR_SHIFT 0
+#define DA9062AA_ALARM_YEAR_MASK (0x3f << 0)
+#define DA9062AA_ALARM_ON_SHIFT 6
+#define DA9062AA_ALARM_ON_MASK (0x01 << 6)
+#define DA9062AA_TICK_ON_SHIFT 7
+#define DA9062AA_TICK_ON_MASK (0x01 << 7)
+
+/* DA9062AA_SECOND_A = 0x04C */
+#define DA9062AA_SECONDS_A_SHIFT 0
+#define DA9062AA_SECONDS_A_MASK (0xff << 0)
+
+/* DA9062AA_SECOND_B = 0x04D */
+#define DA9062AA_SECONDS_B_SHIFT 0
+#define DA9062AA_SECONDS_B_MASK (0xff << 0)
+
+/* DA9062AA_SECOND_C = 0x04E */
+#define DA9062AA_SECONDS_C_SHIFT 0
+#define DA9062AA_SECONDS_C_MASK (0xff << 0)
+
+/* DA9062AA_SECOND_D = 0x04F */
+#define DA9062AA_SECONDS_D_SHIFT 0
+#define DA9062AA_SECONDS_D_MASK (0xff << 0)
+
+/* DA9062AA_SEQ = 0x081 */
+#define DA9062AA_SEQ_POINTER_SHIFT 0
+#define DA9062AA_SEQ_POINTER_MASK (0x0f << 0)
+#define DA9062AA_NXT_SEQ_START_SHIFT 4
+#define DA9062AA_NXT_SEQ_START_MASK (0x0f << 4)
+
+/* DA9062AA_SEQ_TIMER = 0x082 */
+#define DA9062AA_SEQ_TIME_SHIFT 0
+#define DA9062AA_SEQ_TIME_MASK (0x0f << 0)
+#define DA9062AA_SEQ_DUMMY_SHIFT 4
+#define DA9062AA_SEQ_DUMMY_MASK (0x0f << 4)
+
+/* DA9062AA_ID_2_1 = 0x083 */
+#define DA9062AA_LDO1_STEP_SHIFT 0
+#define DA9062AA_LDO1_STEP_MASK (0x0f << 0)
+#define DA9062AA_LDO2_STEP_SHIFT 4
+#define DA9062AA_LDO2_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_4_3 = 0x084 */
+#define DA9062AA_LDO3_STEP_SHIFT 0
+#define DA9062AA_LDO3_STEP_MASK (0x0f << 0)
+#define DA9062AA_LDO4_STEP_SHIFT 4
+#define DA9062AA_LDO4_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_12_11 = 0x088 */
+#define DA9062AA_PD_DIS_STEP_SHIFT 4
+#define DA9062AA_PD_DIS_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_14_13 = 0x089 */
+#define DA9062AA_BUCK1_STEP_SHIFT 0
+#define DA9062AA_BUCK1_STEP_MASK (0x0f << 0)
+#define DA9062AA_BUCK2_STEP_SHIFT 4
+#define DA9062AA_BUCK2_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_16_15 = 0x08A */
+#define DA9062AA_BUCK4_STEP_SHIFT 0
+#define DA9062AA_BUCK4_STEP_MASK (0x0f << 0)
+#define DA9062AA_BUCK3_STEP_SHIFT 4
+#define DA9062AA_BUCK3_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_22_21 = 0x08D */
+#define DA9062AA_GP_RISE1_STEP_SHIFT 0
+#define DA9062AA_GP_RISE1_STEP_MASK (0x0f << 0)
+#define DA9062AA_GP_FALL1_STEP_SHIFT 4
+#define DA9062AA_GP_FALL1_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_24_23 = 0x08E */
+#define DA9062AA_GP_RISE2_STEP_SHIFT 0
+#define DA9062AA_GP_RISE2_STEP_MASK (0x0f << 0)
+#define DA9062AA_GP_FALL2_STEP_SHIFT 4
+#define DA9062AA_GP_FALL2_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_26_25 = 0x08F */
+#define DA9062AA_GP_RISE3_STEP_SHIFT 0
+#define DA9062AA_GP_RISE3_STEP_MASK (0x0f << 0)
+#define DA9062AA_GP_FALL3_STEP_SHIFT 4
+#define DA9062AA_GP_FALL3_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_28_27 = 0x090 */
+#define DA9062AA_GP_RISE4_STEP_SHIFT 0
+#define DA9062AA_GP_RISE4_STEP_MASK (0x0f << 0)
+#define DA9062AA_GP_FALL4_STEP_SHIFT 4
+#define DA9062AA_GP_FALL4_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_30_29 = 0x091 */
+#define DA9062AA_GP_RISE5_STEP_SHIFT 0
+#define DA9062AA_GP_RISE5_STEP_MASK (0x0f << 0)
+#define DA9062AA_GP_FALL5_STEP_SHIFT 4
+#define DA9062AA_GP_FALL5_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_ID_32_31 = 0x092 */
+#define DA9062AA_WAIT_STEP_SHIFT 0
+#define DA9062AA_WAIT_STEP_MASK (0x0f << 0)
+#define DA9062AA_EN32K_STEP_SHIFT 4
+#define DA9062AA_EN32K_STEP_MASK (0x0f << 4)
+
+/* DA9062AA_SEQ_A = 0x095 */
+#define DA9062AA_SYSTEM_END_SHIFT 0
+#define DA9062AA_SYSTEM_END_MASK (0x0f << 0)
+#define DA9062AA_POWER_END_SHIFT 4
+#define DA9062AA_POWER_END_MASK (0x0f << 4)
+
+/* DA9062AA_SEQ_B = 0x096 */
+#define DA9062AA_MAX_COUNT_SHIFT 0
+#define DA9062AA_MAX_COUNT_MASK (0x0f << 0)
+#define DA9062AA_PART_DOWN_SHIFT 4
+#define DA9062AA_PART_DOWN_MASK (0x0f << 4)
+
+/* DA9062AA_WAIT = 0x097 */
+#define DA9062AA_WAIT_TIME_SHIFT 0
+#define DA9062AA_WAIT_TIME_MASK (0x0f << 0)
+#define DA9062AA_WAIT_MODE_SHIFT 4
+#define DA9062AA_WAIT_MODE_MASK (0x01 << 4)
+#define DA9062AA_TIME_OUT_SHIFT 5
+#define DA9062AA_TIME_OUT_MASK (0x01 << 5)
+#define DA9062AA_WAIT_DIR_SHIFT 6
+#define DA9062AA_WAIT_DIR_MASK (0x03 << 6)
+
+/* DA9062AA_EN_32K = 0x098 */
+#define DA9062AA_STABILISATION_TIME_SHIFT 0
+#define DA9062AA_STABILISATION_TIME_MASK (0x07 << 0)
+#define DA9062AA_CRYSTAL_SHIFT 3
+#define DA9062AA_CRYSTAL_MASK (0x01 << 3)
+#define DA9062AA_DELAY_MODE_SHIFT 4
+#define DA9062AA_DELAY_MODE_MASK (0x01 << 4)
+#define DA9062AA_OUT_CLOCK_SHIFT 5
+#define DA9062AA_OUT_CLOCK_MASK (0x01 << 5)
+#define DA9062AA_RTC_CLOCK_SHIFT 6
+#define DA9062AA_RTC_CLOCK_MASK (0x01 << 6)
+#define DA9062AA_EN_32KOUT_SHIFT 7
+#define DA9062AA_EN_32KOUT_MASK (0x01 << 7)
+
+/* DA9062AA_RESET = 0x099 */
+#define DA9062AA_RESET_TIMER_SHIFT 0
+#define DA9062AA_RESET_TIMER_MASK (0x3f << 0)
+#define DA9062AA_RESET_EVENT_SHIFT 6
+#define DA9062AA_RESET_EVENT_MASK (0x03 << 6)
+
+/* DA9062AA_BUCK_ILIM_A = 0x09A */
+#define DA9062AA_BUCK3_ILIM_SHIFT 0
+#define DA9062AA_BUCK3_ILIM_MASK (0x0f << 0)
+
+/* DA9062AA_BUCK_ILIM_B = 0x09B */
+#define DA9062AA_BUCK4_ILIM_SHIFT 0
+#define DA9062AA_BUCK4_ILIM_MASK (0x0f << 0)
+
+/* DA9062AA_BUCK_ILIM_C = 0x09C */
+#define DA9062AA_BUCK1_ILIM_SHIFT 0
+#define DA9062AA_BUCK1_ILIM_MASK (0x0f << 0)
+#define DA9062AA_BUCK2_ILIM_SHIFT 4
+#define DA9062AA_BUCK2_ILIM_MASK (0x0f << 4)
+
+/* DA9062AA_BUCK2_CFG = 0x09D */
+#define DA9062AA_BUCK2_PD_DIS_SHIFT 5
+#define DA9062AA_BUCK2_PD_DIS_MASK (0x01 << 5)
+#define DA9062AA_BUCK2_MODE_SHIFT 6
+#define DA9062AA_BUCK2_MODE_MASK (0x03 << 6)
+
+/* DA9062AA_BUCK1_CFG = 0x09E */
+#define DA9062AA_BUCK1_PD_DIS_SHIFT 5
+#define DA9062AA_BUCK1_PD_DIS_MASK (0x01 << 5)
+#define DA9062AA_BUCK1_MODE_SHIFT 6
+#define DA9062AA_BUCK1_MODE_MASK (0x03 << 6)
+
+/* DA9062AA_BUCK4_CFG = 0x09F */
+#define DA9062AA_BUCK4_VTTR_EN_SHIFT 3
+#define DA9062AA_BUCK4_VTTR_EN_MASK (0x01 << 3)
+#define DA9062AA_BUCK4_VTT_EN_SHIFT 4
+#define DA9062AA_BUCK4_VTT_EN_MASK (0x01 << 4)
+#define DA9062AA_BUCK4_PD_DIS_SHIFT 5
+#define DA9062AA_BUCK4_PD_DIS_MASK (0x01 << 5)
+#define DA9062AA_BUCK4_MODE_SHIFT 6
+#define DA9062AA_BUCK4_MODE_MASK (0x03 << 6)
+
+/* DA9062AA_BUCK3_CFG = 0x0A0 */
+#define DA9062AA_BUCK3_PD_DIS_SHIFT 5
+#define DA9062AA_BUCK3_PD_DIS_MASK (0x01 << 5)
+#define DA9062AA_BUCK3_MODE_SHIFT 6
+#define DA9062AA_BUCK3_MODE_MASK (0x03 << 6)
+
+/* DA9062AA_VBUCK2_A = 0x0A3 */
+#define DA9062AA_VBUCK2_A_SHIFT 0
+#define DA9062AA_VBUCK2_A_MASK (0x7f << 0)
+#define DA9062AA_BUCK2_SL_A_SHIFT 7
+#define DA9062AA_BUCK2_SL_A_MASK (0x01 << 7)
+
+/* DA9062AA_VBUCK1_A = 0x0A4 */
+#define DA9062AA_VBUCK1_A_SHIFT 0
+#define DA9062AA_VBUCK1_A_MASK (0x7f << 0)
+#define DA9062AA_BUCK1_SL_A_SHIFT 7
+#define DA9062AA_BUCK1_SL_A_MASK (0x01 << 7)
+
+/* DA9062AA_VBUCK4_A = 0x0A5 */
+#define DA9062AA_VBUCK4_A_SHIFT 0
+#define DA9062AA_VBUCK4_A_MASK (0x7f << 0)
+#define DA9062AA_BUCK4_SL_A_SHIFT 7
+#define DA9062AA_BUCK4_SL_A_MASK (0x01 << 7)
+
+/* DA9062AA_VBUCK3_A = 0x0A7 */
+#define DA9062AA_VBUCK3_A_SHIFT 0
+#define DA9062AA_VBUCK3_A_MASK (0x7f << 0)
+#define DA9062AA_BUCK3_SL_A_SHIFT 7
+#define DA9062AA_BUCK3_SL_A_MASK (0x01 << 7)
+
+/* DA9062AA_VLDO1_A = 0x0A9 */
+#define DA9062AA_VLDO1_A_SHIFT 0
+#define DA9062AA_VLDO1_A_MASK (0x3f << 0)
+#define DA9062AA_LDO1_SL_A_SHIFT 7
+#define DA9062AA_LDO1_SL_A_MASK (0x01 << 7)
+
+/* DA9062AA_VLDO2_A = 0x0AA */
+#define DA9062AA_VLDO2_A_SHIFT 0
+#define DA9062AA_VLDO2_A_MASK (0x3f << 0)
+#define DA9062AA_LDO2_SL_A_SHIFT 7
+#define DA9062AA_LDO2_SL_A_MASK (0x01 << 7)
+
+/* DA9062AA_VLDO3_A = 0x0AB */
+#define DA9062AA_VLDO3_A_SHIFT 0
+#define DA9062AA_VLDO3_A_MASK (0x3f << 0)
+#define DA9062AA_LDO3_SL_A_SHIFT 7
+#define DA9062AA_LDO3_SL_A_MASK (0x01 << 7)
+
+/* DA9062AA_VLDO4_A = 0x0AC */
+#define DA9062AA_VLDO4_A_SHIFT 0
+#define DA9062AA_VLDO4_A_MASK (0x3f << 0)
+#define DA9062AA_LDO4_SL_A_SHIFT 7
+#define DA9062AA_LDO4_SL_A_MASK (0x01 << 7)
+
+/* DA9062AA_VBUCK2_B = 0x0B4 */
+#define DA9062AA_VBUCK2_B_SHIFT 0
+#define DA9062AA_VBUCK2_B_MASK (0x7f << 0)
+#define DA9062AA_BUCK2_SL_B_SHIFT 7
+#define DA9062AA_BUCK2_SL_B_MASK (0x01 << 7)
+
+/* DA9062AA_VBUCK1_B = 0x0B5 */
+#define DA9062AA_VBUCK1_B_SHIFT 0
+#define DA9062AA_VBUCK1_B_MASK (0x7f << 0)
+#define DA9062AA_BUCK1_SL_B_SHIFT 7
+#define DA9062AA_BUCK1_SL_B_MASK (0x01 << 7)
+
+/* DA9062AA_VBUCK4_B = 0x0B6 */
+#define DA9062AA_VBUCK4_B_SHIFT 0
+#define DA9062AA_VBUCK4_B_MASK (0x7f << 0)
+#define DA9062AA_BUCK4_SL_B_SHIFT 7
+#define DA9062AA_BUCK4_SL_B_MASK (0x01 << 7)
+
+/* DA9062AA_VBUCK3_B = 0x0B8 */
+#define DA9062AA_VBUCK3_B_SHIFT 0
+#define DA9062AA_VBUCK3_B_MASK (0x7f << 0)
+#define DA9062AA_BUCK3_SL_B_SHIFT 7
+#define DA9062AA_BUCK3_SL_B_MASK (0x01 << 7)
+
+/* DA9062AA_VLDO1_B = 0x0BA */
+#define DA9062AA_VLDO1_B_SHIFT 0
+#define DA9062AA_VLDO1_B_MASK (0x3f << 0)
+#define DA9062AA_LDO1_SL_B_SHIFT 7
+#define DA9062AA_LDO1_SL_B_MASK (0x01 << 7)
+
+/* DA9062AA_VLDO2_B = 0x0BB */
+#define DA9062AA_VLDO2_B_SHIFT 0
+#define DA9062AA_VLDO2_B_MASK (0x3f << 0)
+#define DA9062AA_LDO2_SL_B_SHIFT 7
+#define DA9062AA_LDO2_SL_B_MASK (0x01 << 7)
+
+/* DA9062AA_VLDO3_B = 0x0BC */
+#define DA9062AA_VLDO3_B_SHIFT 0
+#define DA9062AA_VLDO3_B_MASK (0x3f << 0)
+#define DA9062AA_LDO3_SL_B_SHIFT 7
+#define DA9062AA_LDO3_SL_B_MASK (0x01 << 7)
+
+/* DA9062AA_VLDO4_B = 0x0BD */
+#define DA9062AA_VLDO4_B_SHIFT 0
+#define DA9062AA_VLDO4_B_MASK (0x3f << 0)
+#define DA9062AA_LDO4_SL_B_SHIFT 7
+#define DA9062AA_LDO4_SL_B_MASK (0x01 << 7)
+
+/* DA9062AA_BBAT_CONT = 0x0C5 */
+#define DA9062AA_BCHG_VSET_SHIFT 0
+#define DA9062AA_BCHG_VSET_MASK (0x0f << 0)
+#define DA9062AA_BCHG_ISET_SHIFT 4
+#define DA9062AA_BCHG_ISET_MASK (0x0f << 4)
+
+/* DA9062AA_INTERFACE = 0x105 */
+#define DA9062AA_IF_BASE_ADDR_SHIFT 4
+#define DA9062AA_IF_BASE_ADDR_MASK (0x0f << 4)
+
+/* DA9062AA_CONFIG_A = 0x106 */
+#define DA9062AA_PM_I_V_SHIFT 0
+#define DA9062AA_PM_I_V_MASK (0x01 << 0)
+#define DA9062AA_PM_O_TYPE_SHIFT 2
+#define DA9062AA_PM_O_TYPE_MASK (0x01 << 2)
+#define DA9062AA_IRQ_TYPE_SHIFT 3
+#define DA9062AA_IRQ_TYPE_MASK (0x01 << 3)
+#define DA9062AA_PM_IF_V_SHIFT 4
+#define DA9062AA_PM_IF_V_MASK (0x01 << 4)
+#define DA9062AA_PM_IF_FMP_SHIFT 5
+#define DA9062AA_PM_IF_FMP_MASK (0x01 << 5)
+#define DA9062AA_PM_IF_HSM_SHIFT 6
+#define DA9062AA_PM_IF_HSM_MASK (0x01 << 6)
+
+/* DA9062AA_CONFIG_B = 0x107 */
+#define DA9062AA_VDD_FAULT_ADJ_SHIFT 0
+#define DA9062AA_VDD_FAULT_ADJ_MASK (0x0f << 0)
+#define DA9062AA_VDD_HYST_ADJ_SHIFT 4
+#define DA9062AA_VDD_HYST_ADJ_MASK (0x07 << 4)
+
+/* DA9062AA_CONFIG_C = 0x108 */
+#define DA9062AA_BUCK_ACTV_DISCHRG_SHIFT 2
+#define DA9062AA_BUCK_ACTV_DISCHRG_MASK (0x01 << 2)
+#define DA9062AA_BUCK1_CLK_INV_SHIFT 3
+#define DA9062AA_BUCK1_CLK_INV_MASK (0x01 << 3)
+#define DA9062AA_BUCK4_CLK_INV_SHIFT 4
+#define DA9062AA_BUCK4_CLK_INV_MASK (0x01 << 4)
+#define DA9062AA_BUCK3_CLK_INV_SHIFT 6
+#define DA9062AA_BUCK3_CLK_INV_MASK (0x01 << 6)
+
+/* DA9062AA_CONFIG_D = 0x109 */
+#define DA9062AA_GPI_V_SHIFT 0
+#define DA9062AA_GPI_V_MASK (0x01 << 0)
+#define DA9062AA_NIRQ_MODE_SHIFT 1
+#define DA9062AA_NIRQ_MODE_MASK (0x01 << 1)
+#define DA9062AA_SYSTEM_EN_RD_SHIFT 2
+#define DA9062AA_SYSTEM_EN_RD_MASK (0x01 << 2)
+#define DA9062AA_FORCE_RESET_SHIFT 5
+#define DA9062AA_FORCE_RESET_MASK (0x01 << 5)
+
+/* DA9062AA_CONFIG_E = 0x10A */
+#define DA9062AA_BUCK1_AUTO_SHIFT 0
+#define DA9062AA_BUCK1_AUTO_MASK (0x01 << 0)
+#define DA9062AA_BUCK2_AUTO_SHIFT 1
+#define DA9062AA_BUCK2_AUTO_MASK (0x01 << 1)
+#define DA9062AA_BUCK4_AUTO_SHIFT 2
+#define DA9062AA_BUCK4_AUTO_MASK (0x01 << 2)
+#define DA9062AA_BUCK3_AUTO_SHIFT 4
+#define DA9062AA_BUCK3_AUTO_MASK (0x01 << 4)
+
+/* DA9062AA_CONFIG_G = 0x10C */
+#define DA9062AA_LDO1_AUTO_SHIFT 0
+#define DA9062AA_LDO1_AUTO_MASK (0x01 << 0)
+#define DA9062AA_LDO2_AUTO_SHIFT 1
+#define DA9062AA_LDO2_AUTO_MASK (0x01 << 1)
+#define DA9062AA_LDO3_AUTO_SHIFT 2
+#define DA9062AA_LDO3_AUTO_MASK (0x01 << 2)
+#define DA9062AA_LDO4_AUTO_SHIFT 3
+#define DA9062AA_LDO4_AUTO_MASK (0x01 << 3)
+
+/* DA9062AA_CONFIG_H = 0x10D */
+#define DA9062AA_BUCK1_2_MERGE_SHIFT 3
+#define DA9062AA_BUCK1_2_MERGE_MASK (0x01 << 3)
+#define DA9062AA_BUCK2_OD_SHIFT 5
+#define DA9062AA_BUCK2_OD_MASK (0x01 << 5)
+#define DA9062AA_BUCK1_OD_SHIFT 6
+#define DA9062AA_BUCK1_OD_MASK (0x01 << 6)
+
+/* DA9062AA_CONFIG_I = 0x10E */
+#define DA9062AA_NONKEY_PIN_SHIFT 0
+#define DA9062AA_NONKEY_PIN_MASK (0x03 << 0)
+#define DA9062AA_nONKEY_SD_SHIFT 2
+#define DA9062AA_nONKEY_SD_MASK (0x01 << 2)
+#define DA9062AA_WATCHDOG_SD_SHIFT 3
+#define DA9062AA_WATCHDOG_SD_MASK (0x01 << 3)
+#define DA9062AA_KEY_SD_MODE_SHIFT 4
+#define DA9062AA_KEY_SD_MODE_MASK (0x01 << 4)
+#define DA9062AA_HOST_SD_MODE_SHIFT 5
+#define DA9062AA_HOST_SD_MODE_MASK (0x01 << 5)
+#define DA9062AA_INT_SD_MODE_SHIFT 6
+#define DA9062AA_INT_SD_MODE_MASK (0x01 << 6)
+#define DA9062AA_LDO_SD_SHIFT 7
+#define DA9062AA_LDO_SD_MASK (0x01 << 7)
+
+/* DA9062AA_CONFIG_J = 0x10F */
+#define DA9062AA_KEY_DELAY_SHIFT 0
+#define DA9062AA_KEY_DELAY_MASK (0x03 << 0)
+#define DA9062AA_SHUT_DELAY_SHIFT 2
+#define DA9062AA_SHUT_DELAY_MASK (0x03 << 2)
+#define DA9062AA_RESET_DURATION_SHIFT 4
+#define DA9062AA_RESET_DURATION_MASK (0x03 << 4)
+#define DA9062AA_TWOWIRE_TO_SHIFT 6
+#define DA9062AA_TWOWIRE_TO_MASK (0x01 << 6)
+#define DA9062AA_IF_RESET_SHIFT 7
+#define DA9062AA_IF_RESET_MASK (0x01 << 7)
+
+/* DA9062AA_CONFIG_K = 0x110 */
+#define DA9062AA_GPIO0_PUPD_SHIFT 0
+#define DA9062AA_GPIO0_PUPD_MASK (0x01 << 0)
+#define DA9062AA_GPIO1_PUPD_SHIFT 1
+#define DA9062AA_GPIO1_PUPD_MASK (0x01 << 1)
+#define DA9062AA_GPIO2_PUPD_SHIFT 2
+#define DA9062AA_GPIO2_PUPD_MASK (0x01 << 2)
+#define DA9062AA_GPIO3_PUPD_SHIFT 3
+#define DA9062AA_GPIO3_PUPD_MASK (0x01 << 3)
+#define DA9062AA_GPIO4_PUPD_SHIFT 4
+#define DA9062AA_GPIO4_PUPD_MASK (0x01 << 4)
+
+/* DA9062AA_CONFIG_M = 0x112 */
+#define DA9062AA_NSHUTDOWN_PU_SHIFT 1
+#define DA9062AA_NSHUTDOWN_PU_MASK (0x01 << 1)
+#define DA9062AA_WDG_MODE_SHIFT 3
+#define DA9062AA_WDG_MODE_MASK (0x01 << 3)
+#define DA9062AA_OSC_FRQ_SHIFT 4
+#define DA9062AA_OSC_FRQ_MASK (0x0f << 4)
+
+/* DA9062AA_TRIM_CLDR = 0x120 */
+#define DA9062AA_TRIM_CLDR_SHIFT 0
+#define DA9062AA_TRIM_CLDR_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_0 = 0x121 */
+#define DA9062AA_GP_0_SHIFT 0
+#define DA9062AA_GP_0_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_1 = 0x122 */
+#define DA9062AA_GP_1_SHIFT 0
+#define DA9062AA_GP_1_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_2 = 0x123 */
+#define DA9062AA_GP_2_SHIFT 0
+#define DA9062AA_GP_2_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_3 = 0x124 */
+#define DA9062AA_GP_3_SHIFT 0
+#define DA9062AA_GP_3_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_4 = 0x125 */
+#define DA9062AA_GP_4_SHIFT 0
+#define DA9062AA_GP_4_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_5 = 0x126 */
+#define DA9062AA_GP_5_SHIFT 0
+#define DA9062AA_GP_5_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_6 = 0x127 */
+#define DA9062AA_GP_6_SHIFT 0
+#define DA9062AA_GP_6_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_7 = 0x128 */
+#define DA9062AA_GP_7_SHIFT 0
+#define DA9062AA_GP_7_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_8 = 0x129 */
+#define DA9062AA_GP_8_SHIFT 0
+#define DA9062AA_GP_8_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_9 = 0x12A */
+#define DA9062AA_GP_9_SHIFT 0
+#define DA9062AA_GP_9_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_10 = 0x12B */
+#define DA9062AA_GP_10_SHIFT 0
+#define DA9062AA_GP_10_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_11 = 0x12C */
+#define DA9062AA_GP_11_SHIFT 0
+#define DA9062AA_GP_11_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_12 = 0x12D */
+#define DA9062AA_GP_12_SHIFT 0
+#define DA9062AA_GP_12_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_13 = 0x12E */
+#define DA9062AA_GP_13_SHIFT 0
+#define DA9062AA_GP_13_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_14 = 0x12F */
+#define DA9062AA_GP_14_SHIFT 0
+#define DA9062AA_GP_14_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_15 = 0x130 */
+#define DA9062AA_GP_15_SHIFT 0
+#define DA9062AA_GP_15_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_16 = 0x131 */
+#define DA9062AA_GP_16_SHIFT 0
+#define DA9062AA_GP_16_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_17 = 0x132 */
+#define DA9062AA_GP_17_SHIFT 0
+#define DA9062AA_GP_17_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_18 = 0x133 */
+#define DA9062AA_GP_18_SHIFT 0
+#define DA9062AA_GP_18_MASK (0xff << 0)
+
+/* DA9062AA_GP_ID_19 = 0x134 */
+#define DA9062AA_GP_19_SHIFT 0
+#define DA9062AA_GP_19_MASK (0xff << 0)
+
+/* DA9062AA_DEVICE_ID = 0x181 */
+#define DA9062AA_DEV_ID_SHIFT 0
+#define DA9062AA_DEV_ID_MASK (0xff << 0)
+
+/* DA9062AA_VARIANT_ID = 0x182 */
+#define DA9062AA_VRC_SHIFT 0
+#define DA9062AA_VRC_MASK (0x0f << 0)
+#define DA9062AA_MRC_SHIFT 4
+#define DA9062AA_MRC_MASK (0x0f << 4)
+
+/* DA9062AA_CUSTOMER_ID = 0x183 */
+#define DA9062AA_CUST_ID_SHIFT 0
+#define DA9062AA_CUST_ID_MASK (0xff << 0)
+
+/* DA9062AA_CONFIG_ID = 0x184 */
+#define DA9062AA_CONFIG_REV_SHIFT 0
+#define DA9062AA_CONFIG_REV_MASK (0xff << 0)
+
+#endif /* __DA9062_H__ */
--
end-of-patch for PATCH V1
From: S Twiss <[email protected]>
Add BUCK and LDO regulator driver support for DA9062
Signed-off-by: Steve Twiss <[email protected]>
---
This patch applies against linux-next and v4.0
drivers/regulator/Kconfig | 10 +
drivers/regulator/Makefile | 1 +
drivers/regulator/da9062-regulator.c | 968 +++++++++++++++++++++++++++++++++++
3 files changed, 979 insertions(+)
create mode 100644 drivers/regulator/da9062-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a6f116a..1fd6dc3 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -178,6 +178,16 @@ config REGULATOR_DA9055
This driver can also be built as a module. If so, the module
will be called da9055-regulator.
+config REGULATOR_DA9062
+ tristate "Dialog Semiconductor DA9062 regulators"
+ depends on MFD_DA9062
+ help
+ Say y here to support the BUCKs and LDOs regulators found on
+ DA9062 PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9062-regulator.
+
config REGULATOR_DA9063
tristate "Dialog Semiconductor DA9063 regulators"
depends on MFD_DA9063
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2c4da15..12ecb65 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
+obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o
obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c
new file mode 100644
index 0000000..503e395
--- /dev/null
+++ b/drivers/regulator/da9062-regulator.c
@@ -0,0 +1,968 @@
+/*
+ * da9062-regulator.c - REGULATOR device driver for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+/* Regulator IDs */
+enum {
+ DA9062_ID_BUCK1,
+ DA9062_ID_BUCK2,
+ DA9062_ID_BUCK3,
+ DA9062_ID_BUCK4,
+ DA9062_ID_LDO1,
+ DA9062_ID_LDO2,
+ DA9062_ID_LDO3,
+ DA9062_ID_LDO4,
+};
+
+/* Regulator capabilities and registers description */
+struct da9062_regulator_info {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned int n_current_limits;
+ const int *current_limits;
+ /* Main register fields */
+ struct reg_field mode;
+ struct reg_field suspend;
+ struct reg_field sleep;
+ struct reg_field suspend_sleep;
+ unsigned int suspend_vsel_reg;
+ struct reg_field ilimit;
+ /* Event detection bit */
+ struct reg_field oc_event;
+};
+
+/* Single regulator settings */
+struct da9062_regulator {
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct da9062 *hw;
+ const struct da9062_regulator_info *info;
+
+ struct regmap_field *mode;
+ struct regmap_field *suspend;
+ struct regmap_field *sleep;
+ struct regmap_field *suspend_sleep;
+ struct regmap_field *ilimit;
+};
+
+/* Encapsulates all information for the regulators driver */
+struct da9062_regulators {
+ int irq_ldo_lim;
+ int irq_vdd_warn;
+ unsigned n_regulators;
+ /* Array size to be defined during init. Keep at end. */
+ struct da9062_regulator regulator[0];
+};
+
+/* BUCK modes */
+enum {
+ BUCK_MODE_MANUAL, /* 0 */
+ BUCK_MODE_SLEEP, /* 1 */
+ BUCK_MODE_SYNC, /* 2 */
+ BUCK_MODE_AUTO /* 3 */
+};
+
+/* Regulator operations */
+
+/* Current limits array (in uA) BUCK1 and BUCK3.
+ Entry indexes corresponds to register values. */
+static const int da9062_buck_a_limits[] = {
+ 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
+ 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
+};
+
+/* Current limits array (in uA) for BUCK2.
+ Entry indexes corresponds to register values. */
+static const int da9062_buck_b_limits[] = {
+ 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
+ 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
+};
+
+static int da9062_set_current_limit(struct regulator_dev *rdev,
+ int min_ua, int max_ua)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ int n, tval;
+
+ for (n = 0; n < rinfo->n_current_limits; n++) {
+ tval = rinfo->current_limits[n];
+ if (tval >= min_ua && tval <= max_ua)
+ return regmap_field_write(regl->ilimit, n);
+ }
+
+ return -EINVAL;
+}
+
+static int da9062_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_field_read(regl->ilimit, &sel);
+ if (ret < 0)
+ return ret;
+
+ if (sel >= rinfo->n_current_limits)
+ sel = rinfo->n_current_limits - 1;
+
+ return rinfo->current_limits[sel];
+}
+
+static int da9062_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+/*
+ * Bucks use single mode register field for normal operation
+ * and suspend state.
+ * There are 3 modes to map to: FAST, NORMAL, and STANDBY.
+ */
+
+static unsigned da9062_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ unsigned int val, mode = 0;
+ int ret;
+
+ ret = regmap_field_read(regl->mode, &val);
+ if (ret < 0)
+ return ret;
+
+ switch (val) {
+ default:
+ case BUCK_MODE_MANUAL:
+ mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY;
+ /* Sleep flag bit decides the mode */
+ break;
+ case BUCK_MODE_SLEEP:
+ return REGULATOR_MODE_STANDBY;
+ case BUCK_MODE_SYNC:
+ return REGULATOR_MODE_FAST;
+ case BUCK_MODE_AUTO:
+ return REGULATOR_MODE_NORMAL;
+ }
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ mode &= REGULATOR_MODE_STANDBY;
+ else
+ mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+
+ return mode;
+}
+
+/*
+ * LDOs use sleep flags - one for normal and one for suspend state.
+ * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
+ */
+
+static int da9062_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->sleep, val);
+}
+
+static unsigned da9062_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ int ret, val;
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ return REGULATOR_MODE_STANDBY;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int da9062_buck_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9062_buck_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9062_ldo_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9062_ldo_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9062_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ int ret, sel;
+
+ sel = regulator_map_voltage_linear(rdev, uv, uv);
+ if (sel < 0)
+ return sel;
+
+ sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+ ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg,
+ rdev->desc->vsel_mask, sel);
+
+ return ret;
+}
+
+static int da9062_suspend_enable(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 1);
+}
+
+static int da9062_suspend_disable(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 0);
+}
+
+static int da9062_buck_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ int val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+static int da9062_ldo_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->suspend_sleep, val);
+}
+
+static struct regulator_ops da9062_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9062_set_current_limit,
+ .get_current_limit = da9062_get_current_limit,
+ .set_mode = da9062_buck_set_mode,
+ .get_mode = da9062_buck_get_mode,
+ .get_status = da9062_buck_get_status,
+ .set_suspend_voltage = da9062_set_suspend_voltage,
+ .set_suspend_enable = da9062_suspend_enable,
+ .set_suspend_disable = da9062_suspend_disable,
+ .set_suspend_mode = da9062_buck_set_suspend_mode,
+};
+
+static struct regulator_ops da9062_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_mode = da9062_ldo_set_mode,
+ .get_mode = da9062_ldo_get_mode,
+ .get_status = da9062_ldo_get_status,
+ .set_suspend_voltage = da9062_set_suspend_voltage,
+ .set_suspend_enable = da9062_suspend_enable,
+ .set_suspend_disable = da9062_suspend_disable,
+ .set_suspend_mode = da9062_ldo_set_suspend_mode,
+};
+
+/* Regulator information */
+static const struct da9062_regulator_info local_regulator_info[] = {
+ {
+ .desc.id = DA9062_ID_BUCK1,
+ .desc.name = "DA9062 BUCK1",
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK1_CONT,
+ .desc.enable_mask = DA9062AA_BUCK1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK1_A,
+ .desc.vsel_mask = DA9062AA_VBUCK1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK1_A,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK1_B,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK1_B,
+ .mode = REG_FIELD(DA9062AA_BUCK1_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK1_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK1_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK2,
+ .desc.name = "DA9062 BUCK2",
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK2_CONT,
+ .desc.enable_mask = DA9062AA_BUCK2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK2_A,
+ .desc.vsel_mask = DA9062AA_VBUCK2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK2_A,
+ __builtin_ffs((int)DA9062AA_BUCK2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK2_B,
+ __builtin_ffs((int)DA9062AA_BUCK2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK2_B,
+ .mode = REG_FIELD(DA9062AA_BUCK2_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK2_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK2_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK3,
+ .desc.name = "DA9062 BUCK3",
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (800) * 1000,
+ .desc.uV_step = (20) * 1000,
+ .desc.n_voltages = ((3340) - (800))/(20) + 1,
+ .current_limits = da9062_buck_b_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_b_limits),
+ .desc.enable_reg = DA9062AA_BUCK3_CONT,
+ .desc.enable_mask = DA9062AA_BUCK3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK3_A,
+ .desc.vsel_mask = DA9062AA_VBUCK3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK3_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK3_B,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK3_B,
+ .mode = REG_FIELD(DA9062AA_BUCK3_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK3_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK3_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK4,
+ .desc.name = "DA9062 BUCK4",
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (530) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1800) - (530))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK4_CONT,
+ .desc.enable_mask = DA9062AA_BUCK4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK4_A,
+ .desc.vsel_mask = DA9062AA_VBUCK4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK4_A,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK4_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK4_B,
+ .mode = REG_FIELD(DA9062AA_BUCK4_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK4_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK4_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO1,
+ .desc.name = "DA9062 LDO1",
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO1_CONT,
+ .desc.enable_mask = DA9062AA_LDO1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO1_A,
+ .desc.vsel_mask = DA9062AA_VLDO1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO1_A,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO1_B,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO1_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO1_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO2,
+ .desc.name = "DA9062 LDO2",
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (600))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO2_CONT,
+ .desc.enable_mask = DA9062AA_LDO2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO2_A,
+ .desc.vsel_mask = DA9062AA_VLDO2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO2_A,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO2_B,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO2_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO2_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO3,
+ .desc.name = "DA9062 LDO3",
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO3_CONT,
+ .desc.enable_mask = DA9062AA_LDO3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO3_A,
+ .desc.vsel_mask = DA9062AA_VLDO3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO3_A,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO3_B,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO3_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO3_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO4,
+ .desc.name = "DA9062 LDO4",
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO4_CONT,
+ .desc.enable_mask = DA9062AA_LDO4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO4_A,
+ .desc.vsel_mask = DA9062AA_VLDO4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO4_A,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO4_B,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO4_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO4_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_ILIM_MASK)) - 1),
+ },
+};
+
+/* Regulator interrupt handlers */
+static irqreturn_t da9062_ldo_lim_event(int irq, void *data)
+{
+ struct da9062_regulators *regulators = data;
+ struct da9062 *hw = regulators->regulator[0].hw;
+ struct da9062_regulator *regl;
+ int bits, i, ret;
+
+ ret = regmap_read(hw->regmap, DA9062AA_STATUS_D, &bits);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = regulators->n_regulators - 1; i >= 0; i--) {
+ regl = ®ulators->regulator[i];
+ if (regl->info->oc_event.reg != DA9062AA_STATUS_D)
+ continue;
+
+ if (BIT(regl->info->oc_event.lsb) & bits)
+ regulator_notifier_call_chain(regl->rdev,
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t da9062_vdd_warn_event(int irq, void *data)
+{
+ return IRQ_HANDLED;
+}
+
+/* Regulators platform data */
+struct da9062_regulator_data {
+ int id;
+ struct regulator_init_data *initdata;
+};
+
+struct da9062_regulators_pdata {
+ unsigned n_regulators;
+ struct da9062_regulator_data *regulator_data;
+};
+
+/*
+ * Probing and Initialisation functions
+ */
+static const struct regulator_init_data *da9062_get_regulator_initdata(
+ const struct da9062_regulators_pdata *pdata, int id)
+{
+ int i;
+
+ for (i = 0; i < pdata->n_regulators; i++) {
+ if (id == pdata->regulator_data[i].id)
+ return pdata->regulator_data[i].initdata;
+ }
+
+ return NULL;
+}
+
+#ifdef CONFIG_OF
+static struct of_regulator_match da9062_matches[] = {
+ [DA9062_ID_BUCK1] = { .name = "buck1" },
+ [DA9062_ID_BUCK2] = { .name = "buck2" },
+ [DA9062_ID_BUCK3] = { .name = "buck3" },
+ [DA9062_ID_BUCK4] = { .name = "buck4" },
+ [DA9062_ID_LDO1] = { .name = "ldo1" },
+ [DA9062_ID_LDO2] = { .name = "ldo2" },
+ [DA9062_ID_LDO3] = { .name = "ldo3" },
+ [DA9062_ID_LDO4] = { .name = "ldo4" },
+};
+
+static struct da9062_regulators_pdata *da9062_parse_regulators_dt(
+ struct platform_device *pdev,
+ struct of_regulator_match **reg_matches)
+{
+ struct da9062_regulators_pdata *pdata;
+ struct da9062_regulator_data *rdata;
+ struct device_node *node;
+ int i, n, num;
+
+ node = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
+ if (!node) {
+ dev_err(&pdev->dev, "Regulators device node not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ num = of_regulator_match(&pdev->dev, node, da9062_matches,
+ ARRAY_SIZE(da9062_matches));
+ of_node_put(node);
+ if (num < 0) {
+ dev_err(&pdev->dev, "Failed to match regulators\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->regulator_data = devm_kzalloc(&pdev->dev,
+ num * sizeof(*pdata->regulator_data),
+ GFP_KERNEL);
+ if (!pdata->regulator_data)
+ return ERR_PTR(-ENOMEM);
+ pdata->n_regulators = num;
+
+ n = 0;
+ for (i = 0; i < ARRAY_SIZE(da9062_matches); i++) {
+ if (!da9062_matches[i].init_data)
+ continue;
+
+ rdata = &pdata->regulator_data[n];
+ rdata->id = i;
+ rdata->initdata = da9062_matches[i].init_data;
+
+ n++;
+ };
+
+ *reg_matches = da9062_matches;
+ return pdata;
+}
+#else
+static struct da9062_regulators_pdata *da9062_parse_regulators_dt(
+ struct platform_device *pdev,
+ struct of_regulator_match **reg_matches)
+{
+ *reg_matches = NULL;
+ return ERR_PTR(-ENODEV);
+}
+#endif
+
+static int da9062_regulator_probe(struct platform_device *pdev)
+{
+ struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
+ struct of_regulator_match *reg_matches = NULL;
+ struct da9062_regulators_pdata *pdata;
+ struct da9062_regulators *regulators;
+ struct da9062_regulator *regl;
+ struct regulator_config config;
+ int id, irq, n, n_regulators, ret;
+ size_t size;
+
+ pdata = da9062_parse_regulators_dt(pdev, ®_matches);
+
+ if (IS_ERR(pdata) || pdata->n_regulators == 0) {
+ dev_err(&pdev->dev,
+ "No regulators defined for the platform\n");
+ return PTR_ERR(pdata);
+ }
+
+ n_regulators = ARRAY_SIZE(local_regulator_info),
+
+ /* Allocate memory required by usable regulators */
+ size = sizeof(struct da9062_regulators) +
+ n_regulators * sizeof(struct da9062_regulator);
+ regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (!regulators)
+ return -ENOMEM;
+
+ regulators->n_regulators = n_regulators;
+ platform_set_drvdata(pdev, regulators);
+
+ /* Register all regulators declared in platform information */
+ n = 0;
+ id = 0;
+ while (n < regulators->n_regulators) {
+ /* Initialise regulator structure */
+ regl = ®ulators->regulator[n];
+ regl->hw = chip;
+ regl->info = &local_regulator_info[id];
+ regl->desc = regl->info->desc;
+ regl->desc.type = REGULATOR_VOLTAGE;
+ regl->desc.owner = THIS_MODULE;
+
+ if (regl->info->mode.reg)
+ regl->mode = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->mode);
+ if (regl->info->suspend.reg)
+ regl->suspend = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->suspend);
+ if (regl->info->sleep.reg)
+ regl->sleep = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->sleep);
+ if (regl->info->suspend_sleep.reg)
+ regl->suspend_sleep = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->suspend_sleep);
+ if (regl->info->ilimit.reg)
+ regl->ilimit = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->ilimit);
+
+ /* Register regulator */
+ memset(&config, 0, sizeof(config));
+ config.dev = &pdev->dev;
+ config.init_data = da9062_get_regulator_initdata(pdata, id);
+ config.driver_data = regl;
+ if (reg_matches)
+ config.of_node = reg_matches[id].of_node;
+ config.regmap = chip->regmap;
+ regl->rdev = devm_regulator_register(&pdev->dev, ®l->desc,
+ &config);
+ if (IS_ERR(regl->rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register %s regulator\n",
+ regl->desc.name);
+ return PTR_ERR(regl->rdev);
+ }
+ id++;
+ n++;
+ }
+
+ /* LDOs overcurrent event support */
+ irq = platform_get_irq_byname(pdev, "LDO_LIM");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ.\n");
+ return irq;
+ }
+ regulators->irq_ldo_lim = irq;
+
+ ret = request_threaded_irq(irq,
+ NULL, da9062_ldo_lim_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "LDO_LIM", regulators);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "Failed to request LDO_LIM IRQ.\n");
+ regulators->irq_ldo_lim = -ENXIO;
+ }
+
+ /* VDD WARN event support */
+ irq = platform_get_irq_byname(pdev, "VDD_WARN");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ.\n");
+ free_irq(regulators->irq_ldo_lim, regulators);
+ return irq;
+ }
+ regulators->irq_vdd_warn = irq;
+
+ ret = request_threaded_irq(irq,
+ NULL, da9062_vdd_warn_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "VDD_WARN", regulators);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "Failed to request VDD_WARN IRQ.\n");
+ regulators->irq_vdd_warn = -ENXIO;
+ }
+
+ return 0;
+}
+
+static int da9062_regulator_remove(struct platform_device *pdev)
+{
+ struct da9062_regulators *regulators = platform_get_drvdata(pdev);
+
+ free_irq(regulators->irq_ldo_lim, regulators);
+ free_irq(regulators->irq_vdd_warn, regulators);
+
+ return 0;
+}
+
+static struct platform_driver da9062_regulator_driver = {
+ .driver = {
+ .name = "da9062-regulators",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9062_regulator_probe,
+ .remove = da9062_regulator_remove,
+};
+
+static int __init da9062_regulator_init(void)
+{
+ return platform_driver_register(&da9062_regulator_driver);
+}
+subsys_initcall(da9062_regulator_init);
+
+static void __exit da9062_regulator_cleanup(void)
+{
+ platform_driver_unregister(&da9062_regulator_driver);
+}
+module_exit(da9062_regulator_cleanup);
+
+/* Module information */
+MODULE_AUTHOR("S Twiss <[email protected]>");
+MODULE_DESCRIPTION("REGULATOR device driver for Dialog DA9062");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform: da9062-regulators");
--
end-of-patch for PATCH V1
From: S Twiss <[email protected]>
Add OnKey driver support for DA9062
Signed-off-by: Steve Twiss <[email protected]>
---
This patch applies against linux-next and v4.0
drivers/input/misc/Kconfig | 10 ++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/da9062-onkey.c | 223 ++++++++++++++++++++++++++++++++++++++
3 files changed, 234 insertions(+)
create mode 100644 drivers/input/misc/da9062-onkey.c
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 6deb8da..472ca38 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -586,6 +586,16 @@ config INPUT_DA9055_ONKEY
To compile this driver as a module, choose M here: the module
will be called da9055_onkey.
+config INPUT_DA9062_ONKEY
+ tristate "Dialog DA9062 OnKey"
+ depends on MFD_DA9062
+ help
+ Support the ONKEY of Dialog DA9062 Power Management IC as an
+ input device reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called da9062-onkey.
+
config INPUT_DM355EVM
tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
depends on MFD_DM355EVM_MSP
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 403a1a5..a631283 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
+obj-$(CONFIG_INPUT_DA9062_ONKEY) += da9062-onkey.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o
obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
diff --git a/drivers/input/misc/da9062-onkey.c b/drivers/input/misc/da9062-onkey.c
new file mode 100644
index 0000000..c4c0295
--- /dev/null
+++ b/drivers/input/misc/da9062-onkey.c
@@ -0,0 +1,223 @@
+/*
+ * da9062-onkey.c - ONKEY device driver for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+struct da9062_onkey {
+ struct da9062 *hw;
+ struct delayed_work work;
+ struct input_dev *input;
+ int irq;
+ bool key_power;
+};
+
+static void da9062_poll_on(struct work_struct *work)
+{
+ struct da9062_onkey *onkey = container_of(work, struct da9062_onkey,
+ work.work);
+ unsigned int val;
+ int fault_log = 0;
+ bool poll = true;
+ int ret;
+
+ /* poll to see when the pin is released */
+ ret = regmap_read(onkey->hw->regmap, DA9062AA_STATUS_A, &val);
+ if (ret < 0) {
+ dev_err(&onkey->input->dev,
+ "Failed to read ON status: %d\n", ret);
+ goto err_poll;
+ }
+
+ if (!(val & DA9062AA_NONKEY_MASK)) {
+ ret = regmap_update_bits(onkey->hw->regmap,
+ DA9062AA_CONTROL_B,
+ DA9062AA_NONKEY_LOCK_MASK, 0);
+ if (ret < 0) {
+ dev_err(&onkey->input->dev,
+ "Failed to reset the Key Delay %d\n", ret);
+ goto err_poll;
+ }
+
+ input_report_key(onkey->input, KEY_POWER, 0);
+ input_sync(onkey->input);
+
+ poll = false;
+ }
+
+ /* if the fault log KEY_RESET is detected,
+ * then clear it and shutdown DA9062 via I2C
+ */
+ ret = regmap_read(onkey->hw->regmap, DA9062AA_FAULT_LOG, &fault_log);
+ if (ret < 0)
+ dev_warn(&onkey->input->dev, "Cannot read FAULT_LOG\n");
+
+ if (fault_log & DA9062AA_KEY_RESET_MASK) {
+ ret = regmap_write(onkey->hw->regmap,
+ DA9062AA_FAULT_LOG,
+ DA9062AA_KEY_RESET_MASK);
+ if (ret < 0)
+ dev_warn(&onkey->input->dev,
+ "Cannot reset KEY_RESET fault log\n");
+ else {
+ /* at this point we do any S/W housekeeping
+ * and then send shutdown command
+ */
+ dev_info(&onkey->input->dev,
+ "Sending SHUTDOWN to DA9062 ...\n");
+ ret = regmap_write(onkey->hw->regmap,
+ DA9062AA_CONTROL_F,
+ DA9062AA_SHUTDOWN_MASK);
+ if (ret < 0)
+ dev_err(&onkey->input->dev,
+ "Cannot SHUTDOWN DA9062\n");
+ }
+ }
+
+err_poll:
+ if (poll)
+ schedule_delayed_work(&onkey->work, 50);
+}
+
+static irqreturn_t da9062_onkey_irq_handler(int irq, void *data)
+{
+ struct da9062_onkey *onkey = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(onkey->hw->regmap, DA9062AA_STATUS_A, &val);
+ if (onkey->key_power && (ret >= 0) && (val & DA9062AA_NONKEY_MASK)) {
+ input_report_key(onkey->input, KEY_POWER, 1);
+ input_sync(onkey->input);
+ schedule_delayed_work(&onkey->work, 0);
+ dev_notice(&onkey->input->dev, "KEY_POWER pressed.\n");
+ } else {
+ input_report_key(onkey->input, KEY_SLEEP, 1);
+ input_sync(onkey->input);
+ input_report_key(onkey->input, KEY_SLEEP, 0);
+ input_sync(onkey->input);
+ dev_notice(&onkey->input->dev, "KEY_SLEEP pressed.\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int da9062_onkey_probe(struct platform_device *pdev)
+{
+ struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
+ struct da9062_onkey *onkey;
+ bool kp_tmp = true;
+ int ret = 0;
+
+ kp_tmp = of_property_read_bool((&pdev->dev)->of_node,
+ "dlg,disable-key-power");
+ kp_tmp = !kp_tmp;
+
+
+ onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9062_onkey),
+ GFP_KERNEL);
+ if (!onkey) {
+ dev_err(&pdev->dev, "Failed to allocate memory.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ INIT_DELAYED_WORK(&onkey->work, da9062_poll_on);
+
+ onkey->input = devm_input_allocate_device(&pdev->dev);
+ if (!onkey->input) {
+ dev_err(&pdev->dev, "Failed to allocated input device.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = platform_get_irq_byname(pdev, "ONKEY");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to get platform IRQ.\n");
+ goto err;
+ }
+ onkey->irq = ret;
+
+ ret = request_threaded_irq(onkey->irq, NULL,
+ da9062_onkey_irq_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "ONKEY", onkey);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request input device IRQ.\n");
+ goto err;
+ }
+
+ onkey->hw = chip;
+ onkey->key_power = kp_tmp;
+ onkey->input->evbit[0] = BIT_MASK(EV_KEY);
+ onkey->input->name = "da9062-onkey";
+ onkey->input->phys = "da9062-onkey/input0";
+ onkey->input->dev.parent = &pdev->dev;
+
+ if (onkey->key_power)
+ input_set_capability(onkey->input, EV_KEY, KEY_POWER);
+ input_set_capability(onkey->input, EV_KEY, KEY_SLEEP);
+
+ ret = input_register_device(onkey->input);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register input device.\n");
+ goto err_irq;
+ }
+
+ platform_set_drvdata(pdev, onkey);
+ return 0;
+
+err_irq:
+ free_irq(onkey->irq, onkey);
+ cancel_delayed_work_sync(&onkey->work);
+err:
+ return ret;
+}
+
+static int da9062_onkey_remove(struct platform_device *pdev)
+{
+ struct da9062_onkey *onkey = platform_get_drvdata(pdev);
+
+ free_irq(onkey->irq, onkey);
+ cancel_delayed_work_sync(&onkey->work);
+ input_unregister_device(onkey->input);
+ return 0;
+}
+
+static struct platform_driver da9062_onkey_driver = {
+ .probe = da9062_onkey_probe,
+ .remove = da9062_onkey_remove,
+ .driver = {
+ .name = "da9062-onkey",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(da9062_onkey_driver);
+
+MODULE_AUTHOR("S Twiss <[email protected]>");
+MODULE_DESCRIPTION("ONKEY device driver for Dialog DA9062");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform: da9062-onkey");
--
end-of-patch for PATCH V1
From: S Twiss <[email protected]>
Add RTC driver support for DA9062
Signed-off-by: Steve Twiss <[email protected]>
---
This patch applies against linux-next and v4.0
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-da9062.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 378 insertions(+)
create mode 100644 drivers/rtc/rtc-da9062.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index b5b5c3d..c4d21bd 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -927,6 +927,16 @@ config RTC_DRV_DA9055
This driver can also be built as a module. If so, the module
will be called rtc-da9055
+config RTC_DRV_DA9062
+ tristate "Dialog Semiconductor DA9062 RTC"
+ depends on MFD_DA9062
+ help
+ If you say yes here you will get support for the RTC subsystem
+ of the Dialog Semiconductor DA9062.
+
+ This driver can also be built as a module. If so, the module
+ will be called "rtc-da9062".
+
config RTC_DRV_DA9063
tristate "Dialog Semiconductor DA9063 RTC"
depends on MFD_DA9063
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 69c8706..9108e62 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
+obj-$(CONFIG_RTC_DRV_DA9062) += rtc-da9062.o
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o
obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
diff --git a/drivers/rtc/rtc-da9062.c b/drivers/rtc/rtc-da9062.c
new file mode 100644
index 0000000..0f3d646
--- /dev/null
+++ b/drivers/rtc/rtc-da9062.c
@@ -0,0 +1,367 @@
+/*
+ * rtc-da9062.c - RTC device driver for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/da9062/registers.h>
+#include <linux/mfd/da9062/core.h>
+
+#define DA9062_GET_TIME_RETRIES 5
+
+#define YEARS_TO_DA9062(year) ((year) - 100)
+#define MONTHS_TO_DA9062(month) ((month) + 1)
+#define YEARS_FROM_DA9062(year) ((year) + 100)
+#define MONTHS_FROM_DA9062(month) ((month) - 1)
+
+#define RTC_DATA_LEN (DA9062AA_COUNT_Y - DA9062AA_COUNT_S + 1)
+enum {
+ RTC_SEC = 0,
+ RTC_MIN = 1,
+ RTC_HOUR = 2,
+ RTC_DAY = 3,
+ RTC_MONTH = 4,
+ RTC_YEAR = 5,
+};
+
+#define DA9062AA_ALARM_STATUS_ENUM_TIMER (0x02 << 6)
+
+struct da9062_rtc {
+ struct rtc_device *rtc_dev;
+ struct da9062 *hw;
+ struct rtc_time alarm_time;
+ bool rtc_sync;
+};
+
+static void da9062_data_to_tm(u8 *data, struct rtc_time *tm)
+{
+ tm->tm_sec = data[RTC_SEC] & DA9062AA_COUNT_SEC_MASK;
+ tm->tm_min = data[RTC_MIN] & DA9062AA_COUNT_MIN_MASK;
+ tm->tm_hour = data[RTC_HOUR] & DA9062AA_COUNT_HOUR_MASK;
+ tm->tm_mday = data[RTC_DAY] & DA9062AA_COUNT_DAY_MASK;
+ tm->tm_mon = MONTHS_FROM_DA9062(data[RTC_MONTH] &
+ DA9062AA_COUNT_MONTH_MASK);
+ tm->tm_year = YEARS_FROM_DA9062(data[RTC_YEAR] &
+ DA9062AA_COUNT_YEAR_MASK);
+}
+
+static void da9062_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] &= ~DA9062AA_COUNT_SEC_MASK;
+ data[RTC_SEC] |= tm->tm_sec & DA9062AA_COUNT_SEC_MASK;
+
+ data[RTC_MIN] &= ~DA9062AA_COUNT_MIN_MASK;
+ data[RTC_MIN] |= tm->tm_min & DA9062AA_COUNT_MIN_MASK;
+
+ data[RTC_HOUR] &= ~DA9062AA_COUNT_HOUR_MASK;
+ data[RTC_HOUR] |= tm->tm_hour & DA9062AA_COUNT_HOUR_MASK;
+
+ data[RTC_DAY] &= ~DA9062AA_COUNT_DAY_MASK;
+ data[RTC_DAY] |= tm->tm_mday & DA9062AA_COUNT_DAY_MASK;
+
+ data[RTC_MONTH] &= ~DA9062AA_COUNT_MONTH_MASK;
+ data[RTC_MONTH] |= MONTHS_TO_DA9062(tm->tm_mon) &
+ DA9062AA_COUNT_MONTH_MASK;
+
+ data[RTC_YEAR] &= ~DA9062AA_COUNT_YEAR_MASK;
+ data[RTC_YEAR] |= YEARS_TO_DA9062(tm->tm_year) &
+ DA9062AA_COUNT_YEAR_MASK;
+}
+
+static int da9062_rtc_stop_alarm(struct device *dev)
+{
+ struct da9062_rtc *rtc = dev_get_drvdata(dev);
+
+ return regmap_update_bits(rtc->hw->regmap,
+ DA9062AA_ALARM_Y,
+ DA9062AA_ALARM_ON_MASK,
+ 0);
+}
+
+static int da9062_rtc_start_alarm(struct device *dev)
+{
+ struct da9062_rtc *rtc = dev_get_drvdata(dev);
+
+ return regmap_update_bits(rtc->hw->regmap,
+ DA9062AA_ALARM_Y,
+ DA9062AA_ALARM_ON_MASK,
+ DA9062AA_ALARM_ON_MASK);
+}
+
+static int da9062_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct da9062_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long tm_secs;
+ unsigned long al_secs;
+ u8 data[RTC_DATA_LEN];
+ int timeout = DA9062_GET_TIME_RETRIES;
+ bool read_done = false;
+ int ret;
+
+ while (timeout--) {
+ ret = regmap_bulk_read(rtc->hw->regmap, DA9062AA_COUNT_S,
+ data, RTC_DATA_LEN);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read RTC time data: %d\n", ret);
+ return ret;
+ }
+
+ /* rtc ready to be read by the host */
+ if (data[RTC_SEC] & DA9062AA_RTC_READ_MASK) {
+ timeout = 0;
+ read_done = true;
+ }
+
+ msleep(20);
+ }
+
+ if (!read_done) {
+ dev_err(dev, "Failed to read the RTC time\n");
+ return -EIO;
+ }
+
+ da9062_data_to_tm(data, tm);
+
+ rtc_tm_to_time(tm, &tm_secs);
+ rtc_tm_to_time(&rtc->alarm_time, &al_secs);
+
+ /* handle the rtc synchronisation delay */
+ if (rtc->rtc_sync == true && al_secs - tm_secs == 1)
+ memcpy(tm, &rtc->alarm_time, sizeof(struct rtc_time));
+ else
+ rtc->rtc_sync = false;
+
+ return rtc_valid_tm(tm);
+}
+
+static int da9062_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct da9062_rtc *rtc = dev_get_drvdata(dev);
+ u8 data[RTC_DATA_LEN];
+ int ret;
+
+ da9062_tm_to_data(tm, data);
+ ret = regmap_bulk_write(rtc->hw->regmap, DA9062AA_COUNT_S,
+ data, RTC_DATA_LEN);
+ if (ret < 0)
+ dev_err(dev, "Failed to set RTC time data: %d\n", ret);
+
+ return ret;
+}
+
+static int da9062_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct da9062_rtc *rtc = dev_get_drvdata(dev);
+ u8 data[RTC_DATA_LEN];
+ int ret;
+ unsigned int val;
+
+ data[RTC_SEC] = 0;
+ ret = regmap_bulk_read(rtc->hw->regmap, DA9062AA_ALARM_S,
+ &data[RTC_SEC], RTC_DATA_LEN);
+ if (ret < 0)
+ return ret;
+
+ da9062_data_to_tm(data, &alrm->time);
+
+ alrm->enabled = !!(data[RTC_YEAR] & DA9062AA_ALARM_ON_MASK);
+
+ ret = regmap_read(rtc->hw->regmap, DA9062AA_EVENT_A, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & DA9062AA_M_ALARM_MASK)
+ alrm->pending = 1;
+ else
+ alrm->pending = 0;
+
+ return 0;
+}
+
+static int da9062_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct da9062_rtc *rtc = dev_get_drvdata(dev);
+ u8 data[RTC_DATA_LEN];
+ int ret;
+
+ da9062_tm_to_data(&alrm->time, data);
+
+ ret = da9062_rtc_stop_alarm(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to stop alarm: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(rtc->hw->regmap, DA9062AA_ALARM_S,
+ &data[RTC_SEC], RTC_DATA_LEN);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write alarm: %d\n", ret);
+ return ret;
+ }
+
+ da9062_data_to_tm(data, &rtc->alarm_time);
+
+ if (alrm->enabled) {
+ ret = da9062_rtc_start_alarm(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to start alarm: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int da9062_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ if (enabled)
+ return da9062_rtc_start_alarm(dev);
+ else
+ return da9062_rtc_stop_alarm(dev);
+}
+
+static irqreturn_t da9062_alarm_event(int irq, void *data)
+{
+ struct da9062_rtc *rtc = data;
+
+ regmap_update_bits(rtc->hw->regmap,
+ DA9062AA_ALARM_Y,
+ DA9062AA_ALARM_ON_MASK,
+ 0);
+
+ rtc->rtc_sync = true;
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops da9062_rtc_ops = {
+ .read_time = da9062_rtc_read_time,
+ .set_time = da9062_rtc_set_time,
+ .read_alarm = da9062_rtc_read_alarm,
+ .set_alarm = da9062_rtc_set_alarm,
+ .alarm_irq_enable = da9062_rtc_alarm_irq_enable,
+};
+
+static int da9062_rtc_probe(struct platform_device *pdev)
+{
+ struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
+ struct da9062_rtc *rtc;
+ int irq_alarm;
+ u8 data[RTC_DATA_LEN];
+ int ret;
+
+ ret = regmap_update_bits(chip->regmap,
+ DA9062AA_CONTROL_E,
+ DA9062AA_RTC_EN_MASK,
+ DA9062AA_RTC_EN_MASK);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to enable RTC\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap,
+ DA9062AA_EN_32K,
+ DA9062AA_CRYSTAL_MASK,
+ DA9062AA_CRYSTAL_MASK);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to run 32kHz oscillator\n");
+ return ret;
+ }
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ ret = regmap_update_bits(chip->regmap,
+ DA9062AA_ALARM_S,
+ DA9062AA_ALARM_STATUS_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to access RTC alarm register\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap,
+ DA9062AA_ALARM_S,
+ DA9062AA_ALARM_STATUS_ENUM_TIMER,
+ DA9062AA_ALARM_STATUS_ENUM_TIMER);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to access RTC alarm register\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap,
+ DA9062AA_ALARM_Y,
+ DA9062AA_TICK_ON_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to disable TICKs\n");
+ return ret;
+ }
+
+ data[RTC_SEC] = 0;
+ ret = regmap_bulk_read(chip->regmap, DA9062AA_ALARM_S,
+ &data[RTC_SEC], RTC_DATA_LEN);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to read initial alarm data: %d\n",
+ ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ irq_alarm = platform_get_irq_byname(pdev, "ALARM");
+ ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL,
+ da9062_alarm_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "ALARM", rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n",
+ irq_alarm, ret);
+ return ret;
+ }
+
+ rtc->hw = chip;
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "da9062-rtc",
+ &da9062_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
+ da9062_data_to_tm(data, &rtc->alarm_time);
+ rtc->rtc_sync = false;
+ return ret;
+}
+
+static struct platform_driver da9062_rtc_driver = {
+ .probe = da9062_rtc_probe,
+ .driver = {
+ .name = "da9062-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(da9062_rtc_driver);
+
+MODULE_AUTHOR("S Twiss <[email protected]>");
+MODULE_DESCRIPTION("RTC device driver for Dialog DA9062");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform: da9062-rtc");
--
end-of-patch for PATCH V1
From: S Twiss <[email protected]>
Add watchdog driver support for DA9062
Signed-off-by: Steve Twiss <[email protected]>
---
This patch applies against linux-next and v4.0
drivers/watchdog/Kconfig | 9 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/da9062_wdt.c | 249 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 259 insertions(+)
create mode 100644 drivers/watchdog/da9062_wdt.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 16f2023..62232d7 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -96,6 +96,15 @@ config DA9063_WATCHDOG
This driver can be built as a module. The module name is da9063_wdt.
+config DA9062_WATCHDOG
+ tristate "Dialog DA9062 Watchdog"
+ depends on MFD_DA9062
+ select WATCHDOG_CORE
+ help
+ Support for the watchdog in the DA9062 PMIC.
+
+ This driver can be built as a module. The module name is da9062_wdt.
+
config GPIO_WATCHDOG
tristate "Watchdog device controlled through GPIO-line"
depends on OF_GPIO
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5c19294..57ba815 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -179,6 +179,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o
# Architecture Independent
obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
+obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o
obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
new file mode 100644
index 0000000..ac4c255
--- /dev/null
+++ b/drivers/watchdog/da9062_wdt.c
@@ -0,0 +1,249 @@
+/*
+ * da9062_wdt.c - WDT device driver for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mfd/da9062/registers.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
+#define DA9062_TWDSCALE_DISABLE 0
+#define DA9062_TWDSCALE_MIN 1
+#define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1)
+#define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN]
+#define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX]
+#define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1]
+
+struct da9062_watchdog {
+ struct da9062 *hw;
+ struct watchdog_device wdtdev;
+};
+
+static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
+{
+ unsigned int i;
+
+ for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) {
+ if (wdt_timeout[i] >= secs)
+ return i;
+ }
+
+ return DA9062_TWDSCALE_MAX;
+}
+
+static int da9062_reset_watchdog_timer(struct da9062 *hw)
+{
+ int ret;
+
+ ret = regmap_update_bits(hw->regmap,
+ DA9062AA_CONTROL_F,
+ DA9062AA_WATCHDOG_MASK,
+ DA9062AA_WATCHDOG_MASK);
+
+ mdelay(300);
+
+ return ret;
+}
+
+static int da9062_wdt_update_timeout_register(struct da9062 *chip,
+ unsigned int regval)
+{
+ int ret;
+
+ ret = da9062_reset_watchdog_timer(chip);
+ if (ret)
+ dev_err(chip->dev, "Failed to ping the watchdog (err = %d)\n",
+ ret);
+
+ return regmap_update_bits(chip->regmap,
+ DA9062AA_CONTROL_D,
+ DA9062AA_TWDSCALE_MASK,
+ regval);
+}
+
+static int da9062_wdt_start(struct watchdog_device *wdd)
+{
+ struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+ unsigned int selector;
+ int ret;
+
+ selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout);
+ ret = da9062_wdt_update_timeout_register(wdt->hw, selector);
+ if (ret)
+ dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n",
+ ret);
+
+ return ret;
+}
+
+static int da9062_wdt_stop(struct watchdog_device *wdd)
+{
+ struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+ int ret;
+
+ ret = da9062_reset_watchdog_timer(wdt->hw);
+ if (ret)
+ dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
+ ret);
+
+ ret = regmap_update_bits(wdt->hw->regmap,
+ DA9062AA_CONTROL_D,
+ DA9062AA_TWDSCALE_MASK,
+ DA9062_TWDSCALE_DISABLE);
+ if (ret)
+ dev_alert(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n",
+ ret);
+
+ return ret;
+}
+
+static int da9062_wdt_ping(struct watchdog_device *wdd)
+{
+ struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+ int ret;
+
+ dev_dbg(wdt->hw->dev, "watchdog ping\n");
+
+ ret = da9062_reset_watchdog_timer(wdt->hw);
+ if (ret)
+ dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
+ ret);
+
+ return ret;
+}
+
+static int da9062_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+ unsigned int selector;
+ int ret;
+
+ selector = da9062_wdt_timeout_to_sel(timeout);
+ ret = da9062_wdt_update_timeout_register(wdt->hw, selector);
+ if (ret)
+ dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n",
+ ret);
+ else
+ wdd->timeout = wdt_timeout[selector];
+
+ return ret;
+}
+
+/* E_WDG_WARN interrupt handler */
+static irqreturn_t da9062_wdt_wdg_warn_irq_handler(int irq, void *data)
+{
+ struct da9062_watchdog *wdt = data;
+
+ dev_notice(wdt->hw->dev, "Watchdog timeout warning trigger.\n");
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_info da9062_watchdog_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "DA9062 WDT",
+};
+
+static const struct watchdog_ops da9062_watchdog_ops = {
+ .owner = THIS_MODULE,
+ .start = da9062_wdt_start,
+ .stop = da9062_wdt_stop,
+ .ping = da9062_wdt_ping,
+ .set_timeout = da9062_wdt_set_timeout,
+};
+
+static int da9062_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct da9062 *chip;
+ struct da9062_watchdog *wdt;
+ int irq;
+
+ chip = dev_get_drvdata(pdev->dev.parent);
+ if (!chip)
+ return -EINVAL;
+
+ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->hw = chip;
+
+ wdt->wdtdev.info = &da9062_watchdog_info;
+ wdt->wdtdev.ops = &da9062_watchdog_ops;
+ wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
+ wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
+ wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
+ wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+
+ watchdog_set_drvdata(&wdt->wdtdev, wdt);
+ dev_set_drvdata(&pdev->dev, wdt);
+
+ irq = platform_get_irq_byname(pdev, "WDG_WARN");
+ if (irq < 0)
+ dev_err(wdt->hw->dev, "Failed to get IRQ.\n");
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ da9062_wdt_wdg_warn_irq_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
+ "WDG_WARN", wdt);
+ if (ret) {
+ dev_err(wdt->hw->dev,
+ "Failed to request watchdog device IRQ.\n");
+ }
+
+ ret = watchdog_register_device(&wdt->wdtdev);
+ if (ret < 0)
+ dev_err(wdt->hw->dev,
+ "watchdog registration incomplete (%d)\n", ret);
+ else
+ dev_info(wdt->hw->dev, "installed DA9062 watchdog\n");
+
+ da9062_wdt_ping(&wdt->wdtdev);
+ if (ret < 0)
+ dev_err(wdt->hw->dev,
+ "failed to ping the watchdog (%d)\n", ret);
+
+ return ret;
+}
+
+static int da9062_wdt_remove(struct platform_device *pdev)
+{
+ struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev);
+
+ watchdog_unregister_device(&wdt->wdtdev);
+ return 0;
+}
+
+static struct platform_driver da9062_wdt_driver = {
+ .probe = da9062_wdt_probe,
+ .remove = da9062_wdt_remove,
+ .driver = {
+ .name = "da9062-watchdog",
+ },
+};
+module_platform_driver(da9062_wdt_driver);
+
+MODULE_AUTHOR("S Twiss <[email protected]>");
+MODULE_DESCRIPTION("WDT device driver for Dialog DA9062");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform: da9062-watchdog");
--
end-of-patch for PATCH V1
From: S Twiss <[email protected]>
Add device tree bindings for the DA9062 driver
Signed-off-by: Steve Twiss <[email protected]>
---
This patch applies against linux-next and v4.0
Documentation/devicetree/bindings/mfd/da9062.txt | 107 +++++++++++++++++++++++
1 file changed, 107 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/da9062.txt
diff --git a/Documentation/devicetree/bindings/mfd/da9062.txt b/Documentation/devicetree/bindings/mfd/da9062.txt
new file mode 100644
index 0000000..cf8f3f6
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/da9062.txt
@@ -0,0 +1,107 @@
+* Dialog DA9062 Power Management Integrated Circuit (PMIC)
+
+DA9062 consists of a large and varied group of sub-devices:
+
+Device Supply Names Description
+------ ------------ -----------
+da9062-regulator : : LDOs & BUCKs
+da9062-onkey : : On Key
+da9062-rtc : : Real-Time Clock
+da9062-watchdog : : Watchdog Timer
+
+======
+
+Required properties:
+
+- compatible : Should be "dlg,da9062".
+- reg : Specifies the I2C slave address (this defaults to 0x58 but it can be
+ modified to match the chip's OTP settings).
+- interrupt-parent : Specifies the reference to the interrupt controller for
+ the DA9062.
+- interrupts : IRQ line information.
+- interrupt-controller
+
+See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
+further information on IRQ bindings.
+
+Sub-nodes:
+
+- regulators : This node defines the settings for the LDOs and BUCKs. The
+ DA9062 regulators are bound using their names listed below:
+
+ buck1 : BUCK_1
+ buck2 : BUCK_2
+ buck3 : BUCK_3
+ buck4 : BUCK_4
+ ldo1 : LDO_1
+ ldo2 : LDO_2
+ ldo3 : LDO_3
+ ldo4 : LDO_4
+
+ The component follows the standard regulator framework and the bindings
+ details of individual regulator device can be found in:
+ Documentation/devicetree/bindings/regulator/regulator.txt
+
+
+- rtc : This node defines settings required for the Real-Time Clock associated
+ with the DA9062. There are currently no entries in this binding, however
+ compatible = "dlg,da9062-rtc" should be added if a node is created.
+
+
+- onkey : This node defines the OnKey settings for controlling the key
+ functionality of the device. The node should contain the compatible property
+ with the value "dlg,da9062-onkey".
+
+ Optional onkey properties:
+
+ - dlg,disable-key-power : Disable power-down using a long key-press. If this
+ entry exists the OnKey driver will remove support for the KEY_POWER key
+ press. If this entry does not exist then by default the key-press
+ triggered power down is enabled and the OnKey will support both KEY_POWER
+ and KEY_SLEEP.
+
+- watchdog: This node defines the settings for the watchdog driver associated
+ with the DA9062 PMIC. The compatible = "dlg,da9062-watchdog" should be added
+ if a node is created.
+
+
+Example:
+
+ pmic0: da9062@58 {
+ compatible = "dlg,da9062";
+ reg = <0x58>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+
+ rtc {
+ compatible = "dlg,da9062-rtc";
+ };
+
+ onkey {
+ compatible = "dlg,da9062-onkey";
+ dlg,disable-key-power;
+ };
+
+ watchdog {
+ compatible = "dlg,da9062-watchdog";
+ };
+
+ regulators {
+ DA9062_BUCK1: buck1 {
+ regulator-name = "BUCK1";
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1570000>;
+ regulator-min-microamp = <500000>;
+ regulator-max-microamp = <2000000>;
+ regulator-boot-on;
+ };
+ DA9062_LDO1: ldo1 {
+ regulator-name = "LDO_1";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <3600000>;
+ regulator-boot-on;
+ };
+ };
+ };
+
--
end-of-patch for PATCH V1
Hi,
On Fri, Apr 17, 2015 at 03:23:33PM +0100, S Twiss wrote:
> From: S Twiss <[email protected]>
>
> Add OnKey driver support for DA9062
>
> Signed-off-by: Steve Twiss <[email protected]>
>
> ---
>
> This patch applies against linux-next and v4.0
>
>
>
> drivers/input/misc/Kconfig | 10 ++
> drivers/input/misc/Makefile | 1 +
> drivers/input/misc/da9062-onkey.c | 223 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 234 insertions(+)
> create mode 100644 drivers/input/misc/da9062-onkey.c
>
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 6deb8da..472ca38 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -586,6 +586,16 @@ config INPUT_DA9055_ONKEY
> To compile this driver as a module, choose M here: the module
> will be called da9055_onkey.
>
> +config INPUT_DA9062_ONKEY
> + tristate "Dialog DA9062 OnKey"
> + depends on MFD_DA9062
> + help
> + Support the ONKEY of Dialog DA9062 Power Management IC as an
> + input device reporting power button status.
> +
> + To compile this driver as a module, choose M here: the module
> + will be called da9062-onkey.
> +
> config INPUT_DM355EVM
> tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
> depends on MFD_DM355EVM_MSP
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 403a1a5..a631283 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
> obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
> obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
> obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
> +obj-$(CONFIG_INPUT_DA9062_ONKEY) += da9062-onkey.o
Can we maybe keep the same naming convention for all of these? Also, any
chance all of them or some of them can be combined?
> obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
> obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o
> obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
> diff --git a/drivers/input/misc/da9062-onkey.c b/drivers/input/misc/da9062-onkey.c
> new file mode 100644
> index 0000000..c4c0295
> --- /dev/null
> +++ b/drivers/input/misc/da9062-onkey.c
> @@ -0,0 +1,223 @@
> +/*
> + * da9062-onkey.c - ONKEY device driver for DA9062
> + * Copyright (C) 2015 Dialog Semiconductor Ltd.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/errno.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/workqueue.h>
> +#include <linux/regmap.h>
> +#include <linux/of.h>
> +#include <linux/mfd/da9062/core.h>
> +#include <linux/mfd/da9062/registers.h>
> +
> +struct da9062_onkey {
> + struct da9062 *hw;
> + struct delayed_work work;
> + struct input_dev *input;
> + int irq;
> + bool key_power;
> +};
> +
> +static void da9062_poll_on(struct work_struct *work)
> +{
> + struct da9062_onkey *onkey = container_of(work, struct da9062_onkey,
> + work.work);
> + unsigned int val;
> + int fault_log = 0;
> + bool poll = true;
> + int ret;
> +
> + /* poll to see when the pin is released */
> + ret = regmap_read(onkey->hw->regmap, DA9062AA_STATUS_A, &val);
> + if (ret < 0) {
> + dev_err(&onkey->input->dev,
> + "Failed to read ON status: %d\n", ret);
> + goto err_poll;
> + }
> +
> + if (!(val & DA9062AA_NONKEY_MASK)) {
> + ret = regmap_update_bits(onkey->hw->regmap,
> + DA9062AA_CONTROL_B,
> + DA9062AA_NONKEY_LOCK_MASK, 0);
> + if (ret < 0) {
> + dev_err(&onkey->input->dev,
> + "Failed to reset the Key Delay %d\n", ret);
> + goto err_poll;
> + }
> +
> + input_report_key(onkey->input, KEY_POWER, 0);
> + input_sync(onkey->input);
> +
> + poll = false;
> + }
> +
> + /* if the fault log KEY_RESET is detected,
> + * then clear it and shutdown DA9062 via I2C
> + */
> + ret = regmap_read(onkey->hw->regmap, DA9062AA_FAULT_LOG, &fault_log);
> + if (ret < 0)
> + dev_warn(&onkey->input->dev, "Cannot read FAULT_LOG\n");
> +
> + if (fault_log & DA9062AA_KEY_RESET_MASK) {
> + ret = regmap_write(onkey->hw->regmap,
> + DA9062AA_FAULT_LOG,
> + DA9062AA_KEY_RESET_MASK);
> + if (ret < 0)
> + dev_warn(&onkey->input->dev,
> + "Cannot reset KEY_RESET fault log\n");
> + else {
> + /* at this point we do any S/W housekeeping
> + * and then send shutdown command
> + */
> + dev_info(&onkey->input->dev,
> + "Sending SHUTDOWN to DA9062 ...\n");
> + ret = regmap_write(onkey->hw->regmap,
> + DA9062AA_CONTROL_F,
> + DA9062AA_SHUTDOWN_MASK);
> + if (ret < 0)
> + dev_err(&onkey->input->dev,
> + "Cannot SHUTDOWN DA9062\n");
> + }
> + }
This entire block seems to not belong to the input portion of MFD. Why
do we do it here?
> +
> +err_poll:
> + if (poll)
> + schedule_delayed_work(&onkey->work, 50);
> +}
> +
> +static irqreturn_t da9062_onkey_irq_handler(int irq, void *data)
> +{
> + struct da9062_onkey *onkey = data;
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(onkey->hw->regmap, DA9062AA_STATUS_A, &val);
> + if (onkey->key_power && (ret >= 0) && (val & DA9062AA_NONKEY_MASK)) {
> + input_report_key(onkey->input, KEY_POWER, 1);
> + input_sync(onkey->input);
> + schedule_delayed_work(&onkey->work, 0);
> + dev_notice(&onkey->input->dev, "KEY_POWER pressed.\n");
> + } else {
> + input_report_key(onkey->input, KEY_SLEEP, 1);
> + input_sync(onkey->input);
> + input_report_key(onkey->input, KEY_SLEEP, 0);
> + input_sync(onkey->input);
> + dev_notice(&onkey->input->dev, "KEY_SLEEP pressed.\n");
> + }
Why do we handle KEY_POWER and KEY_SLEEP completely differently?
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int da9062_onkey_probe(struct platform_device *pdev)
> +{
> + struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
> + struct da9062_onkey *onkey;
> + bool kp_tmp = true;
> + int ret = 0;
> +
> + kp_tmp = of_property_read_bool((&pdev->dev)->of_node,
> + "dlg,disable-key-power");
> + kp_tmp = !kp_tmp;
Should we just allow specifying the keycode instead of hardcoding
KEY_POWER/KEY_SLEEP?
> +
> +
> + onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9062_onkey),
> + GFP_KERNEL);
> + if (!onkey) {
> + dev_err(&pdev->dev, "Failed to allocate memory.\n");
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + INIT_DELAYED_WORK(&onkey->work, da9062_poll_on);
> +
> + onkey->input = devm_input_allocate_device(&pdev->dev);
> + if (!onkey->input) {
> + dev_err(&pdev->dev, "Failed to allocated input device.\n");
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + ret = platform_get_irq_byname(pdev, "ONKEY");
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Failed to get platform IRQ.\n");
> + goto err;
> + }
> + onkey->irq = ret;
> +
> + ret = request_threaded_irq(onkey->irq, NULL,
> + da9062_onkey_irq_handler,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + "ONKEY", onkey);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Failed to request input device IRQ.\n");
> + goto err;
> + }
> +
> + onkey->hw = chip;
> + onkey->key_power = kp_tmp;
> + onkey->input->evbit[0] = BIT_MASK(EV_KEY);
> + onkey->input->name = "da9062-onkey";
> + onkey->input->phys = "da9062-onkey/input0";
> + onkey->input->dev.parent = &pdev->dev;
> +
> + if (onkey->key_power)
> + input_set_capability(onkey->input, EV_KEY, KEY_POWER);
> + input_set_capability(onkey->input, EV_KEY, KEY_SLEEP);
> +
> + ret = input_register_device(onkey->input);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Failed to register input device.\n");
> + goto err_irq;
> + }
> +
> + platform_set_drvdata(pdev, onkey);
> + return 0;
> +
> +err_irq:
> + free_irq(onkey->irq, onkey);
> + cancel_delayed_work_sync(&onkey->work);
> +err:
> + return ret;
> +}
> +
> +static int da9062_onkey_remove(struct platform_device *pdev)
> +{
> + struct da9062_onkey *onkey = platform_get_drvdata(pdev);
> +
> + free_irq(onkey->irq, onkey);
> + cancel_delayed_work_sync(&onkey->work);
> + input_unregister_device(onkey->input);
No need to unregister explicitly if you allocated input device with
devm.
> + return 0;
> +}
> +
> +static struct platform_driver da9062_onkey_driver = {
> + .probe = da9062_onkey_probe,
> + .remove = da9062_onkey_remove,
> + .driver = {
> + .name = "da9062-onkey",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +module_platform_driver(da9062_onkey_driver);
> +
> +MODULE_AUTHOR("S Twiss <[email protected]>");
> +MODULE_DESCRIPTION("ONKEY device driver for Dialog DA9062");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform: da9062-onkey");
> --
> end-of-patch for PATCH V1
>
Thanks.
--
Dmitry
On Fri, 2015-04-17 at 15:23 +0100, S Twiss wrote:
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> +config MFD_DA9062
> + bool "Dialog Semiconductor DA9062 PMIC Support"
> + select MFD_CORE
> + select REGMAP_I2C
> + select REGMAP_IRQ
> + depends on I2C=y
> + help
> + Say yes here for support for the Dialog Semiconductor DA9062 PMIC.
> + This includes the I2C driver and core APIs.
> + Additional drivers must be enabled in order to use the functionality
> + of the device.
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> +obj-$(CONFIG_MFD_DA9062) += da9062-core.o
MFD_DA9062 is a bool symbol. So da9062-core.o can never be part of a
module, right?
> --- /dev/null
> +++ b/drivers/mfd/da9062-core.c
> +#include <linux/module.h>
So you might not need this include.
> +MODULE_DEVICE_TABLE(of, da9062_dt_ids);
This macro will be preprocessed away for built-in code, according to
include/linux/module.h.
> +MODULE_DEVICE_TABLE(i2c, da9062_i2c_id);
Ditto.
> +static struct i2c_driver da9062_i2c_driver = {
> + .driver = {
> + .name = "da9062",
> + .of_match_table = of_match_ptr(da9062_dt_ids),
> + },
> + .probe = da9062_i2c_probe,
> + .remove = da9062_i2c_remove,
> + .id_table = da9062_i2c_id,
> +};
> +
> +module_i2c_driver(da9062_i2c_driver);
After looking at a few levels of #defines I think this is equivalent to
i2c_register_driver(NULL, da9062_i2c_driver);
for built-in code. Did I grep that right?
> +MODULE_DESCRIPTION("CORE device driver for Dialog DA9062");
> +MODULE_AUTHOR("S Twiss <[email protected]>");
> +MODULE_LICENSE("GPL v2");
These macros will be effectively preprocessed away for built-in only
code.
Thanks,
Paul Bolle
I spotted only a license mismatch.
On Fri, 2015-04-17 at 15:23 +0100, S Twiss wrote:
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
This states the license is GPL v2 or later.
> +MODULE_LICENSE("GPL v2");
And, according to include/linux/module.h, this states the license is
just GPL v2. So I think either the license comment or the license ident
used in the MODULE_LICENSE() macro needs to change.
The same mismatch can be found in 3/6, 4/6, and 5/6.
Thanks,
Paul Bolle
On Fri, Apr 17, 2015 at 03:23:32PM +0100, S Twiss wrote:
> +/* Regulator interrupt handlers */
> +static irqreturn_t da9062_ldo_lim_event(int irq, void *data)
> +{
> + struct da9062_regulators *regulators = data;
> + struct da9062 *hw = regulators->regulator[0].hw;
> + struct da9062_regulator *regl;
> + int bits, i, ret;
> +
> + ret = regmap_read(hw->regmap, DA9062AA_STATUS_D, &bits);
> + if (ret < 0)
> + return IRQ_NONE;
Please log an error for this, if we're having trouble talking to the
device that seems like a serious issue.
> + for (i = regulators->n_regulators - 1; i >= 0; i--) {
> + regl = ®ulators->regulator[i];
> + if (regl->info->oc_event.reg != DA9062AA_STATUS_D)
> + continue;
> +
> + if (BIT(regl->info->oc_event.lsb) & bits)
> + regulator_notifier_call_chain(regl->rdev,
> + REGULATOR_EVENT_OVER_CURRENT, NULL);
> + }
> +
> + return IRQ_HANDLED;
This will return IRQ_HANDLED even if none of the regulators were
flagginng an event.
> +static irqreturn_t da9062_vdd_warn_event(int irq, void *data)
> +{
> + return IRQ_HANDLED;
> +}
Ignoring an interrupt is not usefully handling it - at the *least* this
should be generating a log message.
> +static struct da9062_regulators_pdata *da9062_parse_regulators_dt(
> + struct platform_device *pdev,
> + struct of_regulator_match **reg_matches)
> +{
> + struct da9062_regulators_pdata *pdata;
> + struct da9062_regulator_data *rdata;
> + struct device_node *node;
> + int i, n, num;
> +
> + node = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
> + if (!node) {
> + dev_err(&pdev->dev, "Regulators device node not found\n");
> + return ERR_PTR(-ENODEV);
> + }
> +
> + num = of_regulator_match(&pdev->dev, node, da9062_matches,
> + ARRAY_SIZE(da9062_matches));
Don't open code this, describe the DT names in the regualtor_desc and
let the core register.
> + if (IS_ERR(pdata) || pdata->n_regulators == 0) {
> + dev_err(&pdev->dev,
> + "No regulators defined for the platform\n");
> + return PTR_ERR(pdata);
> + }
> +
> + n_regulators = ARRAY_SIZE(local_regulator_info),
This is broken, the set of regulators in the silicon is not a property
of the platform. The driver should just register all the regualtors
that are present in the silicon. I'm fairly sure I've been through this
before...
> + ret = request_threaded_irq(irq,
> + NULL, da9062_vdd_warn_event,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + "VDD_WARN", regulators);
devm_request_threaded_irq().
On 04/17/2015 07:23 AM, S Twiss wrote:
> From: S Twiss <[email protected]>
>
> Add watchdog driver support for DA9062
>
> Signed-off-by: Steve Twiss <[email protected]>
>
Hi Steve,
Key question here is if the da9062 is really so much different to the da9062
that you can not use the same driver. I am especially concerned about
the added da9062_reset_watchdog_timer(), given the delay it introduces.
Comments below.
Thanks,
Guenter
> ---
>
> This patch applies against linux-next and v4.0
>
>
>
> drivers/watchdog/Kconfig | 9 ++
> drivers/watchdog/Makefile | 1 +
> drivers/watchdog/da9062_wdt.c | 249 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 259 insertions(+)
> create mode 100644 drivers/watchdog/da9062_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 16f2023..62232d7 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -96,6 +96,15 @@ config DA9063_WATCHDOG
>
> This driver can be built as a module. The module name is da9063_wdt.
>
> +config DA9062_WATCHDOG
> + tristate "Dialog DA9062 Watchdog"
> + depends on MFD_DA9062
> + select WATCHDOG_CORE
> + help
> + Support for the watchdog in the DA9062 PMIC.
> +
> + This driver can be built as a module. The module name is da9062_wdt.
> +
> config GPIO_WATCHDOG
> tristate "Watchdog device controlled through GPIO-line"
> depends on OF_GPIO
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 5c19294..57ba815 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -179,6 +179,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o
> # Architecture Independent
> obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
> obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
> +obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o
> obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
> obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
> obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
> diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
> new file mode 100644
> index 0000000..ac4c255
> --- /dev/null
> +++ b/drivers/watchdog/da9062_wdt.c
> @@ -0,0 +1,249 @@
> +/*
> + * da9062_wdt.c - WDT device driver for DA9062
> + * Copyright (C) 2015 Dialog Semiconductor Ltd.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/watchdog.h>
> +#include <linux/platform_device.h>
> +#include <linux/uaccess.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/mfd/da9062/registers.h>
> +#include <linux/mfd/da9062/core.h>
> +#include <linux/regmap.h>
> +#include <linux/of.h>
> +
> +static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
> +#define DA9062_TWDSCALE_DISABLE 0
> +#define DA9062_TWDSCALE_MIN 1
> +#define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1)
> +#define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN]
> +#define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX]
> +#define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1]
> +
> +struct da9062_watchdog {
> + struct da9062 *hw;
> + struct watchdog_device wdtdev;
> +};
> +
> +static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
> +{
> + unsigned int i;
> +
> + for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) {
> + if (wdt_timeout[i] >= secs)
> + return i;
> + }
> +
> + return DA9062_TWDSCALE_MAX;
> +}
> +
> +static int da9062_reset_watchdog_timer(struct da9062 *hw)
> +{
> + int ret;
> +
> + ret = regmap_update_bits(hw->regmap,
> + DA9062AA_CONTROL_F,
> + DA9062AA_WATCHDOG_MASK,
> + DA9062AA_WATCHDOG_MASK);
> +
> + mdelay(300);
Really ? That seems to be excessive, especially given how often
this function is called (for each ping!).
> +
> + return ret;
> +}
> +
> +static int da9062_wdt_update_timeout_register(struct da9062 *chip,
> + unsigned int regval)
> +{
> + int ret;
> +
> + ret = da9062_reset_watchdog_timer(chip);
> + if (ret)
> + dev_err(chip->dev, "Failed to ping the watchdog (err = %d)\n",
> + ret);
> +
And no impact, so this doesn't really matter ?
> + return regmap_update_bits(chip->regmap,
> + DA9062AA_CONTROL_D,
> + DA9062AA_TWDSCALE_MASK,
> + regval);
> +}
> +
> +static int da9062_wdt_start(struct watchdog_device *wdd)
> +{
> + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
> + unsigned int selector;
> + int ret;
> +
> + selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout);
> + ret = da9062_wdt_update_timeout_register(wdt->hw, selector);
> + if (ret)
> + dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n",
> + ret);
> +
> + return ret;
> +}
> +
> +static int da9062_wdt_stop(struct watchdog_device *wdd)
> +{
> + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
> + int ret;
> +
> + ret = da9062_reset_watchdog_timer(wdt->hw);
> + if (ret)
> + dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
> + ret);
> +
ping or reset ? And no impact, ie you just ignore the error ?
> + ret = regmap_update_bits(wdt->hw->regmap,
> + DA9062AA_CONTROL_D,
> + DA9062AA_TWDSCALE_MASK,
> + DA9062_TWDSCALE_DISABLE);
> + if (ret)
> + dev_alert(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n",
> + ret);
> +
> + return ret;
> +}
> +
> +static int da9062_wdt_ping(struct watchdog_device *wdd)
> +{
> + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
> + int ret;
> +
> + dev_dbg(wdt->hw->dev, "watchdog ping\n");
> +
> + ret = da9062_reset_watchdog_timer(wdt->hw);
> + if (ret)
> + dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
> + ret);
> +
> + return ret;
> +}
> +
> +static int da9062_wdt_set_timeout(struct watchdog_device *wdd,
> + unsigned int timeout)
> +{
> + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
> + unsigned int selector;
> + int ret;
> +
> + selector = da9062_wdt_timeout_to_sel(timeout);
> + ret = da9062_wdt_update_timeout_register(wdt->hw, selector);
> + if (ret)
> + dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n",
> + ret);
> + else
> + wdd->timeout = wdt_timeout[selector];
> +
> + return ret;
> +}
> +
> +/* E_WDG_WARN interrupt handler */
> +static irqreturn_t da9062_wdt_wdg_warn_irq_handler(int irq, void *data)
> +{
> + struct da9062_watchdog *wdt = data;
> +
> + dev_notice(wdt->hw->dev, "Watchdog timeout warning trigger.\n");
> + return IRQ_HANDLED;
> +}
> +
> +static const struct watchdog_info da9062_watchdog_info = {
> + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
> + .identity = "DA9062 WDT",
> +};
> +
> +static const struct watchdog_ops da9062_watchdog_ops = {
> + .owner = THIS_MODULE,
> + .start = da9062_wdt_start,
> + .stop = da9062_wdt_stop,
> + .ping = da9062_wdt_ping,
> + .set_timeout = da9062_wdt_set_timeout,
> +};
> +
> +static int da9062_wdt_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct da9062 *chip;
> + struct da9062_watchdog *wdt;
> + int irq;
> +
> + chip = dev_get_drvdata(pdev->dev.parent);
> + if (!chip)
> + return -EINVAL;
> +
> + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
> + if (!wdt)
> + return -ENOMEM;
> +
> + wdt->hw = chip;
> +
> + wdt->wdtdev.info = &da9062_watchdog_info;
> + wdt->wdtdev.ops = &da9062_watchdog_ops;
> + wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
> + wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
> + wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
> + wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> +
> + watchdog_set_drvdata(&wdt->wdtdev, wdt);
> + dev_set_drvdata(&pdev->dev, wdt);
> +
> + irq = platform_get_irq_byname(pdev, "WDG_WARN");
> + if (irq < 0)
> + dev_err(wdt->hw->dev, "Failed to get IRQ.\n");
But you still request the negative irq.
> +
> + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> + da9062_wdt_wdg_warn_irq_handler,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
> + "WDG_WARN", wdt);
> + if (ret) {
> + dev_err(wdt->hw->dev,
> + "Failed to request watchdog device IRQ.\n");
Either this is an error, or it isn't. If it is, I would expect the driver
to abort loading. If not, please don't use dev_err.
> + }
> +
> + ret = watchdog_register_device(&wdt->wdtdev);
> + if (ret < 0)
> + dev_err(wdt->hw->dev,
> + "watchdog registration incomplete (%d)\n", ret);
incomplete ?
> + else
> + dev_info(wdt->hw->dev, "installed DA9062 watchdog\n");
Please rop this message.
> +
> + da9062_wdt_ping(&wdt->wdtdev);
> + if (ret < 0)
> + dev_err(wdt->hw->dev,
> + "failed to ping the watchdog (%d)\n", ret);
> +
That ping is asking for an explanation. Does it imply that the watchdog is known to be running
and can not be stopped ?
Also, the ping function already creates an error message. Please be less noisy with
your messages.
> + return ret;
If the above ping fails, this will return an error without unregistering
the watchdog device.
> +}
> +
> +static int da9062_wdt_remove(struct platform_device *pdev)
> +{
> + struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev);
> +
> + watchdog_unregister_device(&wdt->wdtdev);
> + return 0;
> +}
> +
> +static struct platform_driver da9062_wdt_driver = {
> + .probe = da9062_wdt_probe,
> + .remove = da9062_wdt_remove,
> + .driver = {
> + .name = "da9062-watchdog",
> + },
> +};
> +module_platform_driver(da9062_wdt_driver);
> +
> +MODULE_AUTHOR("S Twiss <[email protected]>");
> +MODULE_DESCRIPTION("WDT device driver for Dialog DA9062");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform: da9062-watchdog");
>
On 04/17/2015 07:23 AM, S Twiss wrote:
> From: S Twiss <[email protected]>
>
> This patch set adds support for the Dialog DA9062 Power Management IC.
>
> In this patch set the following is provided:
> - [PATCH V1 1/6]: MFD core support
> - [PATCH V1 2/6]: BUCK and LDO regulator driver
> - [PATCH V1 3/6]: RTC driver
> - [PATCH V1 4/6]: OnKey driver
> - [PATCH V1 5/6]: Watchdog driver
> - [PATCH V1 6/6]: Add bindings for all DA9062 components
>
> This patch applies against linux-next and v4.0
>
> Thank you,
> Steve Twiss, Dialog Semiconductor Ltd.
>
Hi Steve,
It might make sense to use something better than "COVER LETTER" as subject line.
Thanks,
Guenter
On 18 April 2015 12:48 Mark Brown wrote:
> On Fri, Apr 17, 2015 at 03:23:32PM +0100, S Twiss wrote:
>
> > +/* Regulator interrupt handlers */
> > +static irqreturn_t da9062_ldo_lim_event(int irq, void *data)
> > +{
> > + struct da9062_regulators *regulators = data;
> > + struct da9062 *hw = regulators->regulator[0].hw;
> > + struct da9062_regulator *regl;
> > + int bits, i, ret;
> > +
> > + ret = regmap_read(hw->regmap, DA9062AA_STATUS_D, &bits);
> > + if (ret < 0)
> > + return IRQ_NONE;
>
> Please log an error for this, if we're having trouble talking to the
> device that seems like a serious issue.
Will do that with a dev_err()
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Failed to read LDO overcurrent indicator\n");
return IRQ_NONE;
+ }
>
> > + for (i = regulators->n_regulators - 1; i >= 0; i--) {
> > + regl = ®ulators->regulator[i];
> > + if (regl->info->oc_event.reg != DA9062AA_STATUS_D)
> > + continue;
> > +
> > + if (BIT(regl->info->oc_event.lsb) & bits)
> > + regulator_notifier_call_chain(regl->rdev,
> > +
> REGULATOR_EVENT_OVER_CURRENT, NULL);
> > + }
> > +
> > + return IRQ_HANDLED;
>
> This will return IRQ_HANDLED even if none of the regulators were
> flagginng an event.
>
Thanks,
I will refactor that part with a default error path and only return IRQ_HANDLED
when there is a hit on the notifier call.
> > +static irqreturn_t da9062_vdd_warn_event(int irq, void *data)
> > +{
> > + return IRQ_HANDLED;
> > +}
>
> Ignoring an interrupt is not usefully handling it - at the *least* this
> should be generating a log message.
>
Not much use -- yes.
I've taken another look at this and I've decided to move it into the core.
The VDD_WARN applies to the system voltage and is not specific to regulators.
So it would make more sense for this to exist in the core driver instead -- this
would need to exist at all times and if it stays in the regulators it could be
moduled out of existence.
This will mean messing with a few files to fix this one.
drivers/mfd/da9062-core.c
regulator/da9062-regulator.c
include/linux/mfd/da9062/core.h
I'll resend this as PATCH V2.
> > +static struct da9062_regulators_pdata *da9062_parse_regulators_dt(
> > + struct platform_device *pdev,
> > + struct of_regulator_match **reg_matches)
> > +{
> > + struct da9062_regulators_pdata *pdata;
> > + struct da9062_regulator_data *rdata;
> > + struct device_node *node;
> > + int i, n, num;
> > +
> > + node = of_get_child_by_name(pdev->dev.parent->of_node,
> "regulators");
> > + if (!node) {
> > + dev_err(&pdev->dev, "Regulators device node not
> found\n");
> > + return ERR_PTR(-ENODEV);
> > + }
> > +
> > + num = of_regulator_match(&pdev->dev, node, da9062_matches,
> > + ARRAY_SIZE(da9062_matches));
>
> Don't open code this, describe the DT names in the regualtor_desc and
> let the core register.
>
Okay. I think I am getting this.
As of v3.18 there are newer parts to regulator_desc from the commit
a0c7b16 "regulator: of: Provide simplified DT parsing method"
The search function is now in the core.
Am I on on the right track with this one?
> > + if (IS_ERR(pdata) || pdata->n_regulators == 0) {
> > + dev_err(&pdev->dev,
> > + "No regulators defined for the platform\n");
> > + return PTR_ERR(pdata);
> > + }
> > +
> > + n_regulators = ARRAY_SIZE(local_regulator_info),
>
> This is broken, the set of regulators in the silicon is not a property
> of the platform. The driver should just register all the regualtors
> that are present in the silicon. I'm fairly sure I've been through this
> before...
>
> > + ret = request_threaded_irq(irq,
> > + NULL, da9062_vdd_warn_event,
> > + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> > + "VDD_WARN", regulators);
>
> devm_request_threaded_irq().
yes -- this will come as part of the refactoring for the irq handler
static irqreturn_t da9062_vdd_warn_event(int irq, void *data)
into the core.
Regards,
Steve
On 17 April 2015 15:24 Steve Twiss wrote.
> Subject: [PATCH V1 1/6] mfd: da9062: DA9062 MFD core driver
> From: S Twiss <[email protected]>
>
> Add MFD core driver support for DA9062
>
> Signed-off-by: Steve Twiss <[email protected]>
>
> ---
>
> This patch applies against linux-next and v4.0
>
> drivers/mfd/Kconfig | 12 +
> drivers/mfd/Makefile | 3 +-
> drivers/mfd/da9062-core.c | 611 +++++++++++++++++++
> include/linux/mfd/da9062/core.h | 61 ++
> include/linux/mfd/da9062/registers.h | 1108
Hi,
Due to changes in the regulator driver, I am going to alter this with
moving the static irqreturn_t da9062_vdd_warn_event(int irq, void *data)
function from the regulators and into the core.
The VDD_WARN applies to the system voltage and is not specific to regulators.
So it would make more sense for this to exist in the core driver instead
I'll resend this in PATCH V2 soon
Regards,
Steve
On Fri, Apr 24, 2015 at 02:47:06PM +0000, Opensource [Steve Twiss] wrote:
> On 18 April 2015 12:48 Mark Brown wrote:
> Okay. I think I am getting this.
> As of v3.18 there are newer parts to regulator_desc from the commit
> a0c7b16 "regulator: of: Provide simplified DT parsing method"
> The search function is now in the core.
> Am I on on the right track with this one?
Yes.
On Fri, 17 Apr 2015, S Twiss wrote:
> From: S Twiss <[email protected]>
>
> Add device tree bindings for the DA9062 driver
>
> Signed-off-by: Steve Twiss <[email protected]>
>
> ---
>
> This patch applies against linux-next and v4.0
>
>
>
> Documentation/devicetree/bindings/mfd/da9062.txt | 107 +++++++++++++++++++++++
> 1 file changed, 107 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/da9062.txt
I don't see anything groundbreaking or offensive.
Acked-by: Lee Jones <[email protected]>
> diff --git a/Documentation/devicetree/bindings/mfd/da9062.txt b/Documentation/devicetree/bindings/mfd/da9062.txt
> new file mode 100644
> index 0000000..cf8f3f6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/da9062.txt
> @@ -0,0 +1,107 @@
> +* Dialog DA9062 Power Management Integrated Circuit (PMIC)
> +
> +DA9062 consists of a large and varied group of sub-devices:
> +
> +Device Supply Names Description
> +------ ------------ -----------
> +da9062-regulator : : LDOs & BUCKs
> +da9062-onkey : : On Key
> +da9062-rtc : : Real-Time Clock
> +da9062-watchdog : : Watchdog Timer
> +
> +======
> +
> +Required properties:
> +
> +- compatible : Should be "dlg,da9062".
> +- reg : Specifies the I2C slave address (this defaults to 0x58 but it can be
> + modified to match the chip's OTP settings).
> +- interrupt-parent : Specifies the reference to the interrupt controller for
> + the DA9062.
> +- interrupts : IRQ line information.
> +- interrupt-controller
> +
> +See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
> +further information on IRQ bindings.
> +
> +Sub-nodes:
> +
> +- regulators : This node defines the settings for the LDOs and BUCKs. The
> + DA9062 regulators are bound using their names listed below:
> +
> + buck1 : BUCK_1
> + buck2 : BUCK_2
> + buck3 : BUCK_3
> + buck4 : BUCK_4
> + ldo1 : LDO_1
> + ldo2 : LDO_2
> + ldo3 : LDO_3
> + ldo4 : LDO_4
> +
> + The component follows the standard regulator framework and the bindings
> + details of individual regulator device can be found in:
> + Documentation/devicetree/bindings/regulator/regulator.txt
> +
> +
> +- rtc : This node defines settings required for the Real-Time Clock associated
> + with the DA9062. There are currently no entries in this binding, however
> + compatible = "dlg,da9062-rtc" should be added if a node is created.
> +
> +
> +- onkey : This node defines the OnKey settings for controlling the key
> + functionality of the device. The node should contain the compatible property
> + with the value "dlg,da9062-onkey".
> +
> + Optional onkey properties:
> +
> + - dlg,disable-key-power : Disable power-down using a long key-press. If this
> + entry exists the OnKey driver will remove support for the KEY_POWER key
> + press. If this entry does not exist then by default the key-press
> + triggered power down is enabled and the OnKey will support both KEY_POWER
> + and KEY_SLEEP.
> +
> +- watchdog: This node defines the settings for the watchdog driver associated
> + with the DA9062 PMIC. The compatible = "dlg,da9062-watchdog" should be added
> + if a node is created.
> +
> +
> +Example:
> +
> + pmic0: da9062@58 {
> + compatible = "dlg,da9062";
> + reg = <0x58>;
> + interrupt-parent = <&gpio6>;
> + interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
> + interrupt-controller;
> +
> + rtc {
> + compatible = "dlg,da9062-rtc";
> + };
> +
> + onkey {
> + compatible = "dlg,da9062-onkey";
> + dlg,disable-key-power;
> + };
> +
> + watchdog {
> + compatible = "dlg,da9062-watchdog";
> + };
> +
> + regulators {
> + DA9062_BUCK1: buck1 {
> + regulator-name = "BUCK1";
> + regulator-min-microvolt = <300000>;
> + regulator-max-microvolt = <1570000>;
> + regulator-min-microamp = <500000>;
> + regulator-max-microamp = <2000000>;
> + regulator-boot-on;
> + };
> + DA9062_LDO1: ldo1 {
> + regulator-name = "LDO_1";
> + regulator-min-microvolt = <900000>;
> + regulator-max-microvolt = <3600000>;
> + regulator-boot-on;
> + };
> + };
> + };
> +
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
On 29 April 2015 11:54 Lee Jones wrote :
> On Fri, 17 Apr 2015, S Twiss wrote:
> > From: S Twiss <[email protected]>
> >
> > Add device tree bindings for the DA9062 driver
> >
> > Signed-off-by: Steve Twiss <[email protected]>
> >
> > ---
> >
> > This patch applies against linux-next and v4.0
> >
> > Documentation/devicetree/bindings/mfd/da9062.txt | 107
> +++++++++++++++++++++++
> > 1 file changed, 107 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/mfd/da9062.txt
>
> I don't see anything groundbreaking or offensive.
>
> Acked-by: Lee Jones <[email protected]>
>
Thank you for the review and Ack.
I am in the process of updating the DA9063 OnKey driver in a different thread.
This driver is functionally similar to the DA9062 OnKey driver as was discussed in
an earlier thread from DA9063.
http://www.spinics.net/lists/linux-input/msg38137.html
Although the 63 and 62 chips have sequential numbers they are not the same,
however the OnKey driver is functionally very similar and so I am going to try and
merge the code for both drivers in the case of the OnKey.
Due to the DA9063 being currently reviewed, perhaps it would be prudent not
to add this device tree bindings for DA9062 to the kernel just yet -- the device tree
bindings could change for the OnKey in my DA9063 submission and this would affect
the DA9062 driver when I merge the two OnKey drivers.
I will put a full version history in my next patch set to describe what changes I have
made to this file so this review is not deemed a wasted effort.
Regards,
Steve
[...]
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
Hi Dmitry,
Thanks for your patience on this one.
The Dialog OnKey for DA9062 is a fairly complicated set of interrupts
and register read and writes. I've tried to explain the best I can below.
On 17 April 2015 17:12, Dmitry Torokhov [mailto:[email protected]] wrote:
[...]
> > diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> > index 403a1a5..a631283 100644
> > --- a/drivers/input/misc/Makefile
> > +++ b/drivers/input/misc/Makefile
> > @@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) +=
> cma3000_d0x_i2c.o
> > obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
> > obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
> > obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
> > +obj-$(CONFIG_INPUT_DA9062_ONKEY) += da9062-onkey.o
>
> Can we maybe keep the same naming convention for all of these? Also, any
> chance all of them or some of them can be combined?
Yes. I will rename so it uses an underscore.
Unfortunately, the 55 and 52 drivers are not functionally similar. However the
DA9063 and DA9062 OnKey drivers *are* functionally similar and so I will make
an effort to combine those two drivers in future. I would like to submit DA9063
first and drop this DA9062 until I can combine it. However, since the DA9063 driver
is identical -- your comments will be useful for my DA9063 submission attempt.
I have added my replies here below which will also relate to DA9063.
[...]
> > +static void da9062_poll_on(struct work_struct *work)
> > +{
> > + struct da9062_onkey *onkey = container_of(work, struct
> da9062_onkey,
> > + work.work);
> > + unsigned int val;
> > + int fault_log = 0;
> > + bool poll = true;
> > + int ret;
> > +
> > + /* poll to see when the pin is released */
> > + ret = regmap_read(onkey->hw->regmap, DA9062AA_STATUS_A,
> &val);
> > + if (ret < 0) {
> > + dev_err(&onkey->input->dev,
> > + "Failed to read ON status: %d\n", ret);
> > + goto err_poll;
> > + }
> > +
> > + if (!(val & DA9062AA_NONKEY_MASK)) {
> > + ret = regmap_update_bits(onkey->hw->regmap,
> > + DA9062AA_CONTROL_B,
> > + DA9062AA_NONKEY_LOCK_MASK,
> 0);
> > + if (ret < 0) {
> > + dev_err(&onkey->input->dev,
> > + "Failed to reset the Key Delay %d\n", ret);
> > + goto err_poll;
> > + }
> > +
> > + input_report_key(onkey->input, KEY_POWER, 0);
> > + input_sync(onkey->input);
> > +
> > + poll = false;
> > + }
> > +
> > + /* if the fault log KEY_RESET is detected,
> > + * then clear it and shutdown DA9062 via I2C
> > + */
> > + ret = regmap_read(onkey->hw->regmap, DA9062AA_FAULT_LOG,
> &fault_log);
> > + if (ret < 0)
> > + dev_warn(&onkey->input->dev, "Cannot read
> FAULT_LOG\n");
> > +
> > + if (fault_log & DA9062AA_KEY_RESET_MASK) {
> > + ret = regmap_write(onkey->hw->regmap,
> > + DA9062AA_FAULT_LOG,
> > + DA9062AA_KEY_RESET_MASK);
> > + if (ret < 0)
> > + dev_warn(&onkey->input->dev,
> > + "Cannot reset KEY_RESET fault log\n");
> > + else {
> > + /* at this point we do any S/W housekeeping
> > + * and then send shutdown command
> > + */
> > + dev_info(&onkey->input->dev,
> > + "Sending SHUTDOWN to DA9062 ...\n");
> > + ret = regmap_write(onkey->hw->regmap,
> > + DA9062AA_CONTROL_F,
> > + DA9062AA_SHUTDOWN_MASK);
> > + if (ret < 0)
> > + dev_err(&onkey->input->dev,
> > + "Cannot SHUTDOWN DA9062\n");
> > + }
> > + }
>
> This entire block seems to not belong to the input portion of MFD. Why
> do we do it here?
>
There are four modes of OnKey operation here, but only the first three are handled
in software. This block relates to case (3).
(1) short press & release -- SLEEP
(2) long press & release -- POWER
(3) long-long press (no release) -- software power cut
(4) long-long press (no release) -- hardware power cut
For cases (1) and (2) the OnKey driver will handle the events using the Linux
framework. And for case (4) this is completed totally in hardware and is the
emergency power off for holding down the OnKey until the power is cut by
the PMIC -- this is not handled here and in any case it is designed to be used
when the software is not responding: H/W shutdown
There is one more case and this is covered by point (3). This is when the user
holds down the OnKey as though they were attempting an emergency power
off. This is the same operation as point (4) except that software *is* still
able to respond. At this point it sends the power off signal to the PMIC in a similar
way as the hardware power cut operation would do -- but it does this 1 second
before the hardware has chance to step-in and force the PMIC to go down.
There are several reasons for case (3).
- It is to cover the usecase of a user pressing the OnKey until the device goes
off as their main way of turning the power off to their device.
This software power cut allows any critical house-keeping to be completed
before the power switch is flicked and so it mitigates the possibility for loss
of any data.
- It also ensures the FAULT_LOG is cleared. The FAULT_LOG is a persistent register
in the PMIC and holds its information between resets -- in this case the reason the
device was powered off.
In case (4)
If the software was not responsive, then the hardware shutdown would happen
1 second later and the FAULT_LOG would persist enough information to be able
to tell the difference between a software power cut and a hardware power cut
when the device was restarted. If the software had not responded, then due to
the FAULT_LOG persistence, it would be possible to take action the next time the
device was turned on again (maybe in the bootloader -- e.g. say to complete
memory checks or put the device into a safe mode).
> > +
> > +err_poll:
> > + if (poll)
> > + schedule_delayed_work(&onkey->work, 50);
> > +}
> > +
> > +static irqreturn_t da9062_onkey_irq_handler(int irq, void *data)
> > +{
> > + struct da9062_onkey *onkey = data;
> > + unsigned int val;
> > + int ret;
> > +
> > + ret = regmap_read(onkey->hw->regmap, DA9062AA_STATUS_A,
> &val);
> > + if (onkey->key_power && (ret >= 0) && (val &
> DA9062AA_NONKEY_MASK)) {
> > + input_report_key(onkey->input, KEY_POWER, 1);
> > + input_sync(onkey->input);
> > + schedule_delayed_work(&onkey->work, 0);
> > + dev_notice(&onkey->input->dev, "KEY_POWER
> pressed.\n");
> > + } else {
> > + input_report_key(onkey->input, KEY_SLEEP, 1);
> > + input_sync(onkey->input);
> > + input_report_key(onkey->input, KEY_SLEEP, 0);
> > + input_sync(onkey->input);
> > + dev_notice(&onkey->input->dev, "KEY_SLEEP pressed.\n");
> > + }
>
> Why do we handle KEY_POWER and KEY_SLEEP completely differently?
>
The reason for the keys being handled differently is due to the way with the
interrupt is being triggered differently in the OnKey. This depends on whether
the key-press is a short (SLEEP) or a long (POWER) key-press.
For case (1)
(1) short-press & release -- SLEEP
The *release* of the OnKey is the trigger of the interrupt. So we know the device
has a press-release operation. This was the intention of the two key reports, one
for press and the other for release. There is no way to detect the key press here,
only the key release, but both must have happened.
input_report_key(onkey->input, KEY_SLEEP, 1);
input_report_key(onkey->input, KEY_SLEEP, 0);
For the next case (2)
(2) long-press & release -- POWER
The key is pressed but not released for a period of time (say longer than 2 seconds),
and once this time-limit has been passed without the key being released an interrupt
is triggered. This is different to case (1) in that the key release was not the trigger
of the interrupt, it was a time-out trip-line -- hence the single key report, because at
this point we know that the key was pressed, just not released yet.
input_report_key(onkey->input, KEY_POWER, 1);
The second key report is triggered when the key is released, and this can only
be detected during a polling operation and examination of the register
DA9063_REG_STATUS_A. Once the key release is detected, then the second key report
is sent out
input_report_key(onkey->input, KEY_SLEEP, 0);
This last operation happens inside the da9063_poll_on() function.
It is this polling function that also detects for the key never being released and so is
also able to handle case (3)
(3) long-long press (no release) -- software power cut
[...]
> > +static int da9062_onkey_probe(struct platform_device *pdev)
> > +{
> > + struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
> > + struct da9062_onkey *onkey;
> > + bool kp_tmp = true;
> > + int ret = 0;
> > +
> > + kp_tmp = of_property_read_bool((&pdev->dev)->of_node,
> > + "dlg,disable-key-power");
> > + kp_tmp = !kp_tmp;
>
> Should we just allow specifying the keycode instead of hardcoding
> KEY_POWER/KEY_SLEEP?
>
The reason I have hard-coded the key-power and key-sleep is because this
is the configuration written into the default PMIC one-time-programming
registers. Perhaps I am not quite understanding your question properly here.
[...]
> > +static int da9062_onkey_remove(struct platform_device *pdev)
> > +{
> > + struct da9062_onkey *onkey = platform_get_drvdata(pdev);
> > +
> > + free_irq(onkey->irq, onkey);
> > + cancel_delayed_work_sync(&onkey->work);
> > + input_unregister_device(onkey->input);
>
> No need to unregister explicitly if you allocated input device with
> devm.
>
Yep. I can alter this by removing the function input_unregister_device()
as described.
So, just to finish this off. I would like to reiterate that I am going to drop this patch
from my next submission attempt of the DA9062. However because the DA9063
OnKey component is functionally identical, the comments from above will be
used to modify my DA9063 driver submission.
http://www.spinics.net/lists/linux-input/msg38034.html
http://www.spinics.net/lists/linux-input/msg38137.html
Regards,
Steve
On 18 April 2015 16:53 Guenter Roeck wrote:
Hi Guenter,
Thanks for your comments.
> On 04/17/2015 07:23 AM, S Twiss wrote:
> > From: S Twiss <[email protected]>
> >
> > Add watchdog driver support for DA9062
> >
> > Signed-off-by: Steve Twiss <[email protected]>
> >
> Hi Steve,
>
> Key question here is if the da9062 is really so much different to the da9062
> that you can not use the same driver.
The DA9062 watchdog driver does have some similarities with the DA9063 watchdog
base functionality -- however the watchdog component in the DA9062 chip has more
features yet to be added in software. I do intend to add these other features ...
however, if "not adding them here" is a problem I can drop the DA9062 watchdog
driver from this patch-set until I have time to write in the newer changes.
> I am especially concerned about the added da9062_reset_watchdog_timer(),
> given the delay it introduces.
After giving this some thought, I am going to remove this 300ms delay from the
reset_watchdog_timer() function for my next submission attempt. However
I am adding a 300ms delay into the stop() and update_timeout_register() functions
instead.
The DA9062 watchdog ping (register CONTROL_F) is "windowed" for protection
against spurious writes -- i.e. the ping function cannot be called within a 250ms
time limit or the PMIC will reset. This windowing protection also extends to altering
the timeout scale in the CONTROL_D register -- in which case if the timeout
register is altered and the ping() function is called within the 250ms limit, the
PMIC will reset. The delay is there to stop that from happening.
I realised my previous patch was over-sanitised: by putting the time delay into the
ping() function I was protecting CONTROL_D in stop() and update_timeout_register(),
but I was being too over-protective of the ping() function. Therefore if there was an
"incorrect trigger signal", the watchdog would not be allowed to fail because the
driver would have filtered out the errors.
> > +static int da9062_reset_watchdog_timer(struct da9062 *hw)
> > +{
> > + int ret;
> > +
> > + ret = regmap_update_bits(hw->regmap,
> > + DA9062AA_CONTROL_F,
> > + DA9062AA_WATCHDOG_MASK,
> > + DA9062AA_WATCHDOG_MASK);
> > +
> > + mdelay(300);
>
> Really ? That seems to be excessive, especially given how often
> this function is called (for each ping!).
>
Please see above.
> > +
> > + return ret;
> > +}
> > +
> > +static int da9062_wdt_update_timeout_register(struct da9062 *chip,
> > + unsigned int regval)
> > +{
> > + int ret;
> > +
> > + ret = da9062_reset_watchdog_timer(chip);
> > + if (ret)
> > + dev_err(chip->dev, "Failed to ping the watchdog (err =
> %d)\n",
> > + ret);
> > +
> And no impact, so this doesn't really matter ?
>
I really want to send an error so the user can be notified their ping()
has failed. As it stands, allowing thing to fail is not the best error path.
> > + return regmap_update_bits(chip->regmap,
> > + DA9062AA_CONTROL_D,
> > + DA9062AA_TWDSCALE_MASK,
> > + regval);
> > +}
> > +
[...]
> > +static int da9062_wdt_stop(struct watchdog_device *wdd)
> > +{
> > + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
> > + int ret;
> > +
> > + ret = da9062_reset_watchdog_timer(wdt->hw);
> > + if (ret)
> > + dev_err(wdt->hw->dev, "Failed to ping the watchdog (err =
> %d)\n",
> > + ret);
> > +
> ping or reset ? And no impact, ie you just ignore the error ?
>
Same again -- I will add an error path to this
[...]
> > + wdt->wdtdev.info = &da9062_watchdog_info;
> > + wdt->wdtdev.ops = &da9062_watchdog_ops;
> > + wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
> > + wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
> > + wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
> > + wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> > +
> > + watchdog_set_drvdata(&wdt->wdtdev, wdt);
> > + dev_set_drvdata(&pdev->dev, wdt);
> > +
> > + irq = platform_get_irq_byname(pdev, "WDG_WARN");
> > + if (irq < 0)
> > + dev_err(wdt->hw->dev, "Failed to get IRQ.\n");
>
> But you still request the negative irq.
>
> > +
> > + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> > + da9062_wdt_wdg_warn_irq_handler,
> > + IRQF_TRIGGER_LOW | IRQF_ONESHOT |
> IRQF_SHARED,
> > + "WDG_WARN", wdt);
> > + if (ret) {
> > + dev_err(wdt->hw->dev,
> > + "Failed to request watchdog device IRQ.\n");
>
> Either this is an error, or it isn't. If it is, I would expect the driver
> to abort loading. If not, please don't use dev_err.
>
I will put error paths in here also.
> > + }
> > +
> > + ret = watchdog_register_device(&wdt->wdtdev);
> > + if (ret < 0)
> > + dev_err(wdt->hw->dev,
> > + "watchdog registration incomplete (%d)\n", ret);
>
> incomplete ?
>
Will fix this with an error path
> > + else
> > + dev_info(wdt->hw->dev, "installed DA9062 watchdog\n");
>
> Please rop this message.
>
Will do this.
> > +
> > + da9062_wdt_ping(&wdt->wdtdev);
> > + if (ret < 0)
> > + dev_err(wdt->hw->dev,
> > + "failed to ping the watchdog (%d)\n", ret);
> > +
> That ping is asking for an explanation. Does it imply that the watchdog is
> known to be running
> and can not be stopped ?
>
> Also, the ping function already creates an error message. Please be less noisy
> with
> your messages.
>
> > + return ret;
>
> If the above ping fails, this will return an error without unregistering
> the watchdog device.
>
I will refactor this piece.
[...]
Regards,
Steve
On Wed, May 06, 2015 at 02:54:37PM +0000, Opensource [Steve Twiss] wrote:
> On 18 April 2015 16:53 Guenter Roeck wrote:
>
> Hi Guenter,
>
> Thanks for your comments.
>
> > On 04/17/2015 07:23 AM, S Twiss wrote:
> > > From: S Twiss <[email protected]>
> > >
> > > Add watchdog driver support for DA9062
> > >
> > > Signed-off-by: Steve Twiss <[email protected]>
> > >
> > Hi Steve,
> >
> > Key question here is if the da9062 is really so much different to the da9062
> > that you can not use the same driver.
>
> The DA9062 watchdog driver does have some similarities with the DA9063 watchdog
> base functionality -- however the watchdog component in the DA9062 chip has more
> features yet to be added in software. I do intend to add these other features ...
> however, if "not adding them here" is a problem I can drop the DA9062 watchdog
> driver from this patch-set until I have time to write in the newer changes.
>
> > I am especially concerned about the added da9062_reset_watchdog_timer(),
> > given the delay it introduces.
>
> After giving this some thought, I am going to remove this 300ms delay from the
> reset_watchdog_timer() function for my next submission attempt. However
> I am adding a 300ms delay into the stop() and update_timeout_register() functions
> instead.
>
> The DA9062 watchdog ping (register CONTROL_F) is "windowed" for protection
> against spurious writes -- i.e. the ping function cannot be called within a 250ms
> time limit or the PMIC will reset. This windowing protection also extends to altering
> the timeout scale in the CONTROL_D register -- in which case if the timeout
> register is altered and the ping() function is called within the 250ms limit, the
> PMIC will reset. The delay is there to stop that from happening.
>
> I realised my previous patch was over-sanitised: by putting the time delay into the
> ping() function I was protecting CONTROL_D in stop() and update_timeout_register(),
> but I was being too over-protective of the ping() function. Therefore if there was an
> "incorrect trigger signal", the watchdog would not be allowed to fail because the
> driver would have filtered out the errors.
>
Hi Steve,
>From your description, it sounds like the protection is only necessary if there
was a previous write to the same register(s). If so, it might make sense to
record the time of such writes, and only add the delay if necessary, and only
for the remainder of the time.
Would this be possible ?
Thanks,
Guenter
On 06 May 2015 17:02 Guenter Roeck wrote:
> On Wed, May 06, 2015 at 02:54:37PM +0000, Opensource [Steve Twiss] wrote:
> > On 18 April 2015 16:53 Guenter Roeck wrote:
> >
> > Hi Guenter,
> >
> > Thanks for your comments.
> >
> > > On 04/17/2015 07:23 AM, S Twiss wrote:
> > > > From: S Twiss <[email protected]>
> > > >
> > > > Add watchdog driver support for DA9062
> > > >
> > > > Signed-off-by: Steve Twiss <[email protected]>
> > > >
> > > Hi Steve,
> > >
> > > Key question here is if the da9062 is really so much different to the da9062
> > > that you can not use the same driver.
> >
> > The DA9062 watchdog driver does have some similarities with the DA9063 watchdog
> > base functionality -- however the watchdog component in the DA9062 chip has more
> > features yet to be added in software. I do intend to add these other features ...
> > however, if "not adding them here" is a problem I can drop the DA9062 watchdog
> > driver from this patch-set until I have time to write in the newer changes.
> >
> > > I am especially concerned about the added da9062_reset_watchdog_timer(),
> > > given the delay it introduces.
> >
> > After giving this some thought, I am going to remove this 300ms delay from the
> > reset_watchdog_timer() function for my next submission attempt. However
> > I am adding a 300ms delay into the stop() and update_timeout_register() functions
> > instead.
> >
> > The DA9062 watchdog ping (register CONTROL_F) is "windowed" for protection
> > against spurious writes -- i.e. the ping function cannot be called within a 250ms
> > time limit or the PMIC will reset. This windowing protection also extends to altering
> > the timeout scale in the CONTROL_D register -- in which case if the timeout
> > register is altered and the ping() function is called within the 250ms limit, the
> > PMIC will reset. The delay is there to stop that from happening.
> >
> > I realised my previous patch was over-sanitised: by putting the time delay into the
> > ping() function I was protecting CONTROL_D in stop() and update_timeout_register(),
> > but I was being too over-protective of the ping() function. Therefore if there was an
> > "incorrect trigger signal", the watchdog would not be allowed to fail because the
> > driver would have filtered out the errors.
> >
> Hi Steve,
>
> From your description, it sounds like the protection is only necessary if there
> was a previous write to the same register(s). If so, it might make sense to
> record the time of such writes, and only add the delay if necessary, and only
> for the remainder of the time.
>
> Would this be possible ?
>
Hi Guenter,
I think so -- sounds like it should be possible.
Internally, there are several places where the two registers are written in succession.
Also, I'll have to re-write my tests in several places.
Probably the best solution would be to defer this watchdog driver for now, and I'll
re-submit it at a later date once the other parts of the DA9062 driver are [hopefully :)]
accepted. That way I can concentrate a solid block of time on the re-testing ... this
is the most time consuming.
Is that acceptable to you? -- I don't want to lose your existing comments from your
previous posts, so I will keep track of those changes you have already requested.
Regards,
Steve
On Wed, May 06, 2015 at 04:30:50PM +0000, Opensource [Steve Twiss] wrote:
>
> On 06 May 2015 17:02 Guenter Roeck wrote:
>
> > On Wed, May 06, 2015 at 02:54:37PM +0000, Opensource [Steve Twiss] wrote:
> > > On 18 April 2015 16:53 Guenter Roeck wrote:
> > >
> > > Hi Guenter,
> > >
> > > Thanks for your comments.
> > >
> > > > On 04/17/2015 07:23 AM, S Twiss wrote:
> > > > > From: S Twiss <[email protected]>
> > > > >
> > > > > Add watchdog driver support for DA9062
> > > > >
> > > > > Signed-off-by: Steve Twiss <[email protected]>
> > > > >
> > > > Hi Steve,
> > > >
> > > > Key question here is if the da9062 is really so much different to the da9062
> > > > that you can not use the same driver.
> > >
> > > The DA9062 watchdog driver does have some similarities with the DA9063 watchdog
> > > base functionality -- however the watchdog component in the DA9062 chip has more
> > > features yet to be added in software. I do intend to add these other features ...
> > > however, if "not adding them here" is a problem I can drop the DA9062 watchdog
> > > driver from this patch-set until I have time to write in the newer changes.
> > >
> > > > I am especially concerned about the added da9062_reset_watchdog_timer(),
> > > > given the delay it introduces.
> > >
> > > After giving this some thought, I am going to remove this 300ms delay from the
> > > reset_watchdog_timer() function for my next submission attempt. However
> > > I am adding a 300ms delay into the stop() and update_timeout_register() functions
> > > instead.
> > >
> > > The DA9062 watchdog ping (register CONTROL_F) is "windowed" for protection
> > > against spurious writes -- i.e. the ping function cannot be called within a 250ms
> > > time limit or the PMIC will reset. This windowing protection also extends to altering
> > > the timeout scale in the CONTROL_D register -- in which case if the timeout
> > > register is altered and the ping() function is called within the 250ms limit, the
> > > PMIC will reset. The delay is there to stop that from happening.
> > >
> > > I realised my previous patch was over-sanitised: by putting the time delay into the
> > > ping() function I was protecting CONTROL_D in stop() and update_timeout_register(),
> > > but I was being too over-protective of the ping() function. Therefore if there was an
> > > "incorrect trigger signal", the watchdog would not be allowed to fail because the
> > > driver would have filtered out the errors.
> > >
> > Hi Steve,
> >
> > From your description, it sounds like the protection is only necessary if there
> > was a previous write to the same register(s). If so, it might make sense to
> > record the time of such writes, and only add the delay if necessary, and only
> > for the remainder of the time.
> >
> > Would this be possible ?
> >
>
> Hi Guenter,
>
> I think so -- sounds like it should be possible.
> Internally, there are several places where the two registers are written in succession.
> Also, I'll have to re-write my tests in several places.
>
> Probably the best solution would be to defer this watchdog driver for now, and I'll
> re-submit it at a later date once the other parts of the DA9062 driver are [hopefully :)]
> accepted. That way I can concentrate a solid block of time on the re-testing ... this
> is the most time consuming.
>
Keeping track of the necessary timeout is not mandatory - that was just a
thought. If that would hold you up, just ignore the above and keep going.
> Is that acceptable to you? -- I don't want to lose your existing comments from your
> previous posts, so I will keep track of those changes you have already requested.
>
Your call, really. I am fine either way.
Guenter
On 06 May 2015 21:07 Guenter Roeck wrote:
> > > > The DA9062 watchdog ping (register CONTROL_F) is "windowed" for protection
> > > > against spurious writes -- i.e. the ping function cannot be called within a 250ms
> > > > time limit or the PMIC will reset. This windowing protection also extends to altering
> > > > the timeout scale in the CONTROL_D register -- in which case if the timeout
> > > > register is altered and the ping() function is called within the 250ms limit, the
> > > > PMIC will reset. The delay is there to stop that from happening.
> > > >
> > > > I realised my previous patch was over-sanitised: by putting the time delay into the
> > > > ping() function I was protecting CONTROL_D in stop() and update_timeout_register(),
> > > > but I was being too over-protective of the ping() function. Therefore if there was an
> > > > "incorrect trigger signal", the watchdog would not be allowed to fail because the
> > > > driver would have filtered out the errors.
> > > >
> > > Hi Steve,
> > >
> > > From your description, it sounds like the protection is only necessary if there
> > > was a previous write to the same register(s).
Hi Guenter,
A clarification from me. It is not the CONTROL_D register that needs protecting, but when
the CONTROL_D register is altered the function call also performs a CONTROL_F watchdog
ping. Too many pings close together would cause the PMIC reset.
> > > If so, it might make sense to record the time of such writes, and only add the delay
> > > if necessary, and only for the remainder of the time.
I've tried it several ways, but my previous suggestion of putting the delays in the stop() and
update_timeout_register() functions just cause even more lengthy delays.
So, I've followed your suggestion and used a variable delay inside the ping() function instead:
this seems to cause a lot less delay. A debug message can be used to notify the user if the
watchdog is trying to be kicked too quickly -- that would be more preferable than just shutting
the PMIC down and still provide a notification that something wasn't quite right.
> > > Would this be possible ?
I'll run the tests overnight.
I'm going to do something like this:
diff --git a/linux-next/v4.0/drivers/watchdog/da9062_wdt.c b/linux-next/v4.0/drivers/watchdog/da9062_wdt.c
index ad80261..d596910 100644
--- a/gp_sparse/linux-next/v4.0/drivers/watchdog/da9062_wdt.c
+++ b/gp_sparse/linux-next/v4.0/drivers/watchdog/da9062_wdt.c
@@ -32,12 +33,37 @@ static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
#define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN]
#define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX]
#define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1]
+#define DA9062_RESET_PROTECTION_MS 300
struct da9062_watchdog {
struct da9062 *hw;
struct watchdog_device wdtdev;
+ unsigned long j_time_stamp;
};
+static void da9062_set_window_start(struct da9062_watchdog *wdt)
+{
+ wdt->j_time_stamp = jiffies;
+}
+
+static void da9062_apply_window_protection(struct da9062_watchdog *wdt)
+{
+ unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS);
+ unsigned long timeout = wdt->j_time_stamp + delay;
+ unsigned long now = jiffies;
+ unsigned int diff_ms;
+
+ /* if time-limit has not elapsed then wait for remainder */
+ if (time_before(now, timeout)) {
+ diff_ms = jiffies_to_msecs(timeout-now);
+ dev_dbg(wdt->hw->dev,
+ "Delaying watchdog ping by %u msecs\n", diff_ms);
+ mdelay(diff_ms);
+ }
+
+ return;
+}
+
static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
{
unsigned int i;
@@ -50,26 +76,29 @@ static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
return DA9062_TWDSCALE_MAX;
}
-static int da9062_reset_watchdog_timer(struct da9062 *hw)
+static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt)
{
int ret;
- ret = regmap_update_bits(hw->regmap,
+ da9062_apply_window_protection(wdt);
+
+ ret = regmap_update_bits(wdt->hw->regmap,
DA9062AA_CONTROL_F,
DA9062AA_WATCHDOG_MASK,
DA9062AA_WATCHDOG_MASK);
- mdelay(300);
+ da9062_set_window_start(wdt);
return ret;
}
[...]
@@ -216,6 +245,8 @@ static int da9062_wdt_probe(struct platform_device *pdev)
dev_err(wdt->hw->dev,
"watchdog registration incomplete (%d)\n", ret);
+ da9062_set_window_start(wdt);
+
da9062_wdt_ping(&wdt->wdtdev);
if (ret < 0)
dev_err(wdt->hw->dev,
On Thu, May 07, 2015 at 05:45:13PM +0000, Opensource [Steve Twiss] wrote:
> On 06 May 2015 21:07 Guenter Roeck wrote:
>
> > > > > The DA9062 watchdog ping (register CONTROL_F) is "windowed" for protection
> > > > > against spurious writes -- i.e. the ping function cannot be called within a 250ms
> > > > > time limit or the PMIC will reset. This windowing protection also extends to altering
> > > > > the timeout scale in the CONTROL_D register -- in which case if the timeout
> > > > > register is altered and the ping() function is called within the 250ms limit, the
> > > > > PMIC will reset. The delay is there to stop that from happening.
> > > > >
> > > > > I realised my previous patch was over-sanitised: by putting the time delay into the
> > > > > ping() function I was protecting CONTROL_D in stop() and update_timeout_register(),
> > > > > but I was being too over-protective of the ping() function. Therefore if there was an
> > > > > "incorrect trigger signal", the watchdog would not be allowed to fail because the
> > > > > driver would have filtered out the errors.
> > > > >
> > > > Hi Steve,
> > > >
> > > > From your description, it sounds like the protection is only necessary if there
> > > > was a previous write to the same register(s).
>
> Hi Guenter,
>
> A clarification from me. It is not the CONTROL_D register that needs protecting, but when
> the CONTROL_D register is altered the function call also performs a CONTROL_F watchdog
> ping. Too many pings close together would cause the PMIC reset.
>
> > > > If so, it might make sense to record the time of such writes, and only add the delay
> > > > if necessary, and only for the remainder of the time.
>
> I've tried it several ways, but my previous suggestion of putting the delays in the stop() and
> update_timeout_register() functions just cause even more lengthy delays.
>
> So, I've followed your suggestion and used a variable delay inside the ping() function instead:
> this seems to cause a lot less delay. A debug message can be used to notify the user if the
> watchdog is trying to be kicked too quickly -- that would be more preferable than just shutting
> the PMIC down and still provide a notification that something wasn't quite right.
>
> > > > Would this be possible ?
>
> I'll run the tests overnight.
> I'm going to do something like this:
>
> diff --git a/linux-next/v4.0/drivers/watchdog/da9062_wdt.c b/linux-next/v4.0/drivers/watchdog/da9062_wdt.c
> index ad80261..d596910 100644
> --- a/gp_sparse/linux-next/v4.0/drivers/watchdog/da9062_wdt.c
> +++ b/gp_sparse/linux-next/v4.0/drivers/watchdog/da9062_wdt.c
>
> @@ -32,12 +33,37 @@ static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
> #define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN]
> #define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX]
> #define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1]
> +#define DA9062_RESET_PROTECTION_MS 300
>
> struct da9062_watchdog {
> struct da9062 *hw;
> struct watchdog_device wdtdev;
> + unsigned long j_time_stamp;
> };
>
> +static void da9062_set_window_start(struct da9062_watchdog *wdt)
> +{
> + wdt->j_time_stamp = jiffies;
> +}
> +
> +static void da9062_apply_window_protection(struct da9062_watchdog *wdt)
> +{
> + unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS);
> + unsigned long timeout = wdt->j_time_stamp + delay;
> + unsigned long now = jiffies;
> + unsigned int diff_ms;
> +
> + /* if time-limit has not elapsed then wait for remainder */
> + if (time_before(now, timeout)) {
> + diff_ms = jiffies_to_msecs(timeout-now);
> + dev_dbg(wdt->hw->dev,
> + "Delaying watchdog ping by %u msecs\n", diff_ms);
I would not bother about the dev_dbg, but that is your call.
> + mdelay(diff_ms);
Can you use usleep_range() ?
Othewise looks good. BTW, I had to do something similar in
drivers/hwmon/pmbus/zl6100.c; this is where the idea comes from.
Guenter
On 07 May 2015 18:58 Guenter Roeck wrote:
Hi Guenter,
> > +static void da9062_apply_window_protection(struct da9062_watchdog *wdt)
> > +{
> > + unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS);
> > + unsigned long timeout = wdt->j_time_stamp + delay;
> > + unsigned long now = jiffies;
> > + unsigned int diff_ms;
> > +
> > + /* if time-limit has not elapsed then wait for remainder */
> > + if (time_before(now, timeout)) {
> > + diff_ms = jiffies_to_msecs(timeout-now);
> > + dev_dbg(wdt->hw->dev,
> > + "Delaying watchdog ping by %u msecs\n", diff_ms);
>
> I would not bother about the dev_dbg, but that is your call.
>
.. easily removed ... I only have it in because I am worried about the case when the
watchdog gets kicked too often & quickly. If it is okay, I will leave that in because the
edge-case should be made known somewhere. Perhaps I will make it a better
debug description like "Watchdog kicked too quickly. Delaying %d ms"
> > + mdelay(diff_ms);
>
> Can you use usleep_range() ?
>
I put mdelay() ?? I meant to put msleep(). That's probably what I need to do now.
I did take a look at usleep_range() as I trawled for the best delay call, but usleep_range()
requires a lower and upper bound and I only have a lower bound in my case -- also, the
kernel docs "Documentation/timers/timers-howto.txt" suggest:
SLEEPING FOR ~USECS OR SMALL MSECS ( 10us - 20ms):
* Use usleep_range
SLEEPING FOR LARGER MSECS ( 10ms+ )
* Use msleep [...]
I guess the majority of the time there would be zero delay-time and the code branch
wouldn't be triggered anyway, but when it is triggered the time can be anything up to
300 msecs. So I was intending to use msleep().
> Othewise looks good. BTW, I had to do something similar in
> drivers/hwmon/pmbus/zl6100.c; this is where the idea comes from.
Ah, I see, thanks.
Regards,
Steve
On 18 April 2015 16:53 Guenter Roeck wrote:
Hi Guenter,
There were some missing explanations in my previous e-mails for some of
your comments. Please find them below.
During the da9062_wdt_probe() there were several ignored error paths and
a missing cleanup . I intend to rectify this with the following:
--- a/linux-next/v4.0/drivers/watchdog/da9062_wdt.c
+++ b/linux-next/v4.0/drivers/watchdog/da9062_wdt.c
@@ -232,8 +232,11 @@ static int da9062_wdt_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, wdt);
irq = platform_get_irq_byname(pdev, "WDG_WARN");
- if (irq < 0)
+ if (irq < 0) {
dev_err(wdt->hw->dev, "Failed to get IRQ.\n");
+ ret = irq;
+ goto error;
+ }
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
da9062_wdt_wdg_warn_irq_handler,
@@ -242,20 +245,23 @@ static int da9062_wdt_probe(struct platform_device *pdev)
if (ret) {
dev_err(wdt->hw->dev,
"Failed to request watchdog device IRQ.\n");
+ goto error;
}
ret = watchdog_register_device(&wdt->wdtdev);
- if (ret < 0)
+ if (ret < 0) {
dev_err(wdt->hw->dev,
"watchdog registration incomplete (%d)\n", ret);
+ goto error;
+ }
da9062_set_window_start(wdt);
ret = da9062_wdt_ping(&wdt->wdtdev);
if (ret < 0)
- dev_err(wdt->hw->dev,
- "failed to ping the watchdog (%d)\n", ret);
+ watchdog_unregister_device(&wdt->wdtdev);
+error:
return ret;
}
Also there was an explanation required for the confusing ping() function
inside the driver probe() ...
> > + da9062_wdt_ping(&wdt->wdtdev);
[...]
> That ping is asking for an explanation. Does it imply that the watchdog is
> known to be running and cannot be stopped ?
Pinging the DA9062 watchdog has no effect if it is disabled. But I guessed that
in a real application if the watchdog -was- enabled from start-up then the first
thing the driver needed to do was kick the watchdog.
I could have put protection around it, say by reading the TWDSCALE bit for
whether the watchdog was enabled, and that would have made it more explicit
but in this case it wouldn't matter.
This can be removed if you prefer.
Regards,
Steve
Hi,
On 17/04/2015 at 15:23:33 +0100, S Twiss wrote :
> From: S Twiss <[email protected]>
>
> Add RTC driver support for DA9062
>
> Signed-off-by: Steve Twiss <[email protected]>
>
> ---
>
> This patch applies against linux-next and v4.0
>
>
>
> drivers/rtc/Kconfig | 10 ++
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-da9062.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 378 insertions(+)
> create mode 100644 drivers/rtc/rtc-da9062.c
>
This is an almost exact copy of drivers/rtc/rtc-da9063.c, apart from the
retry mechanism in read_time. Can you simply add support for the da9062
in that driver?
Thanks!
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
On 10 May 2015 10:59 Alexandre Belloni wrote:
> Hi,
>
> On 17/04/2015 at 15:23:33 +0100, S Twiss wrote :
> > From: S Twiss <[email protected]>
> >
> > Add RTC driver support for DA9062
[...]
> This is an almost exact copy of drivers/rtc/rtc-da9063.c, apart from the
> retry mechanism in read_time. Can you simply add support for the da9062
> in that driver?
Hi Alexandre,
Something similar is being discussed for the OnKey component of the DA9062.
https://lkml.org/lkml/2015/4/24/304
It is only the OnKey and RTC components that are similar to the DA9063 chip
and I was hoping to keep the 62 RTC separate in this case ... however it should
definitely be possible to re-use the DA9063 RTC driver if this is your requirement.
If this will block my submission of the DA9062 then I will drop the RTC from my
next patch set and try to re-work the existing 63 RTC driver accordingly.
I think this sort of thing has been done before:
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/rtc/rtc-pm8xxx.c
... at first glance -- it seems to support that sort of thing.
I guess it would be possible to rename the da9063-rtc to something more sensible like
da9xxx-rtc.c if this goes ahead?
Regards,
Steve
Hi,
On 13/05/2015 at 12:31:36 +0000, Opensource [Steve Twiss] wrote :
> Something similar is being discussed for the OnKey component of the DA9062.
> https://lkml.org/lkml/2015/4/24/304
>
> It is only the OnKey and RTC components that are similar to the DA9063 chip
> and I was hoping to keep the 62 RTC separate in this case ... however it should
> definitely be possible to re-use the DA9063 RTC driver if this is your requirement.
> If this will block my submission of the DA9062 then I will drop the RTC from my
> next patch set and try to re-work the existing 63 RTC driver accordingly.
>
> I think this sort of thing has been done before:
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/rtc/rtc-pm8xxx.c
> ... at first glance -- it seems to support that sort of thing.
>
Isn't that exactly the point of using the MFD infrastructure? It allows
to reuse existing drivers even when the IP is part of a different chip.
The RTC inside the da9062 and the da9063 are obviously the same so there
is no point in duplicating the driver.
I'm guessing using da9063-rtc instead of da9062-rtc is just working
fine.
> I guess it would be possible to rename the da9063-rtc to something more sensible like
> da9xxx-rtc.c if this goes ahead?
>
Sure, that can be done but this means that the module name will change.
if you feel that your current users can cope with that, I'm fine with it.
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
On 05/13/2015 05:58 AM, Alexandre Belloni wrote:
> Hi,
>
> On 13/05/2015 at 12:31:36 +0000, Opensource [Steve Twiss] wrote :
>> Something similar is being discussed for the OnKey component of the DA9062.
>> https://lkml.org/lkml/2015/4/24/304
>>
>> It is only the OnKey and RTC components that are similar to the DA9063 chip
>> and I was hoping to keep the 62 RTC separate in this case ... however it should
>> definitely be possible to re-use the DA9063 RTC driver if this is your requirement.
>> If this will block my submission of the DA9062 then I will drop the RTC from my
>> next patch set and try to re-work the existing 63 RTC driver accordingly.
>>
>> I think this sort of thing has been done before:
>> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/rtc/rtc-pm8xxx.c
>> ... at first glance -- it seems to support that sort of thing.
>>
>
> Isn't that exactly the point of using the MFD infrastructure? It allows
> to reuse existing drivers even when the IP is part of a different chip.
> The RTC inside the da9062 and the da9063 are obviously the same so there
> is no point in duplicating the driver.
>
> I'm guessing using da9063-rtc instead of da9062-rtc is just working
> fine.
>
>> I guess it would be possible to rename the da9063-rtc to something more sensible like
>> da9xxx-rtc.c if this goes ahead?
>>
>
> Sure, that can be done but this means that the module name will change.
> if you feel that your current users can cope with that, I'm fine with it.
>
Don't know how this is handled for rtc drivers, but in other subsystems
we just live with the original name. I don't see a need to rename a driver
just because it starts supporting more hardware, and xxx is weird anyway
since it suggests everything from 000 to 999, which is much worse than
just sticking with 9063.
Guenter
On 13/05/2015 at 06:04:47 -0700, Guenter Roeck wrote :
> Don't know how this is handled for rtc drivers, but in other subsystems
> we just live with the original name. I don't see a need to rename a driver
> just because it starts supporting more hardware, and xxx is weird anyway
> since it suggests everything from 000 to 999, which is much worse than
> just sticking with 9063.
>
Yes, in particular if at some point in time a new IP matching those xxx
is completely different from the previous one. I'm not fond of renaming
the driver either but if diasemi thinks that what they want, I'm open to
let it happen. But clearly, I don't want to end up in a situation like
the tlv320aic where you don't know which driver correspond to which
chip:
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/tlv320aic31xx.h
sound/soc/codecs/tlv320aic32x4.c
sound/soc/codecs/tlv320aic32x4.h
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320aic3x.h
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/tlv320dac33.h
Go figure that the tlv320aic3104 can be supported by both tlv320aic3x.c
and tlv320aic32x4.c but not tlv320aic31xx.c...
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
On 05/13/2015 06:37 AM, Alexandre Belloni wrote:
> On 13/05/2015 at 06:04:47 -0700, Guenter Roeck wrote :
>> Don't know how this is handled for rtc drivers, but in other subsystems
>> we just live with the original name. I don't see a need to rename a driver
>> just because it starts supporting more hardware, and xxx is weird anyway
>> since it suggests everything from 000 to 999, which is much worse than
>> just sticking with 9063.
>>
>
> Yes, in particular if at some point in time a new IP matching those xxx
> is completely different from the previous one. I'm not fond of renaming
> the driver either but if diasemi thinks that what they want, I'm open to
> let it happen. But clearly, I don't want to end up in a situation like
> the tlv320aic where you don't know which driver correspond to which
> chip:
>
> sound/soc/codecs/tlv320aic31xx.c
> sound/soc/codecs/tlv320aic31xx.h
> sound/soc/codecs/tlv320aic32x4.c
> sound/soc/codecs/tlv320aic32x4.h
> sound/soc/codecs/tlv320aic3x.c
> sound/soc/codecs/tlv320aic3x.h
> sound/soc/codecs/tlv320dac33.c
> sound/soc/codecs/tlv320dac33.h
>
> Go figure that the tlv320aic3104 can be supported by both tlv320aic3x.c
> and tlv320aic32x4.c but not tlv320aic31xx.c...
>
Unfortunately that is exactly what is going to happen.
Guenter
Hi Guenter & Alexandre,
On 13 May 2015 14:47 Guenter Roeck wrote:
> On 05/13/2015 06:37 AM, Alexandre Belloni wrote:
> > On 13/05/2015 at 06:04:47 -0700, Guenter Roeck wrote :
> >> Don't know how this is handled for rtc drivers, but in other subsystems
> >> we just live with the original name. I don't see a need to rename a driver
> >> just because it starts supporting more hardware, and xxx is weird anyway
> >> since it suggests everything from 000 to 999, which is much worse than
> >> just sticking with 9063.
> >>
> >
> > Yes, in particular if at some point in time a new IP matching those xxx
> > is completely different from the previous one. I'm not fond of renaming
> > the driver either but if diasemi thinks that what they want, I'm open to
> > let it happen. But clearly, I don't want to end up in a situation like
> > the tlv320aic where you don't know which driver correspond to which
> > chip:
> >
> > sound/soc/codecs/tlv320aic31xx.c
> > sound/soc/codecs/tlv320aic31xx.h
> > sound/soc/codecs/tlv320aic32x4.c
> > sound/soc/codecs/tlv320aic32x4.h
> > sound/soc/codecs/tlv320aic3x.c
> > sound/soc/codecs/tlv320aic3x.h
> > sound/soc/codecs/tlv320dac33.c
> > sound/soc/codecs/tlv320dac33.h
> >
> > Go figure that the tlv320aic3104 can be supported by both tlv320aic3x.c
> > and tlv320aic32x4.c but not tlv320aic31xx.c...
> >
>
> Unfortunately that is exactly what is going to happen.
Thanks for this clarification.
We'll follow your advice and keep the name as da9063
Regards,
Steve