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

Merge tag 'tags/ib-mfd-iio-power-v5.8' into psy-next

This merges the MP2629 battery charge management immutable branch
between MFD, IIO and power-supply due for the v5.8 merge window
into power-supply for-next branch.

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>

+1090
+8
Documentation/ABI/testing/sysfs-class-power-mp2629
··· 1 + What: /sys/class/power_supply/mp2629_battery/batt_impedance_compen 2 + Date: April 2020 3 + KernelVersion: 5.7 4 + Description: 5 + Represents a battery impedance compensation to accelerate charging. 6 + 7 + Access: Read, Write 8 + Valid values: Represented in milli-ohms. Valid range is [0, 140].
+62
Documentation/devicetree/bindings/mfd/mps,mp2629.yaml
··· 1 + # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/mfd/mps,mp2629.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: MP2629 Battery Charger PMIC from Monolithic Power System. 8 + 9 + maintainers: 10 + - Saravanan Sekar <sravanhome@gmail.com> 11 + 12 + description: | 13 + MP2629 is a PMIC providing battery charging and power supply for smartphones, 14 + wireless camera and portable devices. Chip is controlled over I2C. 15 + 16 + The battery charge management device handles battery charger controller and 17 + ADC IIO device for battery, system voltage 18 + 19 + properties: 20 + compatible: 21 + const: mps,mp2629 22 + 23 + reg: 24 + maxItems: 1 25 + 26 + interrupts: 27 + maxItems: 1 28 + 29 + interrupt-controller: true 30 + 31 + "#interrupt-cells": 32 + const: 2 33 + description: 34 + The first cell is the IRQ number, the second cell is the trigger type. 35 + 36 + required: 37 + - compatible 38 + - reg 39 + - interrupts 40 + - interrupt-controller 41 + - "#interrupt-cells" 42 + 43 + additionalProperties: false 44 + 45 + examples: 46 + - | 47 + #include <dt-bindings/interrupt-controller/irq.h> 48 + #include <dt-bindings/input/linux-event-codes.h> 49 + i2c { 50 + #address-cells = <1>; 51 + #size-cells = <0>; 52 + 53 + pmic@4b { 54 + compatible = "mps,mp2629"; 55 + reg = <0x4b>; 56 + 57 + interrupt-controller; 58 + interrupt-parent = <&gpio2>; 59 + #interrupt-cells = <2>; 60 + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; 61 + }; 62 + };
+5
MAINTAINERS
··· 11395 11395 MONOLITHIC POWER SYSTEM PMIC DRIVER 11396 11396 M: Saravanan Sekar <sravanhome@gmail.com> 11397 11397 S: Maintained 11398 + F: Documentation/devicetree/bindings/mfd/mps,mp2629.yaml 11398 11399 F: Documentation/devicetree/bindings/regulator/mps,mp*.yaml 11400 + F: drivers/iio/adc/mp2629_adc.c 11401 + F: drivers/mfd/mp2629.c 11402 + F: drivers/power/supply/mp2629_charger.c 11399 11403 F: drivers/regulator/mp5416.c 11400 11404 F: drivers/regulator/mpq7920.c 11401 11405 F: drivers/regulator/mpq7920.h 11406 + F: include/linux/mfd/mp2629.h 11402 11407 11403 11408 MOTION EYE VAIO PICTUREBOOK CAMERA DRIVER 11404 11409 S: Orphan
+10
drivers/iio/adc/Kconfig
··· 692 692 To compile this driver as a module, choose M here: the 693 693 module will be called meson_saradc. 694 694 695 + config MP2629_ADC 696 + tristate "Monolithic MP2629 ADC driver" 697 + depends on MFD_MP2629 698 + help 699 + Say yes to have support for battery charger IC MP2629 ADC device 700 + accessed over I2C. 701 + 702 + This driver provides ADC conversion of system, input power supply 703 + and battery voltage & current information. 704 + 695 705 config NAU7802 696 706 tristate "Nuvoton NAU7802 ADC driver" 697 707 depends on I2C
+1
drivers/iio/adc/Makefile
··· 65 65 obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o 66 66 obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o 67 67 obj-$(CONFIG_MESON_SARADC) += meson_saradc.o 68 + obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o 68 69 obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o 69 70 obj-$(CONFIG_NAU7802) += nau7802.o 70 71 obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
+208
drivers/iio/adc/mp2629_adc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * MP2629 Driver for ADC 4 + * 5 + * Copyright 2020 Monolithic Power Systems, Inc 6 + * 7 + * Author: Saravanan Sekar <sravanhome@gmail.com> 8 + */ 9 + 10 + #include <linux/iio/driver.h> 11 + #include <linux/iio/iio.h> 12 + #include <linux/iio/machine.h> 13 + #include <linux/mfd/mp2629.h> 14 + #include <linux/module.h> 15 + #include <linux/mutex.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/regmap.h> 18 + 19 + #define MP2629_REG_ADC_CTRL 0x03 20 + #define MP2629_REG_BATT_VOLT 0x0e 21 + #define MP2629_REG_SYSTEM_VOLT 0x0f 22 + #define MP2629_REG_INPUT_VOLT 0x11 23 + #define MP2629_REG_BATT_CURRENT 0x12 24 + #define MP2629_REG_INPUT_CURRENT 0x13 25 + 26 + #define MP2629_ADC_START BIT(7) 27 + #define MP2629_ADC_CONTINUOUS BIT(6) 28 + 29 + #define MP2629_MAP(_mp, _mpc) IIO_MAP(#_mp, "mp2629_charger", "mp2629-"_mpc) 30 + 31 + #define MP2629_ADC_CHAN(_ch, _type) { \ 32 + .type = _type, \ 33 + .indexed = 1, \ 34 + .datasheet_name = #_ch, \ 35 + .channel = MP2629_ ## _ch, \ 36 + .address = MP2629_REG_ ## _ch, \ 37 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 38 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 39 + } 40 + 41 + struct mp2629_adc { 42 + struct regmap *regmap; 43 + struct device *dev; 44 + }; 45 + 46 + static struct iio_chan_spec mp2629_channels[] = { 47 + MP2629_ADC_CHAN(BATT_VOLT, IIO_VOLTAGE), 48 + MP2629_ADC_CHAN(SYSTEM_VOLT, IIO_VOLTAGE), 49 + MP2629_ADC_CHAN(INPUT_VOLT, IIO_VOLTAGE), 50 + MP2629_ADC_CHAN(BATT_CURRENT, IIO_CURRENT), 51 + MP2629_ADC_CHAN(INPUT_CURRENT, IIO_CURRENT) 52 + }; 53 + 54 + static struct iio_map mp2629_adc_maps[] = { 55 + MP2629_MAP(BATT_VOLT, "batt-volt"), 56 + MP2629_MAP(SYSTEM_VOLT, "system-volt"), 57 + MP2629_MAP(INPUT_VOLT, "input-volt"), 58 + MP2629_MAP(BATT_CURRENT, "batt-current"), 59 + MP2629_MAP(INPUT_CURRENT, "input-current") 60 + }; 61 + 62 + static int mp2629_read_raw(struct iio_dev *indio_dev, 63 + struct iio_chan_spec const *chan, 64 + int *val, int *val2, long mask) 65 + { 66 + struct mp2629_adc *info = iio_priv(indio_dev); 67 + unsigned int rval; 68 + int ret; 69 + 70 + switch (mask) { 71 + case IIO_CHAN_INFO_RAW: 72 + ret = regmap_read(info->regmap, chan->address, &rval); 73 + if (ret) 74 + return ret; 75 + 76 + if (chan->address == MP2629_INPUT_VOLT) 77 + rval &= GENMASK(6, 0); 78 + *val = rval; 79 + return IIO_VAL_INT; 80 + 81 + case IIO_CHAN_INFO_SCALE: 82 + switch (chan->channel) { 83 + case MP2629_BATT_VOLT: 84 + case MP2629_SYSTEM_VOLT: 85 + *val = 20; 86 + return IIO_VAL_INT; 87 + 88 + case MP2629_INPUT_VOLT: 89 + *val = 60; 90 + return IIO_VAL_INT; 91 + 92 + case MP2629_BATT_CURRENT: 93 + *val = 175; 94 + *val2 = 10; 95 + return IIO_VAL_FRACTIONAL; 96 + 97 + case MP2629_INPUT_CURRENT: 98 + *val = 133; 99 + *val2 = 10; 100 + return IIO_VAL_FRACTIONAL; 101 + 102 + default: 103 + return -EINVAL; 104 + } 105 + 106 + default: 107 + return -EINVAL; 108 + } 109 + } 110 + 111 + static const struct iio_info mp2629_adc_info = { 112 + .read_raw = &mp2629_read_raw, 113 + }; 114 + 115 + static int mp2629_adc_probe(struct platform_device *pdev) 116 + { 117 + struct device *dev = &pdev->dev; 118 + struct mp2629_data *ddata = dev_get_drvdata(dev->parent); 119 + struct mp2629_adc *info; 120 + struct iio_dev *indio_dev; 121 + int ret; 122 + 123 + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); 124 + if (!indio_dev) 125 + return -ENOMEM; 126 + 127 + info = iio_priv(indio_dev); 128 + info->regmap = ddata->regmap; 129 + info->dev = dev; 130 + platform_set_drvdata(pdev, indio_dev); 131 + 132 + ret = regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, 133 + MP2629_ADC_START | MP2629_ADC_CONTINUOUS, 134 + MP2629_ADC_START | MP2629_ADC_CONTINUOUS); 135 + if (ret) { 136 + dev_err(dev, "adc enable fail: %d\n", ret); 137 + return ret; 138 + } 139 + 140 + ret = iio_map_array_register(indio_dev, mp2629_adc_maps); 141 + if (ret) { 142 + dev_err(dev, "IIO maps register fail: %d\n", ret); 143 + goto fail_disable; 144 + } 145 + 146 + indio_dev->name = "mp2629-adc"; 147 + indio_dev->dev.parent = dev; 148 + indio_dev->channels = mp2629_channels; 149 + indio_dev->num_channels = ARRAY_SIZE(mp2629_channels); 150 + indio_dev->modes = INDIO_DIRECT_MODE; 151 + indio_dev->info = &mp2629_adc_info; 152 + 153 + ret = iio_device_register(indio_dev); 154 + if (ret) { 155 + dev_err(dev, "IIO device register fail: %d\n", ret); 156 + goto fail_map_unregister; 157 + } 158 + 159 + return 0; 160 + 161 + fail_map_unregister: 162 + iio_map_array_unregister(indio_dev); 163 + 164 + fail_disable: 165 + regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, 166 + MP2629_ADC_CONTINUOUS, 0); 167 + regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, 168 + MP2629_ADC_START, 0); 169 + 170 + return ret; 171 + } 172 + 173 + static int mp2629_adc_remove(struct platform_device *pdev) 174 + { 175 + struct iio_dev *indio_dev = platform_get_drvdata(pdev); 176 + struct mp2629_adc *info = iio_priv(indio_dev); 177 + 178 + iio_device_unregister(indio_dev); 179 + 180 + iio_map_array_unregister(indio_dev); 181 + 182 + regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, 183 + MP2629_ADC_CONTINUOUS, 0); 184 + regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, 185 + MP2629_ADC_START, 0); 186 + 187 + return 0; 188 + } 189 + 190 + static const struct of_device_id mp2629_adc_of_match[] = { 191 + { .compatible = "mps,mp2629_adc"}, 192 + {} 193 + }; 194 + MODULE_DEVICE_TABLE(of, mp2629_adc_of_match); 195 + 196 + static struct platform_driver mp2629_adc_driver = { 197 + .driver = { 198 + .name = "mp2629_adc", 199 + .of_match_table = mp2629_adc_of_match, 200 + }, 201 + .probe = mp2629_adc_probe, 202 + .remove = mp2629_adc_remove, 203 + }; 204 + module_platform_driver(mp2629_adc_driver); 205 + 206 + MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>"); 207 + MODULE_DESCRIPTION("MP2629 ADC driver"); 208 + MODULE_LICENSE("GPL");
+9
drivers/mfd/Kconfig
··· 434 434 help 435 435 Select this if your MC13xxx is connected via an I2C bus. 436 436 437 + config MFD_MP2629 438 + tristate "Monolithic Power Systems MP2629 ADC and Battery charger" 439 + depends on I2C 440 + select REGMAP_I2C 441 + help 442 + Select this option to enable support for Monolithic Power Systems 443 + battery charger. This provides ADC, thermal and battery charger power 444 + management functions. 445 + 437 446 config MFD_MXS_LRADC 438 447 tristate "Freescale i.MX23/i.MX28 LRADC" 439 448 depends on ARCH_MXS || COMPILE_TEST
+2
drivers/mfd/Makefile
··· 170 170 obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o 171 171 obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o 172 172 173 + obj-$(CONFIG_MFD_MP2629) += mp2629.o 174 + 173 175 pcf50633-objs := pcf50633-core.o pcf50633-irq.o 174 176 obj-$(CONFIG_MFD_PCF50633) += pcf50633.o 175 177 obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
+79
drivers/mfd/mp2629.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * MP2629 parent driver for ADC and battery charger 4 + * 5 + * Copyright 2020 Monolithic Power Systems, Inc 6 + * 7 + * Author: Saravanan Sekar <sravanhome@gmail.com> 8 + */ 9 + 10 + #include <linux/i2c.h> 11 + #include <linux/kernel.h> 12 + #include <linux/mfd/core.h> 13 + #include <linux/mfd/mp2629.h> 14 + #include <linux/module.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/regmap.h> 17 + #include <linux/slab.h> 18 + 19 + static const struct mfd_cell mp2629_cell[] = { 20 + { 21 + .name = "mp2629_adc", 22 + .of_compatible = "mps,mp2629_adc", 23 + }, 24 + { 25 + .name = "mp2629_charger", 26 + .of_compatible = "mps,mp2629_charger", 27 + } 28 + }; 29 + 30 + static const struct regmap_config mp2629_regmap_config = { 31 + .reg_bits = 8, 32 + .val_bits = 8, 33 + .max_register = 0x17, 34 + }; 35 + 36 + static int mp2629_probe(struct i2c_client *client) 37 + { 38 + struct mp2629_data *ddata; 39 + int ret; 40 + 41 + ddata = devm_kzalloc(&client->dev, sizeof(*ddata), GFP_KERNEL); 42 + if (!ddata) 43 + return -ENOMEM; 44 + 45 + ddata->dev = &client->dev; 46 + i2c_set_clientdata(client, ddata); 47 + 48 + ddata->regmap = devm_regmap_init_i2c(client, &mp2629_regmap_config); 49 + if (IS_ERR(ddata->regmap)) { 50 + dev_err(ddata->dev, "Failed to allocate regmap\n"); 51 + return PTR_ERR(ddata->regmap); 52 + } 53 + 54 + ret = devm_mfd_add_devices(ddata->dev, PLATFORM_DEVID_AUTO, mp2629_cell, 55 + ARRAY_SIZE(mp2629_cell), NULL, 0, NULL); 56 + if (ret) 57 + dev_err(ddata->dev, "Failed to register sub-devices %d\n", ret); 58 + 59 + return ret; 60 + } 61 + 62 + static const struct of_device_id mp2629_of_match[] = { 63 + { .compatible = "mps,mp2629"}, 64 + { } 65 + }; 66 + MODULE_DEVICE_TABLE(of, mp2629_of_match); 67 + 68 + static struct i2c_driver mp2629_driver = { 69 + .driver = { 70 + .name = "mp2629", 71 + .of_match_table = mp2629_of_match, 72 + }, 73 + .probe_new = mp2629_probe, 74 + }; 75 + module_i2c_driver(mp2629_driver); 76 + 77 + MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>"); 78 + MODULE_DESCRIPTION("MP2629 Battery charger parent driver"); 79 + MODULE_LICENSE("GPL");
+10
drivers/power/supply/Kconfig
··· 552 552 Say Y to enable support for the battery charger control sysfs and 553 553 platform data of MAX8998/LP3974 PMICs. 554 554 555 + config CHARGER_MP2629 556 + tristate "Monolithic power system MP2629 Battery charger" 557 + depends on MFD_MP2629 558 + depends on MP2629_ADC 559 + depends on IIO 560 + help 561 + Select this option to enable support for Monolithic power system 562 + Battery charger. This driver provides Battery charger power management 563 + functions on the systems. 564 + 555 565 config CHARGER_QCOM_SMBB 556 566 tristate "Qualcomm Switch-Mode Battery Charger and Boost" 557 567 depends on MFD_SPMI_PMIC || COMPILE_TEST
+1
drivers/power/supply/Makefile
··· 76 76 obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o 77 77 obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o 78 78 obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o 79 + obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o 79 80 obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o 80 81 obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o 81 82 obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
+669
drivers/power/supply/mp2629_charger.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * MP2629 battery charger driver 4 + * 5 + * Copyright 2020 Monolithic Power Systems, Inc 6 + * 7 + * Author: Saravanan Sekar <sravanhome@gmail.com> 8 + */ 9 + 10 + #include <linux/bits.h> 11 + #include <linux/iio/consumer.h> 12 + #include <linux/iio/types.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/mfd/mp2629.h> 15 + #include <linux/module.h> 16 + #include <linux/mod_devicetable.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/power_supply.h> 19 + #include <linux/regmap.h> 20 + 21 + #define MP2629_REG_INPUT_ILIM 0x00 22 + #define MP2629_REG_INPUT_VLIM 0x01 23 + #define MP2629_REG_CHARGE_CTRL 0x04 24 + #define MP2629_REG_CHARGE_ILIM 0x05 25 + #define MP2629_REG_PRECHARGE 0x06 26 + #define MP2629_REG_TERM_CURRENT 0x06 27 + #define MP2629_REG_CHARGE_VLIM 0x07 28 + #define MP2629_REG_TIMER_CTRL 0x08 29 + #define MP2629_REG_IMPEDANCE_COMP 0x09 30 + #define MP2629_REG_INTERRUPT 0x0b 31 + #define MP2629_REG_STATUS 0x0c 32 + #define MP2629_REG_FAULT 0x0d 33 + 34 + #define MP2629_MASK_INPUT_TYPE GENMASK(7, 5) 35 + #define MP2629_MASK_CHARGE_TYPE GENMASK(4, 3) 36 + #define MP2629_MASK_CHARGE_CTRL GENMASK(5, 4) 37 + #define MP2629_MASK_WDOG_CTRL GENMASK(5, 4) 38 + #define MP2629_MASK_IMPEDANCE GENMASK(7, 4) 39 + 40 + #define MP2629_INPUTSOURCE_CHANGE GENMASK(7, 5) 41 + #define MP2629_CHARGING_CHANGE GENMASK(4, 3) 42 + #define MP2629_FAULT_BATTERY BIT(3) 43 + #define MP2629_FAULT_THERMAL BIT(4) 44 + #define MP2629_FAULT_INPUT BIT(5) 45 + #define MP2629_FAULT_OTG BIT(6) 46 + 47 + #define MP2629_MAX_BATT_CAPACITY 100 48 + 49 + #define MP2629_PROPS(_idx, _min, _max, _step) \ 50 + [_idx] = { \ 51 + .min = _min, \ 52 + .max = _max, \ 53 + .step = _step, \ 54 + } 55 + 56 + enum mp2629_source_type { 57 + MP2629_SOURCE_TYPE_NO_INPUT, 58 + MP2629_SOURCE_TYPE_NON_STD, 59 + MP2629_SOURCE_TYPE_SDP, 60 + MP2629_SOURCE_TYPE_CDP, 61 + MP2629_SOURCE_TYPE_DCP, 62 + MP2629_SOURCE_TYPE_OTG = 7, 63 + }; 64 + 65 + enum mp2629_field { 66 + INPUT_ILIM, 67 + INPUT_VLIM, 68 + CHARGE_ILIM, 69 + CHARGE_VLIM, 70 + PRECHARGE, 71 + TERM_CURRENT, 72 + MP2629_MAX_FIELD 73 + }; 74 + 75 + struct mp2629_charger { 76 + struct device *dev; 77 + int status; 78 + int fault; 79 + 80 + struct regmap *regmap; 81 + struct regmap_field *regmap_fields[MP2629_MAX_FIELD]; 82 + struct mutex lock; 83 + struct power_supply *usb; 84 + struct power_supply *battery; 85 + struct iio_channel *iiochan[MP2629_ADC_CHAN_END]; 86 + }; 87 + 88 + struct mp2629_prop { 89 + int reg; 90 + int mask; 91 + int min; 92 + int max; 93 + int step; 94 + int shift; 95 + }; 96 + 97 + static enum power_supply_usb_type mp2629_usb_types[] = { 98 + POWER_SUPPLY_USB_TYPE_SDP, 99 + POWER_SUPPLY_USB_TYPE_DCP, 100 + POWER_SUPPLY_USB_TYPE_CDP, 101 + POWER_SUPPLY_USB_TYPE_PD_DRP, 102 + POWER_SUPPLY_USB_TYPE_UNKNOWN 103 + }; 104 + 105 + static enum power_supply_property mp2629_charger_usb_props[] = { 106 + POWER_SUPPLY_PROP_ONLINE, 107 + POWER_SUPPLY_PROP_USB_TYPE, 108 + POWER_SUPPLY_PROP_VOLTAGE_NOW, 109 + POWER_SUPPLY_PROP_CURRENT_NOW, 110 + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 111 + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, 112 + }; 113 + 114 + static enum power_supply_property mp2629_charger_bat_props[] = { 115 + POWER_SUPPLY_PROP_STATUS, 116 + POWER_SUPPLY_PROP_HEALTH, 117 + POWER_SUPPLY_PROP_CHARGE_TYPE, 118 + POWER_SUPPLY_PROP_VOLTAGE_NOW, 119 + POWER_SUPPLY_PROP_CURRENT_NOW, 120 + POWER_SUPPLY_PROP_CAPACITY, 121 + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, 122 + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, 123 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 124 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 125 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 126 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 127 + }; 128 + 129 + static struct mp2629_prop props[] = { 130 + MP2629_PROPS(INPUT_ILIM, 100000, 3250000, 50000), 131 + MP2629_PROPS(INPUT_VLIM, 3800000, 5300000, 100000), 132 + MP2629_PROPS(CHARGE_ILIM, 320000, 4520000, 40000), 133 + MP2629_PROPS(CHARGE_VLIM, 3400000, 4670000, 10000), 134 + MP2629_PROPS(PRECHARGE, 120000, 720000, 40000), 135 + MP2629_PROPS(TERM_CURRENT, 80000, 680000, 40000), 136 + }; 137 + 138 + static const struct reg_field mp2629_reg_fields[] = { 139 + [INPUT_ILIM] = REG_FIELD(MP2629_REG_INPUT_ILIM, 0, 5), 140 + [INPUT_VLIM] = REG_FIELD(MP2629_REG_INPUT_VLIM, 0, 3), 141 + [CHARGE_ILIM] = REG_FIELD(MP2629_REG_CHARGE_ILIM, 0, 6), 142 + [CHARGE_VLIM] = REG_FIELD(MP2629_REG_CHARGE_VLIM, 1, 7), 143 + [PRECHARGE] = REG_FIELD(MP2629_REG_PRECHARGE, 4, 7), 144 + [TERM_CURRENT] = REG_FIELD(MP2629_REG_TERM_CURRENT, 0, 3), 145 + }; 146 + 147 + static char *adc_chan_name[] = { 148 + "mp2629-batt-volt", 149 + "mp2629-system-volt", 150 + "mp2629-input-volt", 151 + "mp2629-batt-current", 152 + "mp2629-input-current", 153 + }; 154 + 155 + static int mp2629_read_adc(struct mp2629_charger *charger, 156 + enum mp2629_adc_chan ch, 157 + union power_supply_propval *val) 158 + { 159 + int ret; 160 + int chval; 161 + 162 + ret = iio_read_channel_processed(charger->iiochan[ch], &chval); 163 + if (ret) 164 + return ret; 165 + 166 + val->intval = chval * 1000; 167 + 168 + return 0; 169 + } 170 + 171 + static int mp2629_get_prop(struct mp2629_charger *charger, 172 + enum mp2629_field fld, 173 + union power_supply_propval *val) 174 + { 175 + int ret; 176 + unsigned int rval; 177 + 178 + ret = regmap_field_read(charger->regmap_fields[fld], &rval); 179 + if (ret) 180 + return ret; 181 + 182 + val->intval = rval * props[fld].step + props[fld].min; 183 + 184 + return 0; 185 + } 186 + 187 + static int mp2629_set_prop(struct mp2629_charger *charger, 188 + enum mp2629_field fld, 189 + const union power_supply_propval *val) 190 + { 191 + unsigned int rval; 192 + 193 + if (val->intval < props[fld].min || val->intval > props[fld].max) 194 + return -EINVAL; 195 + 196 + rval = (val->intval - props[fld].min) / props[fld].step; 197 + return regmap_field_write(charger->regmap_fields[fld], rval); 198 + } 199 + 200 + static int mp2629_get_battery_capacity(struct mp2629_charger *charger, 201 + union power_supply_propval *val) 202 + { 203 + union power_supply_propval vnow, vlim; 204 + int ret; 205 + 206 + ret = mp2629_read_adc(charger, MP2629_BATT_VOLT, &vnow); 207 + if (ret) 208 + return ret; 209 + 210 + ret = mp2629_get_prop(charger, CHARGE_VLIM, &vlim); 211 + if (ret) 212 + return ret; 213 + 214 + val->intval = (vnow.intval * 100) / vlim.intval; 215 + val->intval = min(val->intval, MP2629_MAX_BATT_CAPACITY); 216 + 217 + return 0; 218 + } 219 + 220 + static int mp2629_charger_battery_get_prop(struct power_supply *psy, 221 + enum power_supply_property psp, 222 + union power_supply_propval *val) 223 + { 224 + struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent); 225 + unsigned int rval; 226 + int ret = 0; 227 + 228 + switch (psp) { 229 + case POWER_SUPPLY_PROP_VOLTAGE_NOW: 230 + ret = mp2629_read_adc(charger, MP2629_BATT_VOLT, val); 231 + break; 232 + 233 + case POWER_SUPPLY_PROP_CURRENT_NOW: 234 + ret = mp2629_read_adc(charger, MP2629_BATT_CURRENT, val); 235 + break; 236 + 237 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 238 + val->intval = 4520000; 239 + break; 240 + 241 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 242 + val->intval = 4670000; 243 + break; 244 + 245 + case POWER_SUPPLY_PROP_CAPACITY: 246 + ret = mp2629_get_battery_capacity(charger, val); 247 + break; 248 + 249 + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 250 + ret = mp2629_get_prop(charger, TERM_CURRENT, val); 251 + break; 252 + 253 + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: 254 + ret = mp2629_get_prop(charger, PRECHARGE, val); 255 + break; 256 + 257 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 258 + ret = mp2629_get_prop(charger, CHARGE_VLIM, val); 259 + break; 260 + 261 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 262 + ret = mp2629_get_prop(charger, CHARGE_ILIM, val); 263 + break; 264 + 265 + case POWER_SUPPLY_PROP_HEALTH: 266 + if (!charger->fault) 267 + val->intval = POWER_SUPPLY_HEALTH_GOOD; 268 + if (MP2629_FAULT_BATTERY & charger->fault) 269 + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 270 + else if (MP2629_FAULT_THERMAL & charger->fault) 271 + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 272 + else if (MP2629_FAULT_INPUT & charger->fault) 273 + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 274 + break; 275 + 276 + case POWER_SUPPLY_PROP_STATUS: 277 + ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval); 278 + if (ret) 279 + break; 280 + 281 + rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3; 282 + switch (rval) { 283 + case 0x00: 284 + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 285 + break; 286 + case 0x01: 287 + case 0x10: 288 + val->intval = POWER_SUPPLY_STATUS_CHARGING; 289 + break; 290 + case 0x11: 291 + val->intval = POWER_SUPPLY_STATUS_FULL; 292 + } 293 + break; 294 + 295 + case POWER_SUPPLY_PROP_CHARGE_TYPE: 296 + ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval); 297 + if (ret) 298 + break; 299 + 300 + rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3; 301 + switch (rval) { 302 + case 0x00: 303 + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 304 + break; 305 + case 0x01: 306 + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 307 + break; 308 + case 0x10: 309 + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; 310 + break; 311 + default: 312 + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 313 + } 314 + break; 315 + 316 + default: 317 + return -EINVAL; 318 + } 319 + 320 + return ret; 321 + } 322 + 323 + static int mp2629_charger_battery_set_prop(struct power_supply *psy, 324 + enum power_supply_property psp, 325 + const union power_supply_propval *val) 326 + { 327 + struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent); 328 + 329 + switch (psp) { 330 + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 331 + return mp2629_set_prop(charger, TERM_CURRENT, val); 332 + 333 + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: 334 + return mp2629_set_prop(charger, PRECHARGE, val); 335 + 336 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 337 + return mp2629_set_prop(charger, CHARGE_VLIM, val); 338 + 339 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 340 + return mp2629_set_prop(charger, CHARGE_ILIM, val); 341 + 342 + default: 343 + return -EINVAL; 344 + } 345 + } 346 + 347 + static int mp2629_charger_usb_get_prop(struct power_supply *psy, 348 + enum power_supply_property psp, 349 + union power_supply_propval *val) 350 + { 351 + struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent); 352 + unsigned int rval; 353 + int ret; 354 + 355 + switch (psp) { 356 + case POWER_SUPPLY_PROP_ONLINE: 357 + ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval); 358 + if (ret) 359 + break; 360 + 361 + val->intval = !!(rval & MP2629_MASK_INPUT_TYPE); 362 + break; 363 + 364 + case POWER_SUPPLY_PROP_USB_TYPE: 365 + ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval); 366 + if (ret) 367 + break; 368 + 369 + rval = (rval & MP2629_MASK_INPUT_TYPE) >> 5; 370 + switch (rval) { 371 + case MP2629_SOURCE_TYPE_SDP: 372 + val->intval = POWER_SUPPLY_USB_TYPE_SDP; 373 + break; 374 + case MP2629_SOURCE_TYPE_CDP: 375 + val->intval = POWER_SUPPLY_USB_TYPE_CDP; 376 + break; 377 + case MP2629_SOURCE_TYPE_DCP: 378 + val->intval = POWER_SUPPLY_USB_TYPE_DCP; 379 + break; 380 + case MP2629_SOURCE_TYPE_OTG: 381 + val->intval = POWER_SUPPLY_USB_TYPE_PD_DRP; 382 + break; 383 + default: 384 + val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN; 385 + break; 386 + } 387 + break; 388 + 389 + case POWER_SUPPLY_PROP_VOLTAGE_NOW: 390 + ret = mp2629_read_adc(charger, MP2629_INPUT_VOLT, val); 391 + break; 392 + 393 + case POWER_SUPPLY_PROP_CURRENT_NOW: 394 + ret = mp2629_read_adc(charger, MP2629_INPUT_CURRENT, val); 395 + break; 396 + 397 + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: 398 + ret = mp2629_get_prop(charger, INPUT_VLIM, val); 399 + break; 400 + 401 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 402 + ret = mp2629_get_prop(charger, INPUT_ILIM, val); 403 + break; 404 + 405 + default: 406 + return -EINVAL; 407 + } 408 + 409 + return ret; 410 + } 411 + 412 + static int mp2629_charger_usb_set_prop(struct power_supply *psy, 413 + enum power_supply_property psp, 414 + const union power_supply_propval *val) 415 + { 416 + struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent); 417 + 418 + switch (psp) { 419 + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: 420 + return mp2629_set_prop(charger, INPUT_VLIM, val); 421 + 422 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 423 + return mp2629_set_prop(charger, INPUT_ILIM, val); 424 + 425 + default: 426 + return -EINVAL; 427 + } 428 + } 429 + 430 + static int mp2629_charger_battery_prop_writeable(struct power_supply *psy, 431 + enum power_supply_property psp) 432 + { 433 + return (psp == POWER_SUPPLY_PROP_PRECHARGE_CURRENT) || 434 + (psp == POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT) || 435 + (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT) || 436 + (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE); 437 + } 438 + 439 + static int mp2629_charger_usb_prop_writeable(struct power_supply *psy, 440 + enum power_supply_property psp) 441 + { 442 + return (psp == POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT) || 443 + (psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT); 444 + } 445 + 446 + static irqreturn_t mp2629_irq_handler(int irq, void *dev_id) 447 + { 448 + struct mp2629_charger *charger = dev_id; 449 + unsigned int rval; 450 + int ret; 451 + 452 + mutex_lock(&charger->lock); 453 + 454 + ret = regmap_read(charger->regmap, MP2629_REG_FAULT, &rval); 455 + if (ret) 456 + goto unlock; 457 + 458 + if (rval) { 459 + charger->fault = rval; 460 + if (MP2629_FAULT_BATTERY & rval) 461 + dev_err(charger->dev, "Battery fault OVP\n"); 462 + else if (MP2629_FAULT_THERMAL & rval) 463 + dev_err(charger->dev, "Thermal shutdown fault\n"); 464 + else if (MP2629_FAULT_INPUT & rval) 465 + dev_err(charger->dev, "no input or input OVP\n"); 466 + else if (MP2629_FAULT_OTG & rval) 467 + dev_err(charger->dev, "VIN overloaded\n"); 468 + 469 + goto unlock; 470 + } 471 + 472 + ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval); 473 + if (ret) 474 + goto unlock; 475 + 476 + if (rval & MP2629_INPUTSOURCE_CHANGE) 477 + power_supply_changed(charger->usb); 478 + else if (rval & MP2629_CHARGING_CHANGE) 479 + power_supply_changed(charger->battery); 480 + 481 + unlock: 482 + mutex_unlock(&charger->lock); 483 + 484 + return IRQ_HANDLED; 485 + } 486 + 487 + static const struct power_supply_desc mp2629_usb_desc = { 488 + .name = "mp2629_usb", 489 + .type = POWER_SUPPLY_TYPE_USB, 490 + .usb_types = mp2629_usb_types, 491 + .num_usb_types = ARRAY_SIZE(mp2629_usb_types), 492 + .properties = mp2629_charger_usb_props, 493 + .num_properties = ARRAY_SIZE(mp2629_charger_usb_props), 494 + .get_property = mp2629_charger_usb_get_prop, 495 + .set_property = mp2629_charger_usb_set_prop, 496 + .property_is_writeable = mp2629_charger_usb_prop_writeable, 497 + }; 498 + 499 + static const struct power_supply_desc mp2629_battery_desc = { 500 + .name = "mp2629_battery", 501 + .type = POWER_SUPPLY_TYPE_BATTERY, 502 + .properties = mp2629_charger_bat_props, 503 + .num_properties = ARRAY_SIZE(mp2629_charger_bat_props), 504 + .get_property = mp2629_charger_battery_get_prop, 505 + .set_property = mp2629_charger_battery_set_prop, 506 + .property_is_writeable = mp2629_charger_battery_prop_writeable, 507 + }; 508 + 509 + static ssize_t batt_impedance_compensation_show(struct device *dev, 510 + struct device_attribute *attr, 511 + char *buf) 512 + { 513 + struct mp2629_charger *charger = dev_get_drvdata(dev->parent); 514 + unsigned int rval; 515 + int ret; 516 + 517 + ret = regmap_read(charger->regmap, MP2629_REG_IMPEDANCE_COMP, &rval); 518 + if (ret) 519 + return ret; 520 + 521 + rval = (rval >> 4) * 10; 522 + return sprintf(buf, "%d mohm\n", rval); 523 + } 524 + 525 + static ssize_t batt_impedance_compensation_store(struct device *dev, 526 + struct device_attribute *attr, 527 + const char *buf, 528 + size_t count) 529 + { 530 + struct mp2629_charger *charger = dev_get_drvdata(dev->parent); 531 + unsigned int val; 532 + int ret; 533 + 534 + ret = kstrtouint(buf, 10, &val); 535 + if (ret) 536 + return ret; 537 + 538 + if (val > 140) 539 + return -ERANGE; 540 + 541 + /* multiples of 10 mohm so round off */ 542 + val = val / 10; 543 + ret = regmap_update_bits(charger->regmap, MP2629_REG_IMPEDANCE_COMP, 544 + MP2629_MASK_IMPEDANCE, val << 4); 545 + if (ret) 546 + return ret; 547 + 548 + return count; 549 + } 550 + 551 + static DEVICE_ATTR_RW(batt_impedance_compensation); 552 + 553 + static struct attribute *mp2629_charger_sysfs_attrs[] = { 554 + &dev_attr_batt_impedance_compensation.attr, 555 + NULL 556 + }; 557 + ATTRIBUTE_GROUPS(mp2629_charger_sysfs); 558 + 559 + static void mp2629_charger_disable(void *data) 560 + { 561 + struct mp2629_charger *charger = data; 562 + 563 + regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL, 564 + MP2629_MASK_CHARGE_CTRL, 0); 565 + } 566 + 567 + static int mp2629_charger_probe(struct platform_device *pdev) 568 + { 569 + struct device *dev = &pdev->dev; 570 + struct mp2629_data *ddata = dev_get_drvdata(dev->parent); 571 + struct mp2629_charger *charger; 572 + struct power_supply_config psy_cfg = {}; 573 + int ret, i, irq; 574 + 575 + charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL); 576 + if (!charger) 577 + return -ENOMEM; 578 + 579 + charger->regmap = ddata->regmap; 580 + charger->dev = dev; 581 + platform_set_drvdata(pdev, charger); 582 + 583 + irq = platform_get_irq_optional(to_platform_device(dev->parent), 0); 584 + if (irq < 0) { 585 + dev_err(dev, "get irq fail: %d\n", irq); 586 + return irq; 587 + } 588 + 589 + for (i = 0; i < MP2629_MAX_FIELD; i++) { 590 + charger->regmap_fields[i] = devm_regmap_field_alloc(dev, 591 + charger->regmap, mp2629_reg_fields[i]); 592 + if (IS_ERR(charger->regmap_fields[i])) { 593 + dev_err(dev, "regmap field alloc fail %d\n", i); 594 + return PTR_ERR(charger->regmap_fields[i]); 595 + } 596 + } 597 + 598 + for (i = 0; i < MP2629_ADC_CHAN_END; i++) { 599 + charger->iiochan[i] = devm_iio_channel_get(dev, 600 + adc_chan_name[i]); 601 + if (IS_ERR(charger->iiochan[i])) { 602 + dev_err(dev, "iio chan get %s err\n", adc_chan_name[i]); 603 + return PTR_ERR(charger->iiochan[i]); 604 + } 605 + } 606 + 607 + ret = devm_add_action_or_reset(dev, mp2629_charger_disable, charger); 608 + if (ret) 609 + return ret; 610 + 611 + charger->usb = devm_power_supply_register(dev, &mp2629_usb_desc, NULL); 612 + if (IS_ERR(charger->usb)) { 613 + dev_err(dev, "power supply register usb failed\n"); 614 + return PTR_ERR(charger->usb); 615 + } 616 + 617 + psy_cfg.drv_data = charger; 618 + psy_cfg.attr_grp = mp2629_charger_sysfs_groups; 619 + charger->battery = devm_power_supply_register(dev, 620 + &mp2629_battery_desc, &psy_cfg); 621 + if (IS_ERR(charger->battery)) { 622 + dev_err(dev, "power supply register battery failed\n"); 623 + return PTR_ERR(charger->battery); 624 + } 625 + 626 + ret = regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL, 627 + MP2629_MASK_CHARGE_CTRL, BIT(4)); 628 + if (ret) { 629 + dev_err(dev, "enable charge fail: %d\n", ret); 630 + return ret; 631 + } 632 + 633 + regmap_update_bits(charger->regmap, MP2629_REG_TIMER_CTRL, 634 + MP2629_MASK_WDOG_CTRL, 0); 635 + 636 + mutex_init(&charger->lock); 637 + 638 + ret = devm_request_threaded_irq(dev, irq, NULL, mp2629_irq_handler, 639 + IRQF_ONESHOT | IRQF_TRIGGER_RISING, 640 + "mp2629-charger", charger); 641 + if (ret) { 642 + dev_err(dev, "failed to request gpio IRQ\n"); 643 + return ret; 644 + } 645 + 646 + regmap_update_bits(charger->regmap, MP2629_REG_INTERRUPT, 647 + GENMASK(6, 5), BIT(6) | BIT(5)); 648 + 649 + return 0; 650 + } 651 + 652 + static const struct of_device_id mp2629_charger_of_match[] = { 653 + { .compatible = "mps,mp2629_charger"}, 654 + {} 655 + }; 656 + MODULE_DEVICE_TABLE(of, mp2629_charger_of_match); 657 + 658 + static struct platform_driver mp2629_charger_driver = { 659 + .driver = { 660 + .name = "mp2629_charger", 661 + .of_match_table = mp2629_charger_of_match, 662 + }, 663 + .probe = mp2629_charger_probe, 664 + }; 665 + module_platform_driver(mp2629_charger_driver); 666 + 667 + MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>"); 668 + MODULE_DESCRIPTION("MP2629 Charger driver"); 669 + MODULE_LICENSE("GPL");
+26
include/linux/mfd/mp2629.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * Copyright 2020 Monolithic Power Systems, Inc 4 + */ 5 + 6 + #ifndef __MP2629_H__ 7 + #define __MP2629_H__ 8 + 9 + #include <linux/device.h> 10 + #include <linux/regmap.h> 11 + 12 + struct mp2629_data { 13 + struct device *dev; 14 + struct regmap *regmap; 15 + }; 16 + 17 + enum mp2629_adc_chan { 18 + MP2629_BATT_VOLT, 19 + MP2629_SYSTEM_VOLT, 20 + MP2629_INPUT_VOLT, 21 + MP2629_BATT_CURRENT, 22 + MP2629_INPUT_CURRENT, 23 + MP2629_ADC_CHAN_END 24 + }; 25 + 26 + #endif