Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: adc: mt6370: Add MediaTek MT6370 support

MediaTek MT6370 is a SubPMIC consisting of a single cell battery charger
with ADC monitoring, RGB LEDs, dual channel flashlight, WLED backlight
driver, display bias voltage supply, one general purpose LDO, and the
USB Type-C & PD controller complies with the latest USB Type-C and PD
standards.

Add support for the MT6370 ADC driver for system monitoring, including
charger current, voltage, and temperature.

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: ChiaEn Wu <chiaen_wu@richtek.com>
Link: https://lore.kernel.org/r/81ec58ae89030e48508d6810396de2679c40d26c.1665488982.git.chiaen_wu@richtek.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

ChiaEn Wu and committed by
Jonathan Cameron
c1404d1b 8d2caf8b

+318
+12
drivers/iio/adc/Kconfig
··· 765 765 is used in smartphones and tablets and supports a 11 channel 766 766 general purpose ADC. 767 767 768 + config MEDIATEK_MT6370_ADC 769 + tristate "MediaTek MT6370 ADC driver" 770 + depends on MFD_MT6370 771 + help 772 + Say yes here to enable MediaTek MT6370 ADC support. 773 + 774 + This ADC driver provides 9 channels for system monitoring (charger 775 + current, voltage, and temperature). 776 + 777 + This driver can also be built as a module. If so, the module 778 + will be called "mt6370-adc". 779 + 768 780 config MEDIATEK_MT6577_AUXADC 769 781 tristate "MediaTek AUXADC driver" 770 782 depends on ARCH_MEDIATEK || COMPILE_TEST
+1
drivers/iio/adc/Makefile
··· 70 70 obj-$(CONFIG_MCP3422) += mcp3422.o 71 71 obj-$(CONFIG_MCP3911) += mcp3911.o 72 72 obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o 73 + obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o 73 74 obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o 74 75 obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o 75 76 obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
+305
drivers/iio/adc/mt6370-adc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2022 Richtek Technology Corp. 4 + * 5 + * Author: ChiaEn Wu <chiaen_wu@richtek.com> 6 + */ 7 + 8 + #include <linux/bits.h> 9 + #include <linux/bitfield.h> 10 + #include <linux/iio/iio.h> 11 + #include <linux/kernel.h> 12 + #include <linux/mod_devicetable.h> 13 + #include <linux/module.h> 14 + #include <linux/mutex.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/regmap.h> 17 + #include <linux/sysfs.h> 18 + #include <linux/units.h> 19 + 20 + #include <dt-bindings/iio/adc/mediatek,mt6370_adc.h> 21 + 22 + #define MT6370_REG_CHG_CTRL3 0x113 23 + #define MT6370_REG_CHG_CTRL7 0x117 24 + #define MT6370_REG_CHG_ADC 0x121 25 + #define MT6370_REG_ADC_DATA_H 0x14C 26 + 27 + #define MT6370_ADC_START_MASK BIT(0) 28 + #define MT6370_ADC_IN_SEL_MASK GENMASK(7, 4) 29 + #define MT6370_AICR_ICHG_MASK GENMASK(7, 2) 30 + 31 + #define MT6370_AICR_100_mA 0x0 32 + #define MT6370_AICR_150_mA 0x1 33 + #define MT6370_AICR_200_mA 0x2 34 + #define MT6370_AICR_250_mA 0x3 35 + #define MT6370_AICR_300_mA 0x4 36 + #define MT6370_AICR_350_mA 0x5 37 + 38 + #define MT6370_ICHG_100_mA 0x0 39 + #define MT6370_ICHG_200_mA 0x1 40 + #define MT6370_ICHG_300_mA 0x2 41 + #define MT6370_ICHG_400_mA 0x3 42 + #define MT6370_ICHG_500_mA 0x4 43 + #define MT6370_ICHG_600_mA 0x5 44 + #define MT6370_ICHG_700_mA 0x6 45 + #define MT6370_ICHG_800_mA 0x7 46 + 47 + #define ADC_CONV_TIME_MS 35 48 + #define ADC_CONV_POLLING_TIME_US 1000 49 + 50 + struct mt6370_adc_data { 51 + struct device *dev; 52 + struct regmap *regmap; 53 + /* 54 + * This mutex lock is for preventing the different ADC channels 55 + * from being read at the same time. 56 + */ 57 + struct mutex adc_lock; 58 + }; 59 + 60 + static int mt6370_adc_read_channel(struct mt6370_adc_data *priv, int chan, 61 + unsigned long addr, int *val) 62 + { 63 + unsigned int reg_val; 64 + __be16 be_val; 65 + int ret; 66 + 67 + mutex_lock(&priv->adc_lock); 68 + 69 + reg_val = MT6370_ADC_START_MASK | 70 + FIELD_PREP(MT6370_ADC_IN_SEL_MASK, addr); 71 + ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, reg_val); 72 + if (ret) 73 + goto adc_unlock; 74 + 75 + msleep(ADC_CONV_TIME_MS); 76 + 77 + ret = regmap_read_poll_timeout(priv->regmap, 78 + MT6370_REG_CHG_ADC, reg_val, 79 + !(reg_val & MT6370_ADC_START_MASK), 80 + ADC_CONV_POLLING_TIME_US, 81 + ADC_CONV_TIME_MS * MILLI * 3); 82 + if (ret) { 83 + dev_err(priv->dev, "Failed to read ADC register (%d)\n", ret); 84 + goto adc_unlock; 85 + } 86 + 87 + ret = regmap_raw_read(priv->regmap, MT6370_REG_ADC_DATA_H, 88 + &be_val, sizeof(be_val)); 89 + if (ret) 90 + goto adc_unlock; 91 + 92 + *val = be16_to_cpu(be_val); 93 + ret = IIO_VAL_INT; 94 + 95 + adc_unlock: 96 + mutex_unlock(&priv->adc_lock); 97 + 98 + return ret; 99 + } 100 + 101 + static int mt6370_adc_read_scale(struct mt6370_adc_data *priv, 102 + int chan, int *val1, int *val2) 103 + { 104 + unsigned int reg_val; 105 + int ret; 106 + 107 + switch (chan) { 108 + case MT6370_CHAN_VBAT: 109 + case MT6370_CHAN_VSYS: 110 + case MT6370_CHAN_CHG_VDDP: 111 + *val1 = 5; 112 + return IIO_VAL_INT; 113 + case MT6370_CHAN_IBUS: 114 + ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL3, &reg_val); 115 + if (ret) 116 + return ret; 117 + 118 + reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val); 119 + switch (reg_val) { 120 + case MT6370_AICR_100_mA: 121 + case MT6370_AICR_150_mA: 122 + case MT6370_AICR_200_mA: 123 + case MT6370_AICR_250_mA: 124 + case MT6370_AICR_300_mA: 125 + case MT6370_AICR_350_mA: 126 + *val1 = 3350; 127 + break; 128 + default: 129 + *val1 = 5000; 130 + break; 131 + } 132 + 133 + *val2 = 100; 134 + 135 + return IIO_VAL_FRACTIONAL; 136 + case MT6370_CHAN_IBAT: 137 + ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL7, &reg_val); 138 + if (ret) 139 + return ret; 140 + 141 + reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val); 142 + switch (reg_val) { 143 + case MT6370_ICHG_100_mA: 144 + case MT6370_ICHG_200_mA: 145 + case MT6370_ICHG_300_mA: 146 + case MT6370_ICHG_400_mA: 147 + *val1 = 2375; 148 + break; 149 + case MT6370_ICHG_500_mA: 150 + case MT6370_ICHG_600_mA: 151 + case MT6370_ICHG_700_mA: 152 + case MT6370_ICHG_800_mA: 153 + *val1 = 2680; 154 + break; 155 + default: 156 + *val1 = 5000; 157 + break; 158 + } 159 + 160 + *val2 = 100; 161 + 162 + return IIO_VAL_FRACTIONAL; 163 + case MT6370_CHAN_VBUSDIV5: 164 + *val1 = 25; 165 + return IIO_VAL_INT; 166 + case MT6370_CHAN_VBUSDIV2: 167 + *val1 = 10; 168 + return IIO_VAL_INT; 169 + case MT6370_CHAN_TS_BAT: 170 + *val1 = 25; 171 + *val2 = 10000; 172 + return IIO_VAL_FRACTIONAL; 173 + case MT6370_CHAN_TEMP_JC: 174 + *val1 = 2000; 175 + return IIO_VAL_INT; 176 + default: 177 + return -EINVAL; 178 + } 179 + } 180 + 181 + static int mt6370_adc_read_offset(struct mt6370_adc_data *priv, 182 + int chan, int *val) 183 + { 184 + *val = -20; 185 + 186 + return IIO_VAL_INT; 187 + } 188 + 189 + static int mt6370_adc_read_raw(struct iio_dev *iio_dev, 190 + const struct iio_chan_spec *chan, 191 + int *val, int *val2, long mask) 192 + { 193 + struct mt6370_adc_data *priv = iio_priv(iio_dev); 194 + 195 + switch (mask) { 196 + case IIO_CHAN_INFO_RAW: 197 + return mt6370_adc_read_channel(priv, chan->channel, 198 + chan->address, val); 199 + case IIO_CHAN_INFO_SCALE: 200 + return mt6370_adc_read_scale(priv, chan->channel, val, val2); 201 + case IIO_CHAN_INFO_OFFSET: 202 + return mt6370_adc_read_offset(priv, chan->channel, val); 203 + default: 204 + return -EINVAL; 205 + } 206 + } 207 + 208 + static const char * const mt6370_channel_labels[MT6370_CHAN_MAX] = { 209 + [MT6370_CHAN_VBUSDIV5] = "vbusdiv5", 210 + [MT6370_CHAN_VBUSDIV2] = "vbusdiv2", 211 + [MT6370_CHAN_VSYS] = "vsys", 212 + [MT6370_CHAN_VBAT] = "vbat", 213 + [MT6370_CHAN_TS_BAT] = "ts_bat", 214 + [MT6370_CHAN_IBUS] = "ibus", 215 + [MT6370_CHAN_IBAT] = "ibat", 216 + [MT6370_CHAN_CHG_VDDP] = "chg_vddp", 217 + [MT6370_CHAN_TEMP_JC] = "temp_jc", 218 + }; 219 + 220 + static int mt6370_adc_read_label(struct iio_dev *iio_dev, 221 + struct iio_chan_spec const *chan, char *label) 222 + { 223 + return sysfs_emit(label, "%s\n", mt6370_channel_labels[chan->channel]); 224 + } 225 + 226 + static const struct iio_info mt6370_adc_iio_info = { 227 + .read_raw = mt6370_adc_read_raw, 228 + .read_label = mt6370_adc_read_label, 229 + }; 230 + 231 + #define MT6370_ADC_CHAN(_idx, _type, _addr, _extra_info) { \ 232 + .type = _type, \ 233 + .channel = MT6370_CHAN_##_idx, \ 234 + .address = _addr, \ 235 + .scan_index = MT6370_CHAN_##_idx, \ 236 + .indexed = 1, \ 237 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 238 + BIT(IIO_CHAN_INFO_SCALE) | \ 239 + _extra_info, \ 240 + } 241 + 242 + static const struct iio_chan_spec mt6370_adc_channels[] = { 243 + MT6370_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE, 1, 0), 244 + MT6370_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE, 2, 0), 245 + MT6370_ADC_CHAN(VSYS, IIO_VOLTAGE, 3, 0), 246 + MT6370_ADC_CHAN(VBAT, IIO_VOLTAGE, 4, 0), 247 + MT6370_ADC_CHAN(TS_BAT, IIO_VOLTAGE, 6, 0), 248 + MT6370_ADC_CHAN(IBUS, IIO_CURRENT, 8, 0), 249 + MT6370_ADC_CHAN(IBAT, IIO_CURRENT, 9, 0), 250 + MT6370_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE, 11, 0), 251 + MT6370_ADC_CHAN(TEMP_JC, IIO_TEMP, 12, BIT(IIO_CHAN_INFO_OFFSET)), 252 + }; 253 + 254 + static int mt6370_adc_probe(struct platform_device *pdev) 255 + { 256 + struct device *dev = &pdev->dev; 257 + struct mt6370_adc_data *priv; 258 + struct iio_dev *indio_dev; 259 + struct regmap *regmap; 260 + int ret; 261 + 262 + regmap = dev_get_regmap(pdev->dev.parent, NULL); 263 + if (!regmap) 264 + return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n"); 265 + 266 + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); 267 + if (!indio_dev) 268 + return -ENOMEM; 269 + 270 + priv = iio_priv(indio_dev); 271 + priv->dev = dev; 272 + priv->regmap = regmap; 273 + mutex_init(&priv->adc_lock); 274 + 275 + ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, 0); 276 + if (ret) 277 + return dev_err_probe(dev, ret, "Failed to reset ADC\n"); 278 + 279 + indio_dev->name = "mt6370-adc"; 280 + indio_dev->info = &mt6370_adc_iio_info; 281 + indio_dev->modes = INDIO_DIRECT_MODE; 282 + indio_dev->channels = mt6370_adc_channels; 283 + indio_dev->num_channels = ARRAY_SIZE(mt6370_adc_channels); 284 + 285 + return devm_iio_device_register(dev, indio_dev); 286 + } 287 + 288 + static const struct of_device_id mt6370_adc_of_id[] = { 289 + { .compatible = "mediatek,mt6370-adc", }, 290 + {} 291 + }; 292 + MODULE_DEVICE_TABLE(of, mt6370_adc_of_id); 293 + 294 + static struct platform_driver mt6370_adc_driver = { 295 + .driver = { 296 + .name = "mt6370-adc", 297 + .of_match_table = mt6370_adc_of_id, 298 + }, 299 + .probe = mt6370_adc_probe, 300 + }; 301 + module_platform_driver(mt6370_adc_driver); 302 + 303 + MODULE_AUTHOR("ChiaEn Wu <chiaen_wu@richtek.com>"); 304 + MODULE_DESCRIPTION("MT6370 ADC Driver"); 305 + MODULE_LICENSE("GPL v2");