This patch set adds initial support for the Dialog DA9150 Integrated Charger &
Fuel-Gauge IC. The device also provides GPIO and GPADC functionality.
In this patch set the following is provided:
- MFD Core support and DT bindings documentation.
- Fix to IIO framework (inkern.c, of_iio_channel_get_by_name()) to correctly
return NULL for failure cases.
- IIO GPADC support and DT bindings documentation.
- Power Supply Charger support and DT bindings documentation.
- Add Dialog vendor prefix for DT.
To keep patch submission from being too large, support for GPIO and Fuel-Gauge
will come after initial support patches are accepted.
This patch set is baselined against the v3.15 kernel version.
Adam Thomson (8):
mfd: Add support for DA9150 combined charger & fuel-gauge device
mfd: da9150: Add DT binding documentation for core
iio: of_iio_channel_get_by_name() returns non-null pointers for error
legs
iio: Add support for DA9150 GPADC
iio: da9150: Add DT binding documentation for GPADC
power: Add support for DA9150 Charger
power: da9150: Add DT binding documentation for charger
DT: Add vendor prefix for Dialog Semiconductor Ltd.
.../devicetree/bindings/iio/da9150-gpadc.txt | 16 +
Documentation/devicetree/bindings/mfd/da9150.txt | 53 +
.../devicetree/bindings/power/da9150-charger.txt | 29 +
.../devicetree/bindings/vendor-prefixes.txt | 1 +
drivers/iio/adc/Kconfig | 9 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/da9150-gpadc.c | 396 +++++++
drivers/iio/inkern.c | 6 +-
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 2 +
drivers/mfd/da9150-core.c | 398 +++++++
drivers/mfd/da9150-i2c.c | 268 +++++
drivers/power/Kconfig | 23 +
drivers/power/Makefile | 1 +
drivers/power/da9150-charger.c | 790 +++++++++++++
include/linux/mfd/da9150/charger.h | 58 +
include/linux/mfd/da9150/core.h | 125 +++
include/linux/mfd/da9150/gpadc.h | 71 ++
include/linux/mfd/da9150/pdata.h | 148 +++
include/linux/mfd/da9150/registers.h | 1160 ++++++++++++++++++++
20 files changed, 3565 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/da9150-gpadc.txt
create mode 100644 Documentation/devicetree/bindings/mfd/da9150.txt
create mode 100644 Documentation/devicetree/bindings/power/da9150-charger.txt
create mode 100644 drivers/iio/adc/da9150-gpadc.c
create mode 100644 drivers/mfd/da9150-core.c
create mode 100644 drivers/mfd/da9150-i2c.c
create mode 100644 drivers/power/da9150-charger.c
create mode 100644 include/linux/mfd/da9150/charger.h
create mode 100644 include/linux/mfd/da9150/core.h
create mode 100644 include/linux/mfd/da9150/gpadc.h
create mode 100644 include/linux/mfd/da9150/pdata.h
create mode 100644 include/linux/mfd/da9150/registers.h
--
1.9.3
Currently in the inkern.c code for IIO framework, the function
of_iio_channel_get_by_name() will return a non-NULL pointer when
it cannot find a channel using of_iio_channel_get() and when it
tries to search for 'io-channel-ranges' property and fails. This
is incorrect behaviour as the function which calls this expects
a NULL pointer for failure. This patch rectifies the issue.
Signed-off-by: Adam Thomson <[email protected]>
---
drivers/iio/inkern.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 0cf5f8e..1e8e94d 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -183,7 +183,7 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
else if (name && index >= 0) {
pr_err("ERROR: could not get IIO channel %s:%s(%i)\n",
np->full_name, name ? name : "", index);
- return chan;
+ return NULL;
}
/*
@@ -193,8 +193,9 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
*/
np = np->parent;
if (np && !of_get_property(np, "io-channel-ranges", NULL))
- break;
+ return NULL;
}
+
return chan;
}
@@ -317,6 +318,7 @@ struct iio_channel *iio_channel_get(struct device *dev,
if (channel != NULL)
return channel;
}
+
return iio_channel_get_sys(name, channel_name);
}
EXPORT_SYMBOL_GPL(iio_channel_get);
--
1.9.3
This patch adds support for DA9150 Charger & Fuel-Gauge IC GPADC.
Signed-off-by: Adam Thomson <[email protected]>
---
drivers/iio/adc/Kconfig | 9 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/da9150-gpadc.c | 396 +++++++++++++++++++++++++++++++++++++++
include/linux/mfd/da9150/gpadc.h | 71 +++++++
4 files changed, 477 insertions(+)
create mode 100644 drivers/iio/adc/da9150-gpadc.c
create mode 100644 include/linux/mfd/da9150/gpadc.h
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 24c28e3..f5e9f72 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -105,6 +105,15 @@ config AT91_ADC
help
Say yes here to build support for Atmel AT91 ADC.
+config DA9150_GPADC
+ tristate "Dialog DA9150 GPADC driver support"
+ depends on MFD_DA9150
+ help
+ Say yes here to build support for Dialog DA9150 GPADC.
+
+ This driver can also be built as a module. If chosen, the module name
+ will be da9150-gpadc.
+
config EXYNOS_ADC
tristate "Exynos ADC driver support"
depends on OF
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index ab346d8..414b22f 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1363) += max1363.o
diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c
new file mode 100644
index 0000000..2107f86
--- /dev/null
+++ b/drivers/iio/adc/da9150-gpadc.c
@@ -0,0 +1,396 @@
+/*
+ * DA9150 GPADC Driver
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/pdata.h>
+#include <linux/mfd/da9150/registers.h>
+#include <linux/mfd/da9150/gpadc.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+
+/*
+ * IRQ Handling
+ */
+
+static irqreturn_t da9150_gpadc_irq(int irq, void *data)
+{
+
+ struct da9150_gpadc *gpadc = data;
+
+ complete(&gpadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * GPADC access
+ */
+
+static inline int da9150_gpadc_gpio_2v_voltage_now(int raw_val)
+{
+ /* Convert to uV */
+ return (((3 * ((raw_val * 1000) + 500)) / 2048) * 1000);
+}
+
+static inline int da9150_gpadc_gpio_5v_voltage_now(int raw_val)
+{
+ /* Convert to uV */
+ return (((6 * ((raw_val * 1000) + 500)) / 1024) * 1000);
+}
+
+static inline int da9150_gpadc_ibus_current_avg(int raw_val)
+{
+ /* Convert to uA */
+ return (((4 * ((raw_val * 1000) + 500)) / 2048) * 1000);
+}
+
+static inline int da9150_gpadc_vbus_6v_voltage_now(int raw_val)
+{
+ /* Convert to uV */
+ return (((3 * ((raw_val * 1000) + 500)) / 512) * 1000);
+}
+
+static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val)
+{
+ /* Convert to uV */
+ return (((21 * ((raw_val * 1000) + 500)) / 1024) * 1000);
+}
+
+static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val)
+{
+ /* Convert to uV */
+ return (((3 * ((raw_val * 1000) + 500)) / 512) * 1000);
+}
+
+static inline int da9150_gpadc_vsys_1_5v_voltage_now(int raw_val)
+{
+ /* Convert to uV */
+ return (((3 * ((raw_val * 1000) + 500)) / 2048) * 1000);
+}
+
+static inline int da9150_gpadc_tjunc_temp(int raw_val)
+{
+ /* Convert to 0.1 degrees C */
+ return (((879 - (1023 - raw_val)) * 10000) / 4420);
+}
+
+static inline int da9150_gpadc_vbat_voltage_now(int raw_val)
+{
+ /* Convert to uV */
+ return ((2932 * raw_val) + 1500000);
+}
+
+int da9150_gpadc_read_process(int channel, int raw_val)
+{
+ int ret;
+
+ switch (channel) {
+ case DA9150_GPADC_CHAN_GPIOA_2V:
+ case DA9150_GPADC_CHAN_GPIOA_2V_:
+ case DA9150_GPADC_CHAN_GPIOB_2V:
+ case DA9150_GPADC_CHAN_GPIOB_2V_:
+ case DA9150_GPADC_CHAN_GPIOC_2V:
+ case DA9150_GPADC_CHAN_GPIOC_2V_:
+ case DA9150_GPADC_CHAN_GPIOD_2V:
+ case DA9150_GPADC_CHAN_GPIOD_2V_:
+ ret = da9150_gpadc_gpio_2v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_IBUS_SENSE:
+ case DA9150_GPADC_CHAN_IBUS_SENSE_:
+ ret = da9150_gpadc_ibus_current_avg(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VBUS_DIV:
+ ret = da9150_gpadc_vbus_6v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VBUS_DIV_:
+ ret = da9150_gpadc_vbus_21v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VSYS:
+ ret = da9150_gpadc_vsys_6v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VSYS_:
+ ret = da9150_gpadc_vsys_1_5v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_GPIOA_5V:
+ case DA9150_GPADC_CHAN_GPIOA_5V_:
+ case DA9150_GPADC_CHAN_GPIOB_5V:
+ case DA9150_GPADC_CHAN_GPIOB_5V_:
+ case DA9150_GPADC_CHAN_GPIOC_5V:
+ case DA9150_GPADC_CHAN_GPIOC_5V_:
+ case DA9150_GPADC_CHAN_GPIOD_5V:
+ case DA9150_GPADC_CHAN_GPIOD_5V_:
+ ret = da9150_gpadc_gpio_5v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_TJUNC_CORE:
+ case DA9150_GPADC_CHAN_TJUNC_CORE_:
+ case DA9150_GPADC_CHAN_TJUNC_OVP:
+ case DA9150_GPADC_CHAN_TJUNC_OVP_:
+ ret = da9150_gpadc_tjunc_temp(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VBAT:
+ ret = da9150_gpadc_vbat_voltage_now(raw_val);
+ break;
+ default:
+ /* No processing for other channels so return raw value */
+ ret = raw_val;
+ break;
+ }
+
+ return ret;
+}
+
+int da9150_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct da9150_gpadc *gpadc = iio_priv(indio_dev);
+ u8 reg = 0;
+ u8 result_regs[2];
+ u16 result;
+
+ if ((mask != IIO_CHAN_INFO_RAW) && (mask != IIO_CHAN_INFO_PROCESSED))
+ return -EINVAL;
+
+ if ((chan->channel < DA9150_GPADC_CHAN_GPIOA_2V) ||
+ (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP_))
+ return -EINVAL;
+
+ mutex_lock(&gpadc->lock);
+
+ /* Set channel & enable measurement */
+ reg |= DA9150_GPADC_EN_MASK;
+ reg |= chan->channel << DA9150_GPADC_MUX_SHIFT;
+ da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN, reg);
+
+ /* Consume left-over completion from a previous timeout */
+ try_wait_for_completion(&gpadc->complete);
+
+ /* Check for actual completion */
+ wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5));
+
+ /* Read result and status from device */
+ da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs);
+
+ /* Check to make sure device really has completed reading */
+ if (result_regs[1] & DA9150_GPADC_RUN_MASK) {
+ mutex_unlock(&gpadc->lock);
+ dev_err(gpadc->dev, "Timeout on channel %d of GP-ADC\n",
+ chan->channel);
+ return -ETIMEDOUT;
+ }
+
+ mutex_unlock(&gpadc->lock);
+
+ /* LSBs - 2 bits */
+ result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >>
+ DA9150_GPADC_RES_L_SHIFT;
+ /* MSBs - 8 bits */
+ result |= result_regs[0] << 2;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ *val = da9150_gpadc_read_process(chan->channel, result);
+ break;
+ case IIO_CHAN_INFO_RAW:
+ *val = result;
+ break;
+ }
+
+ return IIO_VAL_INT;
+}
+
+
+static const struct iio_info da9150_gpadc_info = {
+ .read_raw = &da9150_gpadc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define GPADC_CHANNEL(_id, _type, chan_info, _ext_name) { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = DA9150_GPADC_CHAN_##_id, \
+ .info_mask_separate = chan_info, \
+ .extend_name = _ext_name, \
+ .datasheet_name = #_id, \
+}
+
+#define GPADC_CHANNEL_RAW(_id, _type, _ext_name) \
+ GPADC_CHANNEL(_id, _type, BIT(IIO_CHAN_INFO_RAW), _ext_name)
+
+#define GPADC_CHANNEL_RAW_PROCESSED(_id, _type, _ext_name) \
+ GPADC_CHANNEL(_id, _type, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
+ _ext_name)
+
+/* Supported channels */
+static const struct iio_chan_spec da9150_gpadc_channels[] = {
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOA_2V, IIO_VOLTAGE, "GPIOA_2V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOA_2V_, IIO_VOLTAGE, "GPIOA_2V_"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOB_2V, IIO_VOLTAGE, "GPIOB_2V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOB_2V_, IIO_VOLTAGE, "GPIOB_2V_"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOC_2V, IIO_VOLTAGE, "GPIOC_2V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOC_2V_, IIO_VOLTAGE, "GPIOC_2V_"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOD_2V, IIO_VOLTAGE, "GPIOD_2V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOD_2V_, IIO_VOLTAGE, "GPIOD_2V_"),
+ GPADC_CHANNEL_RAW_PROCESSED(IBUS_SENSE, IIO_CURRENT, "IBUS"),
+ GPADC_CHANNEL_RAW_PROCESSED(IBUS_SENSE_, IIO_CURRENT, "IBUS_"),
+ GPADC_CHANNEL_RAW_PROCESSED(VBUS_DIV, IIO_VOLTAGE, "VBUS_6V"),
+ GPADC_CHANNEL_RAW_PROCESSED(VBUS_DIV_, IIO_VOLTAGE, "VBUS_21V"),
+ GPADC_CHANNEL_RAW(ID, IIO_VOLTAGE, "ID"),
+ GPADC_CHANNEL_RAW(ID_, IIO_VOLTAGE, "ID_"),
+ GPADC_CHANNEL_RAW_PROCESSED(VSYS, IIO_VOLTAGE, "VSYS_6V"),
+ GPADC_CHANNEL_RAW_PROCESSED(VSYS_, IIO_VOLTAGE, "VSYS_1_5V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOA_5V, IIO_VOLTAGE, "GPIOA_5V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOA_5V_, IIO_VOLTAGE, "GPIOA_5V_"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOB_5V, IIO_VOLTAGE, "GPIOB_5V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOB_5V_, IIO_VOLTAGE, "GPIOB_5V_"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOC_5V, IIO_VOLTAGE, "GPIOC_5V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOC_5V_, IIO_VOLTAGE, "GPIOC_5V_"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOD_5V, IIO_VOLTAGE, "GPIOD_5V"),
+ GPADC_CHANNEL_RAW_PROCESSED(GPIOD_5V_, IIO_VOLTAGE, "GPIOD_5V_"),
+ GPADC_CHANNEL_RAW_PROCESSED(VBAT, IIO_VOLTAGE, "VBAT"),
+ GPADC_CHANNEL_RAW_PROCESSED(VBAT_, IIO_VOLTAGE, "VBAT_"),
+ GPADC_CHANNEL_RAW(TBAT, IIO_VOLTAGE, "TBAT"),
+ GPADC_CHANNEL_RAW(TBAT_, IIO_VOLTAGE, "TBAT_"),
+ GPADC_CHANNEL_RAW_PROCESSED(TJUNC_CORE, IIO_TEMP, "TJUNC_CORE"),
+ GPADC_CHANNEL_RAW_PROCESSED(TJUNC_CORE_, IIO_TEMP, "TJUNC_CORE_"),
+ GPADC_CHANNEL_RAW_PROCESSED(TJUNC_OVP, IIO_TEMP, "TJUNC_OVP"),
+ GPADC_CHANNEL_RAW_PROCESSED(TJUNC_OVP_, IIO_TEMP, "TJUNC_OVP_"),
+};
+
+/* Default maps used by da9150-charger */
+static struct iio_map da9150_gpadc_default_maps[] = {
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_IBUS",
+ .adc_channel_label = "IBUS_SENSE",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_VBUS",
+ .adc_channel_label = "VBUS_DIV_",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_TJUNC",
+ .adc_channel_label = "TJUNC_CORE",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_VBAT",
+ .adc_channel_label = "VBAT",
+ },
+ {},
+};
+
+
+/*
+ * Driver top level functions
+ */
+
+static int da9150_gpadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da9150 *da9150 = dev_get_drvdata(dev->parent);
+ struct da9150_gpadc *gpadc;
+ struct iio_dev *indio_dev;
+
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev,
+ sizeof(struct da9150_gpadc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+ gpadc = iio_priv(indio_dev);
+
+ platform_set_drvdata(pdev, indio_dev);
+ gpadc->da9150 = da9150;
+ gpadc->dev = dev;
+
+ ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO maps: %d\n", ret);
+ goto iio_map_fail;
+ }
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &da9150_gpadc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = da9150_gpadc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO device: %d\n", ret);
+ goto iio_dev_fail;
+ }
+
+ mutex_init(&gpadc->lock);
+ init_completion(&gpadc->complete);
+
+ /* Register IRQ */
+ ret = da9150_register_irq(pdev, gpadc, da9150_gpadc_irq, "GPADC");
+ if (ret < 0)
+ goto irq_fail;
+
+ da9150->gpadc_ready = true;
+ return ret;
+
+irq_fail:
+ iio_device_unregister(indio_dev);
+
+iio_dev_fail:
+ iio_map_array_unregister(indio_dev);
+
+iio_map_fail:
+ return ret;
+}
+
+static int da9150_gpadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_map_array_unregister(indio_dev);
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver da9150_gpadc_driver = {
+ .driver = {
+ .name = "da9150-gpadc",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9150_gpadc_probe,
+ .remove = da9150_gpadc_remove,
+};
+
+module_platform_driver(da9150_gpadc_driver);
+
+MODULE_DESCRIPTION("GPADC Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <[email protected]");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/da9150/gpadc.h b/include/linux/mfd/da9150/gpadc.h
new file mode 100644
index 0000000..3e46164
--- /dev/null
+++ b/include/linux/mfd/da9150/gpadc.h
@@ -0,0 +1,71 @@
+/*
+ * DA9150 GPADC Driver - GPADC Data
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DA9150_GPADC_H
+#define _DA9150_GPADC_H
+
+#include <linux/device.h>
+#include <linux/iio/machine.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+
+#include <linux/mfd/da9150/core.h>
+
+
+/* Channels */
+enum da9150_gpadc_channel {
+ DA9150_GPADC_CHAN_GPIOA_2V = 0,
+ DA9150_GPADC_CHAN_GPIOA_2V_,
+ DA9150_GPADC_CHAN_GPIOB_2V,
+ DA9150_GPADC_CHAN_GPIOB_2V_,
+ DA9150_GPADC_CHAN_GPIOC_2V,
+ DA9150_GPADC_CHAN_GPIOC_2V_,
+ DA9150_GPADC_CHAN_GPIOD_2V,
+ DA9150_GPADC_CHAN_GPIOD_2V_,
+ DA9150_GPADC_CHAN_IBUS_SENSE,
+ DA9150_GPADC_CHAN_IBUS_SENSE_,
+ DA9150_GPADC_CHAN_VBUS_DIV,
+ DA9150_GPADC_CHAN_VBUS_DIV_,
+ DA9150_GPADC_CHAN_ID,
+ DA9150_GPADC_CHAN_ID_,
+ DA9150_GPADC_CHAN_VSYS,
+ DA9150_GPADC_CHAN_VSYS_,
+ DA9150_GPADC_CHAN_GPIOA_5V,
+ DA9150_GPADC_CHAN_GPIOA_5V_,
+ DA9150_GPADC_CHAN_GPIOB_5V,
+ DA9150_GPADC_CHAN_GPIOB_5V_,
+ DA9150_GPADC_CHAN_GPIOC_5V,
+ DA9150_GPADC_CHAN_GPIOC_5V_,
+ DA9150_GPADC_CHAN_GPIOD_5V,
+ DA9150_GPADC_CHAN_GPIOD_5V_,
+ DA9150_GPADC_CHAN_VBAT,
+ DA9150_GPADC_CHAN_VBAT_,
+ DA9150_GPADC_CHAN_TBAT,
+ DA9150_GPADC_CHAN_TBAT_,
+ DA9150_GPADC_CHAN_TJUNC_CORE,
+ DA9150_GPADC_CHAN_TJUNC_CORE_,
+ DA9150_GPADC_CHAN_TJUNC_OVP,
+ DA9150_GPADC_CHAN_TJUNC_OVP_,
+};
+
+
+/* Private data */
+struct da9150_gpadc {
+ struct da9150 *da9150;
+ struct device *dev;
+
+ struct mutex lock;
+ struct completion complete;
+};
+
+#endif /* _DA9150_GPADC_H */
--
1.9.3
Signed-off-by: Adam Thomson <[email protected]>
---
Documentation/devicetree/bindings/mfd/da9150.txt | 53 ++++++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/da9150.txt
diff --git a/Documentation/devicetree/bindings/mfd/da9150.txt b/Documentation/devicetree/bindings/mfd/da9150.txt
new file mode 100644
index 0000000..5c9adc3
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/da9150.txt
@@ -0,0 +1,53 @@
+Dialog Semiconductor DA9150 Combined Charger/Fuel-Gauge MFD bindings
+
+DA9150 consists of a group of sub-devices (I2C Only):
+
+Device Description
+------ -----------
+da9150-gpio : GPIOs
+da9150-gpadc : IIO - GPADC
+da9150-charger : Power Supply (Charger)
+da9150-fg : Power Supply (Fuel-Gauge)
+
+======
+
+Required properties:
+- compatible : Should be "dlg,da9150"
+- reg: Specifies the I2C slave address
+- interrupt-parent: Specifies the phandle of the interrupt controller to which
+ the IRQs from da9150 are delivered to.
+- interrupts: IRQ line info for da9150 chip.
+- interrupt-controller: da9150 has internal IRQs (own IRQ domain).
+
+Sub-devices:
+- da9150-gpio: See Documentation/devicetree/bindings/gpio/gpio-da9150.txt
+- da9150-gpadc: See Documentation/devicetree/bindings/iio/da9150-gpadc.txt
+- da9150-charger: See Documentation/devicetree/bindings/power/da9150-charger.txt
+- da9150-fg: See Documentation/devicetree/bindings/power/da9150-fg.txt
+
+
+Example:
+
+ charger_fg: da9150@58 {
+ compatible = "dlg,da9150";
+ reg = <0x58>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+
+ da9150-gpio {
+ ...
+ };
+
+ gpadc: da9150-gpadc {
+ ...
+ };
+
+ da9150-charger {
+ ...
+ };
+
+ da9150-fg {
+ ...
+ };
+ };
--
1.9.3
DA9150 is a combined Charger and Fuel-Gauge IC, with additional
GPIO and GPADC functionality.
This patch adds core support for the device.
Signed-off-by: Adam Thomson <[email protected]>
---
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 2 +
drivers/mfd/da9150-core.c | 398 ++++++++++++
drivers/mfd/da9150-i2c.c | 268 ++++++++
include/linux/mfd/da9150/core.h | 125 ++++
include/linux/mfd/da9150/pdata.h | 148 +++++
include/linux/mfd/da9150/registers.h | 1160 ++++++++++++++++++++++++++++++++++
7 files changed, 2113 insertions(+)
create mode 100644 drivers/mfd/da9150-core.c
create mode 100644 drivers/mfd/da9150-i2c.c
create mode 100644 include/linux/mfd/da9150/core.h
create mode 100644 include/linux/mfd/da9150/pdata.h
create mode 100644 include/linux/mfd/da9150/registers.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3383412..4af7cc4 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -171,6 +171,18 @@ config MFD_DA9063
Additional drivers must be enabled in order to use the functionality
of the device.
+config MFD_DA9150
+ bool "Dialog Semiconductor DA9150 Charger Fuel-Gauge chip"
+ depends on I2C=y
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ This adds support for the DA9150 integrated charger and fuel-gauge
+ chip. This driver provides common support for accessing the device.
+ Additional drivers must be enabled in order to use the specific
+ features of the device.
+
config MFD_MC13XXX
tristate
depends on (SPI_MASTER || I2C)
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 2851275..0344494 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -112,6 +112,8 @@ obj-$(CONFIG_MFD_DA9055) += da9055.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 da9150-i2c.o
+
obj-$(CONFIG_MFD_MAX14577) += max14577.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c
new file mode 100644
index 0000000..24c62ba
--- /dev/null
+++ b/drivers/mfd/da9150-core.c
@@ -0,0 +1,398 @@
+/*
+ * DA9150 Core MFD Driver
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/registers.h>
+#include <linux/mfd/da9150/pdata.h>
+
+
+/*
+ * Register/Device Access
+ */
+
+int da9150_read_qif(struct da9150 *da9150, u8 addr, int count, u8 *buf)
+{
+ int ret;
+
+ ret = da9150->read_dev(da9150->core_qif, addr, count, buf);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to read from QIF 0x%x: %d\n",
+ addr, ret);
+
+ return ret;
+}
+
+int da9150_write_qif(struct da9150 *da9150, u8 addr, int count, const u8 *buf)
+{
+ int ret;
+
+ ret = da9150->write_dev(da9150->core_qif, addr, count, buf);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to write to QIF 0x%x: %d\n",
+ addr, ret);
+
+ return ret;
+}
+
+u8 da9150_reg_read(struct da9150 *da9150, u16 reg)
+{
+ int val, ret;
+
+ ret = regmap_read(da9150->regmap, reg, &val);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to read from reg 0x%x: %d\n",
+ reg, ret);
+
+ return (u8) val;
+}
+
+int da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val)
+{
+ int ret;
+
+ ret = regmap_write(da9150->regmap, reg, val);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to write to reg 0x%x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+int da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val)
+{
+ int ret;
+
+ ret = regmap_update_bits(da9150->regmap, reg, mask, val);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to set bits in reg 0x%x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+int da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf)
+{
+ int ret;
+
+ ret = regmap_bulk_read(da9150->regmap, reg, buf, count);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to bulk read from reg 0x%x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+int da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf)
+{
+ int ret;
+
+ ret = regmap_raw_write(da9150->regmap, reg, buf, count);
+ if (ret < 0)
+ dev_err(da9150->dev, "Failed to bulk write to reg 0x%x %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+
+/*
+ * IRQ Handling
+ */
+
+static struct regmap_irq da9150_irqs[] = {
+ [DA9150_IRQ_VBUS] = {
+ .reg_offset = 0,
+ .mask = DA9150_E_VBUS_MASK,
+ },
+ [DA9150_IRQ_CHG] = {
+ .reg_offset = 0,
+ .mask = DA9150_E_CHG_MASK,
+ },
+ [DA9150_IRQ_TCLASS] = {
+ .reg_offset = 0,
+ .mask = DA9150_E_TCLASS_MASK,
+ },
+ [DA9150_IRQ_TJUNC] = {
+ .reg_offset = 0,
+ .mask = DA9150_E_TJUNC_MASK,
+ },
+ [DA9150_IRQ_VFAULT] = {
+ .reg_offset = 0,
+ .mask = DA9150_E_VFAULT_MASK,
+ },
+ [DA9150_IRQ_CONF] = {
+ .reg_offset = 1,
+ .mask = DA9150_E_CONF_MASK,
+ },
+ [DA9150_IRQ_DAT] = {
+ .reg_offset = 1,
+ .mask = DA9150_E_DAT_MASK,
+ },
+ [DA9150_IRQ_DTYPE] = {
+ .reg_offset = 1,
+ .mask = DA9150_E_DTYPE_MASK,
+ },
+ [DA9150_IRQ_ID] = {
+ .reg_offset = 1,
+ .mask = DA9150_E_ID_MASK,
+ },
+ [DA9150_IRQ_ADP] = {
+ .reg_offset = 1,
+ .mask = DA9150_E_ADP_MASK,
+ },
+ [DA9150_IRQ_SESS_END] = {
+ .reg_offset = 1,
+ .mask = DA9150_E_SESS_END_MASK,
+ },
+ [DA9150_IRQ_SESS_VLD] = {
+ .reg_offset = 1,
+ .mask = DA9150_E_SESS_VLD_MASK,
+ },
+ [DA9150_IRQ_FG] = {
+ .reg_offset = 2,
+ .mask = DA9150_E_FG_MASK,
+ },
+ [DA9150_IRQ_GP] = {
+ .reg_offset = 2,
+ .mask = DA9150_E_GP_MASK,
+ },
+ [DA9150_IRQ_TBAT] = {
+ .reg_offset = 2,
+ .mask = DA9150_E_TBAT_MASK,
+ },
+ [DA9150_IRQ_GPIOA] = {
+ .reg_offset = 2,
+ .mask = DA9150_E_GPIOA_MASK,
+ },
+ [DA9150_IRQ_GPIOB] = {
+ .reg_offset = 2,
+ .mask = DA9150_E_GPIOB_MASK,
+ },
+ [DA9150_IRQ_GPIOC] = {
+ .reg_offset = 2,
+ .mask = DA9150_E_GPIOC_MASK,
+ },
+ [DA9150_IRQ_GPIOD] = {
+ .reg_offset = 2,
+ .mask = DA9150_E_GPIOD_MASK,
+ },
+ [DA9150_IRQ_GPADC] = {
+ .reg_offset = 2,
+ .mask = DA9150_E_GPADC_MASK,
+ },
+ [DA9150_IRQ_WKUP] = {
+ .reg_offset = 3,
+ .mask = DA9150_E_WKUP_MASK,
+ },
+};
+
+static struct regmap_irq_chip da9150_regmap_irq_chip = {
+ .name = "da9150_irq",
+ .status_base = DA9150_EVENT_E,
+ .mask_base = DA9150_IRQ_MASK_E,
+ .ack_base = DA9150_EVENT_E,
+ .num_regs = DA9150_NUM_IRQ_REGS,
+ .irqs = da9150_irqs,
+ .num_irqs = ARRAY_SIZE(da9150_irqs),
+};
+
+/* Helper function for sub-devices to request IRQs */
+int da9150_register_irq(struct platform_device *pdev, void *dev_id,
+ irq_handler_t handler, const char *name)
+{
+ int irq, ret;
+
+ irq = platform_get_irq_byname(pdev, name);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, handler,
+ IRQF_ONESHOT, name, dev_id);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret);
+
+ return ret;
+}
+
+
+/*
+ * MFDs
+ */
+
+static struct resource da9150_gpio_resources[] = {
+ {
+ .name = "GPIOA",
+ .start = DA9150_IRQ_GPIOA,
+ .end = DA9150_IRQ_GPIOA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource da9150_gpadc_resources[] = {
+ {
+ .name = "GPADC",
+ .start = DA9150_IRQ_GPADC,
+ .end = DA9150_IRQ_GPADC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource da9150_charger_resources[] = {
+ {
+ .name = "CHG_STATUS",
+ .start = DA9150_IRQ_CHG,
+ .end = DA9150_IRQ_CHG,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CHG_TJUNC",
+ .start = DA9150_IRQ_TJUNC,
+ .end = DA9150_IRQ_TJUNC,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CHG_VFAULT",
+ .start = DA9150_IRQ_VFAULT,
+ .end = DA9150_IRQ_VFAULT,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "CHG_VBUS",
+ .start = DA9150_IRQ_VBUS,
+ .end = DA9150_IRQ_VBUS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource da9150_fg_resources[] = {
+ {
+ .name = "FG",
+ .start = DA9150_IRQ_FG,
+ .end = DA9150_IRQ_FG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell da9150_devs[] = {
+ {
+ .name = "da9150-gpio",
+ .of_compatible = "dlg,da9150-gpio",
+ .resources = da9150_gpio_resources,
+ .num_resources = ARRAY_SIZE(da9150_gpio_resources),
+ },
+ {
+ .name = "da9150-gpadc",
+ .of_compatible = "dlg,da9150-gpadc",
+ .resources = da9150_gpadc_resources,
+ .num_resources = ARRAY_SIZE(da9150_gpadc_resources),
+ },
+ {
+ .name = "da9150-charger",
+ .of_compatible = "dlg,da9150-charger",
+ .resources = da9150_charger_resources,
+ .num_resources = ARRAY_SIZE(da9150_charger_resources),
+ },
+ {
+ .name = "da9150-fuelgauge",
+ .of_compatible = "dlg,da9150-fg",
+ .resources = da9150_fg_resources,
+ .num_resources = ARRAY_SIZE(da9150_fg_resources),
+ },
+};
+
+
+/*
+ * Device Init/Exit
+ */
+
+int da9150_device_init(struct da9150 *da9150)
+{
+ struct da9150_pdata *pdata = da9150->dev->platform_data;
+ int ret;
+
+ /* Handle platform data */
+ if (pdata) {
+ da9150->irq_base = pdata->irq_base;
+
+ da9150_devs[0].platform_data = pdata->gpio_pdata;
+ da9150_devs[0].pdata_size = sizeof(struct da9150_gpio_pdata);
+ da9150_devs[2].platform_data = pdata->charger_pdata;
+ da9150_devs[2].pdata_size = sizeof(struct da9150_charger_pdata);
+ da9150_devs[3].platform_data = pdata->fg_pdata;
+ da9150_devs[3].pdata_size = sizeof(struct da9150_fg_pdata);
+ } else {
+ da9150->irq_base = -1;
+ }
+
+ /* Init IRQs */
+ ret = regmap_add_irq_chip(da9150->regmap, da9150->irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ da9150->irq_base, &da9150_regmap_irq_chip,
+ &da9150->regmap_irq_data);
+ if (ret < 0)
+ goto err_irq;
+
+ da9150->irq_base = regmap_irq_chip_get_base(da9150->regmap_irq_data);
+
+ /* Make the IRQ line a wake source */
+ enable_irq_wake(da9150->irq);
+
+ /* Add MFD Devices */
+ ret = mfd_add_devices(da9150->dev, -1, da9150_devs,
+ ARRAY_SIZE(da9150_devs), NULL,
+ da9150->irq_base, NULL);
+ if (ret < 0) {
+ dev_err(da9150->dev, "Failed to add child devices: %d\n", ret);
+ goto err_mfd;
+ }
+
+ return 0;
+
+err_mfd:
+ regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
+err_irq:
+ return ret;
+}
+
+void da9150_device_exit(struct da9150 *da9150)
+{
+ regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
+ mfd_remove_devices(da9150->dev);
+ return;
+}
+
+void da9150_device_shutdown(struct da9150 *da9150)
+{
+ if (da9150->can_shutdown) {
+ /* Make sure we have a wakup source for the device */
+ da9150_set_bits(da9150, DA9150_CONFIG_D,
+ DA9150_WKUP_PM_EN_MASK,
+ DA9150_WKUP_PM_EN_MASK);
+
+ /* Set device to DISABLED mode */
+ da9150_set_bits(da9150, DA9150_CONTROL_C,
+ DA9150_DISABLE_MASK, DA9150_DISABLE_MASK);
+ }
+}
+
+MODULE_DESCRIPTION("MFD Core Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <[email protected]");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/da9150-i2c.c b/drivers/mfd/da9150-i2c.c
new file mode 100644
index 0000000..725aaef
--- /dev/null
+++ b/drivers/mfd/da9150-i2c.c
@@ -0,0 +1,268 @@
+/*
+ * DA9150 I2C Driver
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/registers.h>
+
+
+/*
+ * Regmap Config
+ */
+
+static bool da9150_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DA9150_PAGE_CON:
+ case DA9150_STATUS_A:
+ case DA9150_STATUS_B:
+ case DA9150_STATUS_C:
+ case DA9150_STATUS_D:
+ case DA9150_STATUS_E:
+ case DA9150_STATUS_F:
+ case DA9150_STATUS_G:
+ case DA9150_STATUS_H:
+ case DA9150_STATUS_I:
+ case DA9150_STATUS_J:
+ case DA9150_STATUS_K:
+ case DA9150_STATUS_L:
+ case DA9150_STATUS_N:
+ case DA9150_FAULT_LOG_A:
+ case DA9150_FAULT_LOG_B:
+ case DA9150_EVENT_E:
+ case DA9150_EVENT_F:
+ case DA9150_EVENT_G:
+ case DA9150_EVENT_H:
+ case DA9150_CONTROL_B:
+ case DA9150_CONTROL_C:
+ case DA9150_GPADC_MAN:
+ case DA9150_GPADC_RES_A:
+ case DA9150_GPADC_RES_B:
+ case DA9150_ADETVB_CFG_C:
+ case DA9150_ADETD_STAT:
+ case DA9150_ADET_CMPSTAT:
+ case DA9150_ADET_CTRL_A:
+ case DA9150_PPR_TCTR_B:
+ case DA9150_COREBTLD_STAT_A:
+ case DA9150_CORE_DATA_A:
+ case DA9150_CORE_DATA_B:
+ case DA9150_CORE_DATA_C:
+ case DA9150_CORE_DATA_D:
+ case DA9150_CORE2WIRE_STAT_A:
+ case DA9150_FW_CTRL_C:
+ case DA9150_FG_CTRL_B:
+ case DA9150_FW_CTRL_B:
+ case DA9150_GPADC_CMAN:
+ case DA9150_GPADC_CRES_A:
+ case DA9150_GPADC_CRES_B:
+ case DA9150_CC_ICHG_RES_A:
+ case DA9150_CC_ICHG_RES_B:
+ case DA9150_CC_IAVG_RES_A:
+ case DA9150_CC_IAVG_RES_B:
+ case DA9150_TAUX_CTRL_A:
+ case DA9150_TAUX_VALUE_H:
+ case DA9150_TAUX_VALUE_L:
+ case DA9150_TBAT_RES_A:
+ case DA9150_TBAT_RES_B:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_range_cfg da9150_range_cfg[] = {
+ {
+ .range_min = DA9150_PAGE_CON,
+ .range_max = DA9150_TBAT_RES_B,
+ .selector_reg = DA9150_PAGE_CON,
+ .selector_mask = DA9150_I2C_PAGE_MASK,
+ .selector_shift = DA9150_I2C_PAGE_SHIFT,
+ .window_start = 0,
+ .window_len = 256,
+ },
+};
+
+static struct regmap_config da9150_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .ranges = da9150_range_cfg,
+ .num_ranges = ARRAY_SIZE(da9150_range_cfg),
+ .max_register = DA9150_TBAT_RES_B,
+
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = da9150_volatile_reg,
+};
+
+
+/*
+ * Device access for QIF interface
+ */
+
+static int da9150_i2c_read_device(struct i2c_client *client, u8 addr, int count,
+ u8 *buf)
+{
+ struct i2c_msg xfer;
+ int ret;
+
+ /*
+ * Read is split into two transfers as device expects STOP/START rather
+ * than repeated start to carry out this kind of access.
+ */
+
+ /* Write address */
+ xfer.addr = client->addr;
+ xfer.flags = 0;
+ xfer.len = 1;
+ xfer.buf = &addr;
+
+ ret = i2c_transfer(client->adapter, &xfer, 1);
+ if (ret != 1) {
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+ }
+
+ /* Read data */
+ xfer.addr = client->addr;
+ xfer.flags = I2C_M_RD;
+ xfer.len = count;
+ xfer.buf = buf;
+
+ ret = i2c_transfer(client->adapter, &xfer, 1);
+ if (ret == 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int da9150_i2c_write_device(struct i2c_client *client, u8 addr, int count,
+ const u8 *buf)
+{
+ struct i2c_msg xfer;
+ u8 *reg_data;
+ int ret;
+
+ reg_data = kzalloc(1 + count, GFP_KERNEL);
+ if (!reg_data)
+ return -ENOMEM;
+
+ reg_data[0] = addr;
+ memcpy(®_data[1], buf, count);
+
+ /* Write address & data */
+ xfer.addr = client->addr;
+ xfer.flags = 0;
+ xfer.len = 1 + count;
+ xfer.buf = reg_data;
+
+ ret = i2c_transfer(client->adapter, &xfer, 1);
+ kfree(reg_data);
+ if (ret == 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+
+/*
+ * Driver top level functions
+ */
+
+static int da9150_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct da9150 *da9150;
+ int ret;
+
+ da9150 = devm_kzalloc(&client->dev, sizeof(struct da9150), GFP_KERNEL);
+ if (da9150 == NULL)
+ return -ENOMEM;
+ da9150->dev = &client->dev;
+ da9150->irq = client->irq;
+ i2c_set_clientdata(client, da9150);
+ dev_set_drvdata(da9150->dev, da9150);
+
+ da9150->core_qif = i2c_new_dummy(client->adapter, DA9150_QIF_I2C_ADDR);
+ i2c_set_clientdata(da9150->core_qif, da9150);
+
+ da9150->regmap = devm_regmap_init_i2c(client, &da9150_regmap_config);
+ if (IS_ERR(da9150->regmap)) {
+ ret = PTR_ERR(da9150->regmap);
+ dev_err(da9150->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ da9150->read_dev = (read_dev_t) da9150_i2c_read_device;
+ da9150->write_dev = (write_dev_t) da9150_i2c_write_device;
+
+ return da9150_device_init(da9150);
+}
+
+static int da9150_remove(struct i2c_client *client)
+{
+ struct da9150 *da9150 = i2c_get_clientdata(client);
+
+ da9150_device_exit(da9150);
+ i2c_unregister_device(da9150->core_qif);
+
+ return 0;
+}
+
+static void da9150_shutdown(struct i2c_client *client)
+{
+ struct da9150 *da9150 = i2c_get_clientdata(client);
+
+ da9150_device_shutdown(da9150);
+}
+
+static const struct i2c_device_id da9150_i2c_id[] = {
+ { "da9150", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da9150_i2c_id);
+
+static const struct of_device_id da9150_of_match[] = {
+ { .compatible = "dlg,da9150", },
+ { }
+};
+
+static struct i2c_driver da9150_driver = {
+ .driver = {
+ .name = "da9150",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(da9150_of_match),
+ },
+ .probe = da9150_probe,
+ .remove = da9150_remove,
+ .shutdown = da9150_shutdown,
+ .id_table = da9150_i2c_id,
+};
+
+module_i2c_driver(da9150_driver);
+
+MODULE_DESCRIPTION("I2C Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <[email protected]");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/da9150/core.h b/include/linux/mfd/da9150/core.h
new file mode 100644
index 0000000..39997406
--- /dev/null
+++ b/include/linux/mfd/da9150/core.h
@@ -0,0 +1,125 @@
+/*
+ * DA9150 MFD Driver - Core Data
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DA9150_CORE_H
+#define _DA9150_CORE_H
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/mutex.h>
+
+/*
+ * Defines
+ */
+
+/* I2C sub-device addresses */
+#define DA9150_QIF_I2C_ADDR 0x6D
+
+/* I2C address paging */
+#define DA9150_REG_PAGE_SHIFT 8
+#define DA9150_REG_PAGE_MASK 0xFF
+
+/* IRQs */
+#define DA9150_NUM_IRQ_REGS 4
+#define DA9150_IRQ_VBUS 0
+#define DA9150_IRQ_CHG 1
+#define DA9150_IRQ_TCLASS 2
+#define DA9150_IRQ_TJUNC 3
+#define DA9150_IRQ_VFAULT 4
+#define DA9150_IRQ_CONF 5
+#define DA9150_IRQ_DAT 6
+#define DA9150_IRQ_DTYPE 7
+#define DA9150_IRQ_ID 8
+#define DA9150_IRQ_ADP 9
+#define DA9150_IRQ_SESS_END 10
+#define DA9150_IRQ_SESS_VLD 11
+#define DA9150_IRQ_FG 12
+#define DA9150_IRQ_GP 13
+#define DA9150_IRQ_TBAT 14
+#define DA9150_IRQ_GPIOA 15
+#define DA9150_IRQ_GPIOB 16
+#define DA9150_IRQ_GPIOC 17
+#define DA9150_IRQ_GPIOD 18
+#define DA9150_IRQ_GPADC 19
+#define DA9150_IRQ_WKUP 20
+
+/* Helper macros to convert real world values to register values */
+#define DA9150_CHG_IBAT_FACTOR 25000
+#define DA9150_CHG_IEND_FACTOR 4800
+#define DA9150_CHG_IBAT_UA_TO_REG(val) (val / DA9150_CHG_IBAT_FACTOR)
+#define DA9150_CHG_IEND_UA_TO_REG(val) (val / DA9150_CHG_IEND_FACTOR)
+#define DA9150_CHG_IBAT_REG_TO_UA(val) (val * DA9150_CHG_IBAT_FACTOR)
+#define DA9150_CHG_IEND_REG_TO_UA(val) (val * DA9150_CHG_IEND_FACTOR)
+
+
+/*
+ * I/O Function Typedefs
+ */
+
+typedef int (*read_dev_t)(void *client, u8 addr, int count, u8 *buf);
+typedef int (*write_dev_t)(void *client, u8 addr, int count, const u8 *buf);
+
+/*
+ * Main structure
+ */
+
+struct da9150 {
+ struct device *dev;
+
+ struct regmap *regmap;
+ struct i2c_client *core_qif;
+
+ read_dev_t read_dev;
+ write_dev_t write_dev;
+
+ struct regmap_irq_chip_data *regmap_irq_data;
+ int irq;
+ int irq_base;
+
+ bool can_shutdown;
+ bool gpadc_ready;
+};
+
+/*
+ * APIs
+ */
+
+/* Device I/O */
+int da9150_read_core(struct da9150 *da9150, u8 addr, int count, u8 *buf);
+int da9150_write_core(struct da9150 *da9150, u8 addr, int count, const u8 *buf);
+
+int da9150_read_qif(struct da9150 *da9150, u8 addr, int count, u8 *buf);
+int da9150_write_qif(struct da9150 *da9150, u8 addr, int count, const u8 *buf);
+
+int da9150_read_fwdl(struct da9150 *da9150, u8 addr, int count, u8 *buf);
+int da9150_write_fwdl(struct da9150 *da9150, u8 addr, int count, const u8 *buf);
+
+u8 da9150_reg_read(struct da9150 *da9150, u16 reg);
+int da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val);
+int da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val);
+
+int da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf);
+int da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf);
+
+/* IRQ */
+int da9150_register_irq(struct platform_device *pdev, void *dev_id,
+ irq_handler_t handler, const char *name);
+
+/* Init/Exit */
+int da9150_device_init(struct da9150 *da9150);
+void da9150_device_exit(struct da9150 *da9150);
+void da9150_device_shutdown(struct da9150 *da9150);
+
+#endif /* _DA9150_CORE_H */
diff --git a/include/linux/mfd/da9150/pdata.h b/include/linux/mfd/da9150/pdata.h
new file mode 100644
index 0000000..c1dd2b3
--- /dev/null
+++ b/include/linux/mfd/da9150/pdata.h
@@ -0,0 +1,148 @@
+/*
+ * DA9150 MFD Driver - Platform Data
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DA9150_PDATA_H
+#define _DA9150_PDATA_H
+
+/*
+ * GPIO
+ */
+
+#define DA9150_NUM_GPIOS 4
+
+/* Values are as bit fields for registers */
+enum da9150_gpi_trigger {
+ DA9150_GPI_ACTIVE_LOW = 0,
+ DA9150_GPI_ACTIVE_HIGH,
+};
+
+enum da9150_gpi_mode {
+ DA9150_GPI_DIG = 0,
+ DA9150_GPI_ANA = 3,
+};
+
+enum da9150_gpo_mode {
+ DA9150_GPO_OD = 1,
+ DA9150_GPO_PP,
+};
+
+enum da9150_gpo_supply {
+ DA9150_GPO_SUPPLY_VDDIO1 = 0,
+ DA9150_GPO_SUPPLY_VDDIO2,
+};
+
+enum da9150_gpio_pupd {
+ DA9150_GPIO_PUPD_DISABLED = 0,
+ DA9150_GPIO_PUPD_ENABLED,
+};
+
+struct da9150_gpio_cfg {
+ enum da9150_gpi_trigger gpi_trigger;
+ enum da9150_gpi_mode gpi_mode;
+ enum da9150_gpo_mode gpo_mode;
+ enum da9150_gpo_supply gpo_supply;
+ enum da9150_gpio_pupd gpio_pupd;
+};
+
+enum da9150_gpi_debounce_time {
+ DA9150_GPI_DEBOUNCE_TIME_OFF = 0,
+ DA9150_GPI_DEBOUNCE_TIME_100US,
+ DA9150_GPI_DEBOUNCE_TIME_1MS,
+ DA9150_GPI_DEBOUNCE_TIME_10MS24,
+ DA9150_GPI_DEBOUNCE_TIME_51MS20,
+ DA9150_GPI_DEBOUNCE_TIME_256MS,
+ DA9150_GPI_DEBOUNCE_TIME_512MS,
+ DA9150_GPI_DEBOUNCE_TIME_1024MS,
+};
+
+/* Charging LED Data */
+enum da9150_led_freq {
+ DA9150_LED_FREQ_ALWAYS_ON = 0,
+ DA9150_LED_FREQ_1_S,
+ DA9150_LED_FREQ_2_S,
+ DA9150_LED_FREQ_4_S,
+};
+
+enum da9150_led_ontime {
+ DA9150_LED_ONTIME_10_MS = 0,
+ DA9150_LED_ONTIME_20_MS,
+ DA9150_LED_ONTIME_40_MS,
+ DA9150_LED_ONTIME_HALF_FREQ,
+};
+
+enum da9150_pin {
+ DA9150_PIN_OFF = 0,
+ DA9150_PIN_GPIOA = 1,
+ DA9150_PIN_GPIOB = 2,
+ DA9150_PIN_GPIOC = 3,
+ DA9150_PIN_GPIOD = 4,
+};
+
+
+/* Fault Pin Data */
+enum da9150_fault_type {
+ DA9150_FAULT_TYPE_VFAULT = 0,
+ DA9150_FAULT_TYPE_RESET,
+};
+
+
+/* GPIO data */
+struct da9150_gpio_pdata {
+ int base;
+
+ /* General GPIO config */
+ struct da9150_gpio_cfg gpio_cfg[DA9150_NUM_GPIOS];
+ enum da9150_gpi_debounce_time gpi_debounce_time;
+
+ /* Charge LED features */
+ enum da9150_led_freq led_frequency;
+ bool led_dbl_flash;
+ enum da9150_led_ontime led_ontime;
+ enum da9150_pin led_pin;
+
+ /* Fault Pin features */
+ enum da9150_pin fault_pin;
+ enum da9150_fault_type fault_type;
+};
+
+/*
+ * Fuel-Gauge
+ */
+
+struct da9150_fg_pdata {
+ u32 update_interval; /* msecs */
+ u8 warn_soc_lvl; /* % value */
+ u8 crit_soc_lvl; /* % value */
+};
+
+/*
+ * Charger
+ */
+
+struct da9150_charger_pdata {
+ bool invalid_tbat;
+};
+
+
+/*
+ * Main struct
+ */
+
+struct da9150_pdata {
+ int irq_base;
+ struct da9150_gpio_pdata *gpio_pdata;
+ struct da9150_fg_pdata *fg_pdata;
+ struct da9150_charger_pdata *charger_pdata;
+};
+
+#endif /* _DA9150_PDATA_H */
diff --git a/include/linux/mfd/da9150/registers.h b/include/linux/mfd/da9150/registers.h
new file mode 100644
index 0000000..a6a8e28
--- /dev/null
+++ b/include/linux/mfd/da9150/registers.h
@@ -0,0 +1,1160 @@
+/*
+ * DA9150 MFD Driver - Registers
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DA9150_REGISTERS_H
+#define _DA9150_REGISTERS_H
+
+/*
+ * Registers
+ */
+
+#define DA9150_PAGE_CON 0x000
+#define DA9150_STATUS_A 0x068
+#define DA9150_STATUS_B 0x069
+#define DA9150_STATUS_C 0x06A
+#define DA9150_STATUS_D 0x06B
+#define DA9150_STATUS_E 0x06C
+#define DA9150_STATUS_F 0x06D
+#define DA9150_STATUS_G 0x06E
+#define DA9150_STATUS_H 0x06F
+#define DA9150_STATUS_I 0x070
+#define DA9150_STATUS_J 0x071
+#define DA9150_STATUS_K 0x072
+#define DA9150_STATUS_L 0x073
+#define DA9150_STATUS_N 0x074
+#define DA9150_FAULT_LOG_A 0x076
+#define DA9150_FAULT_LOG_B 0x077
+#define DA9150_EVENT_E 0x078
+#define DA9150_EVENT_F 0x079
+#define DA9150_EVENT_G 0x07A
+#define DA9150_EVENT_H 0x07B
+#define DA9150_IRQ_MASK_E 0x07C
+#define DA9150_IRQ_MASK_F 0x07D
+#define DA9150_IRQ_MASK_G 0x07E
+#define DA9150_IRQ_MASK_H 0x07F
+#define DA9150_PAGE_CON_1 0x080
+#define DA9150_CONFIG_A 0x0E0
+#define DA9150_CONFIG_B 0x0E1
+#define DA9150_CONFIG_C 0x0E2
+#define DA9150_CONFIG_D 0x0E3
+#define DA9150_CONFIG_E 0x0E4
+#define DA9150_CONTROL_A 0x0E5
+#define DA9150_CONTROL_B 0x0E6
+#define DA9150_CONTROL_C 0x0E7
+#define DA9150_GPIO_A_B 0x0E8
+#define DA9150_GPIO_C_D 0x0E9
+#define DA9150_GPIO_MODE_CONT 0x0EA
+#define DA9150_GPIO_CTRL_B 0x0EB
+#define DA9150_GPIO_CTRL_A 0x0EC
+#define DA9150_GPIO_CTRL_C 0x0ED
+#define DA9150_GPIO_CFG_A 0x0EE
+#define DA9150_GPIO_CFG_B 0x0EF
+#define DA9150_GPIO_CFG_C 0x0F0
+#define DA9150_GPADC_MAN 0x0F2
+#define DA9150_GPADC_RES_A 0x0F4
+#define DA9150_GPADC_RES_B 0x0F5
+#define DA9150_PAGE_CON_2 0x100
+#define DA9150_OTP_CONT_SHARED 0x101
+#define DA9150_INTERFACE_SHARED 0x105
+#define DA9150_CONFIG_A_SHARED 0x106
+#define DA9150_CONFIG_D_SHARED 0x109
+#define DA9150_ADETVB_CFG_C 0x150
+#define DA9150_ADETD_STAT 0x151
+#define DA9150_ADET_CMPSTAT 0x152
+#define DA9150_ADET_CTRL_A 0x153
+#define DA9150_ADETVB_CFG_B 0x154
+#define DA9150_ADETVB_CFG_A 0x155
+#define DA9150_ADETAC_CFG_A 0x156
+#define DA9150_ADDETAC_CFG_B 0x157
+#define DA9150_ADETAC_CFG_C 0x158
+#define DA9150_ADETAC_CFG_D 0x159
+#define DA9150_ADETVB_CFG_D 0x15A
+#define DA9150_ADETID_CFG_A 0x15B
+#define DA9150_ADET_RID_PT_CHG_H 0x15C
+#define DA9150_ADET_RID_PT_CHG_L 0x15D
+#define DA9150_PPR_TCTR_B 0x160
+#define DA9150_PPR_BKCTRL_A 0x163
+#define DA9150_PPR_BKCFG_A 0x164
+#define DA9150_PPR_BKCFG_B 0x165
+#define DA9150_PPR_CHGCTRL_A 0x166
+#define DA9150_PPR_CHGCTRL_B 0x167
+#define DA9150_PPR_CHGCTRL_C 0x168
+#define DA9150_PPR_TCTR_A 0x169
+#define DA9150_PPR_CHGCTRL_D 0x16A
+#define DA9150_PPR_CHGCTRL_E 0x16B
+#define DA9150_PPR_CHGCTRL_F 0x16C
+#define DA9150_PPR_CHGCTRL_G 0x16D
+#define DA9150_PPR_CHGCTRL_H 0x16E
+#define DA9150_PPR_CHGCTRL_I 0x16F
+#define DA9150_PPR_CHGCTRL_J 0x170
+#define DA9150_PPR_CHGCTRL_K 0x171
+#define DA9150_PPR_CHGCTRL_L 0x172
+#define DA9150_PPR_CHGCTRL_M 0x173
+#define DA9150_PPR_THYST_A 0x174
+#define DA9150_PPR_THYST_B 0x175
+#define DA9150_PPR_THYST_C 0x176
+#define DA9150_PPR_THYST_D 0x177
+#define DA9150_PPR_THYST_E 0x178
+#define DA9150_PPR_THYST_F 0x179
+#define DA9150_PPR_THYST_G 0x17A
+#define DA9150_PAGE_CON_3 0x180
+#define DA9150_PAGE_CON_4 0x200
+#define DA9150_PAGE_CON_5 0x280
+#define DA9150_PAGE_CON_6 0x300
+#define DA9150_COREBTLD_STAT_A 0x302
+#define DA9150_COREBTLD_CTRL_A 0x303
+#define DA9150_CORE_CONFIG_A 0x304
+#define DA9150_CORE_CONFIG_C 0x305
+#define DA9150_CORE_CONFIG_B 0x306
+#define DA9150_CORE_CFG_DATA_A 0x307
+#define DA9150_CORE_CFG_DATA_B 0x308
+#define DA9150_CORE_CMD_A 0x309
+#define DA9150_CORE_DATA_A 0x30A
+#define DA9150_CORE_DATA_B 0x30B
+#define DA9150_CORE_DATA_C 0x30C
+#define DA9150_CORE_DATA_D 0x30D
+#define DA9150_CORE2WIRE_STAT_A 0x310
+#define DA9150_CORE2WIRE_CTRL_A 0x311
+#define DA9150_FW_CTRL_A 0x312
+#define DA9150_FW_CTRL_C 0x313
+#define DA9150_FW_CTRL_D 0x314
+#define DA9150_FG_CTRL_A 0x315
+#define DA9150_FG_CTRL_B 0x316
+#define DA9150_FW_CTRL_E 0x317
+#define DA9150_FW_CTRL_B 0x318
+#define DA9150_GPADC_CMAN 0x320
+#define DA9150_GPADC_CRES_A 0x322
+#define DA9150_GPADC_CRES_B 0x323
+#define DA9150_CC_CFG_A 0x328
+#define DA9150_CC_CFG_B 0x329
+#define DA9150_CC_ICHG_RES_A 0x32A
+#define DA9150_CC_ICHG_RES_B 0x32B
+#define DA9150_CC_IAVG_RES_A 0x32C
+#define DA9150_CC_IAVG_RES_B 0x32D
+#define DA9150_TAUX_CTRL_A 0x330
+#define DA9150_TAUX_RELOAD_H 0x332
+#define DA9150_TAUX_RELOAD_L 0x333
+#define DA9150_TAUX_VALUE_H 0x334
+#define DA9150_TAUX_VALUE_L 0x335
+#define DA9150_AUX_DATA_0 0x338
+#define DA9150_AUX_DATA_1 0x339
+#define DA9150_AUX_DATA_2 0x33A
+#define DA9150_AUX_DATA_3 0x33B
+#define DA9150_BIF_CTRL 0x340
+#define DA9150_TBAT_CTRL_A 0x342
+#define DA9150_TBAT_CTRL_B 0x343
+#define DA9150_TBAT_RES_A 0x344
+#define DA9150_TBAT_RES_B 0x345
+
+
+/*
+ * Bit fields
+ */
+
+/* DA9150_PAGE_CON = 0x000 */
+#define DA9150_PAGE_SHIFT 0
+#define DA9150_PAGE_MASK (0x3f << 0)
+#define DA9150_I2C_PAGE_SHIFT 1
+#define DA9150_I2C_PAGE_MASK (0x1f << 1)
+#define DA9150_WRITE_MODE_SHIFT 6
+#define DA9150_WRITE_MODE_MASK (0x01 << 6)
+#define DA9150_REVERT_SHIFT 7
+#define DA9150_REVERT_MASK (0x01 << 7)
+
+/* DA9150_STATUS_A = 0x068 */
+#define DA9150_WKUP_STAT_SHIFT 2
+#define DA9150_WKUP_STAT_MASK (0x0f << 2)
+#define DA9150_SLEEP_STAT_SHIFT 6
+#define DA9150_SLEEP_STAT_MASK (0x03 << 6)
+
+/* DA9150_STATUS_B = 0x069 */
+#define DA9150_VFAULT_STAT_SHIFT 0
+#define DA9150_VFAULT_STAT_MASK (0x01 << 0)
+#define DA9150_TFAULT_STAT_SHIFT 1
+#define DA9150_TFAULT_STAT_MASK (0x01 << 1)
+
+/* DA9150_STATUS_C = 0x06A */
+#define DA9150_VDD33_STAT_SHIFT 0
+#define DA9150_VDD33_STAT_MASK (0x01 << 0)
+#define DA9150_VDD33_SLEEP_SHIFT 1
+#define DA9150_VDD33_SLEEP_MASK (0x01 << 1)
+#define DA9150_LFOSC_STAT_SHIFT 7
+#define DA9150_LFOSC_STAT_MASK (0x01 << 7)
+
+/* DA9150_STATUS_D = 0x06B */
+#define DA9150_GPIOA_STAT_SHIFT 0
+#define DA9150_GPIOA_STAT_MASK (0x01 << 0)
+#define DA9150_GPIOB_STAT_SHIFT 1
+#define DA9150_GPIOB_STAT_MASK (0x01 << 1)
+#define DA9150_GPIOC_STAT_SHIFT 2
+#define DA9150_GPIOC_STAT_MASK (0x01 << 2)
+#define DA9150_GPIOD_STAT_SHIFT 3
+#define DA9150_GPIOD_STAT_MASK (0x01 << 3)
+
+/* DA9150_STATUS_E = 0x06C */
+#define DA9150_DTYPE_SHIFT 0
+#define DA9150_DTYPE_MASK (0x1f << 0)
+#define DA9150_DTYPE_DT_NIL (0x00 << 0)
+#define DA9150_DTYPE_DT_USB_OTG (0x01 << 0)
+#define DA9150_DTYPE_DT_USB_STD (0x02 << 0)
+#define DA9150_DTYPE_DT_USB_CHG (0x03 << 0)
+#define DA9150_DTYPE_DT_ACA_CHG (0x04 << 0)
+#define DA9150_DTYPE_DT_ACA_OTG (0x05 << 0)
+#define DA9150_DTYPE_DT_ACA_DOC (0x06 << 0)
+#define DA9150_DTYPE_DT_DED_CHG (0x07 << 0)
+#define DA9150_DTYPE_DT_CR5_CHG (0x08 << 0)
+#define DA9150_DTYPE_DT_CR4_CHG (0x0c << 0)
+#define DA9150_DTYPE_DT_PT_CHG (0x11 << 0)
+#define DA9150_DTYPE_DT_NN_ACC (0x16 << 0)
+#define DA9150_DTYPE_DT_NN_CHG (0x17 << 0)
+
+/* DA9150_STATUS_F = 0x06D */
+#define DA9150_SESS_VLD_SHIFT 0
+#define DA9150_SESS_VLD_MASK (0x01 << 0)
+#define DA9150_ID_ERR_SHIFT 1
+#define DA9150_ID_ERR_MASK (0x01 << 1)
+#define DA9150_PT_CHG_SHIFT 2
+#define DA9150_PT_CHG_MASK (0x01 << 2)
+
+/* DA9150_STATUS_G = 0x06E */
+#define DA9150_RID_SHIFT 0
+#define DA9150_RID_MASK (0xff << 0)
+
+/* DA9150_STATUS_H = 0x06F */
+#define DA9150_VBUS_STAT_SHIFT 0
+#define DA9150_VBUS_STAT_MASK (0x07 << 0)
+#define DA9150_VBUS_STAT_OFF (0x00 << 0)
+#define DA9150_VBUS_STAT_WAIT (0x01 << 0)
+#define DA9150_VBUS_STAT_CHG (0x02 << 0)
+#define DA9150_VBUS_TRED_SHIFT 3
+#define DA9150_VBUS_TRED_MASK (0x01 << 3)
+#define DA9150_VBUS_DROP_STAT_SHIFT 4
+#define DA9150_VBUS_DROP_STAT_MASK (0x0f << 4)
+
+/* DA9150_STATUS_I = 0x070 */
+#define DA9150_VBUS_ISET_STAT_SHIFT 0
+#define DA9150_VBUS_ISET_STAT_MASK (0x1f << 0)
+#define DA9150_VBUS_OT_SHIFT 7
+#define DA9150_VBUS_OT_MASK (0x01 << 7)
+
+/* DA9150_STATUS_J = 0x071 */
+#define DA9150_CHG_STAT_SHIFT 0
+#define DA9150_CHG_STAT_MASK (0x0f << 0)
+#define DA9150_CHG_STAT_OFF (0x00 << 0)
+#define DA9150_CHG_STAT_SUSP (0x01 << 0)
+#define DA9150_CHG_STAT_ACT (0x02 << 0)
+#define DA9150_CHG_STAT_PRE (0x03 << 0)
+#define DA9150_CHG_STAT_CC (0x04 << 0)
+#define DA9150_CHG_STAT_CV (0x05 << 0)
+#define DA9150_CHG_STAT_FULL (0x06 << 0)
+#define DA9150_CHG_STAT_TEMP (0x07 << 0)
+#define DA9150_CHG_STAT_TIME (0x08 << 0)
+#define DA9150_CHG_STAT_BAT (0x09 << 0)
+#define DA9150_CHG_TEMP_SHIFT 4
+#define DA9150_CHG_TEMP_MASK (0x07 << 4)
+#define DA9150_CHG_TEMP_UNDER (0x06 << 4)
+#define DA9150_CHG_TEMP_OVER (0x07 << 4)
+#define DA9150_CHG_IEND_STAT_SHIFT 7
+#define DA9150_CHG_IEND_STAT_MASK (0x01 << 7)
+
+/* DA9150_STATUS_K = 0x072 */
+#define DA9150_CHG_IAV_H_SHIFT 0
+#define DA9150_CHG_IAV_H_MASK (0xff << 0)
+
+/* DA9150_STATUS_L = 0x073 */
+#define DA9150_CHG_IAV_L_SHIFT 5
+#define DA9150_CHG_IAV_L_MASK (0x07 << 5)
+
+/* DA9150_STATUS_N = 0x074 */
+#define DA9150_CHG_TIME_SHIFT 1
+#define DA9150_CHG_TIME_MASK (0x01 << 1)
+#define DA9150_CHG_TRED_SHIFT 2
+#define DA9150_CHG_TRED_MASK (0x01 << 2)
+#define DA9150_CHG_TJUNC_CLASS_SHIFT 3
+#define DA9150_CHG_TJUNC_CLASS_MASK (0x07 << 3)
+#define DA9150_CHG_TJUNC_CLASS_6 (0x06 << 3)
+#define DA9150_EBS_STAT_SHIFT 6
+#define DA9150_EBS_STAT_MASK (0x01 << 6)
+#define DA9150_CHG_BAT_REMOVED_SHIFT 7
+#define DA9150_CHG_BAT_REMOVED_MASK (0x01 << 7)
+
+/* DA9150_FAULT_LOG_A = 0x076 */
+#define DA9150_TEMP_FAULT_SHIFT 0
+#define DA9150_TEMP_FAULT_MASK (0x01 << 0)
+#define DA9150_VSYS_FAULT_SHIFT 1
+#define DA9150_VSYS_FAULT_MASK (0x01 << 1)
+#define DA9150_START_FAULT_SHIFT 2
+#define DA9150_START_FAULT_MASK (0x01 << 2)
+#define DA9150_EXT_FAULT_SHIFT 3
+#define DA9150_EXT_FAULT_MASK (0x01 << 3)
+#define DA9150_POR_FAULT_SHIFT 4
+#define DA9150_POR_FAULT_MASK (0x01 << 4)
+
+/* DA9150_FAULT_LOG_B = 0x077 */
+#define DA9150_VBUS_FAULT_SHIFT 0
+#define DA9150_VBUS_FAULT_MASK (0x01 << 0)
+#define DA9150_OTG_FAULT_SHIFT 1
+#define DA9150_OTG_FAULT_MASK (0x01 << 1)
+
+/* DA9150_EVENT_E = 0x078 */
+#define DA9150_E_VBUS_SHIFT 0
+#define DA9150_E_VBUS_MASK (0x01 << 0)
+#define DA9150_E_CHG_SHIFT 1
+#define DA9150_E_CHG_MASK (0x01 << 1)
+#define DA9150_E_TCLASS_SHIFT 2
+#define DA9150_E_TCLASS_MASK (0x01 << 2)
+#define DA9150_E_TJUNC_SHIFT 3
+#define DA9150_E_TJUNC_MASK (0x01 << 3)
+#define DA9150_E_VFAULT_SHIFT 4
+#define DA9150_E_VFAULT_MASK (0x01 << 4)
+#define DA9150_EVENTS_H_SHIFT 5
+#define DA9150_EVENTS_H_MASK (0x01 << 5)
+#define DA9150_EVENTS_G_SHIFT 6
+#define DA9150_EVENTS_G_MASK (0x01 << 6)
+#define DA9150_EVENTS_F_SHIFT 7
+#define DA9150_EVENTS_F_MASK (0x01 << 7)
+
+/* DA9150_EVENT_F = 0x079 */
+#define DA9150_E_CONF_SHIFT 0
+#define DA9150_E_CONF_MASK (0x01 << 0)
+#define DA9150_E_DAT_SHIFT 1
+#define DA9150_E_DAT_MASK (0x01 << 1)
+#define DA9150_E_DTYPE_SHIFT 3
+#define DA9150_E_DTYPE_MASK (0x01 << 3)
+#define DA9150_E_ID_SHIFT 4
+#define DA9150_E_ID_MASK (0x01 << 4)
+#define DA9150_E_ADP_SHIFT 5
+#define DA9150_E_ADP_MASK (0x01 << 5)
+#define DA9150_E_SESS_END_SHIFT 6
+#define DA9150_E_SESS_END_MASK (0x01 << 6)
+#define DA9150_E_SESS_VLD_SHIFT 7
+#define DA9150_E_SESS_VLD_MASK (0x01 << 7)
+
+/* DA9150_EVENT_G = 0x07A */
+#define DA9150_E_FG_SHIFT 0
+#define DA9150_E_FG_MASK (0x01 << 0)
+#define DA9150_E_GP_SHIFT 1
+#define DA9150_E_GP_MASK (0x01 << 1)
+#define DA9150_E_TBAT_SHIFT 2
+#define DA9150_E_TBAT_MASK (0x01 << 2)
+#define DA9150_E_GPIOA_SHIFT 3
+#define DA9150_E_GPIOA_MASK (0x01 << 3)
+#define DA9150_E_GPIOB_SHIFT 4
+#define DA9150_E_GPIOB_MASK (0x01 << 4)
+#define DA9150_E_GPIOC_SHIFT 5
+#define DA9150_E_GPIOC_MASK (0x01 << 5)
+#define DA9150_E_GPIOD_SHIFT 6
+#define DA9150_E_GPIOD_MASK (0x01 << 6)
+#define DA9150_E_GPADC_SHIFT 7
+#define DA9150_E_GPADC_MASK (0x01 << 7)
+
+/* DA9150_EVENT_H = 0x07B */
+#define DA9150_E_WKUP_SHIFT 0
+#define DA9150_E_WKUP_MASK (0x01 << 0)
+
+/* DA9150_IRQ_MASK_E = 0x07C */
+#define DA9150_M_VBUS_SHIFT 0
+#define DA9150_M_VBUS_MASK (0x01 << 0)
+#define DA9150_M_CHG_SHIFT 1
+#define DA9150_M_CHG_MASK (0x01 << 1)
+#define DA9150_M_TJUNC_SHIFT 3
+#define DA9150_M_TJUNC_MASK (0x01 << 3)
+#define DA9150_M_VFAULT_SHIFT 4
+#define DA9150_M_VFAULT_MASK (0x01 << 4)
+
+/* DA9150_IRQ_MASK_F = 0x07D */
+#define DA9150_M_CONF_SHIFT 0
+#define DA9150_M_CONF_MASK (0x01 << 0)
+#define DA9150_M_DAT_SHIFT 1
+#define DA9150_M_DAT_MASK (0x01 << 1)
+#define DA9150_M_DTYPE_SHIFT 3
+#define DA9150_M_DTYPE_MASK (0x01 << 3)
+#define DA9150_M_ID_SHIFT 4
+#define DA9150_M_ID_MASK (0x01 << 4)
+#define DA9150_M_ADP_SHIFT 5
+#define DA9150_M_ADP_MASK (0x01 << 5)
+#define DA9150_M_SESS_END_SHIFT 6
+#define DA9150_M_SESS_END_MASK (0x01 << 6)
+#define DA9150_M_SESS_VLD_SHIFT 7
+#define DA9150_M_SESS_VLD_MASK (0x01 << 7)
+
+/* DA9150_IRQ_MASK_G = 0x07E */
+#define DA9150_M_FG_SHIFT 0
+#define DA9150_M_FG_MASK (0x01 << 0)
+#define DA9150_M_GP_SHIFT 1
+#define DA9150_M_GP_MASK (0x01 << 1)
+#define DA9150_M_TBAT_SHIFT 2
+#define DA9150_M_TBAT_MASK (0x01 << 2)
+#define DA9150_M_GPIOA_SHIFT 3
+#define DA9150_M_GPIOA_MASK (0x01 << 3)
+#define DA9150_M_GPIOB_SHIFT 4
+#define DA9150_M_GPIOB_MASK (0x01 << 4)
+#define DA9150_M_GPIOC_SHIFT 5
+#define DA9150_M_GPIOC_MASK (0x01 << 5)
+#define DA9150_M_GPIOD_SHIFT 6
+#define DA9150_M_GPIOD_MASK (0x01 << 6)
+#define DA9150_M_GPADC_SHIFT 7
+#define DA9150_M_GPADC_MASK (0x01 << 7)
+
+/* DA9150_IRQ_MASK_H = 0x07F */
+#define DA9150_M_WKUP_SHIFT 0
+#define DA9150_M_WKUP_MASK (0x01 << 0)
+
+/* DA9150_PAGE_CON_1 = 0x080 */
+#define DA9150_PAGE_SHIFT 0
+#define DA9150_PAGE_MASK (0x3f << 0)
+#define DA9150_WRITE_MODE_SHIFT 6
+#define DA9150_WRITE_MODE_MASK (0x01 << 6)
+#define DA9150_REVERT_SHIFT 7
+#define DA9150_REVERT_MASK (0x01 << 7)
+
+/* DA9150_CONFIG_A = 0x0E0 */
+#define DA9150_RESET_DUR_SHIFT 0
+#define DA9150_RESET_DUR_MASK (0x03 << 0)
+#define DA9150_RESET_EXT_SHIFT 2
+#define DA9150_RESET_EXT_MASK (0x03 << 2)
+#define DA9150_START_MAX_SHIFT 4
+#define DA9150_START_MAX_MASK (0x03 << 4)
+#define DA9150_PS_WAIT_EN_SHIFT 6
+#define DA9150_PS_WAIT_EN_MASK (0x01 << 6)
+#define DA9150_PS_DISABLE_DIRECT_SHIFT 7
+#define DA9150_PS_DISABLE_DIRECT_MASK (0x01 << 7)
+
+/* DA9150_CONFIG_B = 0x0E1 */
+#define DA9150_VFAULT_ADJ_SHIFT 0
+#define DA9150_VFAULT_ADJ_MASK (0x0f << 0)
+#define DA9150_VFAULT_HYST_SHIFT 4
+#define DA9150_VFAULT_HYST_MASK (0x07 << 4)
+#define DA9150_VFAULT_EN_SHIFT 7
+#define DA9150_VFAULT_EN_MASK (0x01 << 7)
+
+/* DA9150_CONFIG_C = 0x0E2 */
+#define DA9150_VSYS_MIN_SHIFT 3
+#define DA9150_VSYS_MIN_MASK (0x1f << 3)
+
+/* DA9150_CONFIG_D = 0x0E3 */
+#define DA9150_LFOSC_EXT_SHIFT 0
+#define DA9150_LFOSC_EXT_MASK (0x01 << 0)
+#define DA9150_VDD33_DWN_SHIFT 1
+#define DA9150_VDD33_DWN_MASK (0x01 << 1)
+#define DA9150_WKUP_PM_EN_SHIFT 2
+#define DA9150_WKUP_PM_EN_MASK (0x01 << 2)
+#define DA9150_WKUP_CE_SEL_SHIFT 3
+#define DA9150_WKUP_CE_SEL_MASK (0x03 << 3)
+#define DA9150_WKUP_CLK32K_EN_SHIFT 5
+#define DA9150_WKUP_CLK32K_EN_MASK (0x01 << 5)
+#define DA9150_DISABLE_DEL_SHIFT 7
+#define DA9150_DISABLE_DEL_MASK (0x01 << 7)
+
+/* DA9150_CONFIG_E = 0x0E4 */
+#define DA9150_PM_SPKSUP_DIS_SHIFT 0
+#define DA9150_PM_SPKSUP_DIS_MASK (0x01 << 0)
+#define DA9150_PM_MERGE_SHIFT 1
+#define DA9150_PM_MERGE_MASK (0x01 << 1)
+#define DA9150_PM_SR_OFF_SHIFT 2
+#define DA9150_PM_SR_OFF_MASK (0x01 << 2)
+#define DA9150_PM_TIMEOUT_EN_SHIFT 3
+#define DA9150_PM_TIMEOUT_EN_MASK (0x01 << 3)
+#define DA9150_PM_DLY_SEL_SHIFT 4
+#define DA9150_PM_DLY_SEL_MASK (0x07 << 4)
+#define DA9150_PM_OUT_DLY_SEL_SHIFT 7
+#define DA9150_PM_OUT_DLY_SEL_MASK (0x01 << 7)
+
+/* DA9150_CONTROL_A = 0x0E5 */
+#define DA9150_VDD33_SL_SHIFT 0
+#define DA9150_VDD33_SL_MASK (0x01 << 0)
+#define DA9150_VDD33_LPM_SHIFT 1
+#define DA9150_VDD33_LPM_MASK (0x03 << 1)
+#define DA9150_VDD33_EN_SHIFT 3
+#define DA9150_VDD33_EN_MASK (0x01 << 3)
+#define DA9150_GPI_LPM_SHIFT 6
+#define DA9150_GPI_LPM_MASK (0x01 << 6)
+#define DA9150_PM_IF_LPM_SHIFT 7
+#define DA9150_PM_IF_LPM_MASK (0x01 << 7)
+
+/* DA9150_CONTROL_B = 0x0E6 */
+#define DA9150_LPM_SHIFT 0
+#define DA9150_LPM_MASK (0x01 << 0)
+#define DA9150_RESET_SHIFT 1
+#define DA9150_RESET_MASK (0x01 << 1)
+#define DA9150_RESET_USRCONF_EN_SHIFT 2
+#define DA9150_RESET_USRCONF_EN_MASK (0x01 << 2)
+
+/* DA9150_CONTROL_C = 0x0E7 */
+#define DA9150_DISABLE_SHIFT 0
+#define DA9150_DISABLE_MASK (0x01 << 0)
+
+/* DA9150_GPIO_A_B = 0x0E8 */
+#define DA9150_GPIOA_PIN_SHIFT 0
+#define DA9150_GPIOA_PIN_MASK (0x07 << 0)
+#define DA9150_GPIOA_PIN_GPI (0x00 << 0)
+#define DA9150_GPIOA_PIN_GPO_OD (0x01 << 0)
+#define DA9150_GPIOA_TYPE_SHIFT 3
+#define DA9150_GPIOA_TYPE_MASK (0x01 << 3)
+#define DA9150_GPIOB_PIN_SHIFT 4
+#define DA9150_GPIOB_PIN_MASK (0x07 << 4)
+#define DA9150_GPIOB_PIN_GPI (0x00 << 4)
+#define DA9150_GPIOB_PIN_GPO_OD (0x01 << 4)
+#define DA9150_GPIOB_TYPE_SHIFT 7
+#define DA9150_GPIOB_TYPE_MASK (0x01 << 7)
+
+/* DA9150_GPIO_C_D = 0x0E9 */
+#define DA9150_GPIOC_PIN_SHIFT 0
+#define DA9150_GPIOC_PIN_MASK (0x07 << 0)
+#define DA9150_GPIOC_PIN_GPI (0x00 << 0)
+#define DA9150_GPIOC_PIN_GPO_OD (0x01 << 0)
+#define DA9150_GPIOC_TYPE_SHIFT 3
+#define DA9150_GPIOC_TYPE_MASK (0x01 << 3)
+#define DA9150_GPIOD_PIN_SHIFT 4
+#define DA9150_GPIOD_PIN_MASK (0x07 << 4)
+#define DA9150_GPIOD_PIN_GPI (0x00 << 4)
+#define DA9150_GPIOD_PIN_GPO_OD (0x01 << 4)
+#define DA9150_GPIOD_TYPE_SHIFT 7
+#define DA9150_GPIOD_TYPE_MASK (0x01 << 7)
+
+/* DA9150_GPIO_MODE_CONT = 0x0EA */
+#define DA9150_GPIOA_MODE_SHIFT 0
+#define DA9150_GPIOA_MODE_MASK (0x01 << 0)
+#define DA9150_GPIOB_MODE_SHIFT 1
+#define DA9150_GPIOB_MODE_MASK (0x01 << 1)
+#define DA9150_GPIOC_MODE_SHIFT 2
+#define DA9150_GPIOC_MODE_MASK (0x01 << 2)
+#define DA9150_GPIOD_MODE_SHIFT 3
+#define DA9150_GPIOD_MODE_MASK (0x01 << 3)
+#define DA9150_GPIOA_CONT_SHIFT 4
+#define DA9150_GPIOA_CONT_MASK (0x01 << 4)
+#define DA9150_GPIOB_CONT_SHIFT 5
+#define DA9150_GPIOB_CONT_MASK (0x01 << 5)
+#define DA9150_GPIOC_CONT_SHIFT 6
+#define DA9150_GPIOC_CONT_MASK (0x01 << 6)
+#define DA9150_GPIOD_CONT_SHIFT 7
+#define DA9150_GPIOD_CONT_MASK (0x01 << 7)
+
+/* DA9150_GPIO_CTRL_B = 0x0EB */
+#define DA9150_WAKE_PIN_SHIFT 0
+#define DA9150_WAKE_PIN_MASK (0x03 << 0)
+#define DA9150_WAKE_MODE_SHIFT 2
+#define DA9150_WAKE_MODE_MASK (0x01 << 2)
+#define DA9150_WAKE_CONT_SHIFT 3
+#define DA9150_WAKE_CONT_MASK (0x01 << 3)
+#define DA9150_WAKE_DLY_SHIFT 4
+#define DA9150_WAKE_DLY_MASK (0x01 << 4)
+
+/* DA9150_GPIO_CTRL_A = 0x0EC */
+#define DA9150_GPIOA_ANAEN_SHIFT 0
+#define DA9150_GPIOA_ANAEN_MASK (0x01 << 0)
+#define DA9150_GPIOB_ANAEN_SHIFT 1
+#define DA9150_GPIOB_ANAEN_MASK (0x01 << 1)
+#define DA9150_GPIOC_ANAEN_SHIFT 2
+#define DA9150_GPIOC_ANAEN_MASK (0x01 << 2)
+#define DA9150_GPIOD_ANAEN_SHIFT 3
+#define DA9150_GPIOD_ANAEN_MASK (0x01 << 3)
+#define DA9150_GPIO_ANAEN 0x01
+#define DA9150_GPIO_ANAEN_MASK 0x0F
+#define DA9150_CHGLED_PIN_SHIFT 5
+#define DA9150_CHGLED_PIN_MASK (0x07 << 5)
+
+/* DA9150_GPIO_CTRL_C = 0x0ED */
+#define DA9150_CHGBL_DUR_SHIFT 0
+#define DA9150_CHGBL_DUR_MASK (0x03 << 0)
+#define DA9150_CHGBL_DBL_SHIFT 2
+#define DA9150_CHGBL_DBL_MASK (0x01 << 2)
+#define DA9150_CHGBL_FRQ_SHIFT 3
+#define DA9150_CHGBL_FRQ_MASK (0x03 << 3)
+#define DA9150_CHGBL_FLKR_SHIFT 5
+#define DA9150_CHGBL_FLKR_MASK (0x01 << 5)
+
+/* DA9150_GPIO_CFG_A = 0x0EE */
+#define DA9150_CE_LPM_DEB_SHIFT 0
+#define DA9150_CE_LPM_DEB_MASK (0x07 << 0)
+
+/* DA9150_GPIO_CFG_B = 0x0EF */
+#define DA9150_GPIOA_PUPD_SHIFT 0
+#define DA9150_GPIOA_PUPD_MASK (0x01 << 0)
+#define DA9150_GPIOB_PUPD_SHIFT 1
+#define DA9150_GPIOB_PUPD_MASK (0x01 << 1)
+#define DA9150_GPIOC_PUPD_SHIFT 2
+#define DA9150_GPIOC_PUPD_MASK (0x01 << 2)
+#define DA9150_GPIOD_PUPD_SHIFT 3
+#define DA9150_GPIOD_PUPD_MASK (0x01 << 3)
+#define DA9150_GPIO_PUPD_MASK (0xF << 0)
+#define DA9150_GPI_DEB_SHIFT 4
+#define DA9150_GPI_DEB_MASK (0x07 << 4)
+#define DA9150_LPM_EN_SHIFT 7
+#define DA9150_LPM_EN_MASK (0x01 << 7)
+
+/* DA9150_GPIO_CFG_C = 0x0F0 */
+#define DA9150_GPI_V_SHIFT 0
+#define DA9150_GPI_V_MASK (0x01 << 0)
+#define DA9150_VDDIO_INT_SHIFT 1
+#define DA9150_VDDIO_INT_MASK (0x01 << 1)
+#define DA9150_FAULT_PIN_SHIFT 3
+#define DA9150_FAULT_PIN_MASK (0x07 << 3)
+#define DA9150_FAULT_TYPE_SHIFT 6
+#define DA9150_FAULT_TYPE_MASK (0x01 << 6)
+#define DA9150_NIRQ_PUPD_SHIFT 7
+#define DA9150_NIRQ_PUPD_MASK (0x01 << 7)
+
+/* DA9150_GPADC_MAN = 0x0F2 */
+#define DA9150_GPADC_EN_SHIFT 0
+#define DA9150_GPADC_EN_MASK (0x01 << 0)
+#define DA9150_GPADC_MUX_SHIFT 1
+#define DA9150_GPADC_MUX_MASK (0x1f << 1)
+
+/* DA9150_GPADC_RES_A = 0x0F4 */
+#define DA9150_GPADC_RES_H_SHIFT 0
+#define DA9150_GPADC_RES_H_MASK (0xff << 0)
+
+/* DA9150_GPADC_RES_B = 0x0F5 */
+#define DA9150_GPADC_RUN_SHIFT 0
+#define DA9150_GPADC_RUN_MASK (0x01 << 0)
+#define DA9150_GPADC_RES_L_SHIFT 6
+#define DA9150_GPADC_RES_L_MASK (0x03 << 6)
+
+/* DA9150_PAGE_CON_2 = 0x100 */
+#define DA9150_PAGE_SHIFT 0
+#define DA9150_PAGE_MASK (0x3f << 0)
+#define DA9150_WRITE_MODE_SHIFT 6
+#define DA9150_WRITE_MODE_MASK (0x01 << 6)
+#define DA9150_REVERT_SHIFT 7
+#define DA9150_REVERT_MASK (0x01 << 7)
+
+/* DA9150_OTP_CONT_SHARED = 0x101 */
+#define DA9150_PC_DONE_SHIFT 3
+#define DA9150_PC_DONE_MASK (0x01 << 3)
+
+/* DA9150_INTERFACE_SHARED = 0x105 */
+#define DA9150_IF_BASE_ADDR_SHIFT 4
+#define DA9150_IF_BASE_ADDR_MASK (0x0f << 4)
+
+/* DA9150_CONFIG_A_SHARED = 0x106 */
+#define DA9150_NIRQ_VDD_SHIFT 1
+#define DA9150_NIRQ_VDD_MASK (0x01 << 1)
+#define DA9150_NIRQ_PIN_SHIFT 2
+#define DA9150_NIRQ_PIN_MASK (0x01 << 2)
+#define DA9150_NIRQ_TYPE_SHIFT 3
+#define DA9150_NIRQ_TYPE_MASK (0x01 << 3)
+#define DA9150_PM_IF_V_SHIFT 4
+#define DA9150_PM_IF_V_MASK (0x01 << 4)
+#define DA9150_PM_IF_FMP_SHIFT 5
+#define DA9150_PM_IF_FMP_MASK (0x01 << 5)
+#define DA9150_PM_IF_HSM_SHIFT 6
+#define DA9150_PM_IF_HSM_MASK (0x01 << 6)
+
+/* DA9150_CONFIG_D_SHARED = 0x109 */
+#define DA9150_NIRQ_MODE_SHIFT 1
+#define DA9150_NIRQ_MODE_MASK (0x01 << 1)
+
+/* DA9150_ADETVB_CFG_C = 0x150 */
+#define DA9150_TADP_RISE_SHIFT 0
+#define DA9150_TADP_RISE_MASK (0xff << 0)
+
+/* DA9150_ADETD_STAT = 0x151 */
+#define DA9150_DCD_STAT_SHIFT 0
+#define DA9150_DCD_STAT_MASK (0x01 << 0)
+#define DA9150_PCD_STAT_SHIFT 1
+#define DA9150_PCD_STAT_MASK (0x03 << 1)
+#define DA9150_SCD_STAT_SHIFT 3
+#define DA9150_SCD_STAT_MASK (0x03 << 3)
+#define DA9150_DP_STAT_SHIFT 5
+#define DA9150_DP_STAT_MASK (0x01 << 5)
+#define DA9150_DM_STAT_SHIFT 6
+#define DA9150_DM_STAT_MASK (0x01 << 6)
+
+/* DA9150_ADET_CMPSTAT = 0x152 */
+#define DA9150_DP_COMP_SHIFT 1
+#define DA9150_DP_COMP_MASK (0x01 << 1)
+#define DA9150_DM_COMP_SHIFT 2
+#define DA9150_DM_COMP_MASK (0x01 << 2)
+#define DA9150_ADP_SNS_COMP_SHIFT 3
+#define DA9150_ADP_SNS_COMP_MASK (0x01 << 3)
+#define DA9150_ADP_PRB_COMP_SHIFT 4
+#define DA9150_ADP_PRB_COMP_MASK (0x01 << 4)
+#define DA9150_ID_COMP_SHIFT 5
+#define DA9150_ID_COMP_MASK (0x01 << 5)
+
+/* DA9150_ADET_CTRL_A = 0x153 */
+#define DA9150_AID_DAT_SHIFT 0
+#define DA9150_AID_DAT_MASK (0x01 << 0)
+#define DA9150_AID_ID_SHIFT 1
+#define DA9150_AID_ID_MASK (0x01 << 1)
+#define DA9150_AID_TRIG_SHIFT 2
+#define DA9150_AID_TRIG_MASK (0x01 << 2)
+
+/* DA9150_ADETVB_CFG_B = 0x154 */
+#define DA9150_VB_MODE_SHIFT 0
+#define DA9150_VB_MODE_MASK (0x03 << 0)
+#define DA9150_VB_MODE_VB_SESS (0x01 << 0)
+
+#define DA9150_TADP_PRB_SHIFT 2
+#define DA9150_TADP_PRB_MASK (0x01 << 2)
+#define DA9150_DAT_RPD_EXT_SHIFT 5
+#define DA9150_DAT_RPD_EXT_MASK (0x01 << 5)
+#define DA9150_CONF_RPD_SHIFT 6
+#define DA9150_CONF_RPD_MASK (0x01 << 6)
+#define DA9150_CONF_SRP_SHIFT 7
+#define DA9150_CONF_SRP_MASK (0x01 << 7)
+
+/* DA9150_ADETVB_CFG_A = 0x155 */
+#define DA9150_AID_MODE_SHIFT 0
+#define DA9150_AID_MODE_MASK (0x03 << 0)
+#define DA9150_AID_EXT_POL_SHIFT 2
+#define DA9150_AID_EXT_POL_MASK (0x01 << 2)
+
+/* DA9150_ADETAC_CFG_A = 0x156 */
+#define DA9150_ISET_CDP_SHIFT 0
+#define DA9150_ISET_CDP_MASK (0x1f << 0)
+#define DA9150_CONF_DBP_SHIFT 5
+#define DA9150_CONF_DBP_MASK (0x01 << 5)
+
+/* DA9150_ADDETAC_CFG_B = 0x157 */
+#define DA9150_ISET_DCHG_SHIFT 0
+#define DA9150_ISET_DCHG_MASK (0x1f << 0)
+#define DA9150_CONF_GPIOA_SHIFT 5
+#define DA9150_CONF_GPIOA_MASK (0x01 << 5)
+#define DA9150_CONF_GPIOB_SHIFT 6
+#define DA9150_CONF_GPIOB_MASK (0x01 << 6)
+#define DA9150_AID_VB_SHIFT 7
+#define DA9150_AID_VB_MASK (0x01 << 7)
+
+/* DA9150_ADETAC_CFG_C = 0x158 */
+#define DA9150_ISET_DEF_SHIFT 0
+#define DA9150_ISET_DEF_MASK (0x1f << 0)
+#define DA9150_CONF_MODE_SHIFT 5
+#define DA9150_CONF_MODE_MASK (0x03 << 5)
+#define DA9150_AID_CR_DIS_SHIFT 7
+#define DA9150_AID_CR_DIS_MASK (0x01 << 7)
+
+/* DA9150_ADETAC_CFG_D = 0x159 */
+#define DA9150_ISET_UNIT_SHIFT 0
+#define DA9150_ISET_UNIT_MASK (0x1f << 0)
+#define DA9150_AID_UNCLAMP_SHIFT 5
+#define DA9150_AID_UNCLAMP_MASK (0x01 << 5)
+
+/* DA9150_ADETVB_CFG_D = 0x15A */
+#define DA9150_ID_MODE_SHIFT 0
+#define DA9150_ID_MODE_MASK (0x03 << 0)
+#define DA9150_DAT_MODE_SHIFT 2
+#define DA9150_DAT_MODE_MASK (0x0f << 2)
+#define DA9150_DAT_SWP_SHIFT 6
+#define DA9150_DAT_SWP_MASK (0x01 << 6)
+#define DA9150_DAT_CLAMP_EXT_SHIFT 7
+#define DA9150_DAT_CLAMP_EXT_MASK (0x01 << 7)
+
+/* DA9150_ADETID_CFG_A = 0x15B */
+#define DA9150_TID_POLL_SHIFT 0
+#define DA9150_TID_POLL_MASK (0x07 << 0)
+#define DA9150_RID_CONV_SHIFT 3
+#define DA9150_RID_CONV_MASK (0x01 << 3)
+
+/* DA9150_ADET_RID_PT_CHG_H = 0x15C */
+#define DA9150_RID_PT_CHG_H_SHIFT 0
+#define DA9150_RID_PT_CHG_H_MASK (0xff << 0)
+
+/* DA9150_ADET_RID_PT_CHG_L = 0x15D */
+#define DA9150_RID_PT_CHG_L_SHIFT 6
+#define DA9150_RID_PT_CHG_L_MASK (0x03 << 6)
+
+/* DA9150_PPR_TCTR_B = 0x160 */
+#define DA9150_CHG_TCTR_VAL_SHIFT 0
+#define DA9150_CHG_TCTR_VAL_MASK (0xff << 0)
+
+/* DA9150_PPR_BKCTRL_A = 0x163 */
+#define DA9150_VBUS_MODE_SHIFT 0
+#define DA9150_VBUS_MODE_MASK (0x03 << 0)
+#define DA9150_VBUS_MODE_CHG (0x01 << 0)
+#define DA9150_VBUS_MODE_OTG (0x02 << 0)
+#define DA9150_VBUS_LPM_SHIFT 2
+#define DA9150_VBUS_LPM_MASK (0x03 << 2)
+#define DA9150_VBUS_SUSP_SHIFT 4
+#define DA9150_VBUS_SUSP_MASK (0x01 << 4)
+#define DA9150_VBUS_PWM_SHIFT 5
+#define DA9150_VBUS_PWM_MASK (0x01 << 5)
+#define DA9150_VBUS_ISO_SHIFT 6
+#define DA9150_VBUS_ISO_MASK (0x01 << 6)
+#define DA9150_VBUS_LDO_SHIFT 7
+#define DA9150_VBUS_LDO_MASK (0x01 << 7)
+
+/* DA9150_PPR_BKCFG_A = 0x164 */
+#define DA9150_VBUS_ISET_SHIFT 0
+#define DA9150_VBUS_ISET_MASK (0x1f << 0)
+#define DA9150_VBUS_IMAX_SHIFT 5
+#define DA9150_VBUS_IMAX_MASK (0x01 << 5)
+#define DA9150_VBUS_IOTG_SHIFT 6
+#define DA9150_VBUS_IOTG_MASK (0x03 << 6)
+
+/* DA9150_PPR_BKCFG_B = 0x165 */
+#define DA9150_VBUS_DROP_SHIFT 0
+#define DA9150_VBUS_DROP_MASK (0x0f << 0)
+#define DA9150_VBUS_FAULT_DIS_SHIFT 6
+#define DA9150_VBUS_FAULT_DIS_MASK (0x01 << 6)
+#define DA9150_OTG_FAULT_DIS_SHIFT 7
+#define DA9150_OTG_FAULT_DIS_MASK (0x01 << 7)
+
+/* DA9150_PPR_CHGCTRL_A = 0x166 */
+#define DA9150_CHG_EN_SHIFT 0
+#define DA9150_CHG_EN_MASK (0x01 << 0)
+
+/* DA9150_PPR_CHGCTRL_B = 0x167 */
+#define DA9150_CHG_VBAT_SHIFT 0
+#define DA9150_CHG_VBAT_MASK (0x1f << 0)
+#define DA9150_CHG_VDROP_SHIFT 6
+#define DA9150_CHG_VDROP_MASK (0x03 << 6)
+
+/* DA9150_PPR_CHGCTRL_C = 0x168 */
+#define DA9150_CHG_VFAULT_SHIFT 0
+#define DA9150_CHG_VFAULT_MASK (0x0f << 0)
+#define DA9150_CHG_IPRE_SHIFT 4
+#define DA9150_CHG_IPRE_MASK (0x03 << 4)
+
+/* DA9150_PPR_TCTR_A = 0x169 */
+#define DA9150_CHG_TCTR_SHIFT 0
+#define DA9150_CHG_TCTR_MASK (0x07 << 0)
+#define DA9150_CHG_TCTR_MODE_SHIFT 4
+#define DA9150_CHG_TCTR_MODE_MASK (0x01 << 4)
+
+/* DA9150_PPR_CHGCTRL_D = 0x16A */
+#define DA9150_CHG_IBAT_SHIFT 0
+#define DA9150_CHG_IBAT_MASK (0xff << 0)
+
+/* DA9150_PPR_CHGCTRL_E = 0x16B */
+#define DA9150_CHG_IEND_SHIFT 0
+#define DA9150_CHG_IEND_MASK (0xff << 0)
+
+/* DA9150_PPR_CHGCTRL_F = 0x16C */
+#define DA9150_CHG_VCOLD_SHIFT 0
+#define DA9150_CHG_VCOLD_MASK (0x1f << 0)
+#define DA9150_TBAT_TQA_EN_SHIFT 6
+#define DA9150_TBAT_TQA_EN_MASK (0x01 << 6)
+#define DA9150_TBAT_TDP_EN_SHIFT 7
+#define DA9150_TBAT_TDP_EN_MASK (0x01 << 7)
+
+/* DA9150_PPR_CHGCTRL_G = 0x16D */
+#define DA9150_CHG_VWARM_SHIFT 0
+#define DA9150_CHG_VWARM_MASK (0x1f << 0)
+
+/* DA9150_PPR_CHGCTRL_H = 0x16E */
+#define DA9150_CHG_VHOT_SHIFT 0
+#define DA9150_CHG_VHOT_MASK (0x1f << 0)
+
+/* DA9150_PPR_CHGCTRL_I = 0x16F */
+#define DA9150_CHG_ICOLD_SHIFT 0
+#define DA9150_CHG_ICOLD_MASK (0xff << 0)
+
+/* DA9150_PPR_CHGCTRL_J = 0x170 */
+#define DA9150_CHG_IWARM_SHIFT 0
+#define DA9150_CHG_IWARM_MASK (0xff << 0)
+
+/* DA9150_PPR_CHGCTRL_K = 0x171 */
+#define DA9150_CHG_IHOT_SHIFT 0
+#define DA9150_CHG_IHOT_MASK (0xff << 0)
+
+/* DA9150_PPR_CHGCTRL_L = 0x172 */
+#define DA9150_CHG_IBAT_TRED_SHIFT 0
+#define DA9150_CHG_IBAT_TRED_MASK (0xff << 0)
+
+/* DA9150_PPR_CHGCTRL_M = 0x173 */
+#define DA9150_CHG_VFLOAT_SHIFT 0
+#define DA9150_CHG_VFLOAT_MASK (0x0f << 0)
+#define DA9150_CHG_LPM_SHIFT 5
+#define DA9150_CHG_LPM_MASK (0x01 << 5)
+#define DA9150_CHG_NBLO_SHIFT 6
+#define DA9150_CHG_NBLO_MASK (0x01 << 6)
+#define DA9150_EBS_EN_SHIFT 7
+#define DA9150_EBS_EN_MASK (0x01 << 7)
+
+/* DA9150_PPR_THYST_A = 0x174 */
+#define DA9150_TBAT_T1_SHIFT 0
+#define DA9150_TBAT_T1_MASK (0xff << 0)
+
+/* DA9150_PPR_THYST_B = 0x175 */
+#define DA9150_TBAT_T2_SHIFT 0
+#define DA9150_TBAT_T2_MASK (0xff << 0)
+
+/* DA9150_PPR_THYST_C = 0x176 */
+#define DA9150_TBAT_T3_SHIFT 0
+#define DA9150_TBAT_T3_MASK (0xff << 0)
+
+/* DA9150_PPR_THYST_D = 0x177 */
+#define DA9150_TBAT_T4_SHIFT 0
+#define DA9150_TBAT_T4_MASK (0xff << 0)
+
+/* DA9150_PPR_THYST_E = 0x178 */
+#define DA9150_TBAT_T5_SHIFT 0
+#define DA9150_TBAT_T5_MASK (0xff << 0)
+
+/* DA9150_PPR_THYST_F = 0x179 */
+#define DA9150_TBAT_H1_SHIFT 0
+#define DA9150_TBAT_H1_MASK (0xff << 0)
+
+/* DA9150_PPR_THYST_G = 0x17A */
+#define DA9150_TBAT_H5_SHIFT 0
+#define DA9150_TBAT_H5_MASK (0xff << 0)
+
+/* DA9150_PAGE_CON_3 = 0x180 */
+#define DA9150_PAGE_SHIFT 0
+#define DA9150_PAGE_MASK (0x3f << 0)
+#define DA9150_WRITE_MODE_SHIFT 6
+#define DA9150_WRITE_MODE_MASK (0x01 << 6)
+#define DA9150_REVERT_SHIFT 7
+#define DA9150_REVERT_MASK (0x01 << 7)
+
+/* DA9150_PAGE_CON_4 = 0x200 */
+#define DA9150_PAGE_SHIFT 0
+#define DA9150_PAGE_MASK (0x3f << 0)
+#define DA9150_WRITE_MODE_SHIFT 6
+#define DA9150_WRITE_MODE_MASK (0x01 << 6)
+#define DA9150_REVERT_SHIFT 7
+#define DA9150_REVERT_MASK (0x01 << 7)
+
+/* DA9150_PAGE_CON_5 = 0x280 */
+#define DA9150_PAGE_SHIFT 0
+#define DA9150_PAGE_MASK (0x3f << 0)
+#define DA9150_WRITE_MODE_SHIFT 6
+#define DA9150_WRITE_MODE_MASK (0x01 << 6)
+#define DA9150_REVERT_SHIFT 7
+#define DA9150_REVERT_MASK (0x01 << 7)
+
+/* DA9150_PAGE_CON_6 = 0x300 */
+#define DA9150_PAGE_SHIFT 0
+#define DA9150_PAGE_MASK (0x3f << 0)
+#define DA9150_WRITE_MODE_SHIFT 6
+#define DA9150_WRITE_MODE_MASK (0x01 << 6)
+#define DA9150_REVERT_SHIFT 7
+#define DA9150_REVERT_MASK (0x01 << 7)
+
+/* DA9150_COREBTLD_STAT_A = 0x302 */
+#define DA9150_BOOTLD_STAT_SHIFT 0
+#define DA9150_BOOTLD_STAT_MASK (0x03 << 0)
+#define DA9150_CORE_LOCKUP_SHIFT 2
+#define DA9150_CORE_LOCKUP_MASK (0x01 << 2)
+
+/* DA9150_COREBTLD_CTRL_A = 0x303 */
+#define DA9150_CORE_RESET_SHIFT 0
+#define DA9150_CORE_RESET_MASK (0x01 << 0)
+#define DA9150_CORE_STOP_SHIFT 1
+#define DA9150_CORE_STOP_MASK (0x01 << 1)
+
+/* DA9150_CORE_CONFIG_A = 0x304 */
+#define DA9150_CORE_MEMMUX_SHIFT 0
+#define DA9150_CORE_MEMMUX_MASK (0x03 << 0)
+#define DA9150_WDT_AUTO_START_SHIFT 2
+#define DA9150_WDT_AUTO_START_MASK (0x01 << 2)
+#define DA9150_WDT_AUTO_LOCK_SHIFT 3
+#define DA9150_WDT_AUTO_LOCK_MASK (0x01 << 3)
+#define DA9150_WDT_HLT_NO_CLK_SHIFT 4
+#define DA9150_WDT_HLT_NO_CLK_MASK (0x01 << 4)
+
+/* DA9150_CORE_CONFIG_C = 0x305 */
+#define DA9150_CORE_SW_SIZE_SHIFT 0
+#define DA9150_CORE_SW_SIZE_MASK (0xff << 0)
+
+/* DA9150_CORE_CONFIG_B = 0x306 */
+#define DA9150_BOOTLD_EN_SHIFT 0
+#define DA9150_BOOTLD_EN_MASK (0x01 << 0)
+#define DA9150_CORE_EN_SHIFT 2
+#define DA9150_CORE_EN_MASK (0x01 << 2)
+#define DA9150_CORE_SW_SRC_SHIFT 3
+#define DA9150_CORE_SW_SRC_MASK (0x07 << 3)
+#define DA9150_DEEP_SLEEP_EN_SHIFT 7
+#define DA9150_DEEP_SLEEP_EN_MASK (0x01 << 7)
+
+/* DA9150_CORE_CFG_DATA_A = 0x307 */
+#define DA9150_CORE_CFG_DT_A_SHIFT 0
+#define DA9150_CORE_CFG_DT_A_MASK (0xff << 0)
+
+/* DA9150_CORE_CFG_DATA_B = 0x308 */
+#define DA9150_CORE_CFG_DT_B_SHIFT 0
+#define DA9150_CORE_CFG_DT_B_MASK (0xff << 0)
+
+/* DA9150_CORE_CMD_A = 0x309 */
+#define DA9150_CORE_CMD_SHIFT 0
+#define DA9150_CORE_CMD_MASK (0xff << 0)
+
+/* DA9150_CORE_DATA_A = 0x30A */
+#define DA9150_CORE_DATA_0_SHIFT 0
+#define DA9150_CORE_DATA_0_MASK (0xff << 0)
+
+/* DA9150_CORE_DATA_B = 0x30B */
+#define DA9150_CORE_DATA_1_SHIFT 0
+#define DA9150_CORE_DATA_1_MASK (0xff << 0)
+
+/* DA9150_CORE_DATA_C = 0x30C */
+#define DA9150_CORE_DATA_2_SHIFT 0
+#define DA9150_CORE_DATA_2_MASK (0xff << 0)
+
+/* DA9150_CORE_DATA_D = 0x30D */
+#define DA9150_CORE_DATA_3_SHIFT 0
+#define DA9150_CORE_DATA_3_MASK (0xff << 0)
+
+/* DA9150_CORE2WIRE_STAT_A = 0x310 */
+#define DA9150_FW_FWDL_ERR_SHIFT 7
+#define DA9150_FW_FWDL_ERR_MASK (0x01 << 7)
+
+/* DA9150_CORE2WIRE_CTRL_A = 0x311 */
+#define DA9150_FW_FWDL_EN_SHIFT 0
+#define DA9150_FW_FWDL_EN_MASK (0x01 << 0)
+#define DA9150_FG_QIF_EN_SHIFT 1
+#define DA9150_FG_QIF_EN_MASK (0x01 << 1)
+#define DA9150_CORE_BASE_ADDR_SHIFT 4
+#define DA9150_CORE_BASE_ADDR_MASK (0x0f << 4)
+
+/* DA9150_FW_CTRL_A = 0x312 */
+#define DA9150_FW_SEAL_SHIFT 0
+#define DA9150_FW_SEAL_MASK (0xff << 0)
+
+/* DA9150_FW_CTRL_C = 0x313 */
+#define DA9150_FW_FWDL_CRC_SHIFT 0
+#define DA9150_FW_FWDL_CRC_MASK (0xff << 0)
+
+/* DA9150_FW_CTRL_D = 0x314 */
+#define DA9150_FW_FWDL_BASE_SHIFT 0
+#define DA9150_FW_FWDL_BASE_MASK (0x0f << 0)
+
+/* DA9150_FG_CTRL_A = 0x315 */
+#define DA9150_FG_QIF_CODE_SHIFT 0
+#define DA9150_FG_QIF_CODE_MASK (0xff << 0)
+
+/* DA9150_FG_CTRL_B = 0x316 */
+#define DA9150_FG_QIF_VALUE_SHIFT 0
+#define DA9150_FG_QIF_VALUE_MASK (0xff << 0)
+
+/* DA9150_FW_CTRL_E = 0x317 */
+#define DA9150_FW_FWDL_SEG_SHIFT 0
+#define DA9150_FW_FWDL_SEG_MASK (0xff << 0)
+
+/* DA9150_FW_CTRL_B = 0x318 */
+#define DA9150_FW_FWDL_VALUE_SHIFT 0
+#define DA9150_FW_FWDL_VALUE_MASK (0xff << 0)
+
+/* DA9150_GPADC_CMAN = 0x320 */
+#define DA9150_GPADC_CEN_SHIFT 0
+#define DA9150_GPADC_CEN_MASK (0x01 << 0)
+#define DA9150_GPADC_CMUX_SHIFT 1
+#define DA9150_GPADC_CMUX_MASK (0x1f << 1)
+
+/* DA9150_GPADC_CRES_A = 0x322 */
+#define DA9150_GPADC_CRES_H_SHIFT 0
+#define DA9150_GPADC_CRES_H_MASK (0xff << 0)
+
+/* DA9150_GPADC_CRES_B = 0x323 */
+#define DA9150_GPADC_CRUN_SHIFT 0
+#define DA9150_GPADC_CRUN_MASK (0x01 << 0)
+#define DA9150_GPADC_CRES_L_SHIFT 6
+#define DA9150_GPADC_CRES_L_MASK (0x03 << 6)
+
+/* DA9150_CC_CFG_A = 0x328 */
+#define DA9150_CC_EN_SHIFT 0
+#define DA9150_CC_EN_MASK (0x01 << 0)
+#define DA9150_CC_TIMEBASE_SHIFT 1
+#define DA9150_CC_TIMEBASE_MASK (0x03 << 1)
+#define DA9150_CC_CFG_SHIFT 5
+#define DA9150_CC_CFG_MASK (0x03 << 5)
+#define DA9150_CC_ENDLESS_MODE_SHIFT 7
+#define DA9150_CC_ENDLESS_MODE_MASK (0x01 << 7)
+
+/* DA9150_CC_CFG_B = 0x329 */
+#define DA9150_CC_OPT_SHIFT 0
+#define DA9150_CC_OPT_MASK (0x03 << 0)
+#define DA9150_CC_PREAMP_SHIFT 2
+#define DA9150_CC_PREAMP_MASK (0x03 << 2)
+
+/* DA9150_CC_ICHG_RES_A = 0x32A */
+#define DA9150_CC_ICHG_RES_H_SHIFT 0
+#define DA9150_CC_ICHG_RES_H_MASK (0xff << 0)
+
+/* DA9150_CC_ICHG_RES_B = 0x32B */
+#define DA9150_CC_ICHG_RES_L_SHIFT 3
+#define DA9150_CC_ICHG_RES_L_MASK (0x1f << 3)
+
+/* DA9150_CC_IAVG_RES_A = 0x32C */
+#define DA9150_CC_IAVG_RES_H_SHIFT 0
+#define DA9150_CC_IAVG_RES_H_MASK (0xff << 0)
+
+/* DA9150_CC_IAVG_RES_B = 0x32D */
+#define DA9150_CC_IAVG_RES_L_SHIFT 0
+#define DA9150_CC_IAVG_RES_L_MASK (0xff << 0)
+
+/* DA9150_TAUX_CTRL_A = 0x330 */
+#define DA9150_TAUX_EN_SHIFT 0
+#define DA9150_TAUX_EN_MASK (0x01 << 0)
+#define DA9150_TAUX_MOD_SHIFT 1
+#define DA9150_TAUX_MOD_MASK (0x01 << 1)
+#define DA9150_TAUX_UPDATE_SHIFT 2
+#define DA9150_TAUX_UPDATE_MASK (0x01 << 2)
+
+/* DA9150_TAUX_RELOAD_H = 0x332 */
+#define DA9150_TAUX_RLD_H_SHIFT 0
+#define DA9150_TAUX_RLD_H_MASK (0xff << 0)
+
+/* DA9150_TAUX_RELOAD_L = 0x333 */
+#define DA9150_TAUX_RLD_L_SHIFT 3
+#define DA9150_TAUX_RLD_L_MASK (0x1f << 3)
+
+/* DA9150_TAUX_VALUE_H = 0x334 */
+#define DA9150_TAUX_VAL_H_SHIFT 0
+#define DA9150_TAUX_VAL_H_MASK (0xff << 0)
+
+/* DA9150_TAUX_VALUE_L = 0x335 */
+#define DA9150_TAUX_VAL_L_SHIFT 3
+#define DA9150_TAUX_VAL_L_MASK (0x1f << 3)
+
+/* DA9150_AUX_DATA_0 = 0x338 */
+#define DA9150_AUX_DAT_0_SHIFT 0
+#define DA9150_AUX_DAT_0_MASK (0xff << 0)
+
+/* DA9150_AUX_DATA_1 = 0x339 */
+#define DA9150_AUX_DAT_1_SHIFT 0
+#define DA9150_AUX_DAT_1_MASK (0xff << 0)
+
+/* DA9150_AUX_DATA_2 = 0x33A */
+#define DA9150_AUX_DAT_2_SHIFT 0
+#define DA9150_AUX_DAT_2_MASK (0xff << 0)
+
+/* DA9150_AUX_DATA_3 = 0x33B */
+#define DA9150_AUX_DAT_3_SHIFT 0
+#define DA9150_AUX_DAT_3_MASK (0xff << 0)
+
+/* DA9150_BIF_CTRL = 0x340 */
+#define DA9150_BIF_ISRC_EN_SHIFT 0
+#define DA9150_BIF_ISRC_EN_MASK (0x01 << 0)
+
+/* DA9150_TBAT_CTRL_A = 0x342 */
+#define DA9150_TBAT_EN_SHIFT 0
+#define DA9150_TBAT_EN_MASK (0x01 << 0)
+#define DA9150_TBAT_SW1_SHIFT 1
+#define DA9150_TBAT_SW1_MASK (0x01 << 1)
+#define DA9150_TBAT_SW2_SHIFT 2
+#define DA9150_TBAT_SW2_MASK (0x01 << 2)
+
+/* DA9150_TBAT_CTRL_B = 0x343 */
+#define DA9150_TBAT_SW_FRC_SHIFT 0
+#define DA9150_TBAT_SW_FRC_MASK (0x01 << 0)
+#define DA9150_TBAT_STAT_SW1_SHIFT 1
+#define DA9150_TBAT_STAT_SW1_MASK (0x01 << 1)
+#define DA9150_TBAT_STAT_SW2_SHIFT 2
+#define DA9150_TBAT_STAT_SW2_MASK (0x01 << 2)
+#define DA9150_TBAT_HIGH_CURR_SHIFT 3
+#define DA9150_TBAT_HIGH_CURR_MASK (0x01 << 3)
+
+/* DA9150_TBAT_RES_A = 0x344 */
+#define DA9150_TBAT_RES_H_SHIFT 0
+#define DA9150_TBAT_RES_H_MASK (0xff << 0)
+
+/* DA9150_TBAT_RES_B = 0x345 */
+#define DA9150_TBAT_RES_DIS_SHIFT 0
+#define DA9150_TBAT_RES_DIS_MASK (0x01 << 0)
+#define DA9150_TBAT_RES_L_SHIFT 6
+#define DA9150_TBAT_RES_L_MASK (0x03 << 6)
+
+#endif /* _DA9150_REGISTERS_H */
--
1.9.3
Signed-off-by: Adam Thomson <[email protected]>
---
Documentation/devicetree/bindings/iio/da9150-gpadc.txt | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/da9150-gpadc.txt
diff --git a/Documentation/devicetree/bindings/iio/da9150-gpadc.txt b/Documentation/devicetree/bindings/iio/da9150-gpadc.txt
new file mode 100644
index 0000000..c07228d
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/da9150-gpadc.txt
@@ -0,0 +1,16 @@
+Dialog Semiconductor DA9150 IIO GPADC bindings
+
+Required properties:
+- compatible: "dlg,da9150-gpadc" for DA9150 IIO GPADC
+- #io-channel-cells: Should be set to <1>
+ (See Documentation/devicetree/bindings/iio/iio-bindings.txt for further info)
+
+For further information on GPADC channels, see device datasheet.
+
+
+Example:
+
+ gpadc: da9150-gpadc {
+ compatible = "dlg,da9150-gpadc";
+ #io-channel-cells = <1>;
+ };
--
1.9.3
This patch adds support for DA9150 Charger & Fuel-Guage IC Charger.
Signed-off-by: Adam Thomson <[email protected]>
---
drivers/power/Kconfig | 23 ++
drivers/power/Makefile | 1 +
drivers/power/da9150-charger.c | 790 +++++++++++++++++++++++++++++++++++++
include/linux/mfd/da9150/charger.h | 58 +++
4 files changed, 872 insertions(+)
create mode 100644 drivers/power/da9150-charger.c
create mode 100644 include/linux/mfd/da9150/charger.h
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index ba69751..9835a78 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -185,6 +185,29 @@ config BATTERY_DA9052
Say Y here to enable support for batteries charger integrated into
DA9052 PMIC.
+config CHARGER_DA9150
+ tristate "Dialog Semiconductor DA9150 Charger support"
+ depends on MFD_DA9150
+ depends on DA9150_GPADC
+ depends on IIO
+ help
+ Say Y here to enable support for charger unit of the DA9150
+ Integrated Charger & Fuel-Gauge IC.
+
+ This driver can also be built as a module. If so, the module will be
+ called da9150-charger.
+
+config CHARGER_DA9150_OTG_VBUS_EVENT
+ bool "Dialog Semiconductor DA9150 OTG VBUS event support"
+ depends on CHARGER_DA9150
+ help
+ Say Y here if you have an OTG device driver which can supply events,
+ using the otg transceiver, for when a USB host or charger is connected
+ which provides power over VBUS. This setting overrides the default
+ DA9150 support to indicate this type of event. Note that the default
+ support cannot determine the type of device that is providing VBUS
+ power and therefore assumes AC charger type.
+
config BATTERY_MAX17040
tristate "Maxim MAX17040 Fuel Gauge"
depends on I2C
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ee54a3e..2cdefaa 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
+obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
diff --git a/drivers/power/da9150-charger.c b/drivers/power/da9150-charger.c
new file mode 100644
index 0000000..dd93fb4
--- /dev/null
+++ b/drivers/power/da9150-charger.c
@@ -0,0 +1,790 @@
+/*
+ * DA9150 Charger Driver
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/usb/phy.h>
+#include <linux/iio/consumer.h>
+#include <linux/sysfs.h>
+
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/pdata.h>
+#include <linux/mfd/da9150/registers.h>
+#include <linux/mfd/da9150/charger.h>
+
+
+/*
+ * Common property functions
+ */
+
+static inline int da9150_charger_supply_online(struct da9150_charger *charger,
+ struct power_supply *psy,
+ union power_supply_propval *val)
+{
+ val->intval = (psy == charger->supply_online) ? 1 : 0;
+
+ return 0;
+}
+
+static int da9150_charger_vbus_voltage_now(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ struct iio_channel *chan = charger->vbus_chan;
+
+ if (!chan)
+ return -EINVAL;
+
+ /* Read processed value - uV units */
+ return iio_read_channel_processed(chan, &val->intval);
+}
+
+static int da9150_charger_ibus_current_avg(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ struct iio_channel *chan = charger->ibus_chan;
+
+ if (!chan)
+ return -EINVAL;
+
+ /* Read processed value - uA degrees C units */
+ return iio_read_channel_processed(chan, &val->intval);
+}
+
+static int da9150_charger_tjunc_temp(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ struct iio_channel *chan = charger->tjunc_chan;
+
+ if (!chan)
+ return -EINVAL;
+
+ /* Read processed value - 0.1 degrees C units */
+ return iio_read_channel_processed(chan, &val->intval);
+}
+
+
+/*
+ * USB Properties
+ */
+
+static enum power_supply_property da9150_charger_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static int da9150_charger_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct da9150_charger *charger = dev_get_drvdata(psy->dev->parent);
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = da9150_charger_supply_online(charger, psy, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = da9150_charger_vbus_voltage_now(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ ret = da9150_charger_ibus_current_avg(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ ret = da9150_charger_tjunc_temp(charger, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+
+/*
+ * Battery Properties
+ */
+
+static int da9150_charger_battery_status(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ u8 reg;
+
+ /* Check to see if battery is discharging */
+ reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);
+
+ if (((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_OFF) ||
+ ((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_WAIT)) {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+
+ return 0;
+ }
+
+ reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
+
+ /* Now check for other states */
+ switch (reg & DA9150_CHG_STAT_MASK) {
+ case DA9150_CHG_STAT_ACT:
+ case DA9150_CHG_STAT_PRE:
+ case DA9150_CHG_STAT_CC:
+ case DA9150_CHG_STAT_CV:
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case DA9150_CHG_STAT_OFF:
+ case DA9150_CHG_STAT_SUSP:
+ case DA9150_CHG_STAT_TEMP:
+ case DA9150_CHG_STAT_TIME:
+ case DA9150_CHG_STAT_BAT:
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case DA9150_CHG_STAT_FULL:
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int da9150_charger_battery_health(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ u8 reg;
+
+ reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
+
+ /* Check if temperature limit reached */
+ switch (reg & DA9150_CHG_TEMP_MASK) {
+ case DA9150_CHG_TEMP_UNDER:
+ val->intval = POWER_SUPPLY_HEALTH_COLD;
+ return 0;
+ case DA9150_CHG_TEMP_OVER:
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ return 0;
+ default:
+ break;
+ }
+
+ /* Check for other health states */
+ switch (reg & DA9150_CHG_STAT_MASK) {
+ case DA9150_CHG_STAT_ACT:
+ case DA9150_CHG_STAT_PRE:
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ break;
+ case DA9150_CHG_STAT_TIME:
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ }
+
+ return 0;
+}
+
+static int da9150_charger_battery_present(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ u8 reg;
+
+ /* No valid TBAT reading so assume battery present */
+ if (charger->invalid_tbat) {
+ val->intval = 1;
+ return 0;
+ }
+
+ /* Check if battery present or removed */
+ reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
+ if ((reg & DA9150_CHG_STAT_MASK) == DA9150_CHG_STAT_BAT)
+ val->intval = 0;
+ else
+ val->intval = 1;
+
+ return 0;
+}
+
+static int da9150_charger_battery_charge_type(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ u8 reg;
+
+ reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
+
+ switch (reg & DA9150_CHG_STAT_MASK) {
+ case DA9150_CHG_STAT_CC:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ break;
+ case DA9150_CHG_STAT_ACT:
+ case DA9150_CHG_STAT_PRE:
+ case DA9150_CHG_STAT_CV:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ }
+
+ return 0;
+}
+
+static int da9150_charger_battery_voltage_min(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ u8 reg;
+
+ reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_C);
+
+ /* Value starts at 2500 mV, 50 mV increments, presented in uV */
+ val->intval = ((reg & DA9150_CHG_VFAULT_MASK) * 50000) + 2500000;
+
+ return 0;
+}
+
+static int da9150_charger_battery_voltage_now(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ struct iio_channel *chan = charger->vbat_chan;
+
+ if (!chan)
+ return -EINVAL;
+
+ /* Read processed value - uV units */
+ return iio_read_channel_processed(chan, &val->intval);
+}
+
+static int da9150_charger_battery_current_max(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ int reg;
+
+ reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_D);
+
+ val->intval = DA9150_CHG_IBAT_REG_TO_UA(reg);
+
+ return 0;
+}
+
+static int da9150_charger_battery_voltage_max(struct da9150_charger *charger,
+ union power_supply_propval *val)
+{
+ u8 reg;
+
+ reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_B);
+
+ /* Value starts at 3650 mV, 25 mV increments, presented in uV */
+ val->intval = ((reg & DA9150_CHG_VBAT_MASK) * 25000) + 3650000;
+ return 0;
+}
+
+static enum power_supply_property da9150_charger_bat_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+};
+
+static int da9150_charger_battery_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct da9150_charger *charger = dev_get_drvdata(psy->dev->parent);
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = da9150_charger_battery_status(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = da9150_charger_supply_online(charger, psy, val);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = da9150_charger_battery_health(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ ret = da9150_charger_battery_present(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ ret = da9150_charger_battery_charge_type(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ ret = da9150_charger_battery_voltage_min(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = da9150_charger_battery_voltage_now(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ ret = da9150_charger_battery_current_max(charger, val);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ ret = da9150_charger_battery_voltage_max(charger, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+
+/*
+ * Other properties
+ */
+
+static ssize_t da9150_charger_attr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+#define DA9150_CHARGER_ATTR(_name, _reg, _shift, _mask) \
+ { \
+ .attr = __ATTR(_name, S_IRUGO, \
+ da9150_charger_attr_show, \
+ NULL), \
+ .reg = _reg, \
+ .shift = _shift, \
+ .mask = _mask, \
+ }
+
+static struct da9150_charger_attr_map da9150_charger_attrs[] = {
+ DA9150_CHARGER_ATTR(chg_en, DA9150_PPR_CHGCTRL_A,
+ DA9150_CHG_EN_SHIFT, DA9150_CHG_EN_MASK),
+ DA9150_CHARGER_ATTR(chg_ipre, DA9150_PPR_CHGCTRL_C,
+ DA9150_CHG_IPRE_SHIFT, DA9150_CHG_IPRE_MASK),
+ DA9150_CHARGER_ATTR(chg_iend, DA9150_PPR_CHGCTRL_E,
+ DA9150_CHG_IEND_SHIFT, DA9150_CHG_IEND_MASK),
+ DA9150_CHARGER_ATTR(chg_temp, DA9150_STATUS_J,
+ DA9150_CHG_TEMP_SHIFT, DA9150_CHG_TEMP_MASK),
+ DA9150_CHARGER_ATTR(chg_vdrop, DA9150_PPR_CHGCTRL_B,
+ DA9150_CHG_VDROP_SHIFT, DA9150_CHG_VDROP_MASK),
+ DA9150_CHARGER_ATTR(tbat_upper, DA9150_PPR_THYST_E,
+ DA9150_TBAT_T5_SHIFT, DA9150_TBAT_T5_MASK),
+ DA9150_CHARGER_ATTR(tbat_lower, DA9150_PPR_THYST_A,
+ DA9150_TBAT_T1_SHIFT, DA9150_TBAT_T1_MASK),
+ DA9150_CHARGER_ATTR(vbus_mode, DA9150_PPR_BKCTRL_A,
+ DA9150_VBUS_MODE_SHIFT, DA9150_VBUS_MODE_MASK),
+ DA9150_CHARGER_ATTR(vbus_tred, DA9150_STATUS_H,
+ DA9150_VBUS_TRED_SHIFT, DA9150_VBUS_TRED_MASK),
+ DA9150_CHARGER_ATTR(vbus_drop_stat, DA9150_STATUS_H,
+ DA9150_VBUS_DROP_STAT_SHIFT,
+ DA9150_VBUS_DROP_STAT_MASK),
+};
+
+static ssize_t da9150_charger_attr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct da9150_charger *charger = dev_get_drvdata(dev);
+ struct da9150_charger_attr_map *charger_attrs;
+ int i;
+
+ /* Find attribute, and print out register field contents. */
+ for (i = 0, charger_attrs = da9150_charger_attrs;
+ i < ARRAY_SIZE(da9150_charger_attrs);
+ ++i, ++charger_attrs) {
+ if (attr == &charger_attrs->attr) {
+ u8 val;
+
+ val = da9150_reg_read(charger->da9150,
+ charger_attrs->reg);
+ val &= charger_attrs->mask;
+ val = val >> charger_attrs->shift;
+
+ return sprintf(buf, "0x%x\n", val);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * IRQs
+ */
+
+static irqreturn_t da9150_charger_chg_irq(int irq, void *data)
+{
+ struct da9150_charger *charger = data;
+
+ power_supply_changed(&charger->battery);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t da9150_charger_tjunc_irq(int irq, void *data)
+{
+ struct da9150_charger *charger = data;
+
+ /* Nothing we can really do except report this. */
+ dev_crit(charger->dev, "TJunc over temperature!!!\n");
+ power_supply_changed(&charger->ac);
+ power_supply_changed(&charger->usb);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t da9150_charger_vfault_irq(int irq, void *data)
+{
+ struct da9150_charger *charger = data;
+
+ /* Nothing we can really do except report this. */
+ dev_crit(charger->dev, "VSYS under voltage!!!\n");
+ power_supply_changed(&charger->ac);
+ power_supply_changed(&charger->usb);
+ power_supply_changed(&charger->battery);
+
+ return IRQ_HANDLED;
+}
+
+#ifndef CONFIG_CHARGER_DA9150_OTG_VBUS_EVENT
+static irqreturn_t da9150_charger_vbus_irq(int irq, void *data)
+{
+ struct da9150_charger *charger = data;
+ u8 reg;
+
+ reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);
+
+ /* Charger plugged in or battery only */
+ switch (reg & DA9150_VBUS_STAT_MASK) {
+ case DA9150_VBUS_STAT_OFF:
+ case DA9150_VBUS_STAT_WAIT:
+ charger->supply_online = &charger->battery;
+ break;
+ case DA9150_VBUS_STAT_CHG:
+ /* Cannot determine charger type so assume AC */
+ charger->supply_online = &charger->ac;
+ break;
+ default:
+ dev_warn(charger->dev, "Unknown VBUS state - reg = 0x%x\n",
+ reg);
+ charger->supply_online = NULL;
+ break;
+ }
+
+ power_supply_changed(&charger->ac);
+ power_supply_changed(&charger->battery);
+
+ return IRQ_HANDLED;
+}
+#endif /* CONFIG_CHARGER_DA9150_OTG_VBUS_EVENT */
+
+
+/*
+ * Power Management
+ */
+
+static void da9150_charger_shutdown(struct platform_device *pdev)
+{
+ struct da9150_charger *charger = platform_get_drvdata(pdev);
+
+ /* Only allow device into shutdown state if we're not charging. */
+ if ((charger->supply_online != &charger->ac) &&
+ (charger->supply_online != &charger->usb))
+ charger->da9150->can_shutdown = true;
+}
+
+
+/*
+ * OTG Notification & handling
+ */
+static void da9150_charger_otg_work(struct work_struct *data)
+{
+ struct da9150_charger *charger =
+ container_of(data, struct da9150_charger, otg_work);
+
+ switch (charger->usb_event) {
+#ifdef CONFIG_CHARGER_DA9150_OTG_VBUS_EVENT
+ case USB_EVENT_CHARGER:
+ /* AC Supply online */
+ charger->supply_online = &charger->ac;
+ power_supply_changed(&charger->ac);
+ power_supply_changed(&charger->battery);
+ break;
+ case USB_EVENT_VBUS:
+ case USB_EVENT_ENUMERATED:
+ /* USB Supply online */
+ charger->supply_online = &charger->usb;
+ power_supply_changed(&charger->usb);
+ power_supply_changed(&charger->battery);
+ break;
+#endif /* CONFIG_CHARGER_DA9150_OTG_VBUS_EVENT */
+ case USB_EVENT_ID:
+ /* Enable OTG Boost */
+ da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,
+ DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_OTG);
+ break;
+ case USB_EVENT_NONE:
+ /* Revert to charge mode */
+ power_supply_changed(&charger->ac);
+ power_supply_changed(&charger->usb);
+ power_supply_changed(&charger->battery);
+ da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,
+ DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_CHG);
+ break;
+ }
+}
+
+static int da9150_charger_otg_ncb(struct notifier_block *nb, unsigned long val,
+ void *priv)
+{
+ struct da9150_charger *charger =
+ container_of(nb, struct da9150_charger, otg_nb);
+
+ dev_dbg(charger->dev, "DA9150 OTG notify %lu\n", val);
+
+ charger->usb_event = val;
+ schedule_work(&charger->otg_work);
+
+ return NOTIFY_OK;
+}
+
+
+/*
+ * DT Platform Data
+ */
+
+static struct da9150_charger_pdata *da9150_charger_dt_pdata(struct device *dev)
+{
+ struct device_node *charger_node = dev->of_node;
+ struct da9150_charger_pdata *pdata;
+
+ pdata = devm_kzalloc(dev, sizeof(struct da9150_charger_pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->invalid_tbat =
+ of_property_read_bool(charger_node, "dlg,invalid-tbat");
+
+ return pdata;
+}
+
+
+/*
+ * Driver top level functions
+ */
+
+static int da9150_charger_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da9150_charger_pdata *charger_pdata = dev_get_platdata(dev);
+ struct da9150 *da9150 = dev_get_drvdata(dev->parent);
+ struct da9150_charger *charger;
+ struct power_supply *ac, *usb, *battery;
+ struct iio_channel *chan;
+ u8 reg;
+ int i, ret;
+
+ /* Check GPADC is available, if not defer until it is */
+ if (!da9150->gpadc_ready)
+ return -EPROBE_DEFER;
+
+ charger = devm_kzalloc(dev, sizeof(struct da9150_charger), GFP_KERNEL);
+ if (charger == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, charger);
+ charger->da9150 = da9150;
+ charger->dev = dev;
+
+ /* Handle DT data if provided */
+ if (dev->of_node) {
+ charger_pdata = da9150_charger_dt_pdata(dev);
+ dev->platform_data = charger_pdata;
+ }
+
+ /* Handle any platform data */
+ if (charger_pdata) {
+ /* Does platform expect battery to have valid tbat? */
+ reg = da9150_reg_read(da9150, DA9150_PPR_CHGCTRL_F);
+ if (charger_pdata->invalid_tbat &&
+ (reg & DA9150_TBAT_TQA_EN_MASK))
+ dev_warn(dev, "TBAT based charging expected, "
+ "but not enabled\n");
+
+ charger->invalid_tbat = charger_pdata->invalid_tbat;
+ }
+
+ /* Acquire ADC channels */
+ chan = iio_channel_get(dev, "CHAN_IBUS");
+ charger->ibus_chan = IS_ERR(chan) ? NULL : chan;
+
+ chan = iio_channel_get(dev, "CHAN_VBUS");
+ charger->vbus_chan = IS_ERR(chan) ? NULL : chan;
+
+ chan = iio_channel_get(dev, "CHAN_TJUNC");
+ charger->tjunc_chan = IS_ERR(chan) ? NULL : chan;
+
+ chan = iio_channel_get(dev, "CHAN_VBAT");
+ charger->vbat_chan = IS_ERR(chan) ? NULL : chan;
+
+ /* Register power supplies */
+ ac = &charger->ac;
+ usb = &charger->usb;
+ battery = &charger->battery;
+
+ ac->name = "da9150-ac",
+ ac->type = POWER_SUPPLY_TYPE_MAINS;
+ ac->properties = da9150_charger_props;
+ ac->num_properties = ARRAY_SIZE(da9150_charger_props);
+ ac->get_property = da9150_charger_get_prop;
+ ret = power_supply_register(dev, ac);
+ if (ret)
+ return ret;
+
+ usb->name = "da9150-usb",
+ usb->type = POWER_SUPPLY_TYPE_USB;
+ usb->properties = da9150_charger_props;
+ usb->num_properties = ARRAY_SIZE(da9150_charger_props);
+ usb->get_property = da9150_charger_get_prop;
+ ret = power_supply_register(dev, usb);
+ if (ret)
+ goto usb_fail;
+
+ battery->name = "da9150-battery";
+ battery->type = POWER_SUPPLY_TYPE_BATTERY;
+ battery->properties = da9150_charger_bat_props;
+ battery->num_properties = ARRAY_SIZE(da9150_charger_bat_props);
+ battery->get_property = da9150_charger_battery_get_prop;
+ ret = power_supply_register(dev, battery);
+ if (ret)
+ goto battery_fail;
+
+ /* Create additional sysfs attributes */
+ for (i = 0; i < ARRAY_SIZE(da9150_charger_attrs); ++i) {
+ ret = device_create_file(dev, &da9150_charger_attrs[i].attr);
+ if (ret < 0)
+ goto sysfs_fail;
+ }
+
+ /* Get initial online supply */
+ reg = da9150_reg_read(da9150, DA9150_STATUS_H);
+
+ /* Charger plugged in or battery only */
+ switch (reg & DA9150_VBUS_STAT_MASK) {
+ case DA9150_VBUS_STAT_OFF:
+ case DA9150_VBUS_STAT_WAIT:
+ charger->supply_online = &charger->battery;
+ break;
+ case DA9150_VBUS_STAT_CHG:
+ /* Default to AC charger as we don't know yet */
+ charger->supply_online = &charger->ac;
+ break;
+ default:
+ dev_warn(dev, "Unknown VBUS state - reg = 0x%x\n", reg);
+ charger->supply_online = NULL;
+ break;
+ }
+
+ /* Setup OTG reporting & configuration */
+ charger->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ if (charger->usb_phy != NULL) {
+ INIT_WORK(&charger->otg_work, da9150_charger_otg_work);
+ charger->otg_nb.notifier_call = da9150_charger_otg_ncb;
+ usb_register_notifier(charger->usb_phy, &charger->otg_nb);
+ }
+
+ /* Register IRQs */
+ ret = da9150_register_irq(pdev, charger, da9150_charger_chg_irq,
+ "CHG_STATUS");
+ if (ret < 0)
+ goto irq_fail;
+
+ ret = da9150_register_irq(pdev, charger, da9150_charger_tjunc_irq,
+ "CHG_TJUNC");
+ if (ret < 0)
+ goto irq_fail;
+
+ ret = da9150_register_irq(pdev, charger, da9150_charger_vfault_irq,
+ "CHG_VFAULT");
+ if (ret < 0)
+ goto irq_fail;
+
+#ifndef CONFIG_CHARGER_DA9150_OTG_VBUS_EVENT
+ da9150_register_irq(pdev, charger, da9150_charger_vbus_irq, "CHG_VBUS");
+ if (ret < 0)
+ goto irq_fail;
+#endif /* CONFIG_CHARGER_DA9150_OTG_VBUS_EVENT */
+
+ return ret;
+
+irq_fail:
+sysfs_fail:
+ while (--i >= 0)
+ device_remove_file(&pdev->dev, &da9150_charger_attrs[i].attr);
+
+battery_fail:
+ power_supply_unregister(usb);
+
+usb_fail:
+ power_supply_unregister(ac);
+
+ return ret;
+}
+
+static int da9150_charger_remove(struct platform_device *pdev)
+{
+ struct da9150_charger *charger = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(da9150_charger_attrs); ++i)
+ device_remove_file(&pdev->dev, &da9150_charger_attrs[i].attr);
+
+ /* Release ADC channels */
+ if (charger->ibus_chan)
+ iio_channel_release(charger->ibus_chan);
+ if (charger->vbus_chan)
+ iio_channel_release(charger->vbus_chan);
+ if (charger->tjunc_chan)
+ iio_channel_release(charger->tjunc_chan);
+ if (charger->vbat_chan)
+ iio_channel_release(charger->vbat_chan);
+
+ power_supply_unregister(&charger->battery);
+ power_supply_unregister(&charger->usb);
+ power_supply_unregister(&charger->ac);
+
+ return 0;
+}
+
+static struct platform_driver da9150_charger_driver = {
+ .driver = {
+ .name = "da9150-charger",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9150_charger_probe,
+ .remove = da9150_charger_remove,
+ .shutdown = da9150_charger_shutdown,
+};
+
+module_platform_driver(da9150_charger_driver);
+
+MODULE_DESCRIPTION("Charger Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <[email protected]");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/da9150/charger.h b/include/linux/mfd/da9150/charger.h
new file mode 100644
index 0000000..73c41b3
--- /dev/null
+++ b/include/linux/mfd/da9150/charger.h
@@ -0,0 +1,58 @@
+/*
+ * DA9150 MFD Driver - Charger Data
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _DA9150_CHARGER_H
+#define _DA9150_CHARGER_H
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/usb/otg.h>
+#include <linux/timer.h>
+#include <linux/iio/consumer.h>
+
+#include <linux/mfd/da9150/core.h>
+
+
+struct da9150_charger_attr_map {
+ struct device_attribute attr;
+ u16 reg;
+ u8 shift;
+ u8 mask;
+};
+
+/* Private data */
+struct da9150_charger {
+ struct da9150 *da9150;
+ struct device *dev;
+
+ struct power_supply ac;
+ struct power_supply usb;
+ struct power_supply battery;
+ struct power_supply *supply_online;
+
+ struct usb_phy *usb_phy;
+ struct notifier_block otg_nb;
+ struct work_struct otg_work;
+ unsigned long usb_event;
+
+ struct iio_channel *ibus_chan;
+ struct iio_channel *vbus_chan;
+ struct iio_channel *tjunc_chan;
+ struct iio_channel *vbat_chan;
+
+ bool invalid_tbat;
+};
+
+#endif /* _DA9150_CHARGER_H */
--
1.9.3
Signed-off-by: Adam Thomson <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index abc3080..cf9e582 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -36,6 +36,7 @@ dallas Maxim Integrated Products (formerly Dallas Semiconductor)
davicom DAVICOM Semiconductor, Inc.
denx Denx Software Engineering
digi Digi International Inc.
+dlg Dialog Semiconductor Ltd.
dlink D-Link Corporation
dmo Data Modul AG
ebv EBV Elektronik
--
1.9.3
Signed-off-by: Adam Thomson <[email protected]>
---
.../devicetree/bindings/power/da9150-charger.txt | 29 ++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/da9150-charger.txt
diff --git a/Documentation/devicetree/bindings/power/da9150-charger.txt b/Documentation/devicetree/bindings/power/da9150-charger.txt
new file mode 100644
index 0000000..079931b
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/da9150-charger.txt
@@ -0,0 +1,29 @@
+Dialog Semiconductor DA9150 Charger Power Supply bindings
+
+Required properties:
+- compatible: "dlg,da9150-charger" for DA9150 Charger Power Supply
+
+Optional properties:
+- io-channels: List of phandle and IIO specifier pairs
+- io-channel-names: List of channel names used by charger
+ ["CHAN_IBUS", "CHAN_VBUS", "CHAN_TJUNC", "CHAN_VBAT"]
+ (See Documentation/devicetree/bindings/iio/iio-bindings.txt for further info)
+- dlg,invalid-tbat: bool flag to indicate no valid TBAT pin provided in system.
+
+
+Example:
+
+ da9150-charger {
+ compatible = "dlg,da9150-charger";
+
+ io-channels = <&gpadc 0>,
+ <&gpadc 2>,
+ <&gpadc 8>,
+ <&gpadc 5>;
+ io-channel-names = "CHAN_IBUS",
+ "CHAN_VBUS",
+ "CHAN_TJUNC",
+ "CHAN_VBAT";
+
+ dlg,invalid-tbat;
+ };
--
1.9.3
On Wed, Jun 11, 2014 at 1:11 PM, Adam Thomson
<[email protected]> wrote:
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -36,6 +36,7 @@ dallas Maxim Integrated Products (formerly Dallas Semiconductor)
> davicom DAVICOM Semiconductor, Inc.
> denx Denx Software Engineering
> digi Digi International Inc.
> +dlg Dialog Semiconductor Ltd.
"diasemi" has been proposed before
https://groups.google.com/forum/#!msg/linux.kernel/htUxLpV_Ng0/ClJVAxgK4e4J
"dialog", "diasemi", and "dlg" all seem to be already in use...
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
On Wed, Jun 11, 2014 at 7:09 AM, Geert Uytterhoeven
<[email protected]> wrote:
> On Wed, Jun 11, 2014 at 1:11 PM, Adam Thomson
> <[email protected]> wrote:
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -36,6 +36,7 @@ dallas Maxim Integrated Products (formerly Dallas Semiconductor)
>> davicom DAVICOM Semiconductor, Inc.
>> denx Denx Software Engineering
>> digi Digi International Inc.
>> +dlg Dialog Semiconductor Ltd.
>
> "diasemi" has been proposed before
> https://groups.google.com/forum/#!msg/linux.kernel/htUxLpV_Ng0/ClJVAxgK4e4J
>
> "dialog", "diasemi", and "dlg" all seem to be already in use...
dlg is the correct one and seems to be the most widely used, so we
should go with that and mark the others deprecated.
diasemi is documented for the da9210, but not used anywhere.
There appears to be 2 different da9053 bindings. I'd guess the dialog
variant used on 2 i.MX boards is not used. Other i.MX boards use the
dlg version. I'm not sure what is going on with da9055. Please sort
these out.
Rob
On Wed, Jun 11, 2014 at 2:43 PM, Rob Herring <[email protected]> wrote:
> On Wed, Jun 11, 2014 at 7:09 AM, Geert Uytterhoeven
> <[email protected]> wrote:
>> On Wed, Jun 11, 2014 at 1:11 PM, Adam Thomson
>> <[email protected]> wrote:
>>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>>> @@ -36,6 +36,7 @@ dallas Maxim Integrated Products (formerly Dallas Semiconductor)
>>> davicom DAVICOM Semiconductor, Inc.
>>> denx Denx Software Engineering
>>> digi Digi International Inc.
>>> +dlg Dialog Semiconductor Ltd.
>>
>> "diasemi" has been proposed before
>> https://groups.google.com/forum/#!msg/linux.kernel/htUxLpV_Ng0/ClJVAxgK4e4J
>>
>> "dialog", "diasemi", and "dlg" all seem to be already in use...
>
> dlg is the correct one and seems to be the most widely used, so we
> should go with that and mark the others deprecated.
>
> diasemi is documented for the da9210, but not used anywhere.
It users are queued in Simon's tree, for two Renesas boards.
> There appears to be 2 different da9053 bindings. I'd guess the dialog
> variant used on 2 i.MX boards is not used. Other i.MX boards use the
> dlg version. I'm not sure what is going on with da9055. Please sort
> these out.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
> On Wed, Jun 11, 2014 at 13:43, Rob Herring wrote:
>
> dlg is the correct one and seems to be the most widely used, so we
> should go with that and mark the others deprecated.
>
> diasemi is documented for the da9210, but not used anywhere.
>
> There appears to be 2 different da9053 bindings. I'd guess the dialog
> variant used on 2 i.MX boards is not used. Other i.MX boards use the
> dlg version. I'm not sure what is going on with da9055. Please sort
> these out.
>
The intention is to update the other Dialog drivers after we've agreed this
prefix. With regards to da9055, what's the issue here as I believe that uses
the 'dlg' prefix already?
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
On Wed, Jun 11, 2014 at 14:17, Adam Thomson wrote:
> The intention is to update the other Dialog drivers after we've agreed this
> prefix. With regards to da9055, what's the issue here as I believe that uses
> the 'dlg' prefix already?
Never mind about my DA9055 question. Just spotted what you meant. Will
make sure this is sorted.
Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.
Please consider the environment before printing this e-mail
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
On Wed, Jun 11, 2014 at 8:17 AM, Opensource [Adam Thomson]
<[email protected]> wrote:
>> On Wed, Jun 11, 2014 at 13:43, Rob Herring wrote:
>>
>> dlg is the correct one and seems to be the most widely used, so we
>> should go with that and mark the others deprecated.
>>
>> diasemi is documented for the da9210, but not used anywhere.
>>
>> There appears to be 2 different da9053 bindings. I'd guess the dialog
>> variant used on 2 i.MX boards is not used. Other i.MX boards use the
>> dlg version. I'm not sure what is going on with da9055. Please sort
>> these out.
>>
>
> The intention is to update the other Dialog drivers after we've agreed this
> prefix. With regards to da9055, what's the issue here as I believe that uses
> the 'dlg' prefix already?
In documentation it does, but I'm not sure how these strings are used:
drivers/mfd/da9055-core.c: .of_compatible = "dialog,da9055-gpio",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-regulator",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-regulator",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-regulator",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-regulator",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-regulator",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-regulator",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-regulator",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-regulator",
drivers/mfd/da9055-core.c: .of_compatible = "dialog,da9055-onkey",
drivers/mfd/da9055-core.c: .of_compatible = "dialog,da9055-rtc",
drivers/mfd/da9055-core.c: .of_compatible = "dialog,da9055-hwmon",
drivers/mfd/da9055-core.c: .of_compatible =
"dialog,da9055-watchdog",
Rob
On June 11, 2014 12:11:28 PM GMT+01:00, Adam Thomson <[email protected]> wrote:
>This patch adds support for DA9150 Charger & Fuel-Gauge IC GPADC.
>
>Signed-off-by: Adam Thomson <[email protected]>
Hi Adam. Silly question but what are the _ channels?
I have put in a perhaps optimistic data sheet request via the website, throwing in your name. Hope you don't mind!
Jonathan
>---
> drivers/iio/adc/Kconfig | 9 +
> drivers/iio/adc/Makefile | 1 +
>drivers/iio/adc/da9150-gpadc.c | 396
>+++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/da9150/gpadc.h | 71 +++++++
> 4 files changed, 477 insertions(+)
> create mode 100644 drivers/iio/adc/da9150-gpadc.c
> create mode 100644 include/linux/mfd/da9150/gpadc.h
>
>diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>index 24c28e3..f5e9f72 100644
>--- a/drivers/iio/adc/Kconfig
>+++ b/drivers/iio/adc/Kconfig
>@@ -105,6 +105,15 @@ config AT91_ADC
> help
> Say yes here to build support for Atmel AT91 ADC.
>
>+config DA9150_GPADC
>+ tristate "Dialog DA9150 GPADC driver support"
>+ depends on MFD_DA9150
>+ help
>+ Say yes here to build support for Dialog DA9150 GPADC.
>+
>+ This driver can also be built as a module. If chosen, the module
>name
>+ will be da9150-gpadc.
>+
> config EXYNOS_ADC
> tristate "Exynos ADC driver support"
> depends on OF
>diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>index ab346d8..414b22f 100644
>--- a/drivers/iio/adc/Makefile
>+++ b/drivers/iio/adc/Makefile
>@@ -12,6 +12,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
> obj-$(CONFIG_AD7793) += ad7793.o
> obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
>+obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
> obj-$(CONFIG_MAX1363) += max1363.o
>diff --git a/drivers/iio/adc/da9150-gpadc.c
>b/drivers/iio/adc/da9150-gpadc.c
>new file mode 100644
>index 0000000..2107f86
>--- /dev/null
>+++ b/drivers/iio/adc/da9150-gpadc.c
>@@ -0,0 +1,396 @@
>+/*
>+ * DA9150 GPADC Driver
>+ *
>+ * Copyright (c) 2014 Dialog Semiconductor
>+ *
>+ * Author: Adam Thomson <[email protected]>
>+ *
>+ * 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.
>+ */
>+
>+#include <linux/kernel.h>
>+#include <linux/slab.h>
>+#include <linux/module.h>
>+#include <linux/platform_device.h>
>+#include <linux/interrupt.h>
>+
>+#include <linux/mfd/da9150/core.h>
>+#include <linux/mfd/da9150/pdata.h>
>+#include <linux/mfd/da9150/registers.h>
>+#include <linux/mfd/da9150/gpadc.h>
>+
>+#include <linux/iio/iio.h>
>+#include <linux/iio/machine.h>
>+#include <linux/iio/driver.h>
>+
>+
>+/*
>+ * IRQ Handling
>+ */
>+
>+static irqreturn_t da9150_gpadc_irq(int irq, void *data)
>+{
>+
>+ struct da9150_gpadc *gpadc = data;
>+
>+ complete(&gpadc->complete);
>+
>+ return IRQ_HANDLED;
>+}
>+
>+
>+/*
>+ * GPADC access
>+ */
>+
>+static inline int da9150_gpadc_gpio_2v_voltage_now(int raw_val)
>+{
>+ /* Convert to uV */
>+ return (((3 * ((raw_val * 1000) + 500)) / 2048) * 1000);
>+}
>+
>+static inline int da9150_gpadc_gpio_5v_voltage_now(int raw_val)
>+{
>+ /* Convert to uV */
>+ return (((6 * ((raw_val * 1000) + 500)) / 1024) * 1000);
>+}
>+
>+static inline int da9150_gpadc_ibus_current_avg(int raw_val)
>+{
>+ /* Convert to uA */
>+ return (((4 * ((raw_val * 1000) + 500)) / 2048) * 1000);
>+}
>+
>+static inline int da9150_gpadc_vbus_6v_voltage_now(int raw_val)
>+{
>+ /* Convert to uV */
>+ return (((3 * ((raw_val * 1000) + 500)) / 512) * 1000);
>+}
>+
>+static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val)
>+{
>+ /* Convert to uV */
>+ return (((21 * ((raw_val * 1000) + 500)) / 1024) * 1000);
>+}
>+
>+static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val)
>+{
>+ /* Convert to uV */
>+ return (((3 * ((raw_val * 1000) + 500)) / 512) * 1000);
>+}
>+
>+static inline int da9150_gpadc_vsys_1_5v_voltage_now(int raw_val)
>+{
>+ /* Convert to uV */
>+ return (((3 * ((raw_val * 1000) + 500)) / 2048) * 1000);
>+}
>+
>+static inline int da9150_gpadc_tjunc_temp(int raw_val)
>+{
>+ /* Convert to 0.1 degrees C */
>+ return (((879 - (1023 - raw_val)) * 10000) / 4420);
>+}
>+
>+static inline int da9150_gpadc_vbat_voltage_now(int raw_val)
>+{
>+ /* Convert to uV */
>+ return ((2932 * raw_val) + 1500000);
>+}
>+
>+int da9150_gpadc_read_process(int channel, int raw_val)
>+{
>+ int ret;
>+
>+ switch (channel) {
>+ case DA9150_GPADC_CHAN_GPIOA_2V:
>+ case DA9150_GPADC_CHAN_GPIOA_2V_:
>+ case DA9150_GPADC_CHAN_GPIOB_2V:
>+ case DA9150_GPADC_CHAN_GPIOB_2V_:
>+ case DA9150_GPADC_CHAN_GPIOC_2V:
>+ case DA9150_GPADC_CHAN_GPIOC_2V_:
>+ case DA9150_GPADC_CHAN_GPIOD_2V:
>+ case DA9150_GPADC_CHAN_GPIOD_2V_:
>+ ret = da9150_gpadc_gpio_2v_voltage_now(raw_val);
>+ break;
>+ case DA9150_GPADC_CHAN_IBUS_SENSE:
>+ case DA9150_GPADC_CHAN_IBUS_SENSE_:
>+ ret = da9150_gpadc_ibus_current_avg(raw_val);
>+ break;
>+ case DA9150_GPADC_CHAN_VBUS_DIV:
>+ ret = da9150_gpadc_vbus_6v_voltage_now(raw_val);
>+ break;
>+ case DA9150_GPADC_CHAN_VBUS_DIV_:
>+ ret = da9150_gpadc_vbus_21v_voltage_now(raw_val);
>+ break;
>+ case DA9150_GPADC_CHAN_VSYS:
>+ ret = da9150_gpadc_vsys_6v_voltage_now(raw_val);
>+ break;
>+ case DA9150_GPADC_CHAN_VSYS_:
>+ ret = da9150_gpadc_vsys_1_5v_voltage_now(raw_val);
>+ break;
>+ case DA9150_GPADC_CHAN_GPIOA_5V:
>+ case DA9150_GPADC_CHAN_GPIOA_5V_:
>+ case DA9150_GPADC_CHAN_GPIOB_5V:
>+ case DA9150_GPADC_CHAN_GPIOB_5V_:
>+ case DA9150_GPADC_CHAN_GPIOC_5V:
>+ case DA9150_GPADC_CHAN_GPIOC_5V_:
>+ case DA9150_GPADC_CHAN_GPIOD_5V:
>+ case DA9150_GPADC_CHAN_GPIOD_5V_:
>+ ret = da9150_gpadc_gpio_5v_voltage_now(raw_val);
>+ break;
>+ case DA9150_GPADC_CHAN_TJUNC_CORE:
>+ case DA9150_GPADC_CHAN_TJUNC_CORE_:
>+ case DA9150_GPADC_CHAN_TJUNC_OVP:
>+ case DA9150_GPADC_CHAN_TJUNC_OVP_:
>+ ret = da9150_gpadc_tjunc_temp(raw_val);
>+ break;
>+ case DA9150_GPADC_CHAN_VBAT:
>+ ret = da9150_gpadc_vbat_voltage_now(raw_val);
>+ break;
>+ default:
>+ /* No processing for other channels so return raw value */
>+ ret = raw_val;
>+ break;
>+ }
>+
>+ return ret;
>+}
>+
>+int da9150_gpadc_read_raw(struct iio_dev *indio_dev,
>+ struct iio_chan_spec const *chan,
>+ int *val, int *val2, long mask)
>+{
>+ struct da9150_gpadc *gpadc = iio_priv(indio_dev);
>+ u8 reg = 0;
>+ u8 result_regs[2];
>+ u16 result;
>+
>+ if ((mask != IIO_CHAN_INFO_RAW) && (mask != IIO_CHAN_INFO_PROCESSED))
>+ return -EINVAL;
>+
>+ if ((chan->channel < DA9150_GPADC_CHAN_GPIOA_2V) ||
>+ (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP_))
>+ return -EINVAL;
>+
>+ mutex_lock(&gpadc->lock);
>+
>+ /* Set channel & enable measurement */
>+ reg |= DA9150_GPADC_EN_MASK;
>+ reg |= chan->channel << DA9150_GPADC_MUX_SHIFT;
>+ da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN, reg);
>+
>+ /* Consume left-over completion from a previous timeout */
>+ try_wait_for_completion(&gpadc->complete);
>+
>+ /* Check for actual completion */
>+ wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5));
>+
>+ /* Read result and status from device */
>+ da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs);
>+
>+ /* Check to make sure device really has completed reading */
>+ if (result_regs[1] & DA9150_GPADC_RUN_MASK) {
>+ mutex_unlock(&gpadc->lock);
>+ dev_err(gpadc->dev, "Timeout on channel %d of GP-ADC\n",
>+ chan->channel);
>+ return -ETIMEDOUT;
>+ }
>+
>+ mutex_unlock(&gpadc->lock);
>+
>+ /* LSBs - 2 bits */
>+ result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >>
>+ DA9150_GPADC_RES_L_SHIFT;
>+ /* MSBs - 8 bits */
>+ result |= result_regs[0] << 2;
>+
>+ switch (mask) {
>+ case IIO_CHAN_INFO_PROCESSED:
>+ *val = da9150_gpadc_read_process(chan->channel, result);
>+ break;
>+ case IIO_CHAN_INFO_RAW:
>+ *val = result;
>+ break;
>+ }
>+
>+ return IIO_VAL_INT;
>+}
>+
>+
>+static const struct iio_info da9150_gpadc_info = {
>+ .read_raw = &da9150_gpadc_read_raw,
>+ .driver_module = THIS_MODULE,
>+};
>+
>+#define GPADC_CHANNEL(_id, _type, chan_info, _ext_name) { \
>+ .type = _type, \
>+ .indexed = 1, \
>+ .channel = DA9150_GPADC_CHAN_##_id, \
>+ .info_mask_separate = chan_info, \
>+ .extend_name = _ext_name, \
>+ .datasheet_name = #_id, \
>+}
>+
>+#define GPADC_CHANNEL_RAW(_id, _type, _ext_name) \
>+ GPADC_CHANNEL(_id, _type, BIT(IIO_CHAN_INFO_RAW), _ext_name)
>+
>+#define GPADC_CHANNEL_RAW_PROCESSED(_id, _type, _ext_name) \
>+ GPADC_CHANNEL(_id, _type, \
>+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
>+ _ext_name)
>+
>+/* Supported channels */
>+static const struct iio_chan_spec da9150_gpadc_channels[] = {
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOA_2V, IIO_VOLTAGE, "GPIOA_2V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOA_2V_, IIO_VOLTAGE, "GPIOA_2V_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOB_2V, IIO_VOLTAGE, "GPIOB_2V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOB_2V_, IIO_VOLTAGE, "GPIOB_2V_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOC_2V, IIO_VOLTAGE, "GPIOC_2V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOC_2V_, IIO_VOLTAGE, "GPIOC_2V_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOD_2V, IIO_VOLTAGE, "GPIOD_2V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOD_2V_, IIO_VOLTAGE, "GPIOD_2V_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(IBUS_SENSE, IIO_CURRENT, "IBUS"),
>+ GPADC_CHANNEL_RAW_PROCESSED(IBUS_SENSE_, IIO_CURRENT, "IBUS_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(VBUS_DIV, IIO_VOLTAGE, "VBUS_6V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(VBUS_DIV_, IIO_VOLTAGE, "VBUS_21V"),
>+ GPADC_CHANNEL_RAW(ID, IIO_VOLTAGE, "ID"),
>+ GPADC_CHANNEL_RAW(ID_, IIO_VOLTAGE, "ID_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(VSYS, IIO_VOLTAGE, "VSYS_6V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(VSYS_, IIO_VOLTAGE, "VSYS_1_5V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOA_5V, IIO_VOLTAGE, "GPIOA_5V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOA_5V_, IIO_VOLTAGE, "GPIOA_5V_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOB_5V, IIO_VOLTAGE, "GPIOB_5V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOB_5V_, IIO_VOLTAGE, "GPIOB_5V_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOC_5V, IIO_VOLTAGE, "GPIOC_5V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOC_5V_, IIO_VOLTAGE, "GPIOC_5V_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOD_5V, IIO_VOLTAGE, "GPIOD_5V"),
>+ GPADC_CHANNEL_RAW_PROCESSED(GPIOD_5V_, IIO_VOLTAGE, "GPIOD_5V_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(VBAT, IIO_VOLTAGE, "VBAT"),
>+ GPADC_CHANNEL_RAW_PROCESSED(VBAT_, IIO_VOLTAGE, "VBAT_"),
>+ GPADC_CHANNEL_RAW(TBAT, IIO_VOLTAGE, "TBAT"),
>+ GPADC_CHANNEL_RAW(TBAT_, IIO_VOLTAGE, "TBAT_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(TJUNC_CORE, IIO_TEMP, "TJUNC_CORE"),
>+ GPADC_CHANNEL_RAW_PROCESSED(TJUNC_CORE_, IIO_TEMP, "TJUNC_CORE_"),
>+ GPADC_CHANNEL_RAW_PROCESSED(TJUNC_OVP, IIO_TEMP, "TJUNC_OVP"),
>+ GPADC_CHANNEL_RAW_PROCESSED(TJUNC_OVP_, IIO_TEMP, "TJUNC_OVP_"),
>+};
>+
>+/* Default maps used by da9150-charger */
>+static struct iio_map da9150_gpadc_default_maps[] = {
>+ {
>+ .consumer_dev_name = "da9150-charger",
>+ .consumer_channel = "CHAN_IBUS",
>+ .adc_channel_label = "IBUS_SENSE",
>+ },
>+ {
>+ .consumer_dev_name = "da9150-charger",
>+ .consumer_channel = "CHAN_VBUS",
>+ .adc_channel_label = "VBUS_DIV_",
>+ },
>+ {
>+ .consumer_dev_name = "da9150-charger",
>+ .consumer_channel = "CHAN_TJUNC",
>+ .adc_channel_label = "TJUNC_CORE",
>+ },
>+ {
>+ .consumer_dev_name = "da9150-charger",
>+ .consumer_channel = "CHAN_VBAT",
>+ .adc_channel_label = "VBAT",
>+ },
>+ {},
>+};
>+
>+
>+/*
>+ * Driver top level functions
>+ */
>+
>+static int da9150_gpadc_probe(struct platform_device *pdev)
>+{
>+ struct device *dev = &pdev->dev;
>+ struct da9150 *da9150 = dev_get_drvdata(dev->parent);
>+ struct da9150_gpadc *gpadc;
>+ struct iio_dev *indio_dev;
>+
>+ int ret;
>+
>+ indio_dev = devm_iio_device_alloc(&pdev->dev,
>+ sizeof(struct da9150_gpadc));
>+ if (!indio_dev) {
>+ dev_err(&pdev->dev, "Failed to allocate IIO device\n");
>+ return -ENOMEM;
>+ }
>+ gpadc = iio_priv(indio_dev);
>+
>+ platform_set_drvdata(pdev, indio_dev);
>+ gpadc->da9150 = da9150;
>+ gpadc->dev = dev;
>+
>+ ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps);
>+ if (ret) {
>+ dev_err(dev, "Failed to register IIO maps: %d\n", ret);
>+ goto iio_map_fail;
>+ }
>+
>+ indio_dev->name = dev_name(dev);
>+ indio_dev->dev.parent = dev;
>+ indio_dev->dev.of_node = pdev->dev.of_node;
>+ indio_dev->info = &da9150_gpadc_info;
>+ indio_dev->modes = INDIO_DIRECT_MODE;
>+ indio_dev->channels = da9150_gpadc_channels;
>+ indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels);
>+
>+ ret = iio_device_register(indio_dev);
>+ if (ret) {
>+ dev_err(dev, "Failed to register IIO device: %d\n", ret);
>+ goto iio_dev_fail;
>+ }
>+
>+ mutex_init(&gpadc->lock);
>+ init_completion(&gpadc->complete);
>+
>+ /* Register IRQ */
>+ ret = da9150_register_irq(pdev, gpadc, da9150_gpadc_irq, "GPADC");
>+ if (ret < 0)
>+ goto irq_fail;
>+
>+ da9150->gpadc_ready = true;
>+ return ret;
>+
>+irq_fail:
>+ iio_device_unregister(indio_dev);
>+
>+iio_dev_fail:
>+ iio_map_array_unregister(indio_dev);
>+
>+iio_map_fail:
>+ return ret;
>+}
>+
>+static int da9150_gpadc_remove(struct platform_device *pdev)
>+{
>+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>+
>+ iio_map_array_unregister(indio_dev);
>+ iio_device_unregister(indio_dev);
>+
>+ return 0;
>+}
>+
>+static struct platform_driver da9150_gpadc_driver = {
>+ .driver = {
>+ .name = "da9150-gpadc",
>+ .owner = THIS_MODULE,
>+ },
>+ .probe = da9150_gpadc_probe,
>+ .remove = da9150_gpadc_remove,
>+};
>+
>+module_platform_driver(da9150_gpadc_driver);
>+
>+MODULE_DESCRIPTION("GPADC Driver for DA9150");
>+MODULE_AUTHOR("Adam Thomson <[email protected]");
>+MODULE_LICENSE("GPL");
>diff --git a/include/linux/mfd/da9150/gpadc.h
>b/include/linux/mfd/da9150/gpadc.h
>new file mode 100644
>index 0000000..3e46164
>--- /dev/null
>+++ b/include/linux/mfd/da9150/gpadc.h
>@@ -0,0 +1,71 @@
>+/*
>+ * DA9150 GPADC Driver - GPADC Data
>+ *
>+ * Copyright (c) 2014 Dialog Semiconductor
>+ *
>+ * Author: Adam Thomson <[email protected]>
>+ *
>+ * 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.
>+ */
>+
>+#ifndef _DA9150_GPADC_H
>+#define _DA9150_GPADC_H
>+
>+#include <linux/device.h>
>+#include <linux/iio/machine.h>
>+#include <linux/mutex.h>
>+#include <linux/completion.h>
>+
>+#include <linux/mfd/da9150/core.h>
>+
>+
>+/* Channels */
>+enum da9150_gpadc_channel {
>+ DA9150_GPADC_CHAN_GPIOA_2V = 0,
>+ DA9150_GPADC_CHAN_GPIOA_2V_,
>+ DA9150_GPADC_CHAN_GPIOB_2V,
>+ DA9150_GPADC_CHAN_GPIOB_2V_,
>+ DA9150_GPADC_CHAN_GPIOC_2V,
>+ DA9150_GPADC_CHAN_GPIOC_2V_,
>+ DA9150_GPADC_CHAN_GPIOD_2V,
>+ DA9150_GPADC_CHAN_GPIOD_2V_,
>+ DA9150_GPADC_CHAN_IBUS_SENSE,
>+ DA9150_GPADC_CHAN_IBUS_SENSE_,
>+ DA9150_GPADC_CHAN_VBUS_DIV,
>+ DA9150_GPADC_CHAN_VBUS_DIV_,
>+ DA9150_GPADC_CHAN_ID,
>+ DA9150_GPADC_CHAN_ID_,
>+ DA9150_GPADC_CHAN_VSYS,
>+ DA9150_GPADC_CHAN_VSYS_,
>+ DA9150_GPADC_CHAN_GPIOA_5V,
>+ DA9150_GPADC_CHAN_GPIOA_5V_,
>+ DA9150_GPADC_CHAN_GPIOB_5V,
>+ DA9150_GPADC_CHAN_GPIOB_5V_,
>+ DA9150_GPADC_CHAN_GPIOC_5V,
>+ DA9150_GPADC_CHAN_GPIOC_5V_,
>+ DA9150_GPADC_CHAN_GPIOD_5V,
>+ DA9150_GPADC_CHAN_GPIOD_5V_,
>+ DA9150_GPADC_CHAN_VBAT,
>+ DA9150_GPADC_CHAN_VBAT_,
>+ DA9150_GPADC_CHAN_TBAT,
>+ DA9150_GPADC_CHAN_TBAT_,
>+ DA9150_GPADC_CHAN_TJUNC_CORE,
>+ DA9150_GPADC_CHAN_TJUNC_CORE_,
>+ DA9150_GPADC_CHAN_TJUNC_OVP,
>+ DA9150_GPADC_CHAN_TJUNC_OVP_,
>+};
>+
>+
>+/* Private data */
>+struct da9150_gpadc {
>+ struct da9150 *da9150;
>+ struct device *dev;
>+
>+ struct mutex lock;
>+ struct completion complete;
>+};
>+
>+#endif /* _DA9150_GPADC_H */
>--
>1.9.3
>
>--
>To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>the body of a message to [email protected]
>More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Sent from my Android phone with K-9 Mail. Please excuse my brevity.
On June 11, 2014 17:35, Jonathon Cameron wrote:
> Hi Adam. Silly question but what are the _ channels?
>
> I have put in a perhaps optimistic data sheet request via the website, throwing in
> your name. Hope you don't mind!
>
> Jonathan
Hi Jonathon. Some of the _channels are a different representation of the reading
(e.g. different scale - one would be 0-6V range, and one would be 0-21V for
example). Others provide identical readings to the non _channel but have left
those in to keep the channel numbering sane and easy to reference.
No problem regarding the request.
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
On 11/06/14 12:11, Adam Thomson wrote:
> DA9150 is a combined Charger and Fuel-Gauge IC, with additional
> GPIO and GPADC functionality.
>
> This patch adds core support for the device.
>
> Signed-off-by: Adam Thomson <[email protected]>
Hi Adam,
Some general comments inline.
It's been a while since I've looked at any particularly similar parts,
but it seems to me that a lot of indirection gets added here that
if anything makes the codes slightly harder to follow...
Feel free to disagree with me though!
> ---
> drivers/mfd/Kconfig | 12 +
> drivers/mfd/Makefile | 2 +
> drivers/mfd/da9150-core.c | 398 ++++++++++++
> drivers/mfd/da9150-i2c.c | 268 ++++++++
> include/linux/mfd/da9150/core.h | 125 ++++
> include/linux/mfd/da9150/pdata.h | 148 +++++
> include/linux/mfd/da9150/registers.h | 1160 ++++++++++++++++++++++++++++++++++
> 7 files changed, 2113 insertions(+)
> create mode 100644 drivers/mfd/da9150-core.c
> create mode 100644 drivers/mfd/da9150-i2c.c
> create mode 100644 include/linux/mfd/da9150/core.h
> create mode 100644 include/linux/mfd/da9150/pdata.h
> create mode 100644 include/linux/mfd/da9150/registers.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3383412..4af7cc4 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -171,6 +171,18 @@ config MFD_DA9063
> Additional drivers must be enabled in order to use the functionality
> of the device.
>
> +config MFD_DA9150
> + bool "Dialog Semiconductor DA9150 Charger Fuel-Gauge chip"
> + depends on I2C=y
> + select MFD_CORE
> + select REGMAP_I2C
> + select REGMAP_IRQ
> + help
> + This adds support for the DA9150 integrated charger and fuel-gauge
> + chip. This driver provides common support for accessing the device.
> + Additional drivers must be enabled in order to use the specific
> + features of the device.
> +
> config MFD_MC13XXX
> tristate
> depends on (SPI_MASTER || I2C)
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 2851275..0344494 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -112,6 +112,8 @@ obj-$(CONFIG_MFD_DA9055) += da9055.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 da9150-i2c.o
> +
> obj-$(CONFIG_MFD_MAX14577) += max14577.o
> obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
> obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
> diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c
> new file mode 100644
> index 0000000..24c62ba
> --- /dev/null
> +++ b/drivers/mfd/da9150-core.c
> @@ -0,0 +1,398 @@
> +/*
> + * DA9150 Core MFD Driver
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <[email protected]>
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +
> +#include <linux/mfd/da9150/core.h>
> +#include <linux/mfd/da9150/registers.h>
> +#include <linux/mfd/da9150/pdata.h>
> +
> +
Single line comment syntax.
> +/*
> + * Register/Device Access
> + */
> +
To my mind all these wrappers add nothing significant so you might as well
just call da9150->read_dev etc directly.
Also, what are the read_qif and write_qif for? They don't seem to be used
anywhere.
> +int da9150_read_qif(struct da9150 *da9150, u8 addr, int count, u8 *buf)
> +{
> + int ret;
> +
> + ret = da9150->read_dev(da9150->core_qif, addr, count, buf);
> + if (ret < 0)
> + dev_err(da9150->dev, "Failed to read from QIF 0x%x: %d\n",
> + addr, ret);
> +
> + return ret;
> +}
> +
> +int da9150_write_qif(struct da9150 *da9150, u8 addr, int count, const u8 *buf)
> +{
> + int ret;
> +
> + ret = da9150->write_dev(da9150->core_qif, addr, count, buf);
> + if (ret < 0)
> + dev_err(da9150->dev, "Failed to write to QIF 0x%x: %d\n",
> + addr, ret);
> +
> + return ret;
> +}
> +
The only real reason I can see for these wrappers is because you want
to hide the struct da9150 contents from the children of the mfd. As you
aren't doing that, you might as well drop these in favour of direct
calls to regmap_read and friends.
> +u8 da9150_reg_read(struct da9150 *da9150, u16 reg)
> +{
> + int val, ret;
> +
> + ret = regmap_read(da9150->regmap, reg, &val);
> + if (ret < 0)
> + dev_err(da9150->dev, "Failed to read from reg 0x%x: %d\n",
> + reg, ret);
> +
> + return (u8) val;
> +}
> +
> +int da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val)
> +{
> + int ret;
> +
> + ret = regmap_write(da9150->regmap, reg, val);
> + if (ret < 0)
> + dev_err(da9150->dev, "Failed to write to reg 0x%x: %d\n",
> + reg, ret);
> +
> + return ret;
> +}
> +
> +int da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val)
> +{
> + int ret;
> +
> + ret = regmap_update_bits(da9150->regmap, reg, mask, val);
> + if (ret < 0)
> + dev_err(da9150->dev, "Failed to set bits in reg 0x%x: %d\n",
> + reg, ret);
> +
> + return ret;
> +}
> +
> +int da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf)
> +{
> + int ret;
> +
> + ret = regmap_bulk_read(da9150->regmap, reg, buf, count);
> + if (ret < 0)
> + dev_err(da9150->dev, "Failed to bulk read from reg 0x%x: %d\n",
> + reg, ret);
> +
> + return ret;
> +}
> +
> +int da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf)
> +{
> + int ret;
> +
> + ret = regmap_raw_write(da9150->regmap, reg, buf, count);
> + if (ret < 0)
> + dev_err(da9150->dev, "Failed to bulk write to reg 0x%x %d\n",
> + reg, ret);
> +
> + return ret;
> +}
> +
> +
> +/*
> + * IRQ Handling
> + */
> +
> +static struct regmap_irq da9150_irqs[] = {
> + [DA9150_IRQ_VBUS] = {
> + .reg_offset = 0,
> + .mask = DA9150_E_VBUS_MASK,
> + },
> + [DA9150_IRQ_CHG] = {
> + .reg_offset = 0,
> + .mask = DA9150_E_CHG_MASK,
> + },
> + [DA9150_IRQ_TCLASS] = {
> + .reg_offset = 0,
> + .mask = DA9150_E_TCLASS_MASK,
> + },
> + [DA9150_IRQ_TJUNC] = {
> + .reg_offset = 0,
> + .mask = DA9150_E_TJUNC_MASK,
> + },
> + [DA9150_IRQ_VFAULT] = {
> + .reg_offset = 0,
> + .mask = DA9150_E_VFAULT_MASK,
> + },
> + [DA9150_IRQ_CONF] = {
> + .reg_offset = 1,
> + .mask = DA9150_E_CONF_MASK,
> + },
> + [DA9150_IRQ_DAT] = {
> + .reg_offset = 1,
> + .mask = DA9150_E_DAT_MASK,
> + },
> + [DA9150_IRQ_DTYPE] = {
> + .reg_offset = 1,
> + .mask = DA9150_E_DTYPE_MASK,
> + },
> + [DA9150_IRQ_ID] = {
> + .reg_offset = 1,
> + .mask = DA9150_E_ID_MASK,
> + },
> + [DA9150_IRQ_ADP] = {
> + .reg_offset = 1,
> + .mask = DA9150_E_ADP_MASK,
> + },
> + [DA9150_IRQ_SESS_END] = {
> + .reg_offset = 1,
> + .mask = DA9150_E_SESS_END_MASK,
> + },
> + [DA9150_IRQ_SESS_VLD] = {
> + .reg_offset = 1,
> + .mask = DA9150_E_SESS_VLD_MASK,
> + },
> + [DA9150_IRQ_FG] = {
> + .reg_offset = 2,
> + .mask = DA9150_E_FG_MASK,
> + },
> + [DA9150_IRQ_GP] = {
> + .reg_offset = 2,
> + .mask = DA9150_E_GP_MASK,
> + },
> + [DA9150_IRQ_TBAT] = {
> + .reg_offset = 2,
> + .mask = DA9150_E_TBAT_MASK,
> + },
> + [DA9150_IRQ_GPIOA] = {
> + .reg_offset = 2,
> + .mask = DA9150_E_GPIOA_MASK,
> + },
> + [DA9150_IRQ_GPIOB] = {
> + .reg_offset = 2,
> + .mask = DA9150_E_GPIOB_MASK,
> + },
> + [DA9150_IRQ_GPIOC] = {
> + .reg_offset = 2,
> + .mask = DA9150_E_GPIOC_MASK,
> + },
> + [DA9150_IRQ_GPIOD] = {
> + .reg_offset = 2,
> + .mask = DA9150_E_GPIOD_MASK,
> + },
> + [DA9150_IRQ_GPADC] = {
> + .reg_offset = 2,
> + .mask = DA9150_E_GPADC_MASK,
> + },
> + [DA9150_IRQ_WKUP] = {
> + .reg_offset = 3,
> + .mask = DA9150_E_WKUP_MASK,
> + },
> +};
> +
> +static struct regmap_irq_chip da9150_regmap_irq_chip = {
> + .name = "da9150_irq",
> + .status_base = DA9150_EVENT_E,
> + .mask_base = DA9150_IRQ_MASK_E,
> + .ack_base = DA9150_EVENT_E,
> + .num_regs = DA9150_NUM_IRQ_REGS,
> + .irqs = da9150_irqs,
> + .num_irqs = ARRAY_SIZE(da9150_irqs),
> +};
> +
> +/* Helper function for sub-devices to request IRQs */
> +int da9150_register_irq(struct platform_device *pdev, void *dev_id,
> + irq_handler_t handler, const char *name)
> +{
> + int irq, ret;
> +
> + irq = platform_get_irq_byname(pdev, name);
> + if (irq < 0)
> + return irq;
> +
> + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, handler,
> + IRQF_ONESHOT, name, dev_id);
> + if (ret)
> + dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret);
> +
> + return ret;
> +}
> +
> +
> +/*
> + * MFDs
Multi function devices? Elements of perhaps.
> + */
> +
> +static struct resource da9150_gpio_resources[] = {
> + {
> + .name = "GPIOA",
> + .start = DA9150_IRQ_GPIOA,
> + .end = DA9150_IRQ_GPIOA,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct resource da9150_gpadc_resources[] = {
> + {
> + .name = "GPADC",
> + .start = DA9150_IRQ_GPADC,
> + .end = DA9150_IRQ_GPADC,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct resource da9150_charger_resources[] = {
> + {
> + .name = "CHG_STATUS",
> + .start = DA9150_IRQ_CHG,
> + .end = DA9150_IRQ_CHG,
> + .flags = IORESOURCE_IRQ,
> + },
> + {
> + .name = "CHG_TJUNC",
> + .start = DA9150_IRQ_TJUNC,
> + .end = DA9150_IRQ_TJUNC,
> + .flags = IORESOURCE_IRQ,
> + },
> + {
> + .name = "CHG_VFAULT",
> + .start = DA9150_IRQ_VFAULT,
> + .end = DA9150_IRQ_VFAULT,
> + .flags = IORESOURCE_IRQ,
> + },
> + {
> + .name = "CHG_VBUS",
> + .start = DA9150_IRQ_VBUS,
> + .end = DA9150_IRQ_VBUS,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct resource da9150_fg_resources[] = {
> + {
> + .name = "FG",
> + .start = DA9150_IRQ_FG,
> + .end = DA9150_IRQ_FG,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct mfd_cell da9150_devs[] = {
> + {
> + .name = "da9150-gpio",
> + .of_compatible = "dlg,da9150-gpio",
> + .resources = da9150_gpio_resources,
> + .num_resources = ARRAY_SIZE(da9150_gpio_resources),
> + },
> + {
> + .name = "da9150-gpadc",
> + .of_compatible = "dlg,da9150-gpadc",
> + .resources = da9150_gpadc_resources,
> + .num_resources = ARRAY_SIZE(da9150_gpadc_resources),
> + },
> + {
> + .name = "da9150-charger",
> + .of_compatible = "dlg,da9150-charger",
> + .resources = da9150_charger_resources,
> + .num_resources = ARRAY_SIZE(da9150_charger_resources),
> + },
> + {
> + .name = "da9150-fuelgauge",
> + .of_compatible = "dlg,da9150-fg",
> + .resources = da9150_fg_resources,
> + .num_resources = ARRAY_SIZE(da9150_fg_resources),
> + },
> +};
> +
> +
> +/*
> + * Device Init/Exit
> + */
I'll continue my tirade against obvious comments. Wrong format and
adds nothing to what is here as init and exit functions are clearly
doing what there name suggests (it's one of my pet hates ;)
> +
> +int da9150_device_init(struct da9150 *da9150)
> +{
> + struct da9150_pdata *pdata = da9150->dev->platform_data;
> + int ret;
> +
> + /* Handle platform data */
> + if (pdata) {
> + da9150->irq_base = pdata->irq_base;
> +
As a general good practice point, I'd rather that the driver supported
more than one instance of the chip.. Hence you'd take a copy of da9150_devs
to use here. I guess it is relatively unlikely with one of these, but
you never know ;)
> + da9150_devs[0].platform_data = pdata->gpio_pdata;
> + da9150_devs[0].pdata_size = sizeof(struct da9150_gpio_pdata);
> + da9150_devs[2].platform_data = pdata->charger_pdata;
> + da9150_devs[2].pdata_size = sizeof(struct da9150_charger_pdata);
> + da9150_devs[3].platform_data = pdata->fg_pdata;
> + da9150_devs[3].pdata_size = sizeof(struct da9150_fg_pdata);
> + } else {
> + da9150->irq_base = -1;
> + }
> +
> + /* Init IRQs */
> + ret = regmap_add_irq_chip(da9150->regmap, da9150->irq,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + da9150->irq_base, &da9150_regmap_irq_chip,
> + &da9150->regmap_irq_data);
> + if (ret < 0)
> + goto err_irq;
> +
> + da9150->irq_base = regmap_irq_chip_get_base(da9150->regmap_irq_data);
> +
> + /* Make the IRQ line a wake source */
> + enable_irq_wake(da9150->irq);
> +
> + /* Add MFD Devices */
> + ret = mfd_add_devices(da9150->dev, -1, da9150_devs,
> + ARRAY_SIZE(da9150_devs), NULL,
> + da9150->irq_base, NULL);
> + if (ret < 0) {
> + dev_err(da9150->dev, "Failed to add child devices: %d\n", ret);
> + goto err_mfd;
> + }
> +
> + return 0;
> +
> +err_mfd:
> + regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
> +err_irq:
> + return ret;
> +}
> +
> +void da9150_device_exit(struct da9150 *da9150)
> +{
> + regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
> + mfd_remove_devices(da9150->dev);
> + return;
> +}
> +
> +void da9150_device_shutdown(struct da9150 *da9150)
> +{
> + if (da9150->can_shutdown) {
> + /* Make sure we have a wakup source for the device */
> + da9150_set_bits(da9150, DA9150_CONFIG_D,
> + DA9150_WKUP_PM_EN_MASK,
> + DA9150_WKUP_PM_EN_MASK);
> +
> + /* Set device to DISABLED mode */
> + da9150_set_bits(da9150, DA9150_CONTROL_C,
> + DA9150_DISABLE_MASK, DA9150_DISABLE_MASK);
> + }
> +}
> +
> +MODULE_DESCRIPTION("MFD Core Driver for DA9150");
> +MODULE_AUTHOR("Adam Thomson <[email protected]");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mfd/da9150-i2c.c b/drivers/mfd/da9150-i2c.c
> new file mode 100644
> index 0000000..725aaef
> --- /dev/null
> +++ b/drivers/mfd/da9150-i2c.c
> @@ -0,0 +1,268 @@
> +/*
> + * DA9150 I2C Driver
Why does this need it's own file? Does the DA9150 support any other
interfaces?
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <[email protected]>
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/mfd/da9150/core.h>
> +#include <linux/mfd/da9150/registers.h>
> +
> +
> +/*
> + * Regmap Config
> + */
> +
> +static bool da9150_volatile_reg(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case DA9150_PAGE_CON:
> + case DA9150_STATUS_A:
> + case DA9150_STATUS_B:
> + case DA9150_STATUS_C:
> + case DA9150_STATUS_D:
> + case DA9150_STATUS_E:
> + case DA9150_STATUS_F:
> + case DA9150_STATUS_G:
> + case DA9150_STATUS_H:
> + case DA9150_STATUS_I:
> + case DA9150_STATUS_J:
> + case DA9150_STATUS_K:
> + case DA9150_STATUS_L:
> + case DA9150_STATUS_N:
> + case DA9150_FAULT_LOG_A:
> + case DA9150_FAULT_LOG_B:
> + case DA9150_EVENT_E:
> + case DA9150_EVENT_F:
> + case DA9150_EVENT_G:
> + case DA9150_EVENT_H:
> + case DA9150_CONTROL_B:
> + case DA9150_CONTROL_C:
> + case DA9150_GPADC_MAN:
> + case DA9150_GPADC_RES_A:
> + case DA9150_GPADC_RES_B:
> + case DA9150_ADETVB_CFG_C:
> + case DA9150_ADETD_STAT:
> + case DA9150_ADET_CMPSTAT:
> + case DA9150_ADET_CTRL_A:
> + case DA9150_PPR_TCTR_B:
> + case DA9150_COREBTLD_STAT_A:
> + case DA9150_CORE_DATA_A:
> + case DA9150_CORE_DATA_B:
> + case DA9150_CORE_DATA_C:
> + case DA9150_CORE_DATA_D:
> + case DA9150_CORE2WIRE_STAT_A:
> + case DA9150_FW_CTRL_C:
> + case DA9150_FG_CTRL_B:
> + case DA9150_FW_CTRL_B:
> + case DA9150_GPADC_CMAN:
> + case DA9150_GPADC_CRES_A:
> + case DA9150_GPADC_CRES_B:
> + case DA9150_CC_ICHG_RES_A:
> + case DA9150_CC_ICHG_RES_B:
> + case DA9150_CC_IAVG_RES_A:
> + case DA9150_CC_IAVG_RES_B:
> + case DA9150_TAUX_CTRL_A:
> + case DA9150_TAUX_VALUE_H:
> + case DA9150_TAUX_VALUE_L:
> + case DA9150_TBAT_RES_A:
> + case DA9150_TBAT_RES_B:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static const struct regmap_range_cfg da9150_range_cfg[] = {
> + {
> + .range_min = DA9150_PAGE_CON,
> + .range_max = DA9150_TBAT_RES_B,
> + .selector_reg = DA9150_PAGE_CON,
> + .selector_mask = DA9150_I2C_PAGE_MASK,
> + .selector_shift = DA9150_I2C_PAGE_SHIFT,
> + .window_start = 0,
> + .window_len = 256,
> + },
> +};
> +
> +static struct regmap_config da9150_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .ranges = da9150_range_cfg,
> + .num_ranges = ARRAY_SIZE(da9150_range_cfg),
> + .max_register = DA9150_TBAT_RES_B,
> +
> + .cache_type = REGCACHE_RBTREE,
> +
> + .volatile_reg = da9150_volatile_reg,
> +};
> +
> +
> +/*
> + * Device access for QIF interface
> + */
> +
> +static int da9150_i2c_read_device(struct i2c_client *client, u8 addr, int count,
> + u8 *buf)
> +{
> + struct i2c_msg xfer;
> + int ret;
> +
> + /*
> + * Read is split into two transfers as device expects STOP/START rather
> + * than repeated start to carry out this kind of access.
> + */
> +
> + /* Write address */
> + xfer.addr = client->addr;
> + xfer.flags = 0;
> + xfer.len = 1;
> + xfer.buf = &addr;
> +
> + ret = i2c_transfer(client->adapter, &xfer, 1);
> + if (ret != 1) {
> + if (ret < 0)
> + return ret;
> + else
> + return -EIO;
> + }
Not equivalent of i2c_smbus_write_byte?
> +
> + /* Read data */
> + xfer.addr = client->addr;
> + xfer.flags = I2C_M_RD;
> + xfer.len = count;
> + xfer.buf = buf;
> +
> + ret = i2c_transfer(client->adapter, &xfer, 1);
> + if (ret == 1)
> + return 0;
There is indeed nothing equivalent to this one...
> + else if (ret < 0)
> + return ret;
> + else
> + return -EIO;
> +}
> +
> +static int da9150_i2c_write_device(struct i2c_client *client, u8 addr, int count,
> + const u8 *buf)
> +{
> + struct i2c_msg xfer;
> + u8 *reg_data;
> + int ret;
> +
> + reg_data = kzalloc(1 + count, GFP_KERNEL);
> + if (!reg_data)
> + return -ENOMEM;
> +
This at least superficialy looks like i2c_smbus_i2c_write_block_data.
> + reg_data[0] = addr;
> + memcpy(®_data[1], buf, count);
> +
> + /* Write address & data */
> + xfer.addr = client->addr;
> + xfer.flags = 0;
> + xfer.len = 1 + count;
> + xfer.buf = reg_data;
> +
> + ret = i2c_transfer(client->adapter, &xfer, 1);
> + kfree(reg_data);
> + if (ret == 1)
> + return 0;
> + else if (ret < 0)
> + return ret;
> + else
> + return -EIO;
> +}
> +
> +
> +/*
> + * Driver top level functions
> + */
> +
> +static int da9150_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct da9150 *da9150;
> + int ret;
> +
> + da9150 = devm_kzalloc(&client->dev, sizeof(struct da9150), GFP_KERNEL);
> + if (da9150 == NULL)
> + return -ENOMEM;
> + da9150->dev = &client->dev;
> + da9150->irq = client->irq;
> + i2c_set_clientdata(client, da9150);
> + dev_set_drvdata(da9150->dev, da9150);
> +
> + da9150->core_qif = i2c_new_dummy(client->adapter, DA9150_QIF_I2C_ADDR);
> + i2c_set_clientdata(da9150->core_qif, da9150);
> +
> + da9150->regmap = devm_regmap_init_i2c(client, &da9150_regmap_config);
> + if (IS_ERR(da9150->regmap)) {
> + ret = PTR_ERR(da9150->regmap);
> + dev_err(da9150->dev, "Failed to allocate register map: %d\n",
> + ret);
> + return ret;
> + }
> +
> + da9150->read_dev = (read_dev_t) da9150_i2c_read_device;
> + da9150->write_dev = (write_dev_t) da9150_i2c_write_device;
Why the indirection? The da9150 only supports i2c as far as I can see.
> +
> + return da9150_device_init(da9150);
> +}
> +
> +static int da9150_remove(struct i2c_client *client)
> +{
> + struct da9150 *da9150 = i2c_get_clientdata(client);
> +
> + da9150_device_exit(da9150);
> + i2c_unregister_device(da9150->core_qif);
> +
> + return 0;
> +}
> +
> +static void da9150_shutdown(struct i2c_client *client)
> +{
> + struct da9150 *da9150 = i2c_get_clientdata(client);
> +
> + da9150_device_shutdown(da9150);
I'd roll this into one line and not bother with the local variable...
> +}
> +
> +static const struct i2c_device_id da9150_i2c_id[] = {
> + { "da9150", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, da9150_i2c_id);
> +
> +static const struct of_device_id da9150_of_match[] = {
> + { .compatible = "dlg,da9150", },
> + { }
> +};
> +
> +static struct i2c_driver da9150_driver = {
> + .driver = {
> + .name = "da9150",
> + .owner = THIS_MODULE,
> + .of_match_table = of_match_ptr(da9150_of_match),
> + },
> + .probe = da9150_probe,
> + .remove = da9150_remove,
> + .shutdown = da9150_shutdown,
> + .id_table = da9150_i2c_id,
> +};
> +
> +module_i2c_driver(da9150_driver);
> +
> +MODULE_DESCRIPTION("I2C Driver for DA9150");
> +MODULE_AUTHOR("Adam Thomson <[email protected]");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/da9150/core.h b/include/linux/mfd/da9150/core.h
> new file mode 100644
> index 0000000..39997406
> --- /dev/null
> +++ b/include/linux/mfd/da9150/core.h
> @@ -0,0 +1,125 @@
> +/*
> + * DA9150 MFD Driver - Core Data
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <[email protected]>
> + *
> + * 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.
> + */
> +
> +#ifndef _DA9150_CORE_H
> +#define _DA9150_CORE_H
> +
> +#include <linux/device.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/regmap.h>
> +#include <linux/mutex.h>
> +
> +/*
> + * Defines
> + */
> +
> +/* I2C sub-device addresses */
> +#define DA9150_QIF_I2C_ADDR 0x6D
> +
> +/* I2C address paging */
> +#define DA9150_REG_PAGE_SHIFT 8
> +#define DA9150_REG_PAGE_MASK 0xFF
> +
> +/* IRQs */
> +#define DA9150_NUM_IRQ_REGS 4
> +#define DA9150_IRQ_VBUS 0
> +#define DA9150_IRQ_CHG 1
> +#define DA9150_IRQ_TCLASS 2
> +#define DA9150_IRQ_TJUNC 3
> +#define DA9150_IRQ_VFAULT 4
> +#define DA9150_IRQ_CONF 5
> +#define DA9150_IRQ_DAT 6
> +#define DA9150_IRQ_DTYPE 7
> +#define DA9150_IRQ_ID 8
> +#define DA9150_IRQ_ADP 9
> +#define DA9150_IRQ_SESS_END 10
> +#define DA9150_IRQ_SESS_VLD 11
> +#define DA9150_IRQ_FG 12
> +#define DA9150_IRQ_GP 13
> +#define DA9150_IRQ_TBAT 14
> +#define DA9150_IRQ_GPIOA 15
> +#define DA9150_IRQ_GPIOB 16
> +#define DA9150_IRQ_GPIOC 17
> +#define DA9150_IRQ_GPIOD 18
> +#define DA9150_IRQ_GPADC 19
> +#define DA9150_IRQ_WKUP 20
> +
> +/* Helper macros to convert real world values to register values */
> +#define DA9150_CHG_IBAT_FACTOR 25000
> +#define DA9150_CHG_IEND_FACTOR 4800
> +#define DA9150_CHG_IBAT_UA_TO_REG(val) (val / DA9150_CHG_IBAT_FACTOR)
> +#define DA9150_CHG_IEND_UA_TO_REG(val) (val / DA9150_CHG_IEND_FACTOR)
> +#define DA9150_CHG_IBAT_REG_TO_UA(val) (val * DA9150_CHG_IBAT_FACTOR)
> +#define DA9150_CHG_IEND_REG_TO_UA(val) (val * DA9150_CHG_IEND_FACTOR)
> +
> +
> +/*
> + * I/O Function Typedefs
> + */
> +
> +typedef int (*read_dev_t)(void *client, u8 addr, int count, u8 *buf);
> +typedef int (*write_dev_t)(void *client, u8 addr, int count, const u8 *buf);
> +
> +/*
> + * Main structure
> + */
> +
> +struct da9150 {
> + struct device *dev;
> +
> + struct regmap *regmap;
> + struct i2c_client *core_qif;
> +
> + read_dev_t read_dev;
> + write_dev_t write_dev;
> +
> + struct regmap_irq_chip_data *regmap_irq_data;
> + int irq;
> + int irq_base;
> +
> + bool can_shutdown;
> + bool gpadc_ready;
> +};
> +
> +/*
> + * APIs
> + */
> +
> +/* Device I/O */
> +int da9150_read_core(struct da9150 *da9150, u8 addr, int count, u8 *buf);
> +int da9150_write_core(struct da9150 *da9150, u8 addr, int count, const u8 *buf);
> +
> +int da9150_read_qif(struct da9150 *da9150, u8 addr, int count, u8 *buf);
> +int da9150_write_qif(struct da9150 *da9150, u8 addr, int count, const u8 *buf);
> +
> +int da9150_read_fwdl(struct da9150 *da9150, u8 addr, int count, u8 *buf);
> +int da9150_write_fwdl(struct da9150 *da9150, u8 addr, int count, const u8 *buf);
> +
> +u8 da9150_reg_read(struct da9150 *da9150, u16 reg);
> +int da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val);
> +int da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val);
> +
> +int da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf);
> +int da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf);
> +
> +/* IRQ */
> +int da9150_register_irq(struct platform_device *pdev, void *dev_id,
> + irq_handler_t handler, const char *name);
> +
> +/* Init/Exit */
> +int da9150_device_init(struct da9150 *da9150);
> +void da9150_device_exit(struct da9150 *da9150);
> +void da9150_device_shutdown(struct da9150 *da9150);
> +
> +#endif /* _DA9150_CORE_H */
> diff --git a/include/linux/mfd/da9150/pdata.h b/include/linux/mfd/da9150/pdata.h
> new file mode 100644
> index 0000000..c1dd2b3
> --- /dev/null
> +++ b/include/linux/mfd/da9150/pdata.h
> @@ -0,0 +1,148 @@
> +/*
> + * DA9150 MFD Driver - Platform Data
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <[email protected]>
> + *
> + * 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.
> + */
> +
> +#ifndef _DA9150_PDATA_H
> +#define _DA9150_PDATA_H
> +
> +/*
> + * GPIO
> + */
> +
> +#define DA9150_NUM_GPIOS 4
> +
> +/* Values are as bit fields for registers */
> +enum da9150_gpi_trigger {
> + DA9150_GPI_ACTIVE_LOW = 0,
> + DA9150_GPI_ACTIVE_HIGH,
> +};
> +
> +enum da9150_gpi_mode {
> + DA9150_GPI_DIG = 0,
> + DA9150_GPI_ANA = 3,
> +};
> +
> +enum da9150_gpo_mode {
> + DA9150_GPO_OD = 1,
> + DA9150_GPO_PP,
> +};
> +
> +enum da9150_gpo_supply {
> + DA9150_GPO_SUPPLY_VDDIO1 = 0,
> + DA9150_GPO_SUPPLY_VDDIO2,
> +};
> +
> +enum da9150_gpio_pupd {
> + DA9150_GPIO_PUPD_DISABLED = 0,
> + DA9150_GPIO_PUPD_ENABLED,
> +};
> +
> +struct da9150_gpio_cfg {
> + enum da9150_gpi_trigger gpi_trigger;
> + enum da9150_gpi_mode gpi_mode;
> + enum da9150_gpo_mode gpo_mode;
> + enum da9150_gpo_supply gpo_supply;
> + enum da9150_gpio_pupd gpio_pupd;
> +};
> +
> +enum da9150_gpi_debounce_time {
> + DA9150_GPI_DEBOUNCE_TIME_OFF = 0,
> + DA9150_GPI_DEBOUNCE_TIME_100US,
> + DA9150_GPI_DEBOUNCE_TIME_1MS,
> + DA9150_GPI_DEBOUNCE_TIME_10MS24,
> + DA9150_GPI_DEBOUNCE_TIME_51MS20,
> + DA9150_GPI_DEBOUNCE_TIME_256MS,
> + DA9150_GPI_DEBOUNCE_TIME_512MS,
> + DA9150_GPI_DEBOUNCE_TIME_1024MS,
> +};
> +
> +/* Charging LED Data */
> +enum da9150_led_freq {
> + DA9150_LED_FREQ_ALWAYS_ON = 0,
> + DA9150_LED_FREQ_1_S,
> + DA9150_LED_FREQ_2_S,
> + DA9150_LED_FREQ_4_S,
> +};
> +
> +enum da9150_led_ontime {
> + DA9150_LED_ONTIME_10_MS = 0,
> + DA9150_LED_ONTIME_20_MS,
> + DA9150_LED_ONTIME_40_MS,
> + DA9150_LED_ONTIME_HALF_FREQ,
> +};
> +
> +enum da9150_pin {
> + DA9150_PIN_OFF = 0,
> + DA9150_PIN_GPIOA = 1,
> + DA9150_PIN_GPIOB = 2,
> + DA9150_PIN_GPIOC = 3,
> + DA9150_PIN_GPIOD = 4,
> +};
> +
> +
> +/* Fault Pin Data */
> +enum da9150_fault_type {
> + DA9150_FAULT_TYPE_VFAULT = 0,
> + DA9150_FAULT_TYPE_RESET,
> +};
> +
> +
> +/* GPIO data */
> +struct da9150_gpio_pdata {
> + int base;
> +
> + /* General GPIO config */
> + struct da9150_gpio_cfg gpio_cfg[DA9150_NUM_GPIOS];
> + enum da9150_gpi_debounce_time gpi_debounce_time;
> +
> + /* Charge LED features */
> + enum da9150_led_freq led_frequency;
> + bool led_dbl_flash;
> + enum da9150_led_ontime led_ontime;
> + enum da9150_pin led_pin;
> +
> + /* Fault Pin features */
> + enum da9150_pin fault_pin;
> + enum da9150_fault_type fault_type;
> +};
> +
> +/*
> + * Fuel-Gauge
> + */
> +
> +struct da9150_fg_pdata {
> + u32 update_interval; /* msecs */
> + u8 warn_soc_lvl; /* % value */
> + u8 crit_soc_lvl; /* % value */
> +};
> +
> +/*
> + * Charger
> + */
> +
> +struct da9150_charger_pdata {
> + bool invalid_tbat;
> +};
> +
> +
> +/*
> + * Main struct
> + */
Drop comments on things that are self evident. Also these are one
line comments so should be using the single line comment syntax.
> +
> +struct da9150_pdata {
> + int irq_base;
> + struct da9150_gpio_pdata *gpio_pdata;
> + struct da9150_fg_pdata *fg_pdata;
> + struct da9150_charger_pdata *charger_pdata;
> +};
> +
> +#endif /* _DA9150_PDATA_H */
> diff --git a/include/linux/mfd/da9150/registers.h b/include/linux/mfd/da9150/registers.h
> new file mode 100644
> index 0000000..a6a8e28
> --- /dev/null
> +++ b/include/linux/mfd/da9150/registers.h
> @@ -0,0 +1,1160 @@
> +/*
> + * DA9150 MFD Driver - Registers
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <[email protected]>
> + *
> + * 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.
> + */
> +
> +#ifndef _DA9150_REGISTERS_H
> +#define _DA9150_REGISTERS_H
> +
> +/*
> + * Registers
> + */
> +
> +#define DA9150_PAGE_CON 0x000
> +#define DA9150_STATUS_A 0x068
> +#define DA9150_STATUS_B 0x069
> +#define DA9150_STATUS_C 0x06A
> +#define DA9150_STATUS_D 0x06B
> +#define DA9150_STATUS_E 0x06C
> +#define DA9150_STATUS_F 0x06D
> +#define DA9150_STATUS_G 0x06E
> +#define DA9150_STATUS_H 0x06F
> +#define DA9150_STATUS_I 0x070
> +#define DA9150_STATUS_J 0x071
> +#define DA9150_STATUS_K 0x072
> +#define DA9150_STATUS_L 0x073
> +#define DA9150_STATUS_N 0x074
> +#define DA9150_FAULT_LOG_A 0x076
> +#define DA9150_FAULT_LOG_B 0x077
> +#define DA9150_EVENT_E 0x078
> +#define DA9150_EVENT_F 0x079
> +#define DA9150_EVENT_G 0x07A
> +#define DA9150_EVENT_H 0x07B
> +#define DA9150_IRQ_MASK_E 0x07C
> +#define DA9150_IRQ_MASK_F 0x07D
> +#define DA9150_IRQ_MASK_G 0x07E
> +#define DA9150_IRQ_MASK_H 0x07F
> +#define DA9150_PAGE_CON_1 0x080
> +#define DA9150_CONFIG_A 0x0E0
> +#define DA9150_CONFIG_B 0x0E1
> +#define DA9150_CONFIG_C 0x0E2
> +#define DA9150_CONFIG_D 0x0E3
> +#define DA9150_CONFIG_E 0x0E4
> +#define DA9150_CONTROL_A 0x0E5
> +#define DA9150_CONTROL_B 0x0E6
> +#define DA9150_CONTROL_C 0x0E7
> +#define DA9150_GPIO_A_B 0x0E8
> +#define DA9150_GPIO_C_D 0x0E9
> +#define DA9150_GPIO_MODE_CONT 0x0EA
> +#define DA9150_GPIO_CTRL_B 0x0EB
> +#define DA9150_GPIO_CTRL_A 0x0EC
> +#define DA9150_GPIO_CTRL_C 0x0ED
> +#define DA9150_GPIO_CFG_A 0x0EE
> +#define DA9150_GPIO_CFG_B 0x0EF
> +#define DA9150_GPIO_CFG_C 0x0F0
> +#define DA9150_GPADC_MAN 0x0F2
> +#define DA9150_GPADC_RES_A 0x0F4
> +#define DA9150_GPADC_RES_B 0x0F5
> +#define DA9150_PAGE_CON_2 0x100
> +#define DA9150_OTP_CONT_SHARED 0x101
> +#define DA9150_INTERFACE_SHARED 0x105
> +#define DA9150_CONFIG_A_SHARED 0x106
> +#define DA9150_CONFIG_D_SHARED 0x109
> +#define DA9150_ADETVB_CFG_C 0x150
> +#define DA9150_ADETD_STAT 0x151
> +#define DA9150_ADET_CMPSTAT 0x152
> +#define DA9150_ADET_CTRL_A 0x153
> +#define DA9150_ADETVB_CFG_B 0x154
> +#define DA9150_ADETVB_CFG_A 0x155
> +#define DA9150_ADETAC_CFG_A 0x156
> +#define DA9150_ADDETAC_CFG_B 0x157
> +#define DA9150_ADETAC_CFG_C 0x158
> +#define DA9150_ADETAC_CFG_D 0x159
> +#define DA9150_ADETVB_CFG_D 0x15A
> +#define DA9150_ADETID_CFG_A 0x15B
> +#define DA9150_ADET_RID_PT_CHG_H 0x15C
> +#define DA9150_ADET_RID_PT_CHG_L 0x15D
> +#define DA9150_PPR_TCTR_B 0x160
> +#define DA9150_PPR_BKCTRL_A 0x163
> +#define DA9150_PPR_BKCFG_A 0x164
> +#define DA9150_PPR_BKCFG_B 0x165
> +#define DA9150_PPR_CHGCTRL_A 0x166
> +#define DA9150_PPR_CHGCTRL_B 0x167
> +#define DA9150_PPR_CHGCTRL_C 0x168
> +#define DA9150_PPR_TCTR_A 0x169
> +#define DA9150_PPR_CHGCTRL_D 0x16A
> +#define DA9150_PPR_CHGCTRL_E 0x16B
> +#define DA9150_PPR_CHGCTRL_F 0x16C
> +#define DA9150_PPR_CHGCTRL_G 0x16D
> +#define DA9150_PPR_CHGCTRL_H 0x16E
> +#define DA9150_PPR_CHGCTRL_I 0x16F
> +#define DA9150_PPR_CHGCTRL_J 0x170
> +#define DA9150_PPR_CHGCTRL_K 0x171
> +#define DA9150_PPR_CHGCTRL_L 0x172
> +#define DA9150_PPR_CHGCTRL_M 0x173
> +#define DA9150_PPR_THYST_A 0x174
> +#define DA9150_PPR_THYST_B 0x175
> +#define DA9150_PPR_THYST_C 0x176
> +#define DA9150_PPR_THYST_D 0x177
> +#define DA9150_PPR_THYST_E 0x178
> +#define DA9150_PPR_THYST_F 0x179
> +#define DA9150_PPR_THYST_G 0x17A
> +#define DA9150_PAGE_CON_3 0x180
> +#define DA9150_PAGE_CON_4 0x200
> +#define DA9150_PAGE_CON_5 0x280
> +#define DA9150_PAGE_CON_6 0x300
> +#define DA9150_COREBTLD_STAT_A 0x302
> +#define DA9150_COREBTLD_CTRL_A 0x303
> +#define DA9150_CORE_CONFIG_A 0x304
> +#define DA9150_CORE_CONFIG_C 0x305
> +#define DA9150_CORE_CONFIG_B 0x306
> +#define DA9150_CORE_CFG_DATA_A 0x307
> +#define DA9150_CORE_CFG_DATA_B 0x308
> +#define DA9150_CORE_CMD_A 0x309
> +#define DA9150_CORE_DATA_A 0x30A
> +#define DA9150_CORE_DATA_B 0x30B
> +#define DA9150_CORE_DATA_C 0x30C
> +#define DA9150_CORE_DATA_D 0x30D
> +#define DA9150_CORE2WIRE_STAT_A 0x310
> +#define DA9150_CORE2WIRE_CTRL_A 0x311
> +#define DA9150_FW_CTRL_A 0x312
> +#define DA9150_FW_CTRL_C 0x313
> +#define DA9150_FW_CTRL_D 0x314
> +#define DA9150_FG_CTRL_A 0x315
> +#define DA9150_FG_CTRL_B 0x316
> +#define DA9150_FW_CTRL_E 0x317
> +#define DA9150_FW_CTRL_B 0x318
> +#define DA9150_GPADC_CMAN 0x320
> +#define DA9150_GPADC_CRES_A 0x322
> +#define DA9150_GPADC_CRES_B 0x323
> +#define DA9150_CC_CFG_A 0x328
> +#define DA9150_CC_CFG_B 0x329
> +#define DA9150_CC_ICHG_RES_A 0x32A
> +#define DA9150_CC_ICHG_RES_B 0x32B
> +#define DA9150_CC_IAVG_RES_A 0x32C
> +#define DA9150_CC_IAVG_RES_B 0x32D
> +#define DA9150_TAUX_CTRL_A 0x330
> +#define DA9150_TAUX_RELOAD_H 0x332
> +#define DA9150_TAUX_RELOAD_L 0x333
> +#define DA9150_TAUX_VALUE_H 0x334
> +#define DA9150_TAUX_VALUE_L 0x335
> +#define DA9150_AUX_DATA_0 0x338
> +#define DA9150_AUX_DATA_1 0x339
> +#define DA9150_AUX_DATA_2 0x33A
> +#define DA9150_AUX_DATA_3 0x33B
> +#define DA9150_BIF_CTRL 0x340
> +#define DA9150_TBAT_CTRL_A 0x342
> +#define DA9150_TBAT_CTRL_B 0x343
> +#define DA9150_TBAT_RES_A 0x344
> +#define DA9150_TBAT_RES_B 0x345
> +
> +
> +/*
> + * Bit fields
> + */
> +
> +/* DA9150_PAGE_CON = 0x000 */
> +#define DA9150_PAGE_SHIFT 0
> +#define DA9150_PAGE_MASK (0x3f << 0)
> +#define DA9150_I2C_PAGE_SHIFT 1
> +#define DA9150_I2C_PAGE_MASK (0x1f << 1)
> +#define DA9150_WRITE_MODE_SHIFT 6
> +#define DA9150_WRITE_MODE_MASK (0x01 << 6)
> +#define DA9150_REVERT_SHIFT 7
> +#define DA9150_REVERT_MASK (0x01 << 7)
> +
> +/* DA9150_STATUS_A = 0x068 */
> +#define DA9150_WKUP_STAT_SHIFT 2
> +#define DA9150_WKUP_STAT_MASK (0x0f << 2)
> +#define DA9150_SLEEP_STAT_SHIFT 6
> +#define DA9150_SLEEP_STAT_MASK (0x03 << 6)
> +
> +/* DA9150_STATUS_B = 0x069 */
> +#define DA9150_VFAULT_STAT_SHIFT 0
> +#define DA9150_VFAULT_STAT_MASK (0x01 << 0)
> +#define DA9150_TFAULT_STAT_SHIFT 1
> +#define DA9150_TFAULT_STAT_MASK (0x01 << 1)
> +
> +/* DA9150_STATUS_C = 0x06A */
> +#define DA9150_VDD33_STAT_SHIFT 0
> +#define DA9150_VDD33_STAT_MASK (0x01 << 0)
> +#define DA9150_VDD33_SLEEP_SHIFT 1
> +#define DA9150_VDD33_SLEEP_MASK (0x01 << 1)
> +#define DA9150_LFOSC_STAT_SHIFT 7
> +#define DA9150_LFOSC_STAT_MASK (0x01 << 7)
> +
> +/* DA9150_STATUS_D = 0x06B */
> +#define DA9150_GPIOA_STAT_SHIFT 0
> +#define DA9150_GPIOA_STAT_MASK (0x01 << 0)
> +#define DA9150_GPIOB_STAT_SHIFT 1
> +#define DA9150_GPIOB_STAT_MASK (0x01 << 1)
> +#define DA9150_GPIOC_STAT_SHIFT 2
> +#define DA9150_GPIOC_STAT_MASK (0x01 << 2)
> +#define DA9150_GPIOD_STAT_SHIFT 3
> +#define DA9150_GPIOD_STAT_MASK (0x01 << 3)
> +
> +/* DA9150_STATUS_E = 0x06C */
> +#define DA9150_DTYPE_SHIFT 0
> +#define DA9150_DTYPE_MASK (0x1f << 0)
> +#define DA9150_DTYPE_DT_NIL (0x00 << 0)
> +#define DA9150_DTYPE_DT_USB_OTG (0x01 << 0)
> +#define DA9150_DTYPE_DT_USB_STD (0x02 << 0)
> +#define DA9150_DTYPE_DT_USB_CHG (0x03 << 0)
> +#define DA9150_DTYPE_DT_ACA_CHG (0x04 << 0)
> +#define DA9150_DTYPE_DT_ACA_OTG (0x05 << 0)
> +#define DA9150_DTYPE_DT_ACA_DOC (0x06 << 0)
> +#define DA9150_DTYPE_DT_DED_CHG (0x07 << 0)
> +#define DA9150_DTYPE_DT_CR5_CHG (0x08 << 0)
> +#define DA9150_DTYPE_DT_CR4_CHG (0x0c << 0)
> +#define DA9150_DTYPE_DT_PT_CHG (0x11 << 0)
> +#define DA9150_DTYPE_DT_NN_ACC (0x16 << 0)
> +#define DA9150_DTYPE_DT_NN_CHG (0x17 << 0)
> +
> +/* DA9150_STATUS_F = 0x06D */
> +#define DA9150_SESS_VLD_SHIFT 0
> +#define DA9150_SESS_VLD_MASK (0x01 << 0)
> +#define DA9150_ID_ERR_SHIFT 1
> +#define DA9150_ID_ERR_MASK (0x01 << 1)
> +#define DA9150_PT_CHG_SHIFT 2
> +#define DA9150_PT_CHG_MASK (0x01 << 2)
> +
> +/* DA9150_STATUS_G = 0x06E */
> +#define DA9150_RID_SHIFT 0
> +#define DA9150_RID_MASK (0xff << 0)
> +
> +/* DA9150_STATUS_H = 0x06F */
> +#define DA9150_VBUS_STAT_SHIFT 0
> +#define DA9150_VBUS_STAT_MASK (0x07 << 0)
> +#define DA9150_VBUS_STAT_OFF (0x00 << 0)
> +#define DA9150_VBUS_STAT_WAIT (0x01 << 0)
> +#define DA9150_VBUS_STAT_CHG (0x02 << 0)
> +#define DA9150_VBUS_TRED_SHIFT 3
> +#define DA9150_VBUS_TRED_MASK (0x01 << 3)
> +#define DA9150_VBUS_DROP_STAT_SHIFT 4
> +#define DA9150_VBUS_DROP_STAT_MASK (0x0f << 4)
> +
> +/* DA9150_STATUS_I = 0x070 */
> +#define DA9150_VBUS_ISET_STAT_SHIFT 0
> +#define DA9150_VBUS_ISET_STAT_MASK (0x1f << 0)
> +#define DA9150_VBUS_OT_SHIFT 7
> +#define DA9150_VBUS_OT_MASK (0x01 << 7)
> +
> +/* DA9150_STATUS_J = 0x071 */
> +#define DA9150_CHG_STAT_SHIFT 0
> +#define DA9150_CHG_STAT_MASK (0x0f << 0)
> +#define DA9150_CHG_STAT_OFF (0x00 << 0)
> +#define DA9150_CHG_STAT_SUSP (0x01 << 0)
> +#define DA9150_CHG_STAT_ACT (0x02 << 0)
> +#define DA9150_CHG_STAT_PRE (0x03 << 0)
> +#define DA9150_CHG_STAT_CC (0x04 << 0)
> +#define DA9150_CHG_STAT_CV (0x05 << 0)
> +#define DA9150_CHG_STAT_FULL (0x06 << 0)
> +#define DA9150_CHG_STAT_TEMP (0x07 << 0)
> +#define DA9150_CHG_STAT_TIME (0x08 << 0)
> +#define DA9150_CHG_STAT_BAT (0x09 << 0)
> +#define DA9150_CHG_TEMP_SHIFT 4
> +#define DA9150_CHG_TEMP_MASK (0x07 << 4)
> +#define DA9150_CHG_TEMP_UNDER (0x06 << 4)
> +#define DA9150_CHG_TEMP_OVER (0x07 << 4)
> +#define DA9150_CHG_IEND_STAT_SHIFT 7
> +#define DA9150_CHG_IEND_STAT_MASK (0x01 << 7)
> +
> +/* DA9150_STATUS_K = 0x072 */
> +#define DA9150_CHG_IAV_H_SHIFT 0
> +#define DA9150_CHG_IAV_H_MASK (0xff << 0)
> +
> +/* DA9150_STATUS_L = 0x073 */
> +#define DA9150_CHG_IAV_L_SHIFT 5
> +#define DA9150_CHG_IAV_L_MASK (0x07 << 5)
> +
> +/* DA9150_STATUS_N = 0x074 */
> +#define DA9150_CHG_TIME_SHIFT 1
> +#define DA9150_CHG_TIME_MASK (0x01 << 1)
> +#define DA9150_CHG_TRED_SHIFT 2
> +#define DA9150_CHG_TRED_MASK (0x01 << 2)
> +#define DA9150_CHG_TJUNC_CLASS_SHIFT 3
> +#define DA9150_CHG_TJUNC_CLASS_MASK (0x07 << 3)
> +#define DA9150_CHG_TJUNC_CLASS_6 (0x06 << 3)
> +#define DA9150_EBS_STAT_SHIFT 6
> +#define DA9150_EBS_STAT_MASK (0x01 << 6)
> +#define DA9150_CHG_BAT_REMOVED_SHIFT 7
> +#define DA9150_CHG_BAT_REMOVED_MASK (0x01 << 7)
> +
> +/* DA9150_FAULT_LOG_A = 0x076 */
> +#define DA9150_TEMP_FAULT_SHIFT 0
> +#define DA9150_TEMP_FAULT_MASK (0x01 << 0)
> +#define DA9150_VSYS_FAULT_SHIFT 1
> +#define DA9150_VSYS_FAULT_MASK (0x01 << 1)
> +#define DA9150_START_FAULT_SHIFT 2
> +#define DA9150_START_FAULT_MASK (0x01 << 2)
> +#define DA9150_EXT_FAULT_SHIFT 3
> +#define DA9150_EXT_FAULT_MASK (0x01 << 3)
> +#define DA9150_POR_FAULT_SHIFT 4
> +#define DA9150_POR_FAULT_MASK (0x01 << 4)
> +
> +/* DA9150_FAULT_LOG_B = 0x077 */
> +#define DA9150_VBUS_FAULT_SHIFT 0
> +#define DA9150_VBUS_FAULT_MASK (0x01 << 0)
> +#define DA9150_OTG_FAULT_SHIFT 1
> +#define DA9150_OTG_FAULT_MASK (0x01 << 1)
> +
> +/* DA9150_EVENT_E = 0x078 */
> +#define DA9150_E_VBUS_SHIFT 0
> +#define DA9150_E_VBUS_MASK (0x01 << 0)
> +#define DA9150_E_CHG_SHIFT 1
> +#define DA9150_E_CHG_MASK (0x01 << 1)
> +#define DA9150_E_TCLASS_SHIFT 2
> +#define DA9150_E_TCLASS_MASK (0x01 << 2)
> +#define DA9150_E_TJUNC_SHIFT 3
> +#define DA9150_E_TJUNC_MASK (0x01 << 3)
> +#define DA9150_E_VFAULT_SHIFT 4
> +#define DA9150_E_VFAULT_MASK (0x01 << 4)
> +#define DA9150_EVENTS_H_SHIFT 5
> +#define DA9150_EVENTS_H_MASK (0x01 << 5)
> +#define DA9150_EVENTS_G_SHIFT 6
> +#define DA9150_EVENTS_G_MASK (0x01 << 6)
> +#define DA9150_EVENTS_F_SHIFT 7
> +#define DA9150_EVENTS_F_MASK (0x01 << 7)
> +
> +/* DA9150_EVENT_F = 0x079 */
> +#define DA9150_E_CONF_SHIFT 0
> +#define DA9150_E_CONF_MASK (0x01 << 0)
> +#define DA9150_E_DAT_SHIFT 1
> +#define DA9150_E_DAT_MASK (0x01 << 1)
> +#define DA9150_E_DTYPE_SHIFT 3
> +#define DA9150_E_DTYPE_MASK (0x01 << 3)
> +#define DA9150_E_ID_SHIFT 4
> +#define DA9150_E_ID_MASK (0x01 << 4)
> +#define DA9150_E_ADP_SHIFT 5
> +#define DA9150_E_ADP_MASK (0x01 << 5)
> +#define DA9150_E_SESS_END_SHIFT 6
> +#define DA9150_E_SESS_END_MASK (0x01 << 6)
> +#define DA9150_E_SESS_VLD_SHIFT 7
> +#define DA9150_E_SESS_VLD_MASK (0x01 << 7)
> +
> +/* DA9150_EVENT_G = 0x07A */
> +#define DA9150_E_FG_SHIFT 0
> +#define DA9150_E_FG_MASK (0x01 << 0)
> +#define DA9150_E_GP_SHIFT 1
> +#define DA9150_E_GP_MASK (0x01 << 1)
> +#define DA9150_E_TBAT_SHIFT 2
> +#define DA9150_E_TBAT_MASK (0x01 << 2)
> +#define DA9150_E_GPIOA_SHIFT 3
> +#define DA9150_E_GPIOA_MASK (0x01 << 3)
> +#define DA9150_E_GPIOB_SHIFT 4
> +#define DA9150_E_GPIOB_MASK (0x01 << 4)
> +#define DA9150_E_GPIOC_SHIFT 5
> +#define DA9150_E_GPIOC_MASK (0x01 << 5)
> +#define DA9150_E_GPIOD_SHIFT 6
> +#define DA9150_E_GPIOD_MASK (0x01 << 6)
> +#define DA9150_E_GPADC_SHIFT 7
> +#define DA9150_E_GPADC_MASK (0x01 << 7)
> +
> +/* DA9150_EVENT_H = 0x07B */
> +#define DA9150_E_WKUP_SHIFT 0
> +#define DA9150_E_WKUP_MASK (0x01 << 0)
> +
> +/* DA9150_IRQ_MASK_E = 0x07C */
> +#define DA9150_M_VBUS_SHIFT 0
> +#define DA9150_M_VBUS_MASK (0x01 << 0)
> +#define DA9150_M_CHG_SHIFT 1
> +#define DA9150_M_CHG_MASK (0x01 << 1)
> +#define DA9150_M_TJUNC_SHIFT 3
> +#define DA9150_M_TJUNC_MASK (0x01 << 3)
> +#define DA9150_M_VFAULT_SHIFT 4
> +#define DA9150_M_VFAULT_MASK (0x01 << 4)
> +
> +/* DA9150_IRQ_MASK_F = 0x07D */
> +#define DA9150_M_CONF_SHIFT 0
> +#define DA9150_M_CONF_MASK (0x01 << 0)
> +#define DA9150_M_DAT_SHIFT 1
> +#define DA9150_M_DAT_MASK (0x01 << 1)
> +#define DA9150_M_DTYPE_SHIFT 3
> +#define DA9150_M_DTYPE_MASK (0x01 << 3)
> +#define DA9150_M_ID_SHIFT 4
> +#define DA9150_M_ID_MASK (0x01 << 4)
> +#define DA9150_M_ADP_SHIFT 5
> +#define DA9150_M_ADP_MASK (0x01 << 5)
> +#define DA9150_M_SESS_END_SHIFT 6
> +#define DA9150_M_SESS_END_MASK (0x01 << 6)
> +#define DA9150_M_SESS_VLD_SHIFT 7
> +#define DA9150_M_SESS_VLD_MASK (0x01 << 7)
> +
> +/* DA9150_IRQ_MASK_G = 0x07E */
> +#define DA9150_M_FG_SHIFT 0
> +#define DA9150_M_FG_MASK (0x01 << 0)
> +#define DA9150_M_GP_SHIFT 1
> +#define DA9150_M_GP_MASK (0x01 << 1)
> +#define DA9150_M_TBAT_SHIFT 2
> +#define DA9150_M_TBAT_MASK (0x01 << 2)
> +#define DA9150_M_GPIOA_SHIFT 3
> +#define DA9150_M_GPIOA_MASK (0x01 << 3)
> +#define DA9150_M_GPIOB_SHIFT 4
> +#define DA9150_M_GPIOB_MASK (0x01 << 4)
> +#define DA9150_M_GPIOC_SHIFT 5
> +#define DA9150_M_GPIOC_MASK (0x01 << 5)
> +#define DA9150_M_GPIOD_SHIFT 6
> +#define DA9150_M_GPIOD_MASK (0x01 << 6)
> +#define DA9150_M_GPADC_SHIFT 7
> +#define DA9150_M_GPADC_MASK (0x01 << 7)
> +
> +/* DA9150_IRQ_MASK_H = 0x07F */
> +#define DA9150_M_WKUP_SHIFT 0
> +#define DA9150_M_WKUP_MASK (0x01 << 0)
> +
> +/* DA9150_PAGE_CON_1 = 0x080 */
> +#define DA9150_PAGE_SHIFT 0
> +#define DA9150_PAGE_MASK (0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT 6
> +#define DA9150_WRITE_MODE_MASK (0x01 << 6)
> +#define DA9150_REVERT_SHIFT 7
> +#define DA9150_REVERT_MASK (0x01 << 7)
> +
> +/* DA9150_CONFIG_A = 0x0E0 */
> +#define DA9150_RESET_DUR_SHIFT 0
> +#define DA9150_RESET_DUR_MASK (0x03 << 0)
> +#define DA9150_RESET_EXT_SHIFT 2
> +#define DA9150_RESET_EXT_MASK (0x03 << 2)
> +#define DA9150_START_MAX_SHIFT 4
> +#define DA9150_START_MAX_MASK (0x03 << 4)
> +#define DA9150_PS_WAIT_EN_SHIFT 6
> +#define DA9150_PS_WAIT_EN_MASK (0x01 << 6)
> +#define DA9150_PS_DISABLE_DIRECT_SHIFT 7
> +#define DA9150_PS_DISABLE_DIRECT_MASK (0x01 << 7)
> +
> +/* DA9150_CONFIG_B = 0x0E1 */
> +#define DA9150_VFAULT_ADJ_SHIFT 0
> +#define DA9150_VFAULT_ADJ_MASK (0x0f << 0)
> +#define DA9150_VFAULT_HYST_SHIFT 4
> +#define DA9150_VFAULT_HYST_MASK (0x07 << 4)
> +#define DA9150_VFAULT_EN_SHIFT 7
> +#define DA9150_VFAULT_EN_MASK (0x01 << 7)
> +
> +/* DA9150_CONFIG_C = 0x0E2 */
> +#define DA9150_VSYS_MIN_SHIFT 3
> +#define DA9150_VSYS_MIN_MASK (0x1f << 3)
> +
> +/* DA9150_CONFIG_D = 0x0E3 */
> +#define DA9150_LFOSC_EXT_SHIFT 0
> +#define DA9150_LFOSC_EXT_MASK (0x01 << 0)
> +#define DA9150_VDD33_DWN_SHIFT 1
> +#define DA9150_VDD33_DWN_MASK (0x01 << 1)
> +#define DA9150_WKUP_PM_EN_SHIFT 2
> +#define DA9150_WKUP_PM_EN_MASK (0x01 << 2)
> +#define DA9150_WKUP_CE_SEL_SHIFT 3
> +#define DA9150_WKUP_CE_SEL_MASK (0x03 << 3)
> +#define DA9150_WKUP_CLK32K_EN_SHIFT 5
> +#define DA9150_WKUP_CLK32K_EN_MASK (0x01 << 5)
> +#define DA9150_DISABLE_DEL_SHIFT 7
> +#define DA9150_DISABLE_DEL_MASK (0x01 << 7)
> +
> +/* DA9150_CONFIG_E = 0x0E4 */
> +#define DA9150_PM_SPKSUP_DIS_SHIFT 0
> +#define DA9150_PM_SPKSUP_DIS_MASK (0x01 << 0)
> +#define DA9150_PM_MERGE_SHIFT 1
> +#define DA9150_PM_MERGE_MASK (0x01 << 1)
> +#define DA9150_PM_SR_OFF_SHIFT 2
> +#define DA9150_PM_SR_OFF_MASK (0x01 << 2)
> +#define DA9150_PM_TIMEOUT_EN_SHIFT 3
> +#define DA9150_PM_TIMEOUT_EN_MASK (0x01 << 3)
> +#define DA9150_PM_DLY_SEL_SHIFT 4
> +#define DA9150_PM_DLY_SEL_MASK (0x07 << 4)
> +#define DA9150_PM_OUT_DLY_SEL_SHIFT 7
> +#define DA9150_PM_OUT_DLY_SEL_MASK (0x01 << 7)
> +
> +/* DA9150_CONTROL_A = 0x0E5 */
> +#define DA9150_VDD33_SL_SHIFT 0
> +#define DA9150_VDD33_SL_MASK (0x01 << 0)
> +#define DA9150_VDD33_LPM_SHIFT 1
> +#define DA9150_VDD33_LPM_MASK (0x03 << 1)
> +#define DA9150_VDD33_EN_SHIFT 3
> +#define DA9150_VDD33_EN_MASK (0x01 << 3)
> +#define DA9150_GPI_LPM_SHIFT 6
> +#define DA9150_GPI_LPM_MASK (0x01 << 6)
> +#define DA9150_PM_IF_LPM_SHIFT 7
> +#define DA9150_PM_IF_LPM_MASK (0x01 << 7)
> +
> +/* DA9150_CONTROL_B = 0x0E6 */
> +#define DA9150_LPM_SHIFT 0
> +#define DA9150_LPM_MASK (0x01 << 0)
> +#define DA9150_RESET_SHIFT 1
> +#define DA9150_RESET_MASK (0x01 << 1)
> +#define DA9150_RESET_USRCONF_EN_SHIFT 2
> +#define DA9150_RESET_USRCONF_EN_MASK (0x01 << 2)
> +
> +/* DA9150_CONTROL_C = 0x0E7 */
> +#define DA9150_DISABLE_SHIFT 0
> +#define DA9150_DISABLE_MASK (0x01 << 0)
> +
> +/* DA9150_GPIO_A_B = 0x0E8 */
> +#define DA9150_GPIOA_PIN_SHIFT 0
> +#define DA9150_GPIOA_PIN_MASK (0x07 << 0)
> +#define DA9150_GPIOA_PIN_GPI (0x00 << 0)
> +#define DA9150_GPIOA_PIN_GPO_OD (0x01 << 0)
> +#define DA9150_GPIOA_TYPE_SHIFT 3
> +#define DA9150_GPIOA_TYPE_MASK (0x01 << 3)
> +#define DA9150_GPIOB_PIN_SHIFT 4
> +#define DA9150_GPIOB_PIN_MASK (0x07 << 4)
> +#define DA9150_GPIOB_PIN_GPI (0x00 << 4)
> +#define DA9150_GPIOB_PIN_GPO_OD (0x01 << 4)
> +#define DA9150_GPIOB_TYPE_SHIFT 7
> +#define DA9150_GPIOB_TYPE_MASK (0x01 << 7)
> +
> +/* DA9150_GPIO_C_D = 0x0E9 */
> +#define DA9150_GPIOC_PIN_SHIFT 0
> +#define DA9150_GPIOC_PIN_MASK (0x07 << 0)
> +#define DA9150_GPIOC_PIN_GPI (0x00 << 0)
> +#define DA9150_GPIOC_PIN_GPO_OD (0x01 << 0)
> +#define DA9150_GPIOC_TYPE_SHIFT 3
> +#define DA9150_GPIOC_TYPE_MASK (0x01 << 3)
> +#define DA9150_GPIOD_PIN_SHIFT 4
> +#define DA9150_GPIOD_PIN_MASK (0x07 << 4)
> +#define DA9150_GPIOD_PIN_GPI (0x00 << 4)
> +#define DA9150_GPIOD_PIN_GPO_OD (0x01 << 4)
> +#define DA9150_GPIOD_TYPE_SHIFT 7
> +#define DA9150_GPIOD_TYPE_MASK (0x01 << 7)
> +
> +/* DA9150_GPIO_MODE_CONT = 0x0EA */
> +#define DA9150_GPIOA_MODE_SHIFT 0
> +#define DA9150_GPIOA_MODE_MASK (0x01 << 0)
> +#define DA9150_GPIOB_MODE_SHIFT 1
> +#define DA9150_GPIOB_MODE_MASK (0x01 << 1)
> +#define DA9150_GPIOC_MODE_SHIFT 2
> +#define DA9150_GPIOC_MODE_MASK (0x01 << 2)
> +#define DA9150_GPIOD_MODE_SHIFT 3
> +#define DA9150_GPIOD_MODE_MASK (0x01 << 3)
> +#define DA9150_GPIOA_CONT_SHIFT 4
> +#define DA9150_GPIOA_CONT_MASK (0x01 << 4)
> +#define DA9150_GPIOB_CONT_SHIFT 5
> +#define DA9150_GPIOB_CONT_MASK (0x01 << 5)
> +#define DA9150_GPIOC_CONT_SHIFT 6
> +#define DA9150_GPIOC_CONT_MASK (0x01 << 6)
> +#define DA9150_GPIOD_CONT_SHIFT 7
> +#define DA9150_GPIOD_CONT_MASK (0x01 << 7)
> +
> +/* DA9150_GPIO_CTRL_B = 0x0EB */
> +#define DA9150_WAKE_PIN_SHIFT 0
> +#define DA9150_WAKE_PIN_MASK (0x03 << 0)
> +#define DA9150_WAKE_MODE_SHIFT 2
> +#define DA9150_WAKE_MODE_MASK (0x01 << 2)
> +#define DA9150_WAKE_CONT_SHIFT 3
> +#define DA9150_WAKE_CONT_MASK (0x01 << 3)
> +#define DA9150_WAKE_DLY_SHIFT 4
> +#define DA9150_WAKE_DLY_MASK (0x01 << 4)
> +
> +/* DA9150_GPIO_CTRL_A = 0x0EC */
> +#define DA9150_GPIOA_ANAEN_SHIFT 0
> +#define DA9150_GPIOA_ANAEN_MASK (0x01 << 0)
> +#define DA9150_GPIOB_ANAEN_SHIFT 1
> +#define DA9150_GPIOB_ANAEN_MASK (0x01 << 1)
> +#define DA9150_GPIOC_ANAEN_SHIFT 2
> +#define DA9150_GPIOC_ANAEN_MASK (0x01 << 2)
> +#define DA9150_GPIOD_ANAEN_SHIFT 3
> +#define DA9150_GPIOD_ANAEN_MASK (0x01 << 3)
> +#define DA9150_GPIO_ANAEN 0x01
> +#define DA9150_GPIO_ANAEN_MASK 0x0F
> +#define DA9150_CHGLED_PIN_SHIFT 5
> +#define DA9150_CHGLED_PIN_MASK (0x07 << 5)
> +
> +/* DA9150_GPIO_CTRL_C = 0x0ED */
> +#define DA9150_CHGBL_DUR_SHIFT 0
> +#define DA9150_CHGBL_DUR_MASK (0x03 << 0)
> +#define DA9150_CHGBL_DBL_SHIFT 2
> +#define DA9150_CHGBL_DBL_MASK (0x01 << 2)
> +#define DA9150_CHGBL_FRQ_SHIFT 3
> +#define DA9150_CHGBL_FRQ_MASK (0x03 << 3)
> +#define DA9150_CHGBL_FLKR_SHIFT 5
> +#define DA9150_CHGBL_FLKR_MASK (0x01 << 5)
> +
> +/* DA9150_GPIO_CFG_A = 0x0EE */
> +#define DA9150_CE_LPM_DEB_SHIFT 0
> +#define DA9150_CE_LPM_DEB_MASK (0x07 << 0)
> +
> +/* DA9150_GPIO_CFG_B = 0x0EF */
> +#define DA9150_GPIOA_PUPD_SHIFT 0
> +#define DA9150_GPIOA_PUPD_MASK (0x01 << 0)
> +#define DA9150_GPIOB_PUPD_SHIFT 1
> +#define DA9150_GPIOB_PUPD_MASK (0x01 << 1)
> +#define DA9150_GPIOC_PUPD_SHIFT 2
> +#define DA9150_GPIOC_PUPD_MASK (0x01 << 2)
> +#define DA9150_GPIOD_PUPD_SHIFT 3
> +#define DA9150_GPIOD_PUPD_MASK (0x01 << 3)
> +#define DA9150_GPIO_PUPD_MASK (0xF << 0)
> +#define DA9150_GPI_DEB_SHIFT 4
> +#define DA9150_GPI_DEB_MASK (0x07 << 4)
> +#define DA9150_LPM_EN_SHIFT 7
> +#define DA9150_LPM_EN_MASK (0x01 << 7)
> +
> +/* DA9150_GPIO_CFG_C = 0x0F0 */
> +#define DA9150_GPI_V_SHIFT 0
> +#define DA9150_GPI_V_MASK (0x01 << 0)
> +#define DA9150_VDDIO_INT_SHIFT 1
> +#define DA9150_VDDIO_INT_MASK (0x01 << 1)
> +#define DA9150_FAULT_PIN_SHIFT 3
> +#define DA9150_FAULT_PIN_MASK (0x07 << 3)
> +#define DA9150_FAULT_TYPE_SHIFT 6
> +#define DA9150_FAULT_TYPE_MASK (0x01 << 6)
> +#define DA9150_NIRQ_PUPD_SHIFT 7
> +#define DA9150_NIRQ_PUPD_MASK (0x01 << 7)
> +
> +/* DA9150_GPADC_MAN = 0x0F2 */
> +#define DA9150_GPADC_EN_SHIFT 0
> +#define DA9150_GPADC_EN_MASK (0x01 << 0)
> +#define DA9150_GPADC_MUX_SHIFT 1
> +#define DA9150_GPADC_MUX_MASK (0x1f << 1)
> +
> +/* DA9150_GPADC_RES_A = 0x0F4 */
> +#define DA9150_GPADC_RES_H_SHIFT 0
> +#define DA9150_GPADC_RES_H_MASK (0xff << 0)
> +
> +/* DA9150_GPADC_RES_B = 0x0F5 */
> +#define DA9150_GPADC_RUN_SHIFT 0
> +#define DA9150_GPADC_RUN_MASK (0x01 << 0)
> +#define DA9150_GPADC_RES_L_SHIFT 6
> +#define DA9150_GPADC_RES_L_MASK (0x03 << 6)
> +
> +/* DA9150_PAGE_CON_2 = 0x100 */
> +#define DA9150_PAGE_SHIFT 0
> +#define DA9150_PAGE_MASK (0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT 6
> +#define DA9150_WRITE_MODE_MASK (0x01 << 6)
> +#define DA9150_REVERT_SHIFT 7
> +#define DA9150_REVERT_MASK (0x01 << 7)
> +
> +/* DA9150_OTP_CONT_SHARED = 0x101 */
> +#define DA9150_PC_DONE_SHIFT 3
> +#define DA9150_PC_DONE_MASK (0x01 << 3)
> +
> +/* DA9150_INTERFACE_SHARED = 0x105 */
> +#define DA9150_IF_BASE_ADDR_SHIFT 4
> +#define DA9150_IF_BASE_ADDR_MASK (0x0f << 4)
> +
> +/* DA9150_CONFIG_A_SHARED = 0x106 */
> +#define DA9150_NIRQ_VDD_SHIFT 1
> +#define DA9150_NIRQ_VDD_MASK (0x01 << 1)
> +#define DA9150_NIRQ_PIN_SHIFT 2
> +#define DA9150_NIRQ_PIN_MASK (0x01 << 2)
> +#define DA9150_NIRQ_TYPE_SHIFT 3
> +#define DA9150_NIRQ_TYPE_MASK (0x01 << 3)
> +#define DA9150_PM_IF_V_SHIFT 4
> +#define DA9150_PM_IF_V_MASK (0x01 << 4)
> +#define DA9150_PM_IF_FMP_SHIFT 5
> +#define DA9150_PM_IF_FMP_MASK (0x01 << 5)
> +#define DA9150_PM_IF_HSM_SHIFT 6
> +#define DA9150_PM_IF_HSM_MASK (0x01 << 6)
> +
> +/* DA9150_CONFIG_D_SHARED = 0x109 */
> +#define DA9150_NIRQ_MODE_SHIFT 1
> +#define DA9150_NIRQ_MODE_MASK (0x01 << 1)
> +
> +/* DA9150_ADETVB_CFG_C = 0x150 */
> +#define DA9150_TADP_RISE_SHIFT 0
> +#define DA9150_TADP_RISE_MASK (0xff << 0)
> +
> +/* DA9150_ADETD_STAT = 0x151 */
> +#define DA9150_DCD_STAT_SHIFT 0
> +#define DA9150_DCD_STAT_MASK (0x01 << 0)
> +#define DA9150_PCD_STAT_SHIFT 1
> +#define DA9150_PCD_STAT_MASK (0x03 << 1)
> +#define DA9150_SCD_STAT_SHIFT 3
> +#define DA9150_SCD_STAT_MASK (0x03 << 3)
> +#define DA9150_DP_STAT_SHIFT 5
> +#define DA9150_DP_STAT_MASK (0x01 << 5)
> +#define DA9150_DM_STAT_SHIFT 6
> +#define DA9150_DM_STAT_MASK (0x01 << 6)
> +
> +/* DA9150_ADET_CMPSTAT = 0x152 */
> +#define DA9150_DP_COMP_SHIFT 1
> +#define DA9150_DP_COMP_MASK (0x01 << 1)
> +#define DA9150_DM_COMP_SHIFT 2
> +#define DA9150_DM_COMP_MASK (0x01 << 2)
> +#define DA9150_ADP_SNS_COMP_SHIFT 3
> +#define DA9150_ADP_SNS_COMP_MASK (0x01 << 3)
> +#define DA9150_ADP_PRB_COMP_SHIFT 4
> +#define DA9150_ADP_PRB_COMP_MASK (0x01 << 4)
> +#define DA9150_ID_COMP_SHIFT 5
> +#define DA9150_ID_COMP_MASK (0x01 << 5)
> +
> +/* DA9150_ADET_CTRL_A = 0x153 */
> +#define DA9150_AID_DAT_SHIFT 0
> +#define DA9150_AID_DAT_MASK (0x01 << 0)
> +#define DA9150_AID_ID_SHIFT 1
> +#define DA9150_AID_ID_MASK (0x01 << 1)
> +#define DA9150_AID_TRIG_SHIFT 2
> +#define DA9150_AID_TRIG_MASK (0x01 << 2)
> +
> +/* DA9150_ADETVB_CFG_B = 0x154 */
> +#define DA9150_VB_MODE_SHIFT 0
> +#define DA9150_VB_MODE_MASK (0x03 << 0)
> +#define DA9150_VB_MODE_VB_SESS (0x01 << 0)
> +
> +#define DA9150_TADP_PRB_SHIFT 2
> +#define DA9150_TADP_PRB_MASK (0x01 << 2)
> +#define DA9150_DAT_RPD_EXT_SHIFT 5
> +#define DA9150_DAT_RPD_EXT_MASK (0x01 << 5)
> +#define DA9150_CONF_RPD_SHIFT 6
> +#define DA9150_CONF_RPD_MASK (0x01 << 6)
> +#define DA9150_CONF_SRP_SHIFT 7
> +#define DA9150_CONF_SRP_MASK (0x01 << 7)
> +
> +/* DA9150_ADETVB_CFG_A = 0x155 */
> +#define DA9150_AID_MODE_SHIFT 0
> +#define DA9150_AID_MODE_MASK (0x03 << 0)
> +#define DA9150_AID_EXT_POL_SHIFT 2
> +#define DA9150_AID_EXT_POL_MASK (0x01 << 2)
> +
> +/* DA9150_ADETAC_CFG_A = 0x156 */
> +#define DA9150_ISET_CDP_SHIFT 0
> +#define DA9150_ISET_CDP_MASK (0x1f << 0)
> +#define DA9150_CONF_DBP_SHIFT 5
> +#define DA9150_CONF_DBP_MASK (0x01 << 5)
> +
> +/* DA9150_ADDETAC_CFG_B = 0x157 */
> +#define DA9150_ISET_DCHG_SHIFT 0
> +#define DA9150_ISET_DCHG_MASK (0x1f << 0)
> +#define DA9150_CONF_GPIOA_SHIFT 5
> +#define DA9150_CONF_GPIOA_MASK (0x01 << 5)
> +#define DA9150_CONF_GPIOB_SHIFT 6
> +#define DA9150_CONF_GPIOB_MASK (0x01 << 6)
> +#define DA9150_AID_VB_SHIFT 7
> +#define DA9150_AID_VB_MASK (0x01 << 7)
> +
> +/* DA9150_ADETAC_CFG_C = 0x158 */
> +#define DA9150_ISET_DEF_SHIFT 0
> +#define DA9150_ISET_DEF_MASK (0x1f << 0)
> +#define DA9150_CONF_MODE_SHIFT 5
> +#define DA9150_CONF_MODE_MASK (0x03 << 5)
> +#define DA9150_AID_CR_DIS_SHIFT 7
> +#define DA9150_AID_CR_DIS_MASK (0x01 << 7)
> +
> +/* DA9150_ADETAC_CFG_D = 0x159 */
> +#define DA9150_ISET_UNIT_SHIFT 0
> +#define DA9150_ISET_UNIT_MASK (0x1f << 0)
> +#define DA9150_AID_UNCLAMP_SHIFT 5
> +#define DA9150_AID_UNCLAMP_MASK (0x01 << 5)
> +
> +/* DA9150_ADETVB_CFG_D = 0x15A */
> +#define DA9150_ID_MODE_SHIFT 0
> +#define DA9150_ID_MODE_MASK (0x03 << 0)
> +#define DA9150_DAT_MODE_SHIFT 2
> +#define DA9150_DAT_MODE_MASK (0x0f << 2)
> +#define DA9150_DAT_SWP_SHIFT 6
> +#define DA9150_DAT_SWP_MASK (0x01 << 6)
> +#define DA9150_DAT_CLAMP_EXT_SHIFT 7
> +#define DA9150_DAT_CLAMP_EXT_MASK (0x01 << 7)
> +
> +/* DA9150_ADETID_CFG_A = 0x15B */
> +#define DA9150_TID_POLL_SHIFT 0
> +#define DA9150_TID_POLL_MASK (0x07 << 0)
> +#define DA9150_RID_CONV_SHIFT 3
> +#define DA9150_RID_CONV_MASK (0x01 << 3)
> +
> +/* DA9150_ADET_RID_PT_CHG_H = 0x15C */
> +#define DA9150_RID_PT_CHG_H_SHIFT 0
> +#define DA9150_RID_PT_CHG_H_MASK (0xff << 0)
> +
> +/* DA9150_ADET_RID_PT_CHG_L = 0x15D */
> +#define DA9150_RID_PT_CHG_L_SHIFT 6
> +#define DA9150_RID_PT_CHG_L_MASK (0x03 << 6)
> +
> +/* DA9150_PPR_TCTR_B = 0x160 */
> +#define DA9150_CHG_TCTR_VAL_SHIFT 0
> +#define DA9150_CHG_TCTR_VAL_MASK (0xff << 0)
> +
> +/* DA9150_PPR_BKCTRL_A = 0x163 */
> +#define DA9150_VBUS_MODE_SHIFT 0
> +#define DA9150_VBUS_MODE_MASK (0x03 << 0)
> +#define DA9150_VBUS_MODE_CHG (0x01 << 0)
> +#define DA9150_VBUS_MODE_OTG (0x02 << 0)
> +#define DA9150_VBUS_LPM_SHIFT 2
> +#define DA9150_VBUS_LPM_MASK (0x03 << 2)
> +#define DA9150_VBUS_SUSP_SHIFT 4
> +#define DA9150_VBUS_SUSP_MASK (0x01 << 4)
> +#define DA9150_VBUS_PWM_SHIFT 5
> +#define DA9150_VBUS_PWM_MASK (0x01 << 5)
> +#define DA9150_VBUS_ISO_SHIFT 6
> +#define DA9150_VBUS_ISO_MASK (0x01 << 6)
> +#define DA9150_VBUS_LDO_SHIFT 7
> +#define DA9150_VBUS_LDO_MASK (0x01 << 7)
> +
> +/* DA9150_PPR_BKCFG_A = 0x164 */
> +#define DA9150_VBUS_ISET_SHIFT 0
> +#define DA9150_VBUS_ISET_MASK (0x1f << 0)
> +#define DA9150_VBUS_IMAX_SHIFT 5
> +#define DA9150_VBUS_IMAX_MASK (0x01 << 5)
> +#define DA9150_VBUS_IOTG_SHIFT 6
> +#define DA9150_VBUS_IOTG_MASK (0x03 << 6)
> +
> +/* DA9150_PPR_BKCFG_B = 0x165 */
> +#define DA9150_VBUS_DROP_SHIFT 0
> +#define DA9150_VBUS_DROP_MASK (0x0f << 0)
> +#define DA9150_VBUS_FAULT_DIS_SHIFT 6
> +#define DA9150_VBUS_FAULT_DIS_MASK (0x01 << 6)
> +#define DA9150_OTG_FAULT_DIS_SHIFT 7
> +#define DA9150_OTG_FAULT_DIS_MASK (0x01 << 7)
> +
> +/* DA9150_PPR_CHGCTRL_A = 0x166 */
> +#define DA9150_CHG_EN_SHIFT 0
> +#define DA9150_CHG_EN_MASK (0x01 << 0)
> +
> +/* DA9150_PPR_CHGCTRL_B = 0x167 */
> +#define DA9150_CHG_VBAT_SHIFT 0
> +#define DA9150_CHG_VBAT_MASK (0x1f << 0)
> +#define DA9150_CHG_VDROP_SHIFT 6
> +#define DA9150_CHG_VDROP_MASK (0x03 << 6)
> +
> +/* DA9150_PPR_CHGCTRL_C = 0x168 */
> +#define DA9150_CHG_VFAULT_SHIFT 0
> +#define DA9150_CHG_VFAULT_MASK (0x0f << 0)
> +#define DA9150_CHG_IPRE_SHIFT 4
> +#define DA9150_CHG_IPRE_MASK (0x03 << 4)
> +
> +/* DA9150_PPR_TCTR_A = 0x169 */
> +#define DA9150_CHG_TCTR_SHIFT 0
> +#define DA9150_CHG_TCTR_MASK (0x07 << 0)
> +#define DA9150_CHG_TCTR_MODE_SHIFT 4
> +#define DA9150_CHG_TCTR_MODE_MASK (0x01 << 4)
> +
> +/* DA9150_PPR_CHGCTRL_D = 0x16A */
> +#define DA9150_CHG_IBAT_SHIFT 0
> +#define DA9150_CHG_IBAT_MASK (0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_E = 0x16B */
> +#define DA9150_CHG_IEND_SHIFT 0
> +#define DA9150_CHG_IEND_MASK (0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_F = 0x16C */
> +#define DA9150_CHG_VCOLD_SHIFT 0
> +#define DA9150_CHG_VCOLD_MASK (0x1f << 0)
> +#define DA9150_TBAT_TQA_EN_SHIFT 6
> +#define DA9150_TBAT_TQA_EN_MASK (0x01 << 6)
> +#define DA9150_TBAT_TDP_EN_SHIFT 7
> +#define DA9150_TBAT_TDP_EN_MASK (0x01 << 7)
> +
> +/* DA9150_PPR_CHGCTRL_G = 0x16D */
> +#define DA9150_CHG_VWARM_SHIFT 0
> +#define DA9150_CHG_VWARM_MASK (0x1f << 0)
> +
> +/* DA9150_PPR_CHGCTRL_H = 0x16E */
> +#define DA9150_CHG_VHOT_SHIFT 0
> +#define DA9150_CHG_VHOT_MASK (0x1f << 0)
> +
> +/* DA9150_PPR_CHGCTRL_I = 0x16F */
> +#define DA9150_CHG_ICOLD_SHIFT 0
> +#define DA9150_CHG_ICOLD_MASK (0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_J = 0x170 */
> +#define DA9150_CHG_IWARM_SHIFT 0
> +#define DA9150_CHG_IWARM_MASK (0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_K = 0x171 */
> +#define DA9150_CHG_IHOT_SHIFT 0
> +#define DA9150_CHG_IHOT_MASK (0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_L = 0x172 */
> +#define DA9150_CHG_IBAT_TRED_SHIFT 0
> +#define DA9150_CHG_IBAT_TRED_MASK (0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_M = 0x173 */
> +#define DA9150_CHG_VFLOAT_SHIFT 0
> +#define DA9150_CHG_VFLOAT_MASK (0x0f << 0)
> +#define DA9150_CHG_LPM_SHIFT 5
> +#define DA9150_CHG_LPM_MASK (0x01 << 5)
> +#define DA9150_CHG_NBLO_SHIFT 6
> +#define DA9150_CHG_NBLO_MASK (0x01 << 6)
> +#define DA9150_EBS_EN_SHIFT 7
> +#define DA9150_EBS_EN_MASK (0x01 << 7)
> +
> +/* DA9150_PPR_THYST_A = 0x174 */
> +#define DA9150_TBAT_T1_SHIFT 0
> +#define DA9150_TBAT_T1_MASK (0xff << 0)
> +
> +/* DA9150_PPR_THYST_B = 0x175 */
> +#define DA9150_TBAT_T2_SHIFT 0
> +#define DA9150_TBAT_T2_MASK (0xff << 0)
> +
> +/* DA9150_PPR_THYST_C = 0x176 */
> +#define DA9150_TBAT_T3_SHIFT 0
> +#define DA9150_TBAT_T3_MASK (0xff << 0)
> +
> +/* DA9150_PPR_THYST_D = 0x177 */
> +#define DA9150_TBAT_T4_SHIFT 0
> +#define DA9150_TBAT_T4_MASK (0xff << 0)
> +
> +/* DA9150_PPR_THYST_E = 0x178 */
> +#define DA9150_TBAT_T5_SHIFT 0
> +#define DA9150_TBAT_T5_MASK (0xff << 0)
> +
> +/* DA9150_PPR_THYST_F = 0x179 */
> +#define DA9150_TBAT_H1_SHIFT 0
> +#define DA9150_TBAT_H1_MASK (0xff << 0)
> +
> +/* DA9150_PPR_THYST_G = 0x17A */
> +#define DA9150_TBAT_H5_SHIFT 0
> +#define DA9150_TBAT_H5_MASK (0xff << 0)
> +
> +/* DA9150_PAGE_CON_3 = 0x180 */
> +#define DA9150_PAGE_SHIFT 0
> +#define DA9150_PAGE_MASK (0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT 6
> +#define DA9150_WRITE_MODE_MASK (0x01 << 6)
> +#define DA9150_REVERT_SHIFT 7
> +#define DA9150_REVERT_MASK (0x01 << 7)
> +
> +/* DA9150_PAGE_CON_4 = 0x200 */
> +#define DA9150_PAGE_SHIFT 0
> +#define DA9150_PAGE_MASK (0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT 6
> +#define DA9150_WRITE_MODE_MASK (0x01 << 6)
> +#define DA9150_REVERT_SHIFT 7
> +#define DA9150_REVERT_MASK (0x01 << 7)
> +
> +/* DA9150_PAGE_CON_5 = 0x280 */
> +#define DA9150_PAGE_SHIFT 0
> +#define DA9150_PAGE_MASK (0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT 6
> +#define DA9150_WRITE_MODE_MASK (0x01 << 6)
> +#define DA9150_REVERT_SHIFT 7
> +#define DA9150_REVERT_MASK (0x01 << 7)
> +
> +/* DA9150_PAGE_CON_6 = 0x300 */
> +#define DA9150_PAGE_SHIFT 0
> +#define DA9150_PAGE_MASK (0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT 6
> +#define DA9150_WRITE_MODE_MASK (0x01 << 6)
> +#define DA9150_REVERT_SHIFT 7
> +#define DA9150_REVERT_MASK (0x01 << 7)
> +
> +/* DA9150_COREBTLD_STAT_A = 0x302 */
> +#define DA9150_BOOTLD_STAT_SHIFT 0
> +#define DA9150_BOOTLD_STAT_MASK (0x03 << 0)
> +#define DA9150_CORE_LOCKUP_SHIFT 2
> +#define DA9150_CORE_LOCKUP_MASK (0x01 << 2)
> +
> +/* DA9150_COREBTLD_CTRL_A = 0x303 */
> +#define DA9150_CORE_RESET_SHIFT 0
> +#define DA9150_CORE_RESET_MASK (0x01 << 0)
> +#define DA9150_CORE_STOP_SHIFT 1
> +#define DA9150_CORE_STOP_MASK (0x01 << 1)
> +
> +/* DA9150_CORE_CONFIG_A = 0x304 */
> +#define DA9150_CORE_MEMMUX_SHIFT 0
> +#define DA9150_CORE_MEMMUX_MASK (0x03 << 0)
> +#define DA9150_WDT_AUTO_START_SHIFT 2
> +#define DA9150_WDT_AUTO_START_MASK (0x01 << 2)
> +#define DA9150_WDT_AUTO_LOCK_SHIFT 3
> +#define DA9150_WDT_AUTO_LOCK_MASK (0x01 << 3)
> +#define DA9150_WDT_HLT_NO_CLK_SHIFT 4
> +#define DA9150_WDT_HLT_NO_CLK_MASK (0x01 << 4)
> +
> +/* DA9150_CORE_CONFIG_C = 0x305 */
> +#define DA9150_CORE_SW_SIZE_SHIFT 0
> +#define DA9150_CORE_SW_SIZE_MASK (0xff << 0)
> +
> +/* DA9150_CORE_CONFIG_B = 0x306 */
> +#define DA9150_BOOTLD_EN_SHIFT 0
> +#define DA9150_BOOTLD_EN_MASK (0x01 << 0)
> +#define DA9150_CORE_EN_SHIFT 2
> +#define DA9150_CORE_EN_MASK (0x01 << 2)
> +#define DA9150_CORE_SW_SRC_SHIFT 3
> +#define DA9150_CORE_SW_SRC_MASK (0x07 << 3)
> +#define DA9150_DEEP_SLEEP_EN_SHIFT 7
> +#define DA9150_DEEP_SLEEP_EN_MASK (0x01 << 7)
> +
> +/* DA9150_CORE_CFG_DATA_A = 0x307 */
> +#define DA9150_CORE_CFG_DT_A_SHIFT 0
> +#define DA9150_CORE_CFG_DT_A_MASK (0xff << 0)
> +
> +/* DA9150_CORE_CFG_DATA_B = 0x308 */
> +#define DA9150_CORE_CFG_DT_B_SHIFT 0
> +#define DA9150_CORE_CFG_DT_B_MASK (0xff << 0)
> +
> +/* DA9150_CORE_CMD_A = 0x309 */
> +#define DA9150_CORE_CMD_SHIFT 0
> +#define DA9150_CORE_CMD_MASK (0xff << 0)
> +
> +/* DA9150_CORE_DATA_A = 0x30A */
> +#define DA9150_CORE_DATA_0_SHIFT 0
> +#define DA9150_CORE_DATA_0_MASK (0xff << 0)
> +
> +/* DA9150_CORE_DATA_B = 0x30B */
> +#define DA9150_CORE_DATA_1_SHIFT 0
> +#define DA9150_CORE_DATA_1_MASK (0xff << 0)
> +
> +/* DA9150_CORE_DATA_C = 0x30C */
> +#define DA9150_CORE_DATA_2_SHIFT 0
> +#define DA9150_CORE_DATA_2_MASK (0xff << 0)
> +
> +/* DA9150_CORE_DATA_D = 0x30D */
> +#define DA9150_CORE_DATA_3_SHIFT 0
> +#define DA9150_CORE_DATA_3_MASK (0xff << 0)
> +
> +/* DA9150_CORE2WIRE_STAT_A = 0x310 */
> +#define DA9150_FW_FWDL_ERR_SHIFT 7
> +#define DA9150_FW_FWDL_ERR_MASK (0x01 << 7)
> +
> +/* DA9150_CORE2WIRE_CTRL_A = 0x311 */
> +#define DA9150_FW_FWDL_EN_SHIFT 0
> +#define DA9150_FW_FWDL_EN_MASK (0x01 << 0)
> +#define DA9150_FG_QIF_EN_SHIFT 1
> +#define DA9150_FG_QIF_EN_MASK (0x01 << 1)
> +#define DA9150_CORE_BASE_ADDR_SHIFT 4
> +#define DA9150_CORE_BASE_ADDR_MASK (0x0f << 4)
> +
> +/* DA9150_FW_CTRL_A = 0x312 */
> +#define DA9150_FW_SEAL_SHIFT 0
> +#define DA9150_FW_SEAL_MASK (0xff << 0)
> +
> +/* DA9150_FW_CTRL_C = 0x313 */
> +#define DA9150_FW_FWDL_CRC_SHIFT 0
> +#define DA9150_FW_FWDL_CRC_MASK (0xff << 0)
> +
> +/* DA9150_FW_CTRL_D = 0x314 */
> +#define DA9150_FW_FWDL_BASE_SHIFT 0
> +#define DA9150_FW_FWDL_BASE_MASK (0x0f << 0)
> +
> +/* DA9150_FG_CTRL_A = 0x315 */
> +#define DA9150_FG_QIF_CODE_SHIFT 0
> +#define DA9150_FG_QIF_CODE_MASK (0xff << 0)
> +
> +/* DA9150_FG_CTRL_B = 0x316 */
> +#define DA9150_FG_QIF_VALUE_SHIFT 0
> +#define DA9150_FG_QIF_VALUE_MASK (0xff << 0)
> +
> +/* DA9150_FW_CTRL_E = 0x317 */
> +#define DA9150_FW_FWDL_SEG_SHIFT 0
> +#define DA9150_FW_FWDL_SEG_MASK (0xff << 0)
> +
> +/* DA9150_FW_CTRL_B = 0x318 */
> +#define DA9150_FW_FWDL_VALUE_SHIFT 0
> +#define DA9150_FW_FWDL_VALUE_MASK (0xff << 0)
> +
> +/* DA9150_GPADC_CMAN = 0x320 */
> +#define DA9150_GPADC_CEN_SHIFT 0
> +#define DA9150_GPADC_CEN_MASK (0x01 << 0)
> +#define DA9150_GPADC_CMUX_SHIFT 1
> +#define DA9150_GPADC_CMUX_MASK (0x1f << 1)
> +
> +/* DA9150_GPADC_CRES_A = 0x322 */
> +#define DA9150_GPADC_CRES_H_SHIFT 0
> +#define DA9150_GPADC_CRES_H_MASK (0xff << 0)
> +
> +/* DA9150_GPADC_CRES_B = 0x323 */
> +#define DA9150_GPADC_CRUN_SHIFT 0
> +#define DA9150_GPADC_CRUN_MASK (0x01 << 0)
> +#define DA9150_GPADC_CRES_L_SHIFT 6
> +#define DA9150_GPADC_CRES_L_MASK (0x03 << 6)
> +
> +/* DA9150_CC_CFG_A = 0x328 */
> +#define DA9150_CC_EN_SHIFT 0
> +#define DA9150_CC_EN_MASK (0x01 << 0)
> +#define DA9150_CC_TIMEBASE_SHIFT 1
> +#define DA9150_CC_TIMEBASE_MASK (0x03 << 1)
> +#define DA9150_CC_CFG_SHIFT 5
> +#define DA9150_CC_CFG_MASK (0x03 << 5)
> +#define DA9150_CC_ENDLESS_MODE_SHIFT 7
> +#define DA9150_CC_ENDLESS_MODE_MASK (0x01 << 7)
> +
> +/* DA9150_CC_CFG_B = 0x329 */
> +#define DA9150_CC_OPT_SHIFT 0
> +#define DA9150_CC_OPT_MASK (0x03 << 0)
> +#define DA9150_CC_PREAMP_SHIFT 2
> +#define DA9150_CC_PREAMP_MASK (0x03 << 2)
> +
> +/* DA9150_CC_ICHG_RES_A = 0x32A */
> +#define DA9150_CC_ICHG_RES_H_SHIFT 0
> +#define DA9150_CC_ICHG_RES_H_MASK (0xff << 0)
> +
> +/* DA9150_CC_ICHG_RES_B = 0x32B */
> +#define DA9150_CC_ICHG_RES_L_SHIFT 3
> +#define DA9150_CC_ICHG_RES_L_MASK (0x1f << 3)
> +
> +/* DA9150_CC_IAVG_RES_A = 0x32C */
> +#define DA9150_CC_IAVG_RES_H_SHIFT 0
> +#define DA9150_CC_IAVG_RES_H_MASK (0xff << 0)
> +
> +/* DA9150_CC_IAVG_RES_B = 0x32D */
> +#define DA9150_CC_IAVG_RES_L_SHIFT 0
> +#define DA9150_CC_IAVG_RES_L_MASK (0xff << 0)
> +
> +/* DA9150_TAUX_CTRL_A = 0x330 */
> +#define DA9150_TAUX_EN_SHIFT 0
> +#define DA9150_TAUX_EN_MASK (0x01 << 0)
> +#define DA9150_TAUX_MOD_SHIFT 1
> +#define DA9150_TAUX_MOD_MASK (0x01 << 1)
> +#define DA9150_TAUX_UPDATE_SHIFT 2
> +#define DA9150_TAUX_UPDATE_MASK (0x01 << 2)
> +
> +/* DA9150_TAUX_RELOAD_H = 0x332 */
> +#define DA9150_TAUX_RLD_H_SHIFT 0
> +#define DA9150_TAUX_RLD_H_MASK (0xff << 0)
> +
> +/* DA9150_TAUX_RELOAD_L = 0x333 */
> +#define DA9150_TAUX_RLD_L_SHIFT 3
> +#define DA9150_TAUX_RLD_L_MASK (0x1f << 3)
> +
> +/* DA9150_TAUX_VALUE_H = 0x334 */
> +#define DA9150_TAUX_VAL_H_SHIFT 0
> +#define DA9150_TAUX_VAL_H_MASK (0xff << 0)
> +
> +/* DA9150_TAUX_VALUE_L = 0x335 */
> +#define DA9150_TAUX_VAL_L_SHIFT 3
> +#define DA9150_TAUX_VAL_L_MASK (0x1f << 3)
> +
> +/* DA9150_AUX_DATA_0 = 0x338 */
> +#define DA9150_AUX_DAT_0_SHIFT 0
> +#define DA9150_AUX_DAT_0_MASK (0xff << 0)
> +
> +/* DA9150_AUX_DATA_1 = 0x339 */
> +#define DA9150_AUX_DAT_1_SHIFT 0
> +#define DA9150_AUX_DAT_1_MASK (0xff << 0)
> +
> +/* DA9150_AUX_DATA_2 = 0x33A */
> +#define DA9150_AUX_DAT_2_SHIFT 0
> +#define DA9150_AUX_DAT_2_MASK (0xff << 0)
> +
> +/* DA9150_AUX_DATA_3 = 0x33B */
> +#define DA9150_AUX_DAT_3_SHIFT 0
> +#define DA9150_AUX_DAT_3_MASK (0xff << 0)
> +
> +/* DA9150_BIF_CTRL = 0x340 */
> +#define DA9150_BIF_ISRC_EN_SHIFT 0
> +#define DA9150_BIF_ISRC_EN_MASK (0x01 << 0)
> +
> +/* DA9150_TBAT_CTRL_A = 0x342 */
> +#define DA9150_TBAT_EN_SHIFT 0
> +#define DA9150_TBAT_EN_MASK (0x01 << 0)
> +#define DA9150_TBAT_SW1_SHIFT 1
> +#define DA9150_TBAT_SW1_MASK (0x01 << 1)
> +#define DA9150_TBAT_SW2_SHIFT 2
> +#define DA9150_TBAT_SW2_MASK (0x01 << 2)
> +
> +/* DA9150_TBAT_CTRL_B = 0x343 */
> +#define DA9150_TBAT_SW_FRC_SHIFT 0
> +#define DA9150_TBAT_SW_FRC_MASK (0x01 << 0)
> +#define DA9150_TBAT_STAT_SW1_SHIFT 1
> +#define DA9150_TBAT_STAT_SW1_MASK (0x01 << 1)
> +#define DA9150_TBAT_STAT_SW2_SHIFT 2
> +#define DA9150_TBAT_STAT_SW2_MASK (0x01 << 2)
> +#define DA9150_TBAT_HIGH_CURR_SHIFT 3
> +#define DA9150_TBAT_HIGH_CURR_MASK (0x01 << 3)
> +
> +/* DA9150_TBAT_RES_A = 0x344 */
> +#define DA9150_TBAT_RES_H_SHIFT 0
> +#define DA9150_TBAT_RES_H_MASK (0xff << 0)
> +
> +/* DA9150_TBAT_RES_B = 0x345 */
> +#define DA9150_TBAT_RES_DIS_SHIFT 0
> +#define DA9150_TBAT_RES_DIS_MASK (0x01 << 0)
> +#define DA9150_TBAT_RES_L_SHIFT 6
> +#define DA9150_TBAT_RES_L_MASK (0x03 << 6)
> +
> +#endif /* _DA9150_REGISTERS_H */
> --
> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
On 11/06/14 17:56, Opensource [Adam Thomson] wrote:
> On June 11, 2014 17:35, Jonathon Cameron wrote:
>
>> Hi Adam. Silly question but what are the _ channels?
>>
>> I have put in a perhaps optimistic data sheet request via the website, throwing in
>> your name. Hope you don't mind!
>>
>> Jonathan
>
> Hi Jonathon. Some of the _channels are a different representation of the reading
> (e.g. different scale - one would be 0-6V range, and one would be 0-21V for
> example).
Why? That is, why present the data twice with the different scales?
> Others provide identical readings to the non _channel but have left
> those in to keep the channel numbering sane and easy to reference.
If there is a reason to support this functionality, this is not a sensible
way to do it!
>
> No problem regarding the request.
> N�����r��y���b�X��ǧv�^�){.n�+����{��*"��^n�r���z���h����&���G���h�(�階�ݢj"���m�����z�ޖ���f���h���~�mml==
>
On 11/06/14 12:11, Adam Thomson wrote:
> This patch adds support for DA9150 Charger & Fuel-Gauge IC GPADC.
>
> Signed-off-by: Adam Thomson <[email protected]>
Hi Adam
Reasonably clean code, but the _ channels stuff doesn't comply with the ABI
and is rather confusing.
If it really does make sense to allow reading these channels at different
ranges (presumably to enhance the effective adc resolution) then we need
to control this via existing ABIs. Controlling it via scale with a
range attribute if required (read only but changes with whatever the
scale attribute it set to).
Often, for slow parts at least it makes little sense to have provide
software support for variable input ranges.
Looking at what is covered, is it the case that only one option will make
sense for a given hardware setup? (cover the required range and nothing more).
If so, perhaps this wants to go into the device tree or similar?
Jonathan
> ---
> drivers/iio/adc/Kconfig | 9 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/da9150-gpadc.c | 396 +++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/da9150/gpadc.h | 71 +++++++
> 4 files changed, 477 insertions(+)
> create mode 100644 drivers/iio/adc/da9150-gpadc.c
> create mode 100644 include/linux/mfd/da9150/gpadc.h
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 24c28e3..f5e9f72 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -105,6 +105,15 @@ config AT91_ADC
> help
> Say yes here to build support for Atmel AT91 ADC.
>
> +config DA9150_GPADC
> + tristate "Dialog DA9150 GPADC driver support"
> + depends on MFD_DA9150
> + help
> + Say yes here to build support for Dialog DA9150 GPADC.
> +
> + This driver can also be built as a module. If chosen, the module name
> + will be da9150-gpadc.
> +
> config EXYNOS_ADC
> tristate "Exynos ADC driver support"
> depends on OF
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index ab346d8..414b22f 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
> obj-$(CONFIG_AD7793) += ad7793.o
> obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> +obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
> obj-$(CONFIG_MAX1363) += max1363.o
> diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c
> new file mode 100644
> index 0000000..2107f86
> --- /dev/null
> +++ b/drivers/iio/adc/da9150-gpadc.c
> @@ -0,0 +1,396 @@
> +/*
> + * DA9150 GPADC Driver
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <[email protected]>
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +
> +#include <linux/mfd/da9150/core.h>
> +#include <linux/mfd/da9150/pdata.h>
> +#include <linux/mfd/da9150/registers.h>
> +#include <linux/mfd/da9150/gpadc.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/machine.h>
> +#include <linux/iio/driver.h>
> +
> +
> +/*
> + * IRQ Handling
Please get rid of excess white space and comments that just tell you
something obvious about the code following them.
> + */
> +
> +static irqreturn_t da9150_gpadc_irq(int irq, void *data)
> +{
> +
> + struct da9150_gpadc *gpadc = data;
> +
> + complete(&gpadc->complete);
> +
> + return IRQ_HANDLED;
> +}
> +
> +
> +/*
> + * GPADC access
> + */
> +
> +static inline int da9150_gpadc_gpio_2v_voltage_now(int raw_val)
> +{
> + /* Convert to uV */
> + return (((3 * ((raw_val * 1000) + 500)) / 2048) * 1000);
> +}
> +
> +static inline int da9150_gpadc_gpio_5v_voltage_now(int raw_val)
> +{
> + /* Convert to uV */
> + return (((6 * ((raw_val * 1000) + 500)) / 1024) * 1000);
> +}
> +
> +static inline int da9150_gpadc_ibus_current_avg(int raw_val)
> +{
> + /* Convert to uA */
> + return (((4 * ((raw_val * 1000) + 500)) / 2048) * 1000);
> +}
> +
> +static inline int da9150_gpadc_vbus_6v_voltage_now(int raw_val)
> +{
> + /* Convert to uV */
> + return (((3 * ((raw_val * 1000) + 500)) / 512) * 1000);
> +}
> +
> +static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val)
> +{
> + /* Convert to uV */
> + return (((21 * ((raw_val * 1000) + 500)) / 1024) * 1000);
> +}
> +
> +static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val)
> +{
> + /* Convert to uV */
> + return (((3 * ((raw_val * 1000) + 500)) / 512) * 1000);
> +}
> +
> +static inline int da9150_gpadc_vsys_1_5v_voltage_now(int raw_val)
> +{
> + /* Convert to uV */
> + return (((3 * ((raw_val * 1000) + 500)) / 2048) * 1000);
> +}
> +
> +static inline int da9150_gpadc_tjunc_temp(int raw_val)
> +{
> + /* Convert to 0.1 degrees C */
> + return (((879 - (1023 - raw_val)) * 10000) / 4420);
Linear in the coeeficients, so fine for raw output with a scale and offset.
> +}
> +
> +static inline int da9150_gpadc_vbat_voltage_now(int raw_val)
> +{
> + /* Convert to uV */
> + return ((2932 * raw_val) + 1500000);
> +}
> +
> +int da9150_gpadc_read_process(int channel, int raw_val)
> +{
> + int ret;
> +
> + switch (channel) {
> + case DA9150_GPADC_CHAN_GPIOA_2V:
> + case DA9150_GPADC_CHAN_GPIOA_2V_:
> + case DA9150_GPADC_CHAN_GPIOB_2V:
> + case DA9150_GPADC_CHAN_GPIOB_2V_:
> + case DA9150_GPADC_CHAN_GPIOC_2V:
> + case DA9150_GPADC_CHAN_GPIOC_2V_:
> + case DA9150_GPADC_CHAN_GPIOD_2V:
> + case DA9150_GPADC_CHAN_GPIOD_2V_:
> + ret = da9150_gpadc_gpio_2v_voltage_now(raw_val);
> + break;
> + case DA9150_GPADC_CHAN_IBUS_SENSE:
> + case DA9150_GPADC_CHAN_IBUS_SENSE_:
> + ret = da9150_gpadc_ibus_current_avg(raw_val);
> + break;
Ah, so here is your reasoning behind the 'interesting' underscore channels.
This is just doing different range checks on the channel? If so allow one
at a time and provide a writable scale info_mask element to control which
is used..
Looks like there are only thes two channels doing this, so shouldn't be
too hard to support it via more conventional means.
> + case DA9150_GPADC_CHAN_VBUS_DIV:
> + ret = da9150_gpadc_vbus_6v_voltage_now(raw_val);
> + break;
> + case DA9150_GPADC_CHAN_VBUS_DIV_:
> + ret = da9150_gpadc_vbus_21v_voltage_now(raw_val);
> + break;
> + case DA9150_GPADC_CHAN_VSYS:
> + ret = da9150_gpadc_vsys_6v_voltage_now(raw_val);
> + break;
> + case DA9150_GPADC_CHAN_VSYS_:
> + ret = da9150_gpadc_vsys_1_5v_voltage_now(raw_val);
> + break;
> + case DA9150_GPADC_CHAN_GPIOA_5V:
> + case DA9150_GPADC_CHAN_GPIOA_5V_:
> + case DA9150_GPADC_CHAN_GPIOB_5V:
> + case DA9150_GPADC_CHAN_GPIOB_5V_:
> + case DA9150_GPADC_CHAN_GPIOC_5V:
> + case DA9150_GPADC_CHAN_GPIOC_5V_:
> + case DA9150_GPADC_CHAN_GPIOD_5V:
> + case DA9150_GPADC_CHAN_GPIOD_5V_:
> + ret = da9150_gpadc_gpio_5v_voltage_now(raw_val);
> + break;
> + case DA9150_GPADC_CHAN_TJUNC_CORE:
> + case DA9150_GPADC_CHAN_TJUNC_CORE_:
> + case DA9150_GPADC_CHAN_TJUNC_OVP:
> + case DA9150_GPADC_CHAN_TJUNC_OVP_:
> + ret = da9150_gpadc_tjunc_temp(raw_val);
> + break;
> + case DA9150_GPADC_CHAN_VBAT:
> + ret = da9150_gpadc_vbat_voltage_now(raw_val);
> + break;
> + default:
> + /* No processing for other channels so return raw value */
> + ret = raw_val;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +int da9150_gpadc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct da9150_gpadc *gpadc = iio_priv(indio_dev);
> + u8 reg = 0;
> + u8 result_regs[2];
> + u16 result;
> +
> + if ((mask != IIO_CHAN_INFO_RAW) && (mask != IIO_CHAN_INFO_PROCESSED))
> + return -EINVAL;
> +
> + if ((chan->channel < DA9150_GPADC_CHAN_GPIOA_2V) ||
> + (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP_))
> + return -EINVAL;
> +
> + mutex_lock(&gpadc->lock);
> +
> + /* Set channel & enable measurement */
> + reg |= DA9150_GPADC_EN_MASK;
Don't use the |=, just = and you can't avoid assigning reg above.
Actually, it's not complex enough that you couldn't just do it directly into
the write function and skip this local variable.
> + reg |= chan->channel << DA9150_GPADC_MUX_SHIFT;
> + da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN, reg);
> +
> + /* Consume left-over completion from a previous timeout */
> + try_wait_for_completion(&gpadc->complete);
> +
> + /* Check for actual completion */
> + wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5));
> +
> + /* Read result and status from device */
> + da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs);
> +
> + /* Check to make sure device really has completed reading */
> + if (result_regs[1] & DA9150_GPADC_RUN_MASK) {
> + mutex_unlock(&gpadc->lock);
Unlock first, then check for error.
> + dev_err(gpadc->dev, "Timeout on channel %d of GP-ADC\n",
> + chan->channel);
> + return -ETIMEDOUT;
> + }
> +
> + mutex_unlock(&gpadc->lock);
> +
> + /* LSBs - 2 bits */
> + result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >>
> + DA9150_GPADC_RES_L_SHIFT;
The mixture of having defs here for the shift.
> + /* MSBs - 8 bits */
> + result |= result_regs[0] << 2;
and not here is inconsistent. I honestly don't mind which way, but
just use one style.
You could just use an endian conversion and shift the combined 16bit result
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_PROCESSED:
> + *val = da9150_gpadc_read_process(chan->channel, result);
> + break;
> + case IIO_CHAN_INFO_RAW:
> + *val = result;
> + break;
> + }
> +
> + return IIO_VAL_INT;
> +}
> +
> +
> +static const struct iio_info da9150_gpadc_info = {
> + .read_raw = &da9150_gpadc_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +#define GPADC_CHANNEL(_id, _type, chan_info, _ext_name) { \
> + .type = _type, \
> + .indexed = 1, \
> + .channel = DA9150_GPADC_CHAN_##_id, \
> + .info_mask_separate = chan_info, \
> + .extend_name = _ext_name, \
> + .datasheet_name = #_id, \
> +}
> +
> +#define GPADC_CHANNEL_RAW(_id, _type, _ext_name) \
> + GPADC_CHANNEL(_id, _type, BIT(IIO_CHAN_INFO_RAW), _ext_name)
> +
> +#define GPADC_CHANNEL_RAW_PROCESSED(_id, _type, _ext_name) \
> + GPADC_CHANNEL(_id, _type, \
> + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
It very rarely makes sense to provide both raw and processed interfaces.
If the transform is nasty and non linear then userspace won't have the info
to do anything with it. If the transform is linear, then provide scale
and offset and let userspace perform the computation.
> + _ext_name)
> +
> +/* Supported channels */
Another comment that doesn't add any information.
> +static const struct iio_chan_spec da9150_gpadc_channels[] = {
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOA_2V, IIO_VOLTAGE, "GPIOA_2V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOA_2V_, IIO_VOLTAGE, "GPIOA_2V_"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOB_2V, IIO_VOLTAGE, "GPIOB_2V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOB_2V_, IIO_VOLTAGE, "GPIOB_2V_"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOC_2V, IIO_VOLTAGE, "GPIOC_2V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOC_2V_, IIO_VOLTAGE, "GPIOC_2V_"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOD_2V, IIO_VOLTAGE, "GPIOD_2V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOD_2V_, IIO_VOLTAGE, "GPIOD_2V_"),
> + GPADC_CHANNEL_RAW_PROCESSED(IBUS_SENSE, IIO_CURRENT, "IBUS"),
> + GPADC_CHANNEL_RAW_PROCESSED(IBUS_SENSE_, IIO_CURRENT, "IBUS_"),
> + GPADC_CHANNEL_RAW_PROCESSED(VBUS_DIV, IIO_VOLTAGE, "VBUS_6V"),
> + GPADC_CHANNEL_RAW_PROCESSED(VBUS_DIV_, IIO_VOLTAGE, "VBUS_21V"),
> + GPADC_CHANNEL_RAW(ID, IIO_VOLTAGE, "ID"),
> + GPADC_CHANNEL_RAW(ID_, IIO_VOLTAGE, "ID_"),
> + GPADC_CHANNEL_RAW_PROCESSED(VSYS, IIO_VOLTAGE, "VSYS_6V"),
> + GPADC_CHANNEL_RAW_PROCESSED(VSYS_, IIO_VOLTAGE, "VSYS_1_5V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOA_5V, IIO_VOLTAGE, "GPIOA_5V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOA_5V_, IIO_VOLTAGE, "GPIOA_5V_"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOB_5V, IIO_VOLTAGE, "GPIOB_5V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOB_5V_, IIO_VOLTAGE, "GPIOB_5V_"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOC_5V, IIO_VOLTAGE, "GPIOC_5V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOC_5V_, IIO_VOLTAGE, "GPIOC_5V_"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOD_5V, IIO_VOLTAGE, "GPIOD_5V"),
> + GPADC_CHANNEL_RAW_PROCESSED(GPIOD_5V_, IIO_VOLTAGE, "GPIOD_5V_"),
> + GPADC_CHANNEL_RAW_PROCESSED(VBAT, IIO_VOLTAGE, "VBAT"),
> + GPADC_CHANNEL_RAW_PROCESSED(VBAT_, IIO_VOLTAGE, "VBAT_"),
> + GPADC_CHANNEL_RAW(TBAT, IIO_VOLTAGE, "TBAT"),
> + GPADC_CHANNEL_RAW(TBAT_, IIO_VOLTAGE, "TBAT_"),
> + GPADC_CHANNEL_RAW_PROCESSED(TJUNC_CORE, IIO_TEMP, "TJUNC_CORE"),
> + GPADC_CHANNEL_RAW_PROCESSED(TJUNC_CORE_, IIO_TEMP, "TJUNC_CORE_"),
> + GPADC_CHANNEL_RAW_PROCESSED(TJUNC_OVP, IIO_TEMP, "TJUNC_OVP"),
> + GPADC_CHANNEL_RAW_PROCESSED(TJUNC_OVP_, IIO_TEMP, "TJUNC_OVP_"),
> +};
> +
> +/* Default maps used by da9150-charger */
> +static struct iio_map da9150_gpadc_default_maps[] = {
> + {
> + .consumer_dev_name = "da9150-charger",
> + .consumer_channel = "CHAN_IBUS",
> + .adc_channel_label = "IBUS_SENSE",
> + },
> + {
> + .consumer_dev_name = "da9150-charger",
> + .consumer_channel = "CHAN_VBUS",
> + .adc_channel_label = "VBUS_DIV_",
> + },
> + {
> + .consumer_dev_name = "da9150-charger",
> + .consumer_channel = "CHAN_TJUNC",
> + .adc_channel_label = "TJUNC_CORE",
> + },
> + {
> + .consumer_dev_name = "da9150-charger",
> + .consumer_channel = "CHAN_VBAT",
> + .adc_channel_label = "VBAT",
> + },
> + {},
> +};
> +
> +
> +/*
> + * Driver top level functions
Get rid of this comment and any other ones that don't add any
actual information. Also this is single line comment, please
check the comment syntax.
> + */
> +
> +static int da9150_gpadc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct da9150 *da9150 = dev_get_drvdata(dev->parent);
> + struct da9150_gpadc *gpadc;
> + struct iio_dev *indio_dev;
> +
Why a blank line here.
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev,
> + sizeof(struct da9150_gpadc));
> + if (!indio_dev) {
> + dev_err(&pdev->dev, "Failed to allocate IIO device\n");
> + return -ENOMEM;
> + }
> + gpadc = iio_priv(indio_dev);
> +
> + platform_set_drvdata(pdev, indio_dev);
> + gpadc->da9150 = da9150;
> + gpadc->dev = dev;
> +
> + ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps);
> + if (ret) {
> + dev_err(dev, "Failed to register IIO maps: %d\n", ret);
> + goto iio_map_fail;
> + }
> +
> + indio_dev->name = dev_name(dev);
> + indio_dev->dev.parent = dev;
> + indio_dev->dev.of_node = pdev->dev.of_node;
> + indio_dev->info = &da9150_gpadc_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = da9150_gpadc_channels;
> + indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels);
> +
> + ret = iio_device_register(indio_dev);
> + if (ret) {
> + dev_err(dev, "Failed to register IIO device: %d\n", ret);
> + goto iio_dev_fail;
> + }
The device register call is the one that exposes userspace interfaces. As
a general rule it wants to be the last thing in probe as everything
else should be setup first.
> +
> + mutex_init(&gpadc->lock);
> + init_completion(&gpadc->complete);
> +
> + /* Register IRQ */
> + ret = da9150_register_irq(pdev, gpadc, da9150_gpadc_irq, "GPADC");
> + if (ret < 0)
> + goto irq_fail;
> +
> + da9150->gpadc_ready = true;
> + return ret;
> +
> +irq_fail:
> + iio_device_unregister(indio_dev);
> +
> +iio_dev_fail:
> + iio_map_array_unregister(indio_dev);
> +
> +iio_map_fail:
> + return ret;
> +}
> +
> +static int da9150_gpadc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> + iio_map_array_unregister(indio_dev);
> + iio_device_unregister(indio_dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver da9150_gpadc_driver = {
> + .driver = {
> + .name = "da9150-gpadc",
> + .owner = THIS_MODULE,
> + },
> + .probe = da9150_gpadc_probe,
> + .remove = da9150_gpadc_remove,
> +};
> +
> +module_platform_driver(da9150_gpadc_driver);
> +
> +MODULE_DESCRIPTION("GPADC Driver for DA9150");
> +MODULE_AUTHOR("Adam Thomson <[email protected]");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/da9150/gpadc.h b/include/linux/mfd/da9150/gpadc.h
> new file mode 100644
> index 0000000..3e46164
> --- /dev/null
> +++ b/include/linux/mfd/da9150/gpadc.h
> @@ -0,0 +1,71 @@
> +/*
> + * DA9150 GPADC Driver - GPADC Data
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <[email protected]>
> + *
> + * 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.
> + */
> +
> +#ifndef _DA9150_GPADC_H
> +#define _DA9150_GPADC_H
> +
> +#include <linux/device.h>
> +#include <linux/iio/machine.h>
This isn't used in the header, so should be included in the c file, not here.
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +
> +#include <linux/mfd/da9150/core.h>
Having moved the struct definition into the c file
this header include will not want to be here.
> +
> +
> +/* Channels */
> +enum da9150_gpadc_channel {
> + DA9150_GPADC_CHAN_GPIOA_2V = 0,
> + DA9150_GPADC_CHAN_GPIOA_2V_,
> + DA9150_GPADC_CHAN_GPIOB_2V,
> + DA9150_GPADC_CHAN_GPIOB_2V_,
> + DA9150_GPADC_CHAN_GPIOC_2V,
> + DA9150_GPADC_CHAN_GPIOC_2V_,
> + DA9150_GPADC_CHAN_GPIOD_2V,
> + DA9150_GPADC_CHAN_GPIOD_2V_,
> + DA9150_GPADC_CHAN_IBUS_SENSE,
> + DA9150_GPADC_CHAN_IBUS_SENSE_,
> + DA9150_GPADC_CHAN_VBUS_DIV,
> + DA9150_GPADC_CHAN_VBUS_DIV_,
> + DA9150_GPADC_CHAN_ID,
> + DA9150_GPADC_CHAN_ID_,
> + DA9150_GPADC_CHAN_VSYS,
> + DA9150_GPADC_CHAN_VSYS_,
> + DA9150_GPADC_CHAN_GPIOA_5V,
> + DA9150_GPADC_CHAN_GPIOA_5V_,
> + DA9150_GPADC_CHAN_GPIOB_5V,
> + DA9150_GPADC_CHAN_GPIOB_5V_,
> + DA9150_GPADC_CHAN_GPIOC_5V,
> + DA9150_GPADC_CHAN_GPIOC_5V_,
> + DA9150_GPADC_CHAN_GPIOD_5V,
> + DA9150_GPADC_CHAN_GPIOD_5V_,
> + DA9150_GPADC_CHAN_VBAT,
> + DA9150_GPADC_CHAN_VBAT_,
> + DA9150_GPADC_CHAN_TBAT,
> + DA9150_GPADC_CHAN_TBAT_,
> + DA9150_GPADC_CHAN_TJUNC_CORE,
> + DA9150_GPADC_CHAN_TJUNC_CORE_,
> + DA9150_GPADC_CHAN_TJUNC_OVP,
> + DA9150_GPADC_CHAN_TJUNC_OVP_,
> +};
> +
> +
> +/* Private data */
Why is this in the header? Should be defined directly in the c file
as nothing outside the driver should be touching it.
> +struct da9150_gpadc {
> + struct da9150 *da9150;
> + struct device *dev;
> +
> + struct mutex lock;
> + struct completion complete;
> +};
> +
> +#endif /* _DA9150_GPADC_H */
> --
> 1.9.3
>
On 11/06/14 12:11, Adam Thomson wrote:
> Currently in the inkern.c code for IIO framework, the function
> of_iio_channel_get_by_name() will return a non-NULL pointer when
> it cannot find a channel using of_iio_channel_get() and when it
> tries to search for 'io-channel-ranges' property and fails. This
> is incorrect behaviour as the function which calls this expects
> a NULL pointer for failure. This patch rectifies the issue.
>
> Signed-off-by: Adam Thomson <[email protected]>
Hi Adam,
Good find. I've only recently sent a pull request for fixes upstream,
so I'll pick this up in a day or so once that has gone.
J
> ---
> drivers/iio/inkern.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
> index 0cf5f8e..1e8e94d 100644
> --- a/drivers/iio/inkern.c
> +++ b/drivers/iio/inkern.c
> @@ -183,7 +183,7 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
> else if (name && index >= 0) {
> pr_err("ERROR: could not get IIO channel %s:%s(%i)\n",
> np->full_name, name ? name : "", index);
> - return chan;
> + return NULL;
> }
>
> /*
> @@ -193,8 +193,9 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
> */
> np = np->parent;
> if (np && !of_get_property(np, "io-channel-ranges", NULL))
> - break;
> + return NULL;
> }
> +
> return chan;
> }
>
> @@ -317,6 +318,7 @@ struct iio_channel *iio_channel_get(struct device *dev,
> if (channel != NULL)
> return channel;
> }
> +
> return iio_channel_get_sys(name, channel_name);
> }
> EXPORT_SYMBOL_GPL(iio_channel_get);
> --
> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
On Sun, Jun 15, 2014 at 20:49, Jonathan Cameron wrote:
> Hi Adam,
>
> Some general comments inline.
>
> It's been a while since I've looked at any particularly similar parts,
> but it seems to me that a lot of indirection gets added here that
> if anything makes the codes slightly harder to follow...
>
> Feel free to disagree with me though!
Will do :)
> To my mind all these wrappers add nothing significant so you might as well
> just call da9150->read_dev etc directly.
>
> Also, what are the read_qif and write_qif for? They don't seem to be used
> anywhere.
read_qif and write_qif are for the Fuel-Gauge functionality of the chip. The
associated driver will be submitted after acceptance of initial driver code,
and will make use of these functions.
The wrappers automatically choose the correct client to use (QIF uses a
different slave address to the main chip one). Means the child drivers only need
to pass through the da9150 struct and the rest is dealt with underneath.
> The only real reason I can see for these wrappers is because you want
> to hide the struct da9150 contents from the children of the mfd. As you
> aren't doing that, you might as well drop these in favour of direct
> calls to regmap_read and friends.
As I have a need to pass through the main da9150 struct point for the
aforementioned wrappers, it seemed cleaner and more consistent to have wrappers
for these as well, which did the job of regmap access. Means all HW access
uses the same kind of approach, and all sub-devices just need a point to the
main da9150 struct to be able to use the functions.
> I'll continue my tirade against obvious comments. Wrong format and
> adds nothing to what is here as init and exit functions are clearly
> doing what their name suggests (it's one of my pet hates ;)
I agree the comment doesn't add much in terms of description but for me it
breaks up the code to make it easier to follow. However if I get an overwhelming
hatred for this I can change it. Also, I know the rule regarding single/multiple
line comments but here again I feel it helps separate the code and makes it
easier to read.
> As a general good practice point, I'd rather that the driver supported
> more than one instance of the chip.. Hence you'd take a copy of da9150_devs
> to use here. I guess it is relatively unlikely with one of these, but
> you never know ;)
Have followed the general methods for MFD here, and a number of drivers take the
same approach. Also, I think it would be undesirable to have multiple charger
chips of the same type in one platform. I agree generally it's best to support
multiple instances, but here I don't think we should.
> Why does this need it's own file? Does the DA9150 support any other
> interfaces?
Yes, the DA9150 also has a SPI interface. At present the plan is to just add I2C
support for now, but in the future we may add SPI support, so have written the
code with this in mind.
> Why the indirection? The da9150 only supports i2c as far as I can see.
As per my last comment.
> I'd roll this into one line and not bother with the local variable...
Fair enough but I think this keeps the code cleaner, and to me it makes sense
for the actual logic to be in core file as that's interface agnostic.
> Drop comments on things that are self-evident. Also these are one
> line comments so should be using the single line comment syntax.
As per my previous comment I think it just helps to break up the code and makes
it more readable. Will change it though if the general consensus is to remove
it.
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
On Wed, 11 Jun 2014, Adam Thomson wrote:
> This patch adds support for DA9150 Charger & Fuel-Guage IC Charger.
>
> Signed-off-by: Adam Thomson <[email protected]>
> ---
> drivers/power/Kconfig | 23 ++
> drivers/power/Makefile | 1 +
> drivers/power/da9150-charger.c | 790 +++++++++++++++++++++++++++++++++++++
> include/linux/mfd/da9150/charger.h | 58 +++
> 4 files changed, 872 insertions(+)
> create mode 100644 drivers/power/da9150-charger.c
> create mode 100644 include/linux/mfd/da9150/charger.h
[...]
> diff --git a/include/linux/mfd/da9150/charger.h b/include/linux/mfd/da9150/charger.h
> new file mode 100644
> index 0000000..73c41b3
> --- /dev/null
> +++ b/include/linux/mfd/da9150/charger.h
> @@ -0,0 +1,58 @@
> +/*
> + * DA9150 MFD Driver - Charger Data
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <[email protected]>
> + *
> + * 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.
> + */
> +
> +#ifndef _DA9150_CHARGER_H
> +#define _DA9150_CHARGER_H
Two '_'s are normally preferred.
> +#include <linux/device.h>
> +#include <linux/i2c.h>
What are you using this for?
> +#include <linux/power_supply.h>
> +#include <linux/notifier.h>
> +#include <linux/usb/otg.h>
> +#include <linux/timer.h>
> +#include <linux/iio/consumer.h>
> +
> +#include <linux/mfd/da9150/core.h>
> +
> +
Extra '\n' here.
> +struct da9150_charger_attr_map {
> + struct device_attribute attr;
> + u16 reg;
> + u8 shift;
> + u8 mask;
> +};
> +
> +/* Private data */
> +struct da9150_charger {
> + struct da9150 *da9150;
> + struct device *dev;
> +
> + struct power_supply ac;
> + struct power_supply usb;
> + struct power_supply battery;
Do you want these (or pointers to these) here?
How much space do they take up?
> + struct power_supply *supply_online;
> +
> + struct usb_phy *usb_phy;
> + struct notifier_block otg_nb;
> + struct work_struct otg_work;
> + unsigned long usb_event;
> +
> + struct iio_channel *ibus_chan;
> + struct iio_channel *vbus_chan;
> + struct iio_channel *tjunc_chan;
> + struct iio_channel *vbat_chan;
> +
> + bool invalid_tbat;
> +};
> +
> +#endif /* _DA9150_CHARGER_H */
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
On June 15, 2014 21:19, Jonathon Cameron wrote:
> Hi Adam
>
> Reasonably clean code, but the _ channels stuff doesn't comply with the ABI
> and is rather confusing.
To be honest I did debate this in my head for a while. The reason I went with
the current approach was to make the driver channel layout match that of the
datasheet for the device. Sounds like it wasn't the correct direction though.
> If it really does make sense to allow reading these channels at different
> ranges (presumably to enhance the effective adc resolution) then we need
> to control this via existing ABIs. Controlling it via scale with a
> range attribute if required (read only but changes with whatever the
> scale attribute it set to).
Yes, with smaller range then you get a better degree of accuracy. Ok, will have
a look at this and see if I can improve things based on your comments.
> Often, for slow parts at least it makes little sense to have provide
> software support for variable input ranges.
>
> Looking at what is covered, is it the case that only one option will make
> sense for a given hardware setup? (cover the required range and nothing more).
>
> If so, perhaps this wants to go into the device tree or similar?
Possibly. Having read your comments further on though, and given that
it seems reasonable to use the range/scale attribute to choose the resolution,
I'd prefer to opt for that approach (seems more flexible).
> Please get rid of excess white space and comments that just tell you
> something obvious about the code following them.
Preferably I'd like to keep it this way as I think it makes the code
easier to read, but if you're dead against it then I'll remove them.
> Linear in the coeeficients, so fine for raw output with a scale and offset.
Ok will have a look at re-working this as per your comment.
> Ah, so here is your reasoning behind the 'interesting' underscore channels.
> This is just doing different range checks on the channel? If so allow one
> at a time and provide a writable scale info_mask element to control which
> is used..
>
> Looks like there are only thes two channels doing this, so shouldn't be
> too hard to support it via more conventional means.
The GPIO, VBUS and VSYS related channels provide different ranges. I guess they
should all follow the same approach for implementing this alternate range. So I
will assume that for the 'repeat' channels where the same raw value is provided
on both the _ and non _ channels only one channel should be provided.
> Don't use the |=, just = and you can't avoid assigning reg above.
> Actually, it's not complex enough that you couldn't just do it directly into
> the write function and skip this local variable.
Ok fine. Will update to tidy up.
> Unlock first, then check for error.
Yep. Makes sense. Will update.
> The mixture of having defs here for the shift.
> > + /* MSBs - 8 bits */
> > + result |= result_regs[0] << 2;
> and not here is inconsistent. I honestly don't mind which way, but
> just use one style.
>
> You could just use an endian conversion and shift the combined 16bit result
Fair point. Will make this cleaner.
> It very rarely makes sense to provide both raw and processed interfaces.
> If the transform is nasty and non linear then userspace won't have the info
> to do anything with it. If the transform is linear, then provide scale
> and offset and let userspace perform the computation.
The charger module uses certain channels for its readings, and to me it makes
more sense for that calculation to be done by the GPADC. However having looked
at the inkern.c framework code it looks like if you request a processed channel
and it's doesn't provide that feature, then the code drops to linear scaling if
possible to provide the processed value. I take it this is the preferred
approach then? Just thought it was more consistent to see the calculation using
dedicated functions (processed channel approach).
> Get rid of this comment and any other ones that don't add any
> actual information. Also this is single line comment, please
> check the comment syntax.
As per previous comment on this topic.
> Why a blank line here.
Accidental. Will remove it.
> The device register call is the one that exposes userspace interfaces. As
> a general rule it wants to be the last thing in probe as everything
> else should be setup first.
Ok, that's something I missed. Will make the change.
> > +#include <linux/iio/machine.h>
> This isn't used in the header, so should be included in the c file, not here.
Yes, is something I meant to remove during development increments. Consider it
gone.
> > +#include <linux/mfd/da9150/core.h>
> Having moved the struct definition into the c file
> this header include will not want to be here.
I personally would prefer a separate header for the definitions as I believe
this is tidier. Have never really liked struct definitions in c files. I could
move this header to the same directory as the source file, if that is preferred.
As it stands this header is only related to DA9150 in its current location, so
is this a big issue? If not then I'd rather keep it as is.
> > +/* Private data */
> Why is this in the header? Should be defined directly in the c file
> as nothing outside the driver should be touching it.
As per previous comment.
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
On 16/06/14 14:12, Opensource [Adam Thomson] wrote:
> On Sun, Jun 15, 2014 at 20:49, Jonathan Cameron wrote:
>
>> Hi Adam,
>>
>> Some general comments inline.
>>
>> It's been a while since I've looked at any particularly similar parts,
>> but it seems to me that a lot of indirection gets added here that
>> if anything makes the codes slightly harder to follow...
>>
>> Feel free to disagree with me though!
>
> Will do :)
>
>> To my mind all these wrappers add nothing significant so you might as well
>> just call da9150->read_dev etc directly.
>>
>> Also, what are the read_qif and write_qif for? They don't seem to be used
>> anywhere.
>
> read_qif and write_qif are for the Fuel-Gauge functionality of the chip. The
> associated driver will be submitted after acceptance of initial driver code,
> and will make use of these functions.
I'll be interested to see how this interacts with the rest of the driver.
Or does it? Often these dual i2c address devices are best handled as two
entirely separate drivers sitting on the same bus. No idea if that
is true here as don't have a datasheet!
>
> The wrappers automatically choose the correct client to use (QIF uses a
> different slave address to the main chip one). Means the child drivers only need
> to pass through the da9150 struct and the rest is dealt with underneath.
>
>> The only real reason I can see for these wrappers is because you want
>> to hide the struct da9150 contents from the children of the mfd. As you
>> aren't doing that, you might as well drop these in favour of direct
>> calls to regmap_read and friends.
>
> As I have a need to pass through the main da9150 struct point for the
> aforementioned wrappers, it seemed cleaner and more consistent to have wrappers
> for these as well, which did the job of regmap access. Means all HW access
> uses the same kind of approach, and all sub-devices just need a point to the
> main da9150 struct to be able to use the functions.
>
>> I'll continue my tirade against obvious comments. Wrong format and
>> adds nothing to what is here as init and exit functions are clearly
>> doing what their name suggests (it's one of my pet hates ;)
>
> I agree the comment doesn't add much in terms of description but for me it
> breaks up the code to make it easier to follow. However if I get an overwhelming
> hatred for this I can change it. Also, I know the rule regarding single/multiple
> line comments but here again I feel it helps separate the code and makes it
> easier to read.
Kernel code generally has to keep to the kernel coding style. If you leave the
single line comments as are, then chances are the mfd maintainers will get a
patch soon after 'fixing' them which is always irritating.
Having said that, mfd is their area not mine, so up to Lee / Samuel.
The same is true of the code structure comments. Now for the IIO driver
I get to be picky ;)
>
>> As a general good practice point, I'd rather that the driver supported
>> more than one instance of the chip.. Hence you'd take a copy of da9150_devs
>> to use here. I guess it is relatively unlikely with one of these, but
>> you never know ;)
>
> Have followed the general methods for MFD here, and a number of drivers take the
> same approach. Also, I think it would be undesirable to have multiple charger
> chips of the same type in one platform. I agree generally it's best to support
> multiple instances, but here I don't think we should.
I'm happy to let this go if it is something that would not be done, but we
certainly should not be preventing it if someone wants to build hardware
with more than one of these. If they submit a patch later allowing it because
they have hardware that does this, then there is no way anyone is going to
block it.
>
>> Why does this need it's own file? Does the DA9150 support any other
>> interfaces?
>
> Yes, the DA9150 also has a SPI interface. At present the plan is to just add I2C
> support for now, but in the future we may add SPI support, so have written the
> code with this in mind.
>
>> Why the indirection? The da9150 only supports i2c as far as I can see.
>
> As per my last comment.
Fair enough. I couldn't find a datasheet and the product brief only seemed to mention
i2c.
>
>> I'd roll this into one line and not bother with the local variable...
>
> Fair enough but I think this keeps the code cleaner, and to me it makes sense
> for the actual logic to be in core file as that's interface agnostic.
>
>> Drop comments on things that are self-evident. Also these are one
>> line comments so should be using the single line comment syntax.
>
> As per my previous comment I think it just helps to break up the code and makes
> it more readable. Will change it though if the general consensus is to remove
> it.
On June 16, 2014 14:28, Lee Jones wrote:
> > +#ifndef _DA9150_CHARGER_H
> > +#define _DA9150_CHARGER_H
>
> Two '_'s are normally preferred.
Ok, can change it accordingly.
>
> > +#include <linux/device.h>
> > +#include <linux/i2c.h>
>
> What are you using this for?
Nothing. Will remove them. Thanks.
> > +#include <linux/mfd/da9150/core.h>
> > +
> > +
>
> Extra '\n' here.
Yes, just separate includes from the rest of the file. Find it more readable.
If it's an issue though I can remove it.
> > + struct power_supply ac;
> > + struct power_supply usb;
> > + struct power_supply battery;
>
> Do you want these (or pointers to these) here?
>
> How much space do they take up?
Yes these are required in that structure. This is the common approach for
power_supply drivers. The structures, having had a very quick look, are around
maybe 70 bytes each.
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
On 16/06/14 16:58, Opensource [Adam Thomson] wrote:
> On June 15, 2014 21:19, Jonathon Cameron wrote:
>
>> Hi Adam
>>
>> Reasonably clean code, but the _ channels stuff doesn't comply with the ABI
>> and is rather confusing.
>
> To be honest I did debate this in my head for a while. The reason I went with
> the current approach was to make the driver channel layout match that of the
> datasheet for the device. Sounds like it wasn't the correct direction though.
>
>> If it really does make sense to allow reading these channels at different
>> ranges (presumably to enhance the effective adc resolution) then we need
>> to control this via existing ABIs. Controlling it via scale with a
>> range attribute if required (read only but changes with whatever the
>> scale attribute it set to).
>
> Yes, with smaller range then you get a better degree of accuracy. Ok, will have
> a look at this and see if I can improve things based on your comments.
>
>> Often, for slow parts at least it makes little sense to have provide
>> software support for variable input ranges.
>>
>> Looking at what is covered, is it the case that only one option will make
>> sense for a given hardware setup? (cover the required range and nothing more).
>>
>> If so, perhaps this wants to go into the device tree or similar?
>
> Possibly. Having read your comments further on though, and given that
> it seems reasonable to use the range/scale attribute to choose the resolution,
> I'd prefer to opt for that approach (seems more flexible).
Perhaps, but if the reason for these differing ranges is that they tend
to be wired to lines that have well defined voltage ranges, then I'd move
it into setup data. Leads to a cleaner driver. Flexibility is good, but
only if actually helps anyone!
>
>> Please get rid of excess white space and comments that just tell you
>> something obvious about the code following them.
>
> Preferably I'd like to keep it this way as I think it makes the code
> easier to read, but if you're dead against it then I'll remove them.
The issue here is about making kernel review as easy as possible. That often
involves making code (and particularly comments) conform to somewhat arbitrary
rules and conventions. Just makes the reviewer/maintainers life somewhat easier.
You'll also probably get follow up patches removing all this stuff on the basis
of kernel style soon after we merge the driver anyway and those just get
irritating ;)
>
>> Linear in the coeeficients, so fine for raw output with a scale and offset.
>
> Ok will have a look at re-working this as per your comment.
>
>> Ah, so here is your reasoning behind the 'interesting' underscore channels.
>> This is just doing different range checks on the channel? If so allow one
>> at a time and provide a writable scale info_mask element to control which
>> is used..
>>
>> Looks like there are only thes two channels doing this, so shouldn't be
>> too hard to support it via more conventional means.
>
> The GPIO, VBUS and VSYS related channels provide different ranges. I guess they
> should all follow the same approach for implementing this alternate range. So I
> will assume that for the 'repeat' channels where the same raw value is provided
> on both the _ and non _ channels only one channel should be provided.
Definitely.
>
>> Don't use the |=, just = and you can't avoid assigning reg above.
>> Actually, it's not complex enough that you couldn't just do it directly into
>> the write function and skip this local variable.
>
> Ok fine. Will update to tidy up.
>
>> Unlock first, then check for error.
>
> Yep. Makes sense. Will update.
>
>> The mixture of having defs here for the shift.
>>> + /* MSBs - 8 bits */
>>> + result |= result_regs[0] << 2;
>> and not here is inconsistent. I honestly don't mind which way, but
>> just use one style.
>>
>> You could just use an endian conversion and shift the combined 16bit result
>
> Fair point. Will make this cleaner.
>
>> It very rarely makes sense to provide both raw and processed interfaces.
>> If the transform is nasty and non linear then userspace won't have the info
>> to do anything with it. If the transform is linear, then provide scale
>> and offset and let userspace perform the computation.
>
> The charger module uses certain channels for its readings, and to me it makes
> more sense for that calculation to be done by the GPADC. However having looked
> at the inkern.c framework code it looks like if you request a processed channel
> and it's doesn't provide that feature, then the code drops to linear scaling if
> possible to provide the processed value. I take it this is the preferred
> approach then?
Yup. Saves on replicating code. Mostly the raw access stuff is there for consistency
with buffered access (where speed matters and we may or may not want to convert to
real units). So you should provide the information to do the scaling anyway, so
just let the core code handle it for you.
> Just thought it was more consistent to see the calculation using
> dedicated functions (processed channel approach).
>
>> Get rid of this comment and any other ones that don't add any
>> actual information. Also this is single line comment, please
>> check the comment syntax.
>
> As per previous comment on this topic.
>
>> Why a blank line here.
>
> Accidental. Will remove it.
>
>> The device register call is the one that exposes userspace interfaces. As
>> a general rule it wants to be the last thing in probe as everything
>> else should be setup first.
>
> Ok, that's something I missed. Will make the change.
>
>>> +#include <linux/iio/machine.h>
>> This isn't used in the header, so should be included in the c file, not here.
>
> Yes, is something I meant to remove during development increments. Consider it
> gone.
>
>>> +#include <linux/mfd/da9150/core.h>
>> Having moved the struct definition into the c file
>> this header include will not want to be here.
>
> I personally would prefer a separate header for the definitions as I believe
> this is tidier. Have never really liked struct definitions in c files.
Again, kernel coding style. Have everything as local as you can.
Makes it obvious nothing else uses the structure and also ever so slightly
cuts down on compile time (all helps!)
Anyhow, please bring them into the c code. Have only stuff that is genuinely
shared between different c files in headers. If those c files are in the same
directory then have the header within the directory as well.
> I could
> move this header to the same directory as the source file, if that is preferred.
> As it stands this header is only related to DA9150 in its current location, so
> is this a big issue? If not then I'd rather keep it as is.
>
>>> +/* Private data */
>> Why is this in the header? Should be defined directly in the c file
>> as nothing outside the driver should be touching it.
>
> As per previous comment.
>
On 15/06/14 21:20, Jonathan Cameron wrote:
> On 11/06/14 12:11, Adam Thomson wrote:
>> Currently in the inkern.c code for IIO framework, the function
>> of_iio_channel_get_by_name() will return a non-NULL pointer when
>> it cannot find a channel using of_iio_channel_get() and when it
>> tries to search for 'io-channel-ranges' property and fails. This
>> is incorrect behaviour as the function which calls this expects
>> a NULL pointer for failure. This patch rectifies the issue.
>>
>> Signed-off-by: Adam Thomson <[email protected]>
> Hi Adam,
>
> Good find. I've only recently sent a pull request for fixes upstream,
> so I'll pick this up in a day or so once that has gone.
Applied to the fixes-togreg branch of iio.git and cc'd to stable.
Thanks,
Jonathan
>
> J
>> ---
>> drivers/iio/inkern.c | 6 ++++--
>> 1 file changed, 4 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
>> index 0cf5f8e..1e8e94d 100644
>> --- a/drivers/iio/inkern.c
>> +++ b/drivers/iio/inkern.c
>> @@ -183,7 +183,7 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
>> else if (name && index >= 0) {
>> pr_err("ERROR: could not get IIO channel %s:%s(%i)\n",
>> np->full_name, name ? name : "", index);
>> - return chan;
>> + return NULL;
>> }
>>
>> /*
>> @@ -193,8 +193,9 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
>> */
>> np = np->parent;
>> if (np && !of_get_property(np, "io-channel-ranges", NULL))
>> - break;
>> + return NULL;
>> }
>> +
>> return chan;
>> }
>>
>> @@ -317,6 +318,7 @@ struct iio_channel *iio_channel_get(struct device *dev,
>> if (channel != NULL)
>> return channel;
>> }
>> +
>> return iio_channel_get_sys(name, channel_name);
>> }
>> EXPORT_SYMBOL_GPL(iio_channel_get);
>> --
>> 1.9.3
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On 16/06/14 14:12, Opensource [Adam Thomson] wrote:
> On Sun, Jun 15, 2014 at 20:49, Jonathan Cameron wrote:
>
>> Hi Adam,
>>
>> Some general comments inline.
>>
>> It's been a while since I've looked at any particularly similar parts,
>> but it seems to me that a lot of indirection gets added here that
>> if anything makes the codes slightly harder to follow...
>>
>> Feel free to disagree with me though!
>
> Will do :)
>
>> To my mind all these wrappers add nothing significant so you might as well
>> just call da9150->read_dev etc directly.
>>
>> Also, what are the read_qif and write_qif for? They don't seem to be used
>> anywhere.
>
> read_qif and write_qif are for the Fuel-Gauge functionality of the chip. The
> associated driver will be submitted after acceptance of initial driver code,
> and will make use of these functions.
Ideally drop these for now and bring them in as a precursor patch in the series
that introduces them being used.
>
> The wrappers automatically choose the correct client to use (QIF uses a
> different slave address to the main chip one). Means the child drivers only need
> to pass through the da9150 struct and the rest is dealt with underneath.
>
>> The only real reason I can see for these wrappers is because you want
>> to hide the struct da9150 contents from the children of the mfd. As you
>> aren't doing that, you might as well drop these in favour of direct
>> calls to regmap_read and friends.
>
> As I have a need to pass through the main da9150 struct point for the
> aforementioned wrappers, it seemed cleaner and more consistent to have wrappers
> for these as well, which did the job of regmap access. Means all HW access
> uses the same kind of approach, and all sub-devices just need a point to the
> main da9150 struct to be able to use the functions.
>
>> I'll continue my tirade against obvious comments. Wrong format and
>> adds nothing to what is here as init and exit functions are clearly
>> doing what their name suggests (it's one of my pet hates ;)
>
> I agree the comment doesn't add much in terms of description but for me it
> breaks up the code to make it easier to follow.
They really don't make it significantly easier to follow and after a few
cycles of the driver being patched with new stuff etc, they tend to become
actively misleading.
>However if I get an overwhelming
> hatred for this I can change it. Also, I know the rule regarding single/multiple
> line comments but here again I feel it helps separate the code and makes it
> easier to read.
I'll leave it up to the other maintainers to say they don't mind. But for IIO
please keep strictly to the style (including all the unwritten bits ;)
>
>> As a general good practice point, I'd rather that the driver supported
>> more than one instance of the chip.. Hence you'd take a copy of da9150_devs
>> to use here. I guess it is relatively unlikely with one of these, but
>> you never know ;)
>
> Have followed the general methods for MFD here, and a number of drivers take the
> same approach. Also, I think it would be undesirable to have multiple charger
> chips of the same type in one platform. I agree generally it's best to support
> multiple instances, but here I don't think we should.
You are a brave man to tell your customers what to do. If some crazy person
does use multiple of these chips on a device you get to deal with the inevitable
question of why doesn't it work ;)
>
>> Why does this need it's own file? Does the DA9150 support any other
>> interfaces?
>
> Yes, the DA9150 also has a SPI interface. At present the plan is to just add I2C
> support for now, but in the future we may add SPI support, so have written the
> code with this in mind.
Ah, I didn't find that from the details I could find via google. In that
case fair enough.
>
>> Why the indirection? The da9150 only supports i2c as far as I can see.
>
> As per my last comment.
>
>> I'd roll this into one line and not bother with the local variable...
>
> Fair enough but I think this keeps the code cleaner, and to me it makes sense
> for the actual logic to be in core file as that's interface agnostic.
>
>> Drop comments on things that are self-evident. Also these are one
>> line comments so should be using the single line comment syntax.
>
> As per my previous comment I think it just helps to break up the code and makes
> it more readable. Will change it though if the general consensus is to remove
> it.
On September 27, 2014 11:37, Jonathan Cameron wrote:
> On 23/09/14 11:53, Adam Thomson wrote:
> > Signed-off-by: Adam Thomson <[email protected]>
> Obviously this really wants a review from one of the device tree guys, but I
> have a few
> bits based on what Mark has recently said in other reviews ;)
> > ---
> > Documentation/devicetree/bindings/mfd/da9150.txt | 41
> ++++++++++++++++++++++++
> > 1 file changed, 41 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/mfd/da9150.txt
> >
> > diff --git a/Documentation/devicetree/bindings/mfd/da9150.txt
> b/Documentation/devicetree/bindings/mfd/da9150.txt
> > new file mode 100644
> > index 0000000..d7de150
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/da9150.txt
> > @@ -0,0 +1,41 @@
> > +Dialog Semiconductor DA9150 Combined Charger/Fuel-Gauge MFD bindings
> > +
> > +DA9150 consists of a group of sub-devices (I2C Only):
> What does I2C only add to the description?
Nothing really. Will remove.
> > +
> > +Device Description
> > +------ -----------
> > +da9150-gpadc : IIO - GPADC
> Given usual aversion to anything driver specific in the device tree description
> you probably
> just want to describe what they do rather than what subsystem provides the driver.
Ok, can update accordingly.
>
> > +da9150-charger : Power Supply (Charger)
> > +
> > +======
> > +
> > +Required properties:
> > +- compatible : Should be "dlg,da9150"
> > +- reg: Specifies the I2C slave address
> > +- interrupt-parent: Specifies the phandle of the interrupt controller to which
> > + the IRQs from da9150 are delivered to.
> > +- interrupts: IRQ line info for da9150 chip.
> Cross refer to the standard interrupts doc for these...
>
Ok, can do that.
> > +- interrupt-controller: da9150 has internal IRQs (own IRQ domain).
> > +
> > +Sub-devices:
> > +- da9150-gpadc: See Documentation/devicetree/bindings/iio/adc/da9150-
> gpadc.txt
> > +- da9150-charger: See Documentation/devicetree/bindings/power/da9150-
> charger.txt
> > +
> > +
> > +Example:
> > +
> > + charger_fg: da9150@58 {
> > + compatible = "dlg,da9150";
> > + reg = <0x58>;
> > + interrupt-parent = <&gpio6>;
> > + interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
> > + interrupt-controller;
> > +
> > + gpadc: da9150-gpadc {
> > + ...
> > + };
> > +
> > + da9150-charger {
> > + ...
> > + };
> > + };
> > --
> > 1.9.3
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?