2020-06-19 12:55:51

by Gene Chen

[permalink] [raw]
Subject: [PATCH v2 0/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360


This patch series add mt6360 sub-device adc/regulator and
fix mfd architecture and add dt-binding document

changelogs between v1 & v2
- adjust binding document schema include mfd/adc/regulator
- adc: use IIO_CHAN_INFO_PROCESSED only
- adc: use devm_iio_triggered_buffer_setup
- adc: use use s64 to record timestamp
- regulator: merge regmap to mfd driver for r/w with crc

Gene Chen (4)
dt-bindings: mfd: Add bindings for the Mediatek MT6360
mfd: mt6360: implement i2c R/W with CRC
iio: adc: mt6360: Add ADC driver for MT6360
regulator: mt6360: Add support for MT6360 regulator

Documentation/devicetree/bindings/mfd/mt6360.txt | 122 +++++
drivers/iio/adc/Kconfig | 11
drivers/iio/adc/Makefile | 1
drivers/iio/adc/mt6360-adc.c | 388 ++++++++++++++++
drivers/mfd/Kconfig | 1
drivers/mfd/mt6360-core.c | 541 +++++++++++++++--------
drivers/regulator/Kconfig | 9
drivers/regulator/Makefile | 1
drivers/regulator/mt6360-regulator.c | 485 ++++++++++++++++++++
include/dt-bindings/mfd/mt6360.h | 15
include/linux/mfd/mt6360.h | 240 ----------
11 files changed, 1389 insertions(+), 425 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/mt6360.txt
create mode 100644 include/dt-bindings/mfd/mt6360.h
delete mode 100644 include/linux/mfd/mt6360.h
create mode 100644 drivers/iio/adc/mt6360-adc.c
create mode 100644 drivers/regulator/mt6360-regulator.c


2020-06-19 12:55:56

by Gene Chen

[permalink] [raw]
Subject: [PATCH v2 1/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 PMIC

From: Gene Chen <[email protected]>

Add devicetree binding document support Mediatek MT6360 PMIC

Signed-off-by: Gene Chen <[email protected]>
---
Documentation/devicetree/bindings/mfd/mt6360.txt | 122 +++++++++++++++++++++++
include/dt-bindings/mfd/mt6360.h | 15 +++
2 files changed, 137 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/mt6360.txt
create mode 100644 include/dt-bindings/mfd/mt6360.h

diff --git a/Documentation/devicetree/bindings/mfd/mt6360.txt b/Documentation/devicetree/bindings/mfd/mt6360.txt
new file mode 100644
index 0000000..7d7d349
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/mt6360.txt
@@ -0,0 +1,122 @@
+MediaTek MT6360 PMIC Driver
+
+MT6360 is a multifunction device with the following sub modules:
+It is interfaced to host controller using I2C interface.
+This document describes the binding for PMIC device and its sub module.
+
+- ADC
+- Battery Charger/OTG boost
+- Flash LED/RGB LED/moonlight LED
+- 2-channel Buck and 6-channel LDO
+- USB_PD
+
+Required properties:
+- compatible: Must be "mediatek,mt6360-pmu"
+- reg: Specifies the I2C slave address of PMIC block, Must be <0x34>
+- interrupts: I2C device IRQ line connected to the main SoC.
+
+Optional subnodes:
+- ADC
+ Required properties:
+ - compatible: "mediatek,mt6360-adc"
+- battery charger/OTG boost
+ Required properties:
+ - compatible: "mediatek,mt6360-chg"
+- Flash LED/RGB LED/moonlight LED
+ Required properties:
+ - compatible: "mediatek,mt6360-led"
+- 2-channel Buck and 6-channel LDO
+ Required properties:
+ - compatible: "mediatek,mt6360-regulator"
+- USB_PD
+ Required properties:
+ - compatible: "mediatek,mt6360-tcpc"
+
+Example:
+
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/mfd/mt6360.h>
+
+ mt6360@34 {
+ compatible = "mediatek,mt6360";
+ reg = <0x34>;
+ wakeup-source;
+ interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "IRQB";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ adc {
+ compatible = "mediatek,mt6360-adc";
+ #io-channel-cells = <1>;
+ };
+ regulator {
+ compatible = "mediatek,mt6360-regulator";
+ LDO_VIN3-supply = <&BUCK2>;
+ buck1 {
+ regulator-compatible = "BUCK1";
+ regulator-name = "mt6360,buck1";
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-allowed-modes = <MT6360_OPMODE_NORMAL
+ MT6360_OPMODE_LP
+ MT6360_OPMODE_ULP>;
+ };
+ BUCK2: buck2 {
+ regulator-compatible = "BUCK2";
+ regulator-name = "mt6360,buck2";
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-allowed-modes = <MT6360_OPMODE_NORMAL
+ MT6360_OPMODE_LP
+ MT6360_OPMODE_ULP>;
+ };
+ ldo6 {
+ regulator-compatible = "LDO6";
+ regulator-name = "mt6360,ldo6";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2100000>;
+ regulator-allowed-modes = <MT6360_OPMODE_NORMAL
+ MT6360_OPMODE_LP>;
+ };
+ ldo7 {
+ regulator-compatible = "LDO7";
+ regulator-name = "mt6360,ldo7";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2100000>;
+ regulator-allowed-modes = <MT6360_OPMODE_NORMAL
+ MT6360_OPMODE_LP>;
+ };
+ ldo1 {
+ regulator-compatible = "LDO1";
+ regulator-name = "mt6360,ldo1";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3600000>;
+ regulator-allowed-modes = <MT6360_OPMODE_NORMAL
+ MT6360_OPMODE_LP>;
+ };
+ ldo2 {
+ regulator-compatible = "LDO2";
+ regulator-name = "mt6360,ldo2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3600000>;
+ regulator-allowed-modes = <MT6360_OPMODE_NORMAL
+ MT6360_OPMODE_LP>;
+ };
+ ldo3 {
+ regulator-compatible = "LDO3";
+ regulator-name = "mt6360,ldo3";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3600000>;
+ regulator-allowed-modes = <MT6360_OPMODE_NORMAL
+ MT6360_OPMODE_LP>;
+ };
+ ldo5 {
+ regulator-compatible = "LDO5";
+ regulator-name = "mt6360,ldo5";
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <3600000>;
+ regulator-allowed-modes = <MT6360_OPMODE_NORMAL
+ MT6360_OPMODE_LP>;
+ };
+ };
+ };
diff --git a/include/dt-bindings/mfd/mt6360.h b/include/dt-bindings/mfd/mt6360.h
new file mode 100644
index 0000000..6368388
--- /dev/null
+++ b/include/dt-bindings/mfd/mt6360.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This header provides macros for MT6360 device bindings.
+ *
+ * Copyright (c) 2020 Mediatek Inc.
+ */
+
+#ifndef __DT_BINDINGS_MT6360_H__
+#define __DT_BINDINGS_MT6360_H__
+
+#define MT6360_OPMODE_LP (2)
+#define MT6360_OPMODE_ULP (3)
+#define MT6360_OPMODE_NORMAL (0)
+
+#endif /* __DT_BINDINGS_MT6360_H__ */
--
2.7.4

2020-06-19 18:24:56

by Gene Chen

[permalink] [raw]
Subject: [PATCH v2 3/4] iio: adc: mt6360: Add ADC driver for MT6360

From: Gene Chen <[email protected]>

Add MT6360 ADC driver include Charger Current, Voltage, and
Temperature.

Signed-off-by: Gene Chen <[email protected]>
---
drivers/iio/adc/Kconfig | 11 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/mt6360-adc.c | 388 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 400 insertions(+)
create mode 100644 drivers/iio/adc/mt6360-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index ff35696..7c77424 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -702,6 +702,17 @@ config MCP3911
This driver can also be built as a module. If so, the module will be
called mcp3911.

+config MEDIATEK_MT6360_ADC
+ tristate "Mediatek MT6360 ADC Part"
+ depends on MFD_MT6360
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here to enable MT6360 ADC Part.
+ Integrated for System Monitoring include
+ Charger and Battery Current, Voltage and
+ Temperature
+
config MEDIATEK_MT6577_AUXADC
tristate "MediaTek AUXADC driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 90f94ad..5fca90a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MCP3911) += mcp3911.o
+obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c
new file mode 100644
index 0000000..a8ca80d
--- /dev/null
+++ b/drivers/iio/adc/mt6360-adc.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * Author: Gene Chen <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/ktime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define MT6360_REG_PMUCHGCTRL3 0x313
+#define MT6360_REG_PMUADCCFG 0x356
+#define MT6360_REG_PMUADCRPT1 0x35A
+
+/* PMUCHGCTRL3 0x313 */
+#define MT6360_AICR_MASK 0xFC
+#define MT6360_AICR_SHFT 2
+#define MT6360_AICR_400MA 0x6
+/* PMUADCCFG 0x356 */
+#define MT6360_ADCEN_MASK 0x8000
+/* PMUADCRPT1 0x35A */
+#define MT6360_PREFERCH_MASK 0xF0
+#define MT6360_PREFERCH_SHFT 4
+#define MT6360_RPTCH_MASK 0x0F
+
+enum {
+ MT6360_CHAN_USBID = 0,
+ MT6360_CHAN_VBUSDIV5,
+ MT6360_CHAN_VBUSDIV2,
+ MT6360_CHAN_VSYS,
+ MT6360_CHAN_VBAT,
+ MT6360_CHAN_IBUS,
+ MT6360_CHAN_IBAT,
+ MT6360_CHAN_CHG_VDDP,
+ MT6360_CHAN_TEMP_JC,
+ MT6360_CHAN_VREF_TS,
+ MT6360_CHAN_TS,
+ MT6360_CHAN_MAX,
+};
+
+struct mt6360_adc_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct completion adc_complete;
+ struct mutex adc_lock;
+ ktime_t last_off_timestamps[MT6360_CHAN_MAX];
+ int irq;
+};
+
+static inline int mt6360_adc_val_converter(int val, int multiplier,
+ int offset, int divisor)
+{
+ return ((val * multiplier) + offset) / divisor;
+}
+
+static int mt6360_adc_convert_processed_val(struct mt6360_adc_data *info,
+ int chan_idx, int *val)
+{
+ unsigned int regval = 0;
+ const struct converter {
+ int multiplier;
+ int offset;
+ int divisor;
+ } adc_converter[MT6360_CHAN_MAX] = {
+ { 1250, 0, 1}, /* USBID */
+ { 6250, 0, 1}, /* VBUSDIV5 */
+ { 2500, 0, 1}, /* VBUSDIV2 */
+ { 1250, 0, 1}, /* VSYS */
+ { 1250, 0, 1}, /* VBAT */
+ { 2500, 0, 1}, /* IBUS */
+ { 2500, 0, 1}, /* IBAT */
+ { 1250, 0, 1}, /* CHG_VDDP */
+ { 105, -8000, 100}, /* TEMP_JC */
+ { 1250, 0, 1}, /* VREF_TS */
+ { 1250, 0, 1}, /* TS */
+ }, sp_ibus_adc_converter = { 1900, 0, 1 }, *sel_converter;
+ int ret;
+
+ sel_converter = adc_converter + chan_idx;
+ if (chan_idx == MT6360_CHAN_IBUS) {
+ /* ibus chan will be affected by aicr config */
+ /* if aicr < 400, apply the special ibus converter */
+ ret = regmap_read(info->regmap,
+ MT6360_REG_PMUCHGCTRL3, &regval);
+ if (ret)
+ return ret;
+
+ regval = (regval & MT6360_AICR_MASK) >> MT6360_AICR_SHFT;
+ if (regval < MT6360_AICR_400MA)
+ sel_converter = &sp_ibus_adc_converter;
+ }
+
+ *val = mt6360_adc_val_converter(*val, sel_converter->multiplier,
+ sel_converter->offset,
+ sel_converter->divisor);
+
+ return 0;
+}
+
+static int mt6360_adc_read_processed(struct mt6360_adc_data *mad,
+ int channel, int *val)
+{
+ u16 adc_enable;
+ u8 rpt[3];
+ ktime_t start_t, predict_end_t;
+ long timeout;
+ int value, ret;
+
+ mutex_lock(&mad->adc_lock);
+
+ /* select preferred channel that we want */
+ ret = regmap_update_bits(mad->regmap,
+ MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK,
+ channel << MT6360_PREFERCH_SHFT);
+ if (ret)
+ goto out_adc;
+
+ /* enable adc channel we want and adc_en */
+ adc_enable = MT6360_ADCEN_MASK | BIT(channel);
+ adc_enable = cpu_to_be16(adc_enable);
+ ret = regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG,
+ (void *)&adc_enable, sizeof(u16));
+ if (ret)
+ goto out_adc;
+
+ start_t = ktime_get();
+ predict_end_t = ktime_add_ms(mad->last_off_timestamps[channel], 50);
+
+ if (ktime_after(start_t, predict_end_t))
+ predict_end_t = ktime_add_ms(start_t, 25);
+ else
+ predict_end_t = ktime_add_ms(start_t, 75);
+
+ enable_irq(mad->irq);
+adc_retry:
+ reinit_completion(&mad->adc_complete);
+
+ /* wait for conversion to complete */
+ timeout = wait_for_completion_timeout(&mad->adc_complete,
+ msecs_to_jiffies(200));
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ goto out_adc_conv;
+ } else if (timeout < 0) {
+ ret = -EINTR;
+ goto out_adc_conv;
+ }
+
+ ret = regmap_raw_read(mad->regmap,
+ MT6360_REG_PMUADCRPT1, rpt, sizeof(rpt));
+ if (ret)
+ goto out_adc_conv;
+
+ /* check the current reported channel */
+ if ((rpt[0] & MT6360_RPTCH_MASK) != channel) {
+ dev_dbg(mad->dev,
+ "not wanted channel report [%02x]\n", rpt[0]);
+ goto adc_retry;
+ }
+
+ if (!ktime_after(ktime_get(), predict_end_t)) {
+ dev_dbg(mad->dev, "time is not after one adc_conv_t\n");
+ goto adc_retry;
+ }
+
+ value = (rpt[1] << 8) | rpt[2];
+
+ ret = mt6360_adc_convert_processed_val(mad, channel, &value);
+ if (ret)
+ goto out_adc_conv;
+
+ *val = value;
+ ret = IIO_VAL_INT;
+
+out_adc_conv:
+ disable_irq(mad->irq);
+ adc_enable = MT6360_ADCEN_MASK;
+ adc_enable = cpu_to_be16(adc_enable);
+ regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG,
+ (void *)&adc_enable, sizeof(u16));
+ mad->last_off_timestamps[channel] = ktime_get();
+ /* set prefer channel to 0xf */
+ regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1,
+ MT6360_PREFERCH_MASK, 0xF << MT6360_PREFERCH_SHFT);
+out_adc:
+ mutex_unlock(&mad->adc_lock);
+
+ return ret;
+}
+
+static int mt6360_adc_read_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct mt6360_adc_data *mad = iio_priv(iio_dev);
+
+ if (mask == IIO_CHAN_INFO_PROCESSED)
+ return mt6360_adc_read_processed(mad, chan->channel, val);
+
+ return -EINVAL;
+}
+
+static const struct iio_info mt6360_adc_iio_info = {
+ .read_raw = mt6360_adc_read_raw,
+};
+
+#define MT6360_ADC_CHAN(_idx, _type) { \
+ .type = _type, \
+ .channel = MT6360_CHAN_##_idx, \
+ .scan_index = MT6360_CHAN_##_idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .shift = 0, \
+ .endianness = IIO_CPU, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .extend_name = #_idx, \
+ .datasheet_name = #_idx, \
+ .indexed = 1, \
+}
+
+static const struct iio_chan_spec mt6360_adc_channels[] = {
+ MT6360_ADC_CHAN(USBID, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VSYS, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBAT, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(IBUS, IIO_CURRENT),
+ MT6360_ADC_CHAN(IBAT, IIO_CURRENT),
+ MT6360_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(TEMP_JC, IIO_TEMP),
+ MT6360_ADC_CHAN(VREF_TS, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(TS, IIO_VOLTAGE),
+ IIO_CHAN_SOFT_TIMESTAMP(MT6360_CHAN_MAX),
+};
+
+static irqreturn_t mt6360_pmu_adc_donei_handler(int irq, void *data)
+{
+ struct mt6360_adc_data *mad = iio_priv(data);
+
+ complete(&mad->adc_complete);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ /* 11 ch s32 numbers + 1 s64 timestamp */
+ s32 data[MT6360_CHAN_MAX + 2] = { };
+ int i = 0, bit, val, ret;
+
+ for_each_set_bit(bit,
+ indio_dev->active_scan_mask, indio_dev->masklength) {
+ const struct iio_chan_spec *chan = indio_dev->channels + bit;
+
+ ret = mt6360_adc_read_raw(indio_dev, chan, &val,
+ NULL, IIO_CHAN_INFO_PROCESSED);
+ if (ret != IIO_VAL_INT) {
+ dev_warn(&indio_dev->dev,
+ "Failed to get %d conversion val\n", bit);
+ goto out;
+ }
+
+ data[i++] = val;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ data, iio_get_time_ns(indio_dev));
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static inline int mt6360_adc_reset(struct mt6360_adc_data *info)
+{
+ u8 configs[3] = {0x80, 0, 0};
+ ktime_t all_off_time;
+ int i;
+
+ all_off_time = ktime_get();
+ for (i = 0; i < MT6360_CHAN_MAX; i++)
+ info->last_off_timestamps[i] = all_off_time;
+
+ /* enable adc_en, clear adc_chn_en/zcv_en/adc_wait_t/adc_idle_t */
+ return regmap_raw_write(info->regmap,
+ MT6360_REG_PMUADCCFG, configs, sizeof(configs));
+}
+
+static int mt6360_adc_probe(struct platform_device *pdev)
+{
+ struct mt6360_adc_data *mad;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*mad));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ mad = iio_priv(indio_dev);
+ mad->dev = &pdev->dev;
+ init_completion(&mad->adc_complete);
+ mutex_init(&mad->adc_lock);
+
+ mad->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!mad->regmap) {
+ dev_err(&pdev->dev, "Failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ ret = mt6360_adc_reset(mad);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to reset adc\n");
+ return ret;
+ }
+
+ mad->irq = platform_get_irq_byname(pdev, "adc_donei");
+ if (mad->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get adc_done irq\n");
+ return mad->irq;
+ }
+
+ irq_set_status_flags(mad->irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(&pdev->dev, mad->irq, NULL,
+ mt6360_pmu_adc_donei_handler,
+ IRQF_TRIGGER_NONE, "adc_donei",
+ indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register adc_done irq\n");
+ return ret;
+ }
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &mt6360_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mt6360_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mt6360_adc_channels);
+
+ ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, NULL,
+ mt6360_adc_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to allocate iio trigger buffer\n");
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register iio device\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused mt6360_adc_of_id[] = {
+ { .compatible = "mediatek,mt6360-adc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6360_adc_of_id);
+
+static struct platform_driver mt6360_adc_driver = {
+ .driver = {
+ .name = "mt6360-adc",
+ .of_match_table = mt6360_adc_of_id,
+ },
+ .probe = mt6360_adc_probe,
+};
+module_platform_driver(mt6360_adc_driver);
+
+MODULE_AUTHOR("Gene Chen <[email protected]>");
+MODULE_DESCRIPTION("MT6360 ADC Driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4

2020-06-19 18:24:59

by Gene Chen

[permalink] [raw]
Subject: [PATCH v2 4/4] regulator: mt6360: Add support for MT6360 regulator

From: Gene Chen <[email protected]>

Add MT6360 regulator driver include 2-channel buck and
6-channel ldo

Signed-off-by: Gene Chen <[email protected]>
---
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/mt6360-regulator.c | 485 +++++++++++++++++++++++++++++++++++
3 files changed, 495 insertions(+)
create mode 100644 drivers/regulator/mt6360-regulator.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8f677f5..9ae5711 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -691,6 +691,15 @@ config REGULATOR_MT6358
This driver supports the control of different power rails of device
through regulator interface.

+config REGULATOR_MT6360
+ tristate "MT6360 SubPMIC Regulator"
+ depends on MFD_MT6360
+ help
+ Say Y here to enable MT6360 regulator support.
+ This is support MT6360 PMIC/LDO part include
+ 2-channel buck with Thermal Shutdown and Overload Protection
+ 6-channel High PSRR and Low Dropout LDO.
+
config REGULATOR_MT6380
tristate "MediaTek MT6380 PMIC"
depends on MTK_PMIC_WRAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e8f1633..7256457 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o
+obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
diff --git a/drivers/regulator/mt6360-regulator.c b/drivers/regulator/mt6360-regulator.c
new file mode 100644
index 0000000..f3c8911
--- /dev/null
+++ b/drivers/regulator/mt6360-regulator.c
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (c) 2020 MediaTek Inc.
+
+// Author: Gene Chen <[email protected]>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/version.h>
+
+#include <dt-bindings/mfd/mt6360.h>
+
+enum {
+ MT6360_REGULATOR_BUCK1 = 0,
+ MT6360_REGULATOR_BUCK2,
+ MT6360_REGULATOR_LDO6,
+ MT6360_REGULATOR_LDO7,
+ MT6360_REGULATOR_LDO1,
+ MT6360_REGULATOR_LDO2,
+ MT6360_REGULATOR_LDO3,
+ MT6360_REGULATOR_LDO5,
+ MT6360_REGULATOR_MAX,
+};
+
+struct mt6360_irq_mapping {
+ const char *name;
+ irq_handler_t handler;
+};
+
+struct mt6360_regulator_desc {
+ const struct regulator_desc desc;
+ unsigned int mode_reg;
+ unsigned int mode_mask;
+ unsigned int state_reg;
+ unsigned int state_mask;
+ const struct mt6360_irq_mapping *irq_tables;
+ int irq_table_size;
+};
+
+struct mt6360_regulator_data {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+static irqreturn_t mt6360_pgb_event_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_FAIL, NULL);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6360_oc_event_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6360_ov_event_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev,
+ REGULATOR_EVENT_REGULATION_OUT, NULL);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6360_uv_event_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev,
+ REGULATOR_EVENT_UNDER_VOLTAGE, NULL);
+ return IRQ_HANDLED;
+}
+
+static const struct mt6360_irq_mapping buck1_irq_tbls[] = {
+ { "buck1_pgb_evt", mt6360_pgb_event_handler },
+ { "buck1_oc_evt", mt6360_oc_event_handler },
+ { "buck1_ov_evt", mt6360_ov_event_handler },
+ { "buck1_uv_evt", mt6360_uv_event_handler },
+};
+
+static const struct mt6360_irq_mapping buck2_irq_tbls[] = {
+ { "buck2_pgb_evt", mt6360_pgb_event_handler },
+ { "buck2_oc_evt", mt6360_oc_event_handler },
+ { "buck2_ov_evt", mt6360_ov_event_handler },
+ { "buck2_uv_evt", mt6360_uv_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo6_irq_tbls[] = {
+ { "ldo6_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo6_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo7_irq_tbls[] = {
+ { "ldo7_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo7_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo1_irq_tbls[] = {
+ { "ldo1_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo1_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo2_irq_tbls[] = {
+ { "ldo2_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo2_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo3_irq_tbls[] = {
+ { "ldo3_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo3_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo5_irq_tbls[] = {
+ { "ldo5_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo5_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct linear_range buck_vout_ranges[] = {
+ REGULATOR_LINEAR_RANGE(300000, 0x00, 0xc7, 5000),
+ REGULATOR_LINEAR_RANGE(1300000, 0xc8, 0xff, 0),
+};
+
+static const struct linear_range ldo_vout_ranges1[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x00, 0x09, 10000),
+ REGULATOR_LINEAR_RANGE(600000, 0x0a, 0x10, 0),
+ REGULATOR_LINEAR_RANGE(610000, 0x11, 0x19, 10000),
+ REGULATOR_LINEAR_RANGE(700000, 0x1a, 0x20, 0),
+ REGULATOR_LINEAR_RANGE(710000, 0x21, 0x29, 10000),
+ REGULATOR_LINEAR_RANGE(800000, 0x2a, 0x30, 0),
+ REGULATOR_LINEAR_RANGE(810000, 0x31, 0x39, 10000),
+ REGULATOR_LINEAR_RANGE(900000, 0x3a, 0x40, 0),
+ REGULATOR_LINEAR_RANGE(910000, 0x41, 0x49, 10000),
+ REGULATOR_LINEAR_RANGE(1000000, 0x4a, 0x50, 0),
+ REGULATOR_LINEAR_RANGE(1010000, 0x51, 0x59, 10000),
+ REGULATOR_LINEAR_RANGE(1100000, 0x5a, 0x60, 0),
+ REGULATOR_LINEAR_RANGE(1110000, 0x61, 0x69, 10000),
+ REGULATOR_LINEAR_RANGE(1200000, 0x6a, 0x70, 0),
+ REGULATOR_LINEAR_RANGE(1210000, 0x71, 0x79, 10000),
+ REGULATOR_LINEAR_RANGE(1300000, 0x7a, 0x80, 0),
+ REGULATOR_LINEAR_RANGE(1310000, 0x81, 0x89, 10000),
+ REGULATOR_LINEAR_RANGE(1400000, 0x8a, 0x90, 0),
+ REGULATOR_LINEAR_RANGE(1410000, 0x91, 0x99, 10000),
+ REGULATOR_LINEAR_RANGE(1500000, 0x9a, 0xa0, 0),
+ REGULATOR_LINEAR_RANGE(1510000, 0xa1, 0xa9, 10000),
+ REGULATOR_LINEAR_RANGE(1600000, 0xaa, 0xb0, 0),
+ REGULATOR_LINEAR_RANGE(1610000, 0xb1, 0xb9, 10000),
+ REGULATOR_LINEAR_RANGE(1700000, 0xba, 0xc0, 0),
+ REGULATOR_LINEAR_RANGE(1710000, 0xc1, 0xc9, 10000),
+ REGULATOR_LINEAR_RANGE(1800000, 0xca, 0xd0, 0),
+ REGULATOR_LINEAR_RANGE(1810000, 0xd1, 0xd9, 10000),
+ REGULATOR_LINEAR_RANGE(1900000, 0xda, 0xe0, 0),
+ REGULATOR_LINEAR_RANGE(1910000, 0xe1, 0xe9, 10000),
+ REGULATOR_LINEAR_RANGE(2000000, 0xea, 0xf0, 0),
+ REGULATOR_LINEAR_RANGE(2010000, 0xf1, 0xf9, 10000),
+ REGULATOR_LINEAR_RANGE(2100000, 0xfa, 0xff, 0),
+};
+
+static const struct linear_range ldo_vout_ranges2[] = {
+ REGULATOR_LINEAR_RANGE(1200000, 0x00, 0x09, 10000),
+ REGULATOR_LINEAR_RANGE(1300000, 0x0a, 0x10, 0),
+ REGULATOR_LINEAR_RANGE(1310000, 0x11, 0x19, 10000),
+ REGULATOR_LINEAR_RANGE(1400000, 0x1a, 0x1f, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 0x20, 0x29, 10000),
+ REGULATOR_LINEAR_RANGE(1600000, 0x2a, 0x2f, 0),
+ REGULATOR_LINEAR_RANGE(1700000, 0x30, 0x39, 10000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x3a, 0x40, 0),
+ REGULATOR_LINEAR_RANGE(1810000, 0x41, 0x49, 10000),
+ REGULATOR_LINEAR_RANGE(1900000, 0x4a, 0x4f, 0),
+ REGULATOR_LINEAR_RANGE(2000000, 0x50, 0x59, 10000),
+ REGULATOR_LINEAR_RANGE(2100000, 0x5a, 0x60, 0),
+ REGULATOR_LINEAR_RANGE(2110000, 0x61, 0x69, 10000),
+ REGULATOR_LINEAR_RANGE(2200000, 0x6a, 0x6f, 0),
+ REGULATOR_LINEAR_RANGE(2500000, 0x70, 0x79, 10000),
+ REGULATOR_LINEAR_RANGE(2600000, 0x7a, 0x7f, 0),
+ REGULATOR_LINEAR_RANGE(2700000, 0x80, 0x89, 10000),
+ REGULATOR_LINEAR_RANGE(2800000, 0x8a, 0x90, 0),
+ REGULATOR_LINEAR_RANGE(2810000, 0x91, 0x99, 10000),
+ REGULATOR_LINEAR_RANGE(2900000, 0x9a, 0xa0, 0),
+ REGULATOR_LINEAR_RANGE(2910000, 0xa1, 0xa9, 10000),
+ REGULATOR_LINEAR_RANGE(3000000, 0xaa, 0xb0, 0),
+ REGULATOR_LINEAR_RANGE(3010000, 0xb1, 0xb9, 10000),
+ REGULATOR_LINEAR_RANGE(3100000, 0xba, 0xc0, 0),
+ REGULATOR_LINEAR_RANGE(3110000, 0xc1, 0xc9, 10000),
+ REGULATOR_LINEAR_RANGE(3200000, 0xca, 0xcf, 0),
+ REGULATOR_LINEAR_RANGE(3300000, 0xd0, 0xd9, 10000),
+ REGULATOR_LINEAR_RANGE(3400000, 0xda, 0xe0, 0),
+ REGULATOR_LINEAR_RANGE(3410000, 0xe1, 0xe9, 10000),
+ REGULATOR_LINEAR_RANGE(3500000, 0xea, 0xf0, 0),
+ REGULATOR_LINEAR_RANGE(3510000, 0xf1, 0xf9, 10000),
+ REGULATOR_LINEAR_RANGE(3600000, 0xfa, 0xff, 0),
+};
+
+static const struct linear_range ldo_vout_ranges3[] = {
+ REGULATOR_LINEAR_RANGE(2700000, 0x00, 0x09, 10000),
+ REGULATOR_LINEAR_RANGE(2800000, 0x0a, 0x10, 0),
+ REGULATOR_LINEAR_RANGE(2810000, 0x11, 0x19, 10000),
+ REGULATOR_LINEAR_RANGE(2900000, 0x1a, 0x20, 0),
+ REGULATOR_LINEAR_RANGE(2910000, 0x21, 0x29, 10000),
+ REGULATOR_LINEAR_RANGE(3000000, 0x2a, 0x30, 0),
+ REGULATOR_LINEAR_RANGE(3010000, 0x31, 0x39, 10000),
+ REGULATOR_LINEAR_RANGE(3100000, 0x3a, 0x40, 0),
+ REGULATOR_LINEAR_RANGE(3110000, 0x41, 0x49, 10000),
+ REGULATOR_LINEAR_RANGE(3200000, 0x4a, 0x4f, 0),
+ REGULATOR_LINEAR_RANGE(3300000, 0x50, 0x59, 10000),
+ REGULATOR_LINEAR_RANGE(3400000, 0x5a, 0x60, 0),
+ REGULATOR_LINEAR_RANGE(3410000, 0x61, 0x69, 10000),
+ REGULATOR_LINEAR_RANGE(3500000, 0x6a, 0x70, 0),
+ REGULATOR_LINEAR_RANGE(3510000, 0x71, 0x79, 10000),
+ REGULATOR_LINEAR_RANGE(3600000, 0x7a, 0x7f, 0),
+};
+
+static int mt6360_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ const struct mt6360_regulator_desc *rdesc =
+ (const struct mt6360_regulator_desc *)rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int shift = ffs(rdesc->mode_mask) - 1;
+ unsigned int val;
+ int ret;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = MT6360_OPMODE_NORMAL;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = MT6360_OPMODE_ULP;
+ break;
+ case REGULATOR_MODE_IDLE:
+ val = MT6360_OPMODE_LP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(regmap, rdesc->mode_reg,
+ rdesc->mode_mask, val << shift);
+ if (ret) {
+ dev_err(&rdev->dev, "%s: fail (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static unsigned int mt6360_regulator_get_mode(struct regulator_dev *rdev)
+{
+ const struct mt6360_regulator_desc *rdesc =
+ (const struct mt6360_regulator_desc *)rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int shift = ffs(rdesc->mode_mask) - 1;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, rdesc->mode_reg, &val);
+ if (ret)
+ return ret;
+
+ val &= rdesc->mode_mask;
+ val >>= shift;
+
+ switch (val) {
+ case MT6360_OPMODE_LP:
+ return REGULATOR_MODE_IDLE;
+ case MT6360_OPMODE_ULP:
+ return REGULATOR_MODE_STANDBY;
+ case MT6360_OPMODE_NORMAL:
+ return REGULATOR_MODE_NORMAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt6360_regulator_get_status(struct regulator_dev *rdev)
+{
+ const struct mt6360_regulator_desc *rdesc =
+ (const struct mt6360_regulator_desc *)rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, rdesc->state_reg, &val);
+ if (ret)
+ return ret;
+
+ if (val & rdesc->state_mask)
+ return REGULATOR_STATUS_ON;
+
+ return REGULATOR_STATUS_OFF;
+}
+
+static const struct regulator_ops mt6360_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_mode = mt6360_regulator_set_mode,
+ .get_mode = mt6360_regulator_get_mode,
+ .get_status = mt6360_regulator_get_status,
+};
+
+static unsigned int mt6360_regulator_of_map_mode(unsigned int hw_mode)
+{
+ switch (hw_mode) {
+ case MT6360_OPMODE_NORMAL:
+ return REGULATOR_MODE_NORMAL;
+ case MT6360_OPMODE_LP:
+ return REGULATOR_MODE_IDLE;
+ case MT6360_OPMODE_ULP:
+ return REGULATOR_MODE_STANDBY;
+ default:
+ return REGULATOR_MODE_INVALID;
+ }
+}
+
+#define MT6360_REGULATOR_DESC(_name, _sname, ereg, emask, vreg, vmask, \
+ mreg, mmask, streg, stmask, vranges, \
+ vcnts, offon_delay, irq_tbls) \
+{ \
+ .desc = { \
+ .name = #_name, \
+ .supply_name = #_sname, \
+ .id = MT6360_REGULATOR_##_name, \
+ .of_match = of_match_ptr(#_name), \
+ .of_map_mode = mt6360_regulator_of_map_mode, \
+ .owner = THIS_MODULE, \
+ .ops = &mt6360_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .vsel_reg = vreg, \
+ .vsel_mask = vmask, \
+ .enable_reg = ereg, \
+ .enable_mask = emask, \
+ .linear_ranges = vranges, \
+ .n_linear_ranges = ARRAY_SIZE(vranges), \
+ .n_voltages = vcnts, \
+ .off_on_delay = offon_delay, \
+ }, \
+ .mode_reg = mreg, \
+ .mode_mask = mmask, \
+ .state_reg = streg, \
+ .state_mask = stmask, \
+ .irq_tables = irq_tbls, \
+ .irq_table_size = ARRAY_SIZE(irq_tbls), \
+}
+
+static const struct mt6360_regulator_desc mt6360_regulator_descs[] = {
+ MT6360_REGULATOR_DESC(BUCK1, BUCK1_VIN, 0x117, 0x40, 0x110, 0xff, 0x117,
+ 0x30, 0x117, 0x04, buck_vout_ranges, 256, 0,
+ buck1_irq_tbls),
+ MT6360_REGULATOR_DESC(BUCK2, BUCK2_VIN, 0x127, 0x40, 0x120, 0xff, 0x127,
+ 0x30, 0x127, 0x04, buck_vout_ranges, 256, 0,
+ buck2_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO6, LDO_VIN3, 0x137, 0x40, 0x13B, 0xff, 0x137,
+ 0x30, 0x137, 0x04, ldo_vout_ranges1, 256, 0,
+ ldo6_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO7, LDO_VIN3, 0x131, 0x40, 0x135, 0xff, 0x131,
+ 0x30, 0x131, 0x04, ldo_vout_ranges1, 256, 0,
+ ldo7_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO1, LDO_VIN1, 0x217, 0x40, 0x21B, 0xff, 0x217,
+ 0x30, 0x217, 0x04, ldo_vout_ranges2, 256, 0,
+ ldo1_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO2, LDO_VIN1, 0x211, 0x40, 0x215, 0xff, 0x211,
+ 0x30, 0x211, 0x04, ldo_vout_ranges2, 256, 0,
+ ldo2_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO3, LDO_VIN1, 0x205, 0x40, 0x209, 0xff, 0x205,
+ 0x30, 0x205, 0x04, ldo_vout_ranges2, 256, 100,
+ ldo3_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO5, LDO_VIN2, 0x20B, 0x40, 0x20F, 0x7f, 0x20B,
+ 0x30, 0x20B, 0x04, ldo_vout_ranges3, 128, 100,
+ ldo5_irq_tbls),
+};
+
+static int mt6360_regulator_irq_register(struct platform_device *pdev,
+ struct regulator_dev *rdev,
+ const struct mt6360_irq_mapping *tbls,
+ int tbl_size)
+{
+ int i, irq, ret;
+
+ for (i = 0; i < tbl_size; i++) {
+ const struct mt6360_irq_mapping *irq_desc = tbls + i;
+
+ irq = platform_get_irq_byname(pdev, irq_desc->name);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "Fail to get %s irq\n", irq_desc->name);
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ irq_desc->handler,
+ IRQF_TRIGGER_NONE,
+ irq_desc->name,
+ rdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Fail to request %s irq\n", irq_desc->name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mt6360_regulator_probe(struct platform_device *pdev)
+{
+ struct mt6360_regulator_data *mrd;
+ struct regulator_config config = {};
+ int i, ret;
+
+ mrd = devm_kzalloc(&pdev->dev, sizeof(*mrd), GFP_KERNEL);
+ if (!mrd)
+ return -ENOMEM;
+
+ mrd->dev = &pdev->dev;
+
+ mrd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!mrd->regmap) {
+ dev_err(&pdev->dev, "Failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ config.dev = &pdev->dev;
+ config.driver_data = mrd;
+ config.regmap = mrd->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(mt6360_regulator_descs); i++) {
+ const struct mt6360_regulator_desc *rdesc =
+ mt6360_regulator_descs + i;
+ struct regulator_dev *rdev;
+
+ rdev = devm_regulator_register(&pdev->dev,
+ &rdesc->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register %d regulaotr\n", i);
+ return PTR_ERR(rdev);
+ }
+
+ ret = mt6360_regulator_irq_register(pdev, rdev,
+ rdesc->irq_tables,
+ rdesc->irq_table_size);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register %d regulaotr irqs\n", i);
+ return ret;
+ }
+ }
+
+ platform_set_drvdata(pdev, mrd);
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused mt6360_regulator_of_id[] = {
+ { .compatible = "mediatek,mt6360-regulator", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6360_regulator_of_id);
+
+static struct platform_driver mt6360_regulator_driver = {
+ .driver = {
+ .name = "mt6360-regulator",
+ .of_match_table = mt6360_regulator_of_id,
+ },
+ .probe = mt6360_regulator_probe,
+};
+module_platform_driver(mt6360_regulator_driver);
+
+MODULE_AUTHOR("Gene Chen <[email protected]>");
+MODULE_DESCRIPTION("MT6360 Regulator Driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4

2020-06-20 16:57:44

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 0/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360

On Fri, 19 Jun 2020 19:53:47 +0800
Gene Chen <[email protected]> wrote:

> This patch series add mt6360 sub-device adc/regulator and
> fix mfd architecture and add dt-binding document

Hi Gene

Please make sure you include [email protected] in the cc list
for any iio related series. For now I'll take a quick look at the
ADC driver patch (and +cc the list for my reply)

Thanks,

Jonathan

>
> changelogs between v1 & v2
> - adjust binding document schema include mfd/adc/regulator
> - adc: use IIO_CHAN_INFO_PROCESSED only
> - adc: use devm_iio_triggered_buffer_setup
> - adc: use use s64 to record timestamp
> - regulator: merge regmap to mfd driver for r/w with crc
>
> Gene Chen (4)
> dt-bindings: mfd: Add bindings for the Mediatek MT6360
> mfd: mt6360: implement i2c R/W with CRC
> iio: adc: mt6360: Add ADC driver for MT6360
> regulator: mt6360: Add support for MT6360 regulator
>
> Documentation/devicetree/bindings/mfd/mt6360.txt | 122 +++++
> drivers/iio/adc/Kconfig | 11
> drivers/iio/adc/Makefile | 1
> drivers/iio/adc/mt6360-adc.c | 388 ++++++++++++++++
> drivers/mfd/Kconfig | 1
> drivers/mfd/mt6360-core.c | 541 +++++++++++++++--------
> drivers/regulator/Kconfig | 9
> drivers/regulator/Makefile | 1
> drivers/regulator/mt6360-regulator.c | 485 ++++++++++++++++++++
> include/dt-bindings/mfd/mt6360.h | 15
> include/linux/mfd/mt6360.h | 240 ----------
> 11 files changed, 1389 insertions(+), 425 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/mfd/mt6360.txt
> create mode 100644 include/dt-bindings/mfd/mt6360.h
> delete mode 100644 include/linux/mfd/mt6360.h
> create mode 100644 drivers/iio/adc/mt6360-adc.c
> create mode 100644 drivers/regulator/mt6360-regulator.c

2020-06-20 17:06:38

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 3/4] iio: adc: mt6360: Add ADC driver for MT6360

On Fri, 19 Jun 2020 19:53:50 +0800
Gene Chen <[email protected]> wrote:

> From: Gene Chen <[email protected]>
>
> Add MT6360 ADC driver include Charger Current, Voltage, and
> Temperature.
>
> Signed-off-by: Gene Chen <[email protected]>
I've taken a quick look and highlighted a few things.
As mentioned in my reply to the cover letter please make sure to cc
the IIO mailing list. I'm far from the only person who might review
this driver and more to the point I look there first rather than
checking my personal email so you are bit lucky I noticed this today!

Jonathan

> ---
> drivers/iio/adc/Kconfig | 11 ++
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/mt6360-adc.c | 388 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 400 insertions(+)
> create mode 100644 drivers/iio/adc/mt6360-adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index ff35696..7c77424 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -702,6 +702,17 @@ config MCP3911
> This driver can also be built as a module. If so, the module will be
> called mcp3911.
>
> +config MEDIATEK_MT6360_ADC
> + tristate "Mediatek MT6360 ADC Part"
> + depends on MFD_MT6360
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + help
> + Say Y here to enable MT6360 ADC Part.
> + Integrated for System Monitoring include
> + Charger and Battery Current, Voltage and
> + Temperature
> +
> config MEDIATEK_MT6577_AUXADC
> tristate "MediaTek AUXADC driver"
> depends on ARCH_MEDIATEK || COMPILE_TEST
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 90f94ad..5fca90a 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -65,6 +65,7 @@ obj-$(CONFIG_MAX9611) += max9611.o
> obj-$(CONFIG_MCP320X) += mcp320x.o
> obj-$(CONFIG_MCP3422) += mcp3422.o
> obj-$(CONFIG_MCP3911) += mcp3911.o
> +obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
> obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
> obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
> obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
> diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c
> new file mode 100644
> index 0000000..a8ca80d
> --- /dev/null
> +++ b/drivers/iio/adc/mt6360-adc.c
> @@ -0,0 +1,388 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2020 MediaTek Inc.
> + *
> + * Author: Gene Chen <[email protected]>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/completion.h>
> +#include <linux/mutex.h>
> +#include <linux/regmap.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/ktime.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#define MT6360_REG_PMUCHGCTRL3 0x313
> +#define MT6360_REG_PMUADCCFG 0x356
> +#define MT6360_REG_PMUADCRPT1 0x35A
> +
> +/* PMUCHGCTRL3 0x313 */
> +#define MT6360_AICR_MASK 0xFC
> +#define MT6360_AICR_SHFT 2
> +#define MT6360_AICR_400MA 0x6
> +/* PMUADCCFG 0x356 */
> +#define MT6360_ADCEN_MASK 0x8000
> +/* PMUADCRPT1 0x35A */
> +#define MT6360_PREFERCH_MASK 0xF0
> +#define MT6360_PREFERCH_SHFT 4
> +#define MT6360_RPTCH_MASK 0x0F
> +
> +enum {
> + MT6360_CHAN_USBID = 0,
> + MT6360_CHAN_VBUSDIV5,
> + MT6360_CHAN_VBUSDIV2,
> + MT6360_CHAN_VSYS,
> + MT6360_CHAN_VBAT,
> + MT6360_CHAN_IBUS,
> + MT6360_CHAN_IBAT,
> + MT6360_CHAN_CHG_VDDP,
> + MT6360_CHAN_TEMP_JC,
> + MT6360_CHAN_VREF_TS,
> + MT6360_CHAN_TS,
> + MT6360_CHAN_MAX,
> +};
> +
> +struct mt6360_adc_data {
> + struct device *dev;
> + struct regmap *regmap;
> + struct completion adc_complete;
> + struct mutex adc_lock;
> + ktime_t last_off_timestamps[MT6360_CHAN_MAX];
> + int irq;
> +};
> +
> +static inline int mt6360_adc_val_converter(int val, int multiplier,
> + int offset, int divisor)
> +{
> + return ((val * multiplier) + offset) / divisor;
> +}
> +
> +static int mt6360_adc_convert_processed_val(struct mt6360_adc_data *info,
> + int chan_idx, int *val)
> +{
> + unsigned int regval = 0;
> + const struct converter {
> + int multiplier;
> + int offset;
> + int divisor;
> + } adc_converter[MT6360_CHAN_MAX] = {
> + { 1250, 0, 1}, /* USBID */
> + { 6250, 0, 1}, /* VBUSDIV5 */
> + { 2500, 0, 1}, /* VBUSDIV2 */
> + { 1250, 0, 1}, /* VSYS */
> + { 1250, 0, 1}, /* VBAT */
> + { 2500, 0, 1}, /* IBUS */
> + { 2500, 0, 1}, /* IBAT */
> + { 1250, 0, 1}, /* CHG_VDDP */
> + { 105, -8000, 100}, /* TEMP_JC */
> + { 1250, 0, 1}, /* VREF_TS */
> + { 1250, 0, 1}, /* TS */
> + }, sp_ibus_adc_converter = { 1900, 0, 1 }, *sel_converter;
> + int ret;
> +
> + sel_converter = adc_converter + chan_idx;
> + if (chan_idx == MT6360_CHAN_IBUS) {
> + /* ibus chan will be affected by aicr config */
> + /* if aicr < 400, apply the special ibus converter */
> + ret = regmap_read(info->regmap,
> + MT6360_REG_PMUCHGCTRL3, &regval);
> + if (ret)
> + return ret;
> +
> + regval = (regval & MT6360_AICR_MASK) >> MT6360_AICR_SHFT;
> + if (regval < MT6360_AICR_400MA)
> + sel_converter = &sp_ibus_adc_converter;
> + }
> +
> + *val = mt6360_adc_val_converter(*val, sel_converter->multiplier,
> + sel_converter->offset,
> + sel_converter->divisor);
> +
> + return 0;
> +}
> +
> +static int mt6360_adc_read_processed(struct mt6360_adc_data *mad,
> + int channel, int *val)
> +{
> + u16 adc_enable;
> + u8 rpt[3];
> + ktime_t start_t, predict_end_t;
> + long timeout;
> + int value, ret;
> +
> + mutex_lock(&mad->adc_lock);
> +
> + /* select preferred channel that we want */
> + ret = regmap_update_bits(mad->regmap,
> + MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK,
> + channel << MT6360_PREFERCH_SHFT);
> + if (ret)
> + goto out_adc;
> +
> + /* enable adc channel we want and adc_en */
> + adc_enable = MT6360_ADCEN_MASK | BIT(channel);
> + adc_enable = cpu_to_be16(adc_enable);
> + ret = regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG,
> + (void *)&adc_enable, sizeof(u16));
> + if (ret)
> + goto out_adc;
> +
> + start_t = ktime_get();
> + predict_end_t = ktime_add_ms(mad->last_off_timestamps[channel], 50);
> +
> + if (ktime_after(start_t, predict_end_t))
> + predict_end_t = ktime_add_ms(start_t, 25);
> + else
> + predict_end_t = ktime_add_ms(start_t, 75);
> +
> + enable_irq(mad->irq);
> +adc_retry:
> + reinit_completion(&mad->adc_complete);
> +
> + /* wait for conversion to complete */
> + timeout = wait_for_completion_timeout(&mad->adc_complete,
> + msecs_to_jiffies(200));
> + if (timeout == 0) {
> + ret = -ETIMEDOUT;
> + goto out_adc_conv;
> + } else if (timeout < 0) {
> + ret = -EINTR;
> + goto out_adc_conv;
> + }
> +
> + ret = regmap_raw_read(mad->regmap,
> + MT6360_REG_PMUADCRPT1, rpt, sizeof(rpt));
> + if (ret)
> + goto out_adc_conv;
> +
> + /* check the current reported channel */
> + if ((rpt[0] & MT6360_RPTCH_MASK) != channel) {
> + dev_dbg(mad->dev,
> + "not wanted channel report [%02x]\n", rpt[0]);
> + goto adc_retry;
> + }
> +
> + if (!ktime_after(ktime_get(), predict_end_t)) {
> + dev_dbg(mad->dev, "time is not after one adc_conv_t\n");
> + goto adc_retry;
> + }
> +
> + value = (rpt[1] << 8) | rpt[2];
> +
> + ret = mt6360_adc_convert_processed_val(mad, channel, &value);
> + if (ret)
> + goto out_adc_conv;
> +
> + *val = value;
> + ret = IIO_VAL_INT;
> +
> +out_adc_conv:
> + disable_irq(mad->irq);
> + adc_enable = MT6360_ADCEN_MASK;
> + adc_enable = cpu_to_be16(adc_enable);
> + regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG,
> + (void *)&adc_enable, sizeof(u16));
> + mad->last_off_timestamps[channel] = ktime_get();
> + /* set prefer channel to 0xf */
> + regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1,
> + MT6360_PREFERCH_MASK, 0xF << MT6360_PREFERCH_SHFT);
> +out_adc:
> + mutex_unlock(&mad->adc_lock);
> +
> + return ret;
> +}
> +
> +static int mt6360_adc_read_raw(struct iio_dev *iio_dev,
> + const struct iio_chan_spec *chan,
> + int *val, int *val2, long mask)
> +{
> + struct mt6360_adc_data *mad = iio_priv(iio_dev);
> +
> + if (mask == IIO_CHAN_INFO_PROCESSED)
> + return mt6360_adc_read_processed(mad, chan->channel, val);
> +
> + return -EINVAL;
> +}
> +
> +static const struct iio_info mt6360_adc_iio_info = {
> + .read_raw = mt6360_adc_read_raw,
> +};
> +
> +#define MT6360_ADC_CHAN(_idx, _type) { \
> + .type = _type, \
> + .channel = MT6360_CHAN_##_idx, \
> + .scan_index = MT6360_CHAN_##_idx, \
> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 32, \
> + .storagebits = 32, \
> + .shift = 0, \

shift = 0 is an obvious default so no need to specify it.

> + .endianness = IIO_CPU, \
> + }, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
> + .extend_name = #_idx, \

Extend name means you are defining new userspace ABI, so that
needs to be documented so we can discuss the necessity of doing so.
Documentation/ABI/testing/sysfs-bus-iio-*

> + .datasheet_name = #_idx, \
> + .indexed = 1, \
> +}
> +
> +static const struct iio_chan_spec mt6360_adc_channels[] = {
> + MT6360_ADC_CHAN(USBID, IIO_VOLTAGE),
> + MT6360_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE),
> + MT6360_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE),
> + MT6360_ADC_CHAN(VSYS, IIO_VOLTAGE),
> + MT6360_ADC_CHAN(VBAT, IIO_VOLTAGE),
> + MT6360_ADC_CHAN(IBUS, IIO_CURRENT),
> + MT6360_ADC_CHAN(IBAT, IIO_CURRENT),
> + MT6360_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE),
> + MT6360_ADC_CHAN(TEMP_JC, IIO_TEMP),
> + MT6360_ADC_CHAN(VREF_TS, IIO_VOLTAGE),
> + MT6360_ADC_CHAN(TS, IIO_VOLTAGE),
> + IIO_CHAN_SOFT_TIMESTAMP(MT6360_CHAN_MAX),
> +};
> +
> +static irqreturn_t mt6360_pmu_adc_donei_handler(int irq, void *data)
> +{
> + struct mt6360_adc_data *mad = iio_priv(data);
> +
> + complete(&mad->adc_complete);
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + /* 11 ch s32 numbers + 1 s64 timestamp */
> + s32 data[MT6360_CHAN_MAX + 2] = { };

IIO requires that the timestamp that may be inserted in here
by iio_push_to_buffers_with_timestamp is 64 bit aligned.

In theory this array can be only 32 bit aligned. Hence tell
the compiler about this requirement with __aligned(8).

Note we are slowly working our way through fixing various historical
drivers that fell into this hole so you will currently find drivers
with this same problem in tree.

> + int i = 0, bit, val, ret;
> +
> + for_each_set_bit(bit,
> + indio_dev->active_scan_mask, indio_dev->masklength) {

Given the new relaxed view on the line length as long as it's below 100 and
helps readability, put the above on one line.

> + const struct iio_chan_spec *chan = indio_dev->channels + bit;
> +
> + ret = mt6360_adc_read_raw(indio_dev, chan, &val,
> + NULL, IIO_CHAN_INFO_PROCESSED);
> + if (ret != IIO_VAL_INT) {
> + dev_warn(&indio_dev->dev,
> + "Failed to get %d conversion val\n", bit);
> + goto out;
> + }
> +
> + data[i++] = val;
> + }
> + iio_push_to_buffers_with_timestamp(indio_dev,
> + data, iio_get_time_ns(indio_dev));
> +out:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static inline int mt6360_adc_reset(struct mt6360_adc_data *info)
> +{
> + u8 configs[3] = {0x80, 0, 0};
> + ktime_t all_off_time;
> + int i;
> +
> + all_off_time = ktime_get();
> + for (i = 0; i < MT6360_CHAN_MAX; i++)
> + info->last_off_timestamps[i] = all_off_time;
> +
> + /* enable adc_en, clear adc_chn_en/zcv_en/adc_wait_t/adc_idle_t */
> + return regmap_raw_write(info->regmap,
> + MT6360_REG_PMUADCCFG, configs, sizeof(configs));
> +}
> +
> +static int mt6360_adc_probe(struct platform_device *pdev)
> +{
> + struct mt6360_adc_data *mad;
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*mad));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + mad = iio_priv(indio_dev);
> + mad->dev = &pdev->dev;
> + init_completion(&mad->adc_complete);
> + mutex_init(&mad->adc_lock);
> +
> + mad->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> + if (!mad->regmap) {
> + dev_err(&pdev->dev, "Failed to get parent regmap\n");
> + return -ENODEV;
> + }
> +
> + ret = mt6360_adc_reset(mad);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Failed to reset adc\n");
> + return ret;
> + }
> +
> + mad->irq = platform_get_irq_byname(pdev, "adc_donei");
> + if (mad->irq < 0) {
> + dev_err(&pdev->dev, "Failed to get adc_done irq\n");
> + return mad->irq;
> + }
> +
> + irq_set_status_flags(mad->irq, IRQ_NOAUTOEN);
> + ret = devm_request_threaded_irq(&pdev->dev, mad->irq, NULL,
> + mt6360_pmu_adc_donei_handler,
> + IRQF_TRIGGER_NONE, "adc_donei",
> + indio_dev);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register adc_done irq\n");
> + return ret;
> + }
> +
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->info = &mt6360_adc_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = mt6360_adc_channels;
> + indio_dev->num_channels = ARRAY_SIZE(mt6360_adc_channels);
> +
> + ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, NULL,
> + mt6360_adc_trigger_handler, NULL);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to allocate iio trigger buffer\n");
> + return ret;
> + }
> +
> + ret = devm_iio_device_register(&pdev->dev, indio_dev);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register iio device\n");
> + return ret;
> + }
> +
> + platform_set_drvdata(pdev, indio_dev);

I can't immediately see where we use it?

> +
> + return 0;
> +}
> +
> +static const struct of_device_id __maybe_unused mt6360_adc_of_id[] = {
> + { .compatible = "mediatek,mt6360-adc", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, mt6360_adc_of_id);
> +
> +static struct platform_driver mt6360_adc_driver = {
> + .driver = {
> + .name = "mt6360-adc",
> + .of_match_table = mt6360_adc_of_id,
> + },
> + .probe = mt6360_adc_probe,
> +};
> +module_platform_driver(mt6360_adc_driver);
> +
> +MODULE_AUTHOR("Gene Chen <[email protected]>");
> +MODULE_DESCRIPTION("MT6360 ADC Driver");
> +MODULE_LICENSE("GPL v2");

2020-06-22 10:29:08

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH v2 1/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 PMIC

On Fri, 19 Jun 2020, Gene Chen wrote:

> From: Gene Chen <[email protected]>
>
> Add devicetree binding document support Mediatek MT6360 PMIC
>
> Signed-off-by: Gene Chen <[email protected]>
> ---
> Documentation/devicetree/bindings/mfd/mt6360.txt | 122 +++++++++++++++++++++++

This needs converting to YAML.

> include/dt-bindings/mfd/mt6360.h | 15 +++
> 2 files changed, 137 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/mt6360.txt
> create mode 100644 include/dt-bindings/mfd/mt6360.h
>
> diff --git a/Documentation/devicetree/bindings/mfd/mt6360.txt b/Documentation/devicetree/bindings/mfd/mt6360.txt
> new file mode 100644
> index 0000000..7d7d349
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/mt6360.txt
> @@ -0,0 +1,122 @@
> +MediaTek MT6360 PMIC Driver
> +
> +MT6360 is a multifunction device with the following sub modules:

Sub modules do not follow this sentence.

Please do not use the term "multi-function" in DT bindings. An MFD is
something we made up. It's a Linuxisum and has no real meaning in
DT. This device is not multi-functional. It has one function, to
control Power. The multi-function part comes from the fact that we
like things split-up into subsystem for organisation purposes and
simplicity.

> +It is interfaced to host controller using I2C interface.
> +This document describes the binding for PMIC device and its sub module.

This sentence should be at the top.

s/sub module/sub-devices/

> +- ADC
> +- Battery Charger/OTG boost
> +- Flash LED/RGB LED/moonlight LED
> +- 2-channel Buck and 6-channel LDO
> +- USB_PD

These should follow the sentence reviewing to the "sub modules".

> +Required properties:
> +- compatible: Must be "mediatek,mt6360-pmu"
> +- reg: Specifies the I2C slave address of PMIC block, Must be <0x34>
> +- interrupts: I2C device IRQ line connected to the main SoC.

Remove the '.'.

> +Optional subnodes:
> +- ADC
> + Required properties:
> + - compatible: "mediatek,mt6360-adc"
> +- battery charger/OTG boost

"Battery"

> + Required properties:
> + - compatible: "mediatek,mt6360-chg"
> +- Flash LED/RGB LED/moonlight LED
> + Required properties:
> + - compatible: "mediatek,mt6360-led"
> +- 2-channel Buck and 6-channel LDO
> + Required properties:
> + - compatible: "mediatek,mt6360-regulator"
> +- USB_PD

Remove the "_" and expand PD.

> + Required properties:
> + - compatible: "mediatek,mt6360-tcpc"
> +
> +Example:
> +
> + #include <dt-bindings/interrupt-controller/irq.h>
> + #include <dt-bindings/mfd/mt6360.h>
> +
> + mt6360@34 {
> + compatible = "mediatek,mt6360";
> + reg = <0x34>;
> + wakeup-source;
> + interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;
> + interrupt-names = "IRQB";
> + interrupt-controller;
> + #interrupt-cells = <1>;

'\n'

> + adc {
> + compatible = "mediatek,mt6360-adc";
> + #io-channel-cells = <1>;

Where is the channel cell?

> + };

'\n'

> + regulator {
> + compatible = "mediatek,mt6360-regulator";
> + LDO_VIN3-supply = <&BUCK2>;

'\n'

> + buck1 {
> + regulator-compatible = "BUCK1";
> + regulator-name = "mt6360,buck1";
> + regulator-min-microvolt = <300000>;
> + regulator-max-microvolt = <1300000>;
> + regulator-allowed-modes = <MT6360_OPMODE_NORMAL
> + MT6360_OPMODE_LP
> + MT6360_OPMODE_ULP>;
> + };

'\n'

Etc etc.

> + BUCK2: buck2 {
> + regulator-compatible = "BUCK2";
> + regulator-name = "mt6360,buck2";
> + regulator-min-microvolt = <300000>;
> + regulator-max-microvolt = <1300000>;
> + regulator-allowed-modes = <MT6360_OPMODE_NORMAL
> + MT6360_OPMODE_LP
> + MT6360_OPMODE_ULP>;
> + };
> + ldo6 {
> + regulator-compatible = "LDO6";
> + regulator-name = "mt6360,ldo6";
> + regulator-min-microvolt = <500000>;
> + regulator-max-microvolt = <2100000>;
> + regulator-allowed-modes = <MT6360_OPMODE_NORMAL
> + MT6360_OPMODE_LP>;
> + };
> + ldo7 {
> + regulator-compatible = "LDO7";
> + regulator-name = "mt6360,ldo7";
> + regulator-min-microvolt = <500000>;
> + regulator-max-microvolt = <2100000>;
> + regulator-allowed-modes = <MT6360_OPMODE_NORMAL
> + MT6360_OPMODE_LP>;
> + };
> + ldo1 {
> + regulator-compatible = "LDO1";
> + regulator-name = "mt6360,ldo1";
> + regulator-min-microvolt = <1200000>;
> + regulator-max-microvolt = <3600000>;
> + regulator-allowed-modes = <MT6360_OPMODE_NORMAL
> + MT6360_OPMODE_LP>;
> + };
> + ldo2 {
> + regulator-compatible = "LDO2";
> + regulator-name = "mt6360,ldo2";
> + regulator-min-microvolt = <1200000>;
> + regulator-max-microvolt = <3600000>;
> + regulator-allowed-modes = <MT6360_OPMODE_NORMAL
> + MT6360_OPMODE_LP>;
> + };
> + ldo3 {
> + regulator-compatible = "LDO3";
> + regulator-name = "mt6360,ldo3";
> + regulator-min-microvolt = <1200000>;
> + regulator-max-microvolt = <3600000>;
> + regulator-allowed-modes = <MT6360_OPMODE_NORMAL
> + MT6360_OPMODE_LP>;
> + };
> + ldo5 {
> + regulator-compatible = "LDO5";
> + regulator-name = "mt6360,ldo5";
> + regulator-min-microvolt = <2700000>;
> + regulator-max-microvolt = <3600000>;
> + regulator-allowed-modes = <MT6360_OPMODE_NORMAL
> + MT6360_OPMODE_LP>;
> + };

Why aren't the LDO cells in order?

> + };
> + };
> diff --git a/include/dt-bindings/mfd/mt6360.h b/include/dt-bindings/mfd/mt6360.h
> new file mode 100644
> index 0000000..6368388
> --- /dev/null
> +++ b/include/dt-bindings/mfd/mt6360.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * This header provides macros for MT6360 device bindings.
> + *
> + * Copyright (c) 2020 Mediatek Inc.
> + */
> +
> +#ifndef __DT_BINDINGS_MT6360_H__
> +#define __DT_BINDINGS_MT6360_H__
> +
> +#define MT6360_OPMODE_LP (2)
> +#define MT6360_OPMODE_ULP (3)
> +#define MT6360_OPMODE_NORMAL (0)
> +
> +#endif /* __DT_BINDINGS_MT6360_H__ */
> --
> 2.7.4
>

--
Lee Jones [李琼斯]
Senior Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog