]> www.infradead.org Git - users/willy/xarray.git/commitdiff
power: supply: max77705: Add charger driver for Maxim 77705
authorDzmitry Sankouski <dsankouski@gmail.com>
Thu, 23 Jan 2025 15:04:28 +0000 (18:04 +0300)
committerLee Jones <lee@kernel.org>
Thu, 20 Feb 2025 16:37:23 +0000 (16:37 +0000)
Add driver for Maxim 77705 switch-mode charger.
It providing power supply class information to userspace.

The driver is configured through DTS (battery and system related
settings).

Signed-off-by: Dzmitry Sankouski <dsankouski@gmail.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Acked-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://lore.kernel.org/r/20250123-starqltechn_integration_upstream-v17-3-8b06685b6612@gmail.com
Signed-off-by: Lee Jones <lee@kernel.org>
drivers/power/supply/Kconfig
drivers/power/supply/Makefile
drivers/power/supply/max77705_charger.c [new file with mode: 0644]
include/linux/power/max77705_charger.h [new file with mode: 0644]

index 7b18358f194a70b716f760c3624cff44485f3749..c120d8ed870f28628e0ee3a439a6dc5b2c688f6e 100644 (file)
@@ -583,6 +583,12 @@ config CHARGER_MAX77693
        help
          Say Y to enable support for the Maxim MAX77693 battery charger.
 
+config CHARGER_MAX77705
+       tristate "Maxim MAX77705 battery charger driver"
+       depends on MFD_MAX77705
+       help
+         Say Y to enable support for the Maxim MAX77705 battery charger.
+
 config CHARGER_MAX77976
        tristate "Maxim MAX77976 battery charger driver"
        depends on I2C
index b55cc48a4c86f89ff133a0165c56be43b7a9b738..a4669383d53eb3f64534b355be82058361ed5809 100644 (file)
@@ -80,6 +80,7 @@ obj-$(CONFIG_CHARGER_MAX14577)        += max14577_charger.o
 obj-$(CONFIG_CHARGER_DETECTOR_MAX14656)        += max14656_charger_detector.o
 obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o
 obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
+obj-$(CONFIG_CHARGER_MAX77705) += max77705_charger.o
 obj-$(CONFIG_CHARGER_MAX77976) += max77976_charger.o
 obj-$(CONFIG_CHARGER_MAX8997)  += max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
diff --git a/drivers/power/supply/max77705_charger.c b/drivers/power/supply/max77705_charger.c
new file mode 100644 (file)
index 0000000..eec5e9e
--- /dev/null
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Based on max77650-charger.c
+ *
+ * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.org>
+ *
+ * Battery charger driver for MAXIM 77705 charger/power-supply.
+ */
+
+#include <linux/devm-helpers.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77693-common.h>
+#include <linux/mfd/max77705-private.h>
+#include <linux/power/max77705_charger.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+
+static const char *max77705_charger_model              = "max77705";
+static const char *max77705_charger_manufacturer       = "Maxim Integrated";
+
+static const struct regmap_config max77705_chg_regmap_config = {
+       .reg_base = MAX77705_CHG_REG_BASE,
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = MAX77705_CHG_REG_SAFEOUT_CTRL,
+};
+
+static enum power_supply_property max77705_charger_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+};
+
+static int max77705_chgin_irq(void *irq_drv_data)
+{
+       struct max77705_charger_data *charger = irq_drv_data;
+
+       queue_work(charger->wqueue, &charger->chgin_work);
+
+       return 0;
+}
+
+static const struct regmap_irq max77705_charger_irqs[] = {
+       { .mask = MAX77705_BYP_IM,   },
+       { .mask = MAX77705_INP_LIMIT_IM,   },
+       { .mask = MAX77705_BATP_IM,   },
+       { .mask = MAX77705_BAT_IM,   },
+       { .mask = MAX77705_CHG_IM,   },
+       { .mask = MAX77705_WCIN_IM,   },
+       { .mask = MAX77705_CHGIN_IM,   },
+       { .mask = MAX77705_AICL_IM,   },
+};
+
+static struct regmap_irq_chip max77705_charger_irq_chip = {
+       .name                   = "max77705-charger",
+       .status_base            = MAX77705_CHG_REG_INT,
+       .mask_base              = MAX77705_CHG_REG_INT_MASK,
+       .handle_post_irq        = max77705_chgin_irq,
+       .num_regs               = 1,
+       .irqs                   = max77705_charger_irqs,
+       .num_irqs               = ARRAY_SIZE(max77705_charger_irqs),
+};
+
+static int max77705_charger_enable(struct max77705_charger_data *chg)
+{
+       int rv;
+
+       rv = regmap_update_bits(chg->regmap, MAX77705_CHG_REG_CNFG_09,
+                               MAX77705_CHG_EN_MASK, MAX77705_CHG_EN_MASK);
+       if (rv)
+               dev_err(chg->dev, "unable to enable the charger: %d\n", rv);
+
+       return rv;
+}
+
+static void max77705_charger_disable(void *data)
+{
+       struct max77705_charger_data *chg = data;
+       int rv;
+
+       rv = regmap_update_bits(chg->regmap,
+                               MAX77705_CHG_REG_CNFG_09,
+                               MAX77705_CHG_EN_MASK,
+                               MAX77705_CHG_DISABLE);
+       if (rv)
+               dev_err(chg->dev, "unable to disable the charger: %d\n", rv);
+}
+
+static int max77705_get_online(struct regmap *regmap, int *val)
+{
+       unsigned int data;
+       int ret;
+
+       ret = regmap_read(regmap, MAX77705_CHG_REG_INT_OK, &data);
+       if (ret < 0)
+               return ret;
+
+       *val = !!(data & MAX77705_CHGIN_OK);
+
+       return 0;
+}
+
+static int max77705_check_battery(struct max77705_charger_data *charger, int *val)
+{
+       unsigned int reg_data;
+       unsigned int reg_data2;
+       struct regmap *regmap = charger->regmap;
+
+       regmap_read(regmap, MAX77705_CHG_REG_INT_OK, &reg_data);
+
+       dev_dbg(charger->dev, "CHG_INT_OK(0x%x)\n", reg_data);
+
+       regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, &reg_data2);
+
+       dev_dbg(charger->dev, "CHG_DETAILS00(0x%x)\n", reg_data2);
+
+       if ((reg_data & MAX77705_BATP_OK) || !(reg_data2 & MAX77705_BATP_DTLS))
+               *val = true;
+       else
+               *val = false;
+
+       return 0;
+}
+
+static int max77705_get_charge_type(struct max77705_charger_data *charger, int *val)
+{
+       struct regmap *regmap = charger->regmap;
+       unsigned int reg_data;
+
+       regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, &reg_data);
+       if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) {
+               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               return 0;
+       }
+
+       regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &reg_data);
+       reg_data &= MAX77705_CHG_DTLS;
+
+       switch (reg_data) {
+       case 0x0:
+       case MAX77705_CHARGER_CONSTANT_CURRENT:
+       case MAX77705_CHARGER_CONSTANT_VOLTAGE:
+               *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               return 0;
+       default:
+               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               return 0;
+       }
+
+       return 0;
+}
+
+static int max77705_get_status(struct max77705_charger_data *charger, int *val)
+{
+       struct regmap *regmap = charger->regmap;
+       unsigned int reg_data;
+
+       regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, &reg_data);
+       if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) {
+               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               return 0;
+       }
+
+       regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &reg_data);
+       reg_data &= MAX77705_CHG_DTLS;
+
+       switch (reg_data) {
+       case 0x0:
+       case MAX77705_CHARGER_CONSTANT_CURRENT:
+       case MAX77705_CHARGER_CONSTANT_VOLTAGE:
+               *val = POWER_SUPPLY_STATUS_CHARGING;
+               return 0;
+       case MAX77705_CHARGER_END_OF_CHARGE:
+       case MAX77705_CHARGER_DONE:
+               *val = POWER_SUPPLY_STATUS_FULL;
+               return 0;
+       /* those values hard coded as in vendor kernel, because of */
+       /* failure to determine it's actual meaning. */
+       case 0x05:
+       case 0x06:
+       case 0x07:
+               *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               return 0;
+       case 0x08:
+       case 0xA:
+       case 0xB:
+               *val = POWER_SUPPLY_STATUS_DISCHARGING;
+               return 0;
+       default:
+               *val = POWER_SUPPLY_STATUS_UNKNOWN;
+               return 0;
+       }
+
+       return 0;
+}
+
+static int max77705_get_vbus_state(struct regmap *regmap, int *value)
+{
+       int ret;
+       unsigned int charge_dtls;
+
+       ret = regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, &charge_dtls);
+       if (ret)
+               return ret;
+
+       charge_dtls = ((charge_dtls & MAX77705_CHGIN_DTLS) >>
+                       MAX77705_CHGIN_DTLS_SHIFT);
+
+       switch (charge_dtls) {
+       case 0x00:
+               *value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
+               break;
+       case 0x01:
+               *value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
+               break;
+       case 0x02:
+               *value = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               break;
+       case 0x03:
+               *value = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       default:
+               return 0;
+       }
+       return 0;
+}
+
+static int max77705_get_battery_health(struct max77705_charger_data *charger,
+                                       int *value)
+{
+       struct regmap *regmap = charger->regmap;
+       unsigned int bat_dtls;
+
+       regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &bat_dtls);
+       bat_dtls = ((bat_dtls & MAX77705_BAT_DTLS) >> MAX77705_BAT_DTLS_SHIFT);
+
+       switch (bat_dtls) {
+       case MAX77705_BATTERY_NOBAT:
+               dev_dbg(charger->dev, "%s: No battery and the charger is suspended\n",
+                       __func__);
+               *value = POWER_SUPPLY_HEALTH_NO_BATTERY;
+               break;
+       case MAX77705_BATTERY_PREQUALIFICATION:
+               dev_dbg(charger->dev, "%s: battery is okay but its voltage is low(~VPQLB)\n",
+                       __func__);
+               break;
+       case MAX77705_BATTERY_DEAD:
+               dev_dbg(charger->dev, "%s: battery dead\n", __func__);
+               *value = POWER_SUPPLY_HEALTH_DEAD;
+               break;
+       case MAX77705_BATTERY_GOOD:
+       case MAX77705_BATTERY_LOWVOLTAGE:
+               *value = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case MAX77705_BATTERY_OVERVOLTAGE:
+               dev_dbg(charger->dev, "%s: battery ovp\n", __func__);
+               *value = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               break;
+       default:
+               dev_dbg(charger->dev, "%s: battery unknown\n", __func__);
+               *value = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               break;
+       }
+
+       return 0;
+}
+
+static int max77705_get_health(struct max77705_charger_data *charger, int *val)
+{
+       struct regmap *regmap = charger->regmap;
+       int ret, is_online = 0;
+
+       ret = max77705_get_online(regmap, &is_online);
+       if (ret)
+               return ret;
+       if (is_online) {
+               ret = max77705_get_vbus_state(regmap, val);
+               if (ret || (*val != POWER_SUPPLY_HEALTH_GOOD))
+                       return ret;
+       }
+       return max77705_get_battery_health(charger, val);
+}
+
+static int max77705_get_input_current(struct max77705_charger_data *charger,
+                                       int *val)
+{
+       unsigned int reg_data;
+       int get_current = 0;
+       struct regmap *regmap = charger->regmap;
+
+       regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, &reg_data);
+
+       reg_data &= MAX77705_CHG_CHGIN_LIM_MASK;
+
+       if (reg_data <= 3)
+               get_current = MAX77705_CURRENT_CHGIN_MIN;
+       else if (reg_data >= MAX77705_CHG_CHGIN_LIM_MASK)
+               get_current = MAX77705_CURRENT_CHGIN_MAX;
+       else
+               get_current = (reg_data + 1) * MAX77705_CURRENT_CHGIN_STEP;
+
+       *val = get_current;
+
+       return 0;
+}
+
+static int max77705_get_charge_current(struct max77705_charger_data *charger,
+                                       int *val)
+{
+       unsigned int reg_data;
+       struct regmap *regmap = charger->regmap;
+
+       regmap_read(regmap, MAX77705_CHG_REG_CNFG_02, &reg_data);
+       reg_data &= MAX77705_CHG_CC;
+
+       *val = reg_data <= 0x2 ? MAX77705_CURRENT_CHGIN_MIN : reg_data * MAX77705_CURRENT_CHG_STEP;
+
+       return 0;
+}
+
+static int max77705_set_float_voltage(struct max77705_charger_data *charger,
+                                       int float_voltage)
+{
+       int float_voltage_mv;
+       unsigned int reg_data = 0;
+       struct regmap *regmap = charger->regmap;
+
+       float_voltage_mv = float_voltage / 1000;
+       reg_data = float_voltage_mv <= 4000 ? 0x0 :
+               float_voltage_mv >= 4500 ? 0x23 :
+               (float_voltage_mv <= 4200) ? (float_voltage_mv - 4000) / 50 :
+               (((float_voltage_mv - 4200) / 10) + 0x04);
+
+       return regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_04,
+                               MAX77705_CHG_CV_PRM_MASK,
+                               (reg_data << MAX77705_CHG_CV_PRM_SHIFT));
+}
+
+static int max77705_get_float_voltage(struct max77705_charger_data *charger,
+                                       int *val)
+{
+       unsigned int reg_data = 0;
+       int voltage_mv;
+       struct regmap *regmap = charger->regmap;
+
+       regmap_read(regmap, MAX77705_CHG_REG_CNFG_04, &reg_data);
+       reg_data &= MAX77705_CHG_PRM_MASK;
+       voltage_mv = reg_data <= 0x04 ? reg_data * 50 + 4000 :
+                                       (reg_data - 4) * 10 + 4200;
+       *val = voltage_mv * 1000;
+
+       return 0;
+}
+
+static int max77705_chg_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct max77705_charger_data *charger = power_supply_get_drvdata(psy);
+       struct regmap *regmap = charger->regmap;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               return max77705_get_online(regmap, &val->intval);
+       case POWER_SUPPLY_PROP_PRESENT:
+               return max77705_check_battery(charger, &val->intval);
+       case POWER_SUPPLY_PROP_STATUS:
+               return max77705_get_status(charger, &val->intval);
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               return max77705_get_charge_type(charger, &val->intval);
+       case POWER_SUPPLY_PROP_HEALTH:
+               return max77705_get_health(charger, &val->intval);
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               return max77705_get_input_current(charger, &val->intval);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               return max77705_get_charge_current(charger, &val->intval);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               return max77705_get_float_voltage(charger, &val->intval);
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = charger->bat_info->voltage_max_design_uv;
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = max77705_charger_model;
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = max77705_charger_manufacturer;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static const struct power_supply_desc max77705_charger_psy_desc = {
+       .name = "max77705-charger",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties = max77705_charger_props,
+       .num_properties = ARRAY_SIZE(max77705_charger_props),
+       .get_property = max77705_chg_get_property,
+};
+
+static void max77705_chgin_isr_work(struct work_struct *work)
+{
+       struct max77705_charger_data *charger =
+               container_of(work, struct max77705_charger_data, chgin_work);
+
+       power_supply_changed(charger->psy_chg);
+}
+
+static void max77705_charger_initialize(struct max77705_charger_data *chg)
+{
+       u8 reg_data;
+       struct power_supply_battery_info *info;
+       struct regmap *regmap = chg->regmap;
+
+       if (power_supply_get_battery_info(chg->psy_chg, &info) < 0)
+               return;
+
+       chg->bat_info = info;
+
+       /* unlock charger setting protect */
+       /* slowest LX slope */
+       reg_data = MAX77705_CHGPROT_MASK | MAX77705_SLOWEST_LX_SLOPE;
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_06, reg_data,
+                                               reg_data);
+
+       /* fast charge timer disable */
+       /* restart threshold disable */
+       /* pre-qual charge disable */
+       reg_data = (MAX77705_FCHGTIME_DISABLE << MAX77705_FCHGTIME_SHIFT) |
+                       (MAX77705_CHG_RSTRT_DISABLE << MAX77705_CHG_RSTRT_SHIFT) |
+                       (MAX77705_CHG_PQEN_DISABLE << MAX77705_PQEN_SHIFT);
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_01,
+                                               (MAX77705_FCHGTIME_MASK |
+                                               MAX77705_CHG_RSTRT_MASK |
+                                               MAX77705_PQEN_MASK),
+                                               reg_data);
+
+       /* OTG off(UNO on), boost off */
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00,
+                               MAX77705_OTG_CTRL, 0);
+
+       /* charge current 450mA(default) */
+       /* otg current limit 900mA */
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_02,
+                               MAX77705_OTG_ILIM_MASK,
+                               MAX77705_OTG_ILIM_900 << MAX77705_OTG_ILIM_SHIFT);
+
+       /* BAT to SYS OCP 4.80A */
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_05,
+                               MAX77705_REG_B2SOVRC_MASK,
+                               MAX77705_B2SOVRC_4_8A << MAX77705_REG_B2SOVRC_SHIFT);
+       /* top off current 150mA */
+       /* top off timer 30min */
+       reg_data = (MAX77705_TO_ITH_150MA << MAX77705_TO_ITH_SHIFT) |
+                       (MAX77705_TO_TIME_30M << MAX77705_TO_TIME_SHIFT) |
+                       (MAX77705_SYS_TRACK_DISABLE << MAX77705_SYS_TRACK_DIS_SHIFT);
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_03,
+                          (MAX77705_TO_ITH_MASK |
+                          MAX77705_TO_TIME_MASK |
+                          MAX77705_SYS_TRACK_DIS_MASK), reg_data);
+
+       /* cv voltage 4.2V or 4.35V */
+       /* MINVSYS 3.6V(default) */
+       if (info->voltage_max_design_uv < 0) {
+               dev_warn(chg->dev, "missing battery:voltage-max-design-microvolt\n");
+               max77705_set_float_voltage(chg, 4200000);
+       } else {
+               max77705_set_float_voltage(chg, info->voltage_max_design_uv);
+       }
+
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12,
+                               MAX77705_VCHGIN_REG_MASK, MAX77705_VCHGIN_4_5);
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12,
+                               MAX77705_WCIN_REG_MASK, MAX77705_WCIN_4_5);
+
+       /* Watchdog timer */
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00,
+                               MAX77705_WDTEN_MASK, 0);
+
+       /* Active Discharge Enable */
+       regmap_update_bits(regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1);
+
+       /* VBYPSET=5.0V */
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_11, MAX77705_VBYPSET_MASK, 0);
+
+       /* Switching Frequency : 1.5MHz */
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_08, MAX77705_REG_FSW_MASK,
+                               (MAX77705_CHG_FSW_1_5MHz << MAX77705_REG_FSW_SHIFT));
+
+       /* Auto skip mode */
+       regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, MAX77705_REG_DISKIP_MASK,
+                               (MAX77705_AUTO_SKIP << MAX77705_REG_DISKIP_SHIFT));
+}
+
+static int max77705_charger_probe(struct i2c_client *i2c)
+{
+       struct power_supply_config pscfg = {};
+       struct max77705_charger_data *chg;
+       struct device *dev;
+       struct regmap_irq_chip_data *irq_data;
+       int ret;
+
+       dev = &i2c->dev;
+
+       chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
+       if (!chg)
+               return -ENOMEM;
+
+       chg->dev = dev;
+       i2c_set_clientdata(i2c, chg);
+
+       chg->regmap = devm_regmap_init_i2c(i2c, &max77705_chg_regmap_config);
+       if (IS_ERR(chg->regmap))
+               return PTR_ERR(chg->regmap);
+
+       ret = regmap_update_bits(chg->regmap,
+                               MAX77705_CHG_REG_INT_MASK,
+                               MAX77705_CHGIN_IM, 0);
+       if (ret)
+               return ret;
+
+       pscfg.fwnode = dev_fwnode(dev);
+       pscfg.drv_data = chg;
+
+       chg->psy_chg = devm_power_supply_register(dev, &max77705_charger_psy_desc, &pscfg);
+       if (IS_ERR(chg->psy_chg))
+               return PTR_ERR(chg->psy_chg);
+
+       max77705_charger_irq_chip.irq_drv_data = chg;
+       ret = devm_regmap_add_irq_chip(chg->dev, chg->regmap, i2c->irq,
+                                       IRQF_ONESHOT | IRQF_SHARED, 0,
+                                       &max77705_charger_irq_chip,
+                                       &irq_data);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to add irq chip\n");
+
+       chg->wqueue = create_singlethread_workqueue(dev_name(dev));
+       if (IS_ERR(chg->wqueue))
+               return dev_err_probe(dev, PTR_ERR(chg->wqueue), "failed to create workqueue\n");
+
+       ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to initialize interrupt work\n");
+
+       max77705_charger_initialize(chg);
+
+       ret = max77705_charger_enable(chg);
+       if (ret)
+               return dev_err_probe(dev, ret, "failed to enable charge\n");
+
+       return devm_add_action_or_reset(dev, max77705_charger_disable, chg);
+}
+
+static const struct of_device_id max77705_charger_of_match[] = {
+       { .compatible = "maxim,max77705-charger" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, max77705_charger_of_match);
+
+static struct i2c_driver max77705_charger_driver = {
+       .driver = {
+               .name = "max77705-charger",
+               .of_match_table = max77705_charger_of_match,
+       },
+       .probe = max77705_charger_probe,
+};
+module_i2c_driver(max77705_charger_driver);
+
+MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
+MODULE_DESCRIPTION("Maxim MAX77705 charger driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/power/max77705_charger.h b/include/linux/power/max77705_charger.h
new file mode 100644 (file)
index 0000000..fdec9af
--- /dev/null
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Maxim MAX77705 definitions.
+ *
+ * Copyright (C) 2015 Samsung Electronics, Inc.
+ * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
+ */
+
+#ifndef __MAX77705_CHARGER_H
+#define __MAX77705_CHARGER_H __FILE__
+
+/* MAX77705_CHG_REG_CHG_INT */
+#define MAX77705_BYP_I         BIT(0)
+#define MAX77705_INP_LIMIT_I   BIT(1)
+#define MAX77705_BATP_I                BIT(2)
+#define MAX77705_BAT_I         BIT(3)
+#define MAX77705_CHG_I         BIT(4)
+#define MAX77705_WCIN_I                BIT(5)
+#define MAX77705_CHGIN_I       BIT(6)
+#define MAX77705_AICL_I                BIT(7)
+
+/* MAX77705_CHG_REG_CHG_INT_MASK */
+#define MAX77705_BYP_IM                BIT(0)
+#define MAX77705_INP_LIMIT_IM  BIT(1)
+#define MAX77705_BATP_IM       BIT(2)
+#define MAX77705_BAT_IM                BIT(3)
+#define MAX77705_CHG_IM                BIT(4)
+#define MAX77705_WCIN_IM       BIT(5)
+#define MAX77705_CHGIN_IM      BIT(6)
+#define MAX77705_AICL_IM       BIT(7)
+
+/* MAX77705_CHG_REG_CHG_INT_OK */
+#define MAX77705_BYP_OK                BIT(0)
+#define MAX77705_DISQBAT_OK    BIT(1)
+#define MAX77705_BATP_OK       BIT(2)
+#define MAX77705_BAT_OK                BIT(3)
+#define MAX77705_CHG_OK                BIT(4)
+#define MAX77705_WCIN_OK       BIT(5)
+#define MAX77705_CHGIN_OK      BIT(6)
+#define MAX77705_AICL_OK       BIT(7)
+
+/* MAX77705_CHG_REG_DETAILS_00 */
+#define MAX77705_BATP_DTLS             BIT(0)
+#define MAX77705_WCIN_DTLS             GENMASK(4, 3)
+#define MAX77705_WCIN_DTLS_SHIFT       3
+#define MAX77705_CHGIN_DTLS            GENMASK(6, 5)
+#define MAX77705_CHGIN_DTLS_SHIFT      5
+
+/* MAX77705_CHG_REG_DETAILS_01 */
+#define MAX77705_CHG_DTLS      GENMASK(3, 0)
+#define MAX77705_CHG_DTLS_SHIFT        0
+#define MAX77705_BAT_DTLS      GENMASK(6, 4)
+#define MAX77705_BAT_DTLS_SHIFT        4
+
+/* MAX77705_CHG_REG_DETAILS_02 */
+#define MAX77705_BYP_DTLS      GENMASK(3, 0)
+#define MAX77705_BYP_DTLS_SHIFT        0
+
+/* MAX77705_CHG_REG_CNFG_00 */
+#define MAX77705_CHG_SHIFT     0
+#define MAX77705_UNO_SHIFT     1
+#define MAX77705_OTG_SHIFT     1
+#define MAX77705_BUCK_SHIFT    2
+#define MAX77705_BOOST_SHIFT   3
+#define MAX77705_WDTEN_SHIFT   4
+#define MAX77705_MODE_MASK     GENMASK(3, 0)
+#define MAX77705_CHG_MASK      BIT(MAX77705_CHG_SHIFT)
+#define MAX77705_UNO_MASK      BIT(MAX77705_UNO_SHIFT)
+#define MAX77705_OTG_MASK      BIT(MAX77705_OTG_SHIFT)
+#define MAX77705_BUCK_MASK     BIT(MAX77705_BUCK_SHIFT)
+#define MAX77705_BOOST_MASK    BIT(MAX77705_BOOST_SHIFT)
+#define MAX77705_WDTEN_MASK    BIT(MAX77705_WDTEN_SHIFT)
+#define MAX77705_UNO_CTRL      (MAX77705_UNO_MASK | MAX77705_BOOST_MASK)
+#define MAX77705_OTG_CTRL      (MAX77705_OTG_MASK | MAX77705_BOOST_MASK)
+
+/* MAX77705_CHG_REG_CNFG_01 */
+#define MAX77705_FCHGTIME_SHIFT                0
+#define MAX77705_FCHGTIME_MASK         GENMASK(2, 0)
+#define MAX77705_CHG_RSTRT_SHIFT       4
+#define MAX77705_CHG_RSTRT_MASK                GENMASK(5, 4)
+#define MAX77705_FCHGTIME_DISABLE      0
+#define MAX77705_CHG_RSTRT_DISABLE     0x3
+
+#define MAX77705_PQEN_SHIFT            7
+#define MAX77705_PQEN_MASK             BIT(7)
+#define MAX77705_CHG_PQEN_DISABLE      0
+#define MAX77705_CHG_PQEN_ENABLE       1
+
+/* MAX77705_CHG_REG_CNFG_02 */
+#define MAX77705_OTG_ILIM_SHIFT                6
+#define MAX77705_OTG_ILIM_MASK         GENMASK(7, 6)
+#define MAX77705_OTG_ILIM_500          0
+#define MAX77705_OTG_ILIM_900          1
+#define MAX77705_OTG_ILIM_1200         2
+#define MAX77705_OTG_ILIM_1500         3
+#define MAX77705_CHG_CC                        GENMASK(5, 0)
+
+/* MAX77705_CHG_REG_CNFG_03 */
+#define MAX77705_TO_ITH_SHIFT          0
+#define MAX77705_TO_ITH_MASK           GENMASK(2, 0)
+#define MAX77705_TO_TIME_SHIFT         3
+#define MAX77705_TO_TIME_MASK          GENMASK(5, 3)
+#define MAX77705_SYS_TRACK_DIS_SHIFT   7
+#define MAX77705_SYS_TRACK_DIS_MASK    BIT(7)
+#define MAX77705_TO_ITH_150MA          0
+#define MAX77705_TO_TIME_30M           3
+#define MAX77705_SYS_TRACK_ENABLE      0
+#define MAX77705_SYS_TRACK_DISABLE     1
+
+/* MAX77705_CHG_REG_CNFG_04 */
+#define MAX77705_CHG_MINVSYS_SHIFT     6
+#define MAX77705_CHG_MINVSYS_MASK      GENMASK(7, 6)
+#define MAX77705_CHG_PRM_SHIFT         0
+#define MAX77705_CHG_PRM_MASK          GENMASK(5, 0)
+
+#define MAX77705_CHG_CV_PRM_SHIFT      0
+#define MAX77705_CHG_CV_PRM_MASK       GENMASK(5, 0)
+
+/* MAX77705_CHG_REG_CNFG_05 */
+#define MAX77705_REG_B2SOVRC_SHIFT     0
+#define MAX77705_REG_B2SOVRC_MASK      GENMASK(3, 0)
+#define MAX77705_B2SOVRC_DISABLE       0
+#define MAX77705_B2SOVRC_4_5A          6
+#define MAX77705_B2SOVRC_4_8A          8
+#define MAX77705_B2SOVRC_5_0A          9
+
+/* MAX77705_CHG_CNFG_06 */
+#define MAX77705_WDTCLR_SHIFT          0
+#define MAX77705_WDTCLR_MASK           GENMASK(1, 0)
+#define MAX77705_WDTCLR                        1
+#define MAX77705_CHGPROT_MASK          GENMASK(3, 2)
+#define MAX77705_CHGPROT_UNLOCKED      GENMASK(3, 2)
+#define MAX77705_SLOWEST_LX_SLOPE      GENMASK(6, 5)
+
+/* MAX77705_CHG_REG_CNFG_07 */
+#define MAX77705_CHG_FMBST             4
+#define MAX77705_REG_FMBST_SHIFT       2
+#define MAX77705_REG_FMBST_MASK                BIT(MAX77705_REG_FMBST_SHIFT)
+#define MAX77705_REG_FGSRC_SHIFT       1
+#define MAX77705_REG_FGSRC_MASK                BIT(MAX77705_REG_FGSRC_SHIFT)
+
+/* MAX77705_CHG_REG_CNFG_08 */
+#define MAX77705_REG_FSW_SHIFT         0
+#define MAX77705_REG_FSW_MASK          GENMASK(1, 0)
+#define MAX77705_CHG_FSW_3MHz          0
+#define MAX77705_CHG_FSW_2MHz          1
+#define MAX77705_CHG_FSW_1_5MHz                2
+
+/* MAX77705_CHG_REG_CNFG_09 */
+#define MAX77705_CHG_CHGIN_LIM_MASK            GENMASK(6, 0)
+#define MAX77705_CHG_EN_MASK                   BIT(7)
+#define MAX77705_CHG_DISABLE                   0
+#define MAX77705_CHARGER_CHG_CHARGING(_reg) \
+                               (((_reg) & MAX77705_CHG_EN_MASK) > 1)
+
+
+/* MAX77705_CHG_REG_CNFG_10 */
+#define MAX77705_CHG_WCIN_LIM          GENMASK(5, 0)
+
+/* MAX77705_CHG_REG_CNFG_11 */
+#define MAX77705_VBYPSET_SHIFT         0
+#define MAX77705_VBYPSET_MASK          GENMASK(6, 0)
+
+/* MAX77705_CHG_REG_CNFG_12 */
+#define MAX77705_CHGINSEL_SHIFT                5
+#define MAX77705_CHGINSEL_MASK         BIT(MAX77705_CHGINSEL_SHIFT)
+#define MAX77705_WCINSEL_SHIFT         6
+#define MAX77705_WCINSEL_MASK          BIT(MAX77705_WCINSEL_SHIFT)
+#define MAX77705_VCHGIN_REG_MASK       GENMASK(4, 3)
+#define MAX77705_WCIN_REG_MASK         GENMASK(2, 1)
+#define MAX77705_REG_DISKIP_SHIFT      0
+#define MAX77705_REG_DISKIP_MASK       BIT(MAX77705_REG_DISKIP_SHIFT)
+/* REG=4.5V, UVLO=4.7V */
+#define MAX77705_VCHGIN_4_5            0
+/* REG=4.5V, UVLO=4.7V */
+#define MAX77705_WCIN_4_5              0
+#define MAX77705_DISABLE_SKIP          1
+#define MAX77705_AUTO_SKIP             0
+
+/* uA */
+#define MAX77705_CURRENT_CHGIN_STEP    25000
+#define MAX77705_CURRENT_CHG_STEP      50000
+#define MAX77705_CURRENT_CHGIN_MIN     100000
+#define MAX77705_CURRENT_CHGIN_MAX     3200000
+
+struct max77705_charger_data {
+       struct device                   *dev;
+       struct regmap           *regmap;
+       struct power_supply_battery_info *bat_info;
+       struct workqueue_struct *wqueue;
+       struct work_struct      chgin_work;
+       struct power_supply     *psy_chg;
+};
+
+#endif /* __MAX77705_CHARGER_H */