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

Configure Feed

Select the types of activity you want to include in your feed.

power: supply: max77705: Add charger driver for Maxim 77705

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>

authored by

Dzmitry Sankouski and committed by
Lee Jones
a6a494c8 2ae4ffff

+783
+6
drivers/power/supply/Kconfig
··· 583 583 help 584 584 Say Y to enable support for the Maxim MAX77693 battery charger. 585 585 586 + config CHARGER_MAX77705 587 + tristate "Maxim MAX77705 battery charger driver" 588 + depends on MFD_MAX77705 589 + help 590 + Say Y to enable support for the Maxim MAX77705 battery charger. 591 + 586 592 config CHARGER_MAX77976 587 593 tristate "Maxim MAX77976 battery charger driver" 588 594 depends on I2C
+1
drivers/power/supply/Makefile
··· 80 80 obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o 81 81 obj-$(CONFIG_CHARGER_MAX77650) += max77650-charger.o 82 82 obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o 83 + obj-$(CONFIG_CHARGER_MAX77705) += max77705_charger.o 83 84 obj-$(CONFIG_CHARGER_MAX77976) += max77976_charger.o 84 85 obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o 85 86 obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
+581
drivers/power/supply/max77705_charger.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Based on max77650-charger.c 4 + * 5 + * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.org> 6 + * 7 + * Battery charger driver for MAXIM 77705 charger/power-supply. 8 + */ 9 + 10 + #include <linux/devm-helpers.h> 11 + #include <linux/i2c.h> 12 + #include <linux/interrupt.h> 13 + #include <linux/mfd/max77693-common.h> 14 + #include <linux/mfd/max77705-private.h> 15 + #include <linux/power/max77705_charger.h> 16 + #include <linux/module.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/power_supply.h> 19 + #include <linux/regmap.h> 20 + 21 + static const char *max77705_charger_model = "max77705"; 22 + static const char *max77705_charger_manufacturer = "Maxim Integrated"; 23 + 24 + static const struct regmap_config max77705_chg_regmap_config = { 25 + .reg_base = MAX77705_CHG_REG_BASE, 26 + .reg_bits = 8, 27 + .val_bits = 8, 28 + .max_register = MAX77705_CHG_REG_SAFEOUT_CTRL, 29 + }; 30 + 31 + static enum power_supply_property max77705_charger_props[] = { 32 + POWER_SUPPLY_PROP_ONLINE, 33 + POWER_SUPPLY_PROP_PRESENT, 34 + POWER_SUPPLY_PROP_STATUS, 35 + POWER_SUPPLY_PROP_CHARGE_TYPE, 36 + POWER_SUPPLY_PROP_HEALTH, 37 + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 38 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 39 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 40 + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 41 + }; 42 + 43 + static int max77705_chgin_irq(void *irq_drv_data) 44 + { 45 + struct max77705_charger_data *charger = irq_drv_data; 46 + 47 + queue_work(charger->wqueue, &charger->chgin_work); 48 + 49 + return 0; 50 + } 51 + 52 + static const struct regmap_irq max77705_charger_irqs[] = { 53 + { .mask = MAX77705_BYP_IM, }, 54 + { .mask = MAX77705_INP_LIMIT_IM, }, 55 + { .mask = MAX77705_BATP_IM, }, 56 + { .mask = MAX77705_BAT_IM, }, 57 + { .mask = MAX77705_CHG_IM, }, 58 + { .mask = MAX77705_WCIN_IM, }, 59 + { .mask = MAX77705_CHGIN_IM, }, 60 + { .mask = MAX77705_AICL_IM, }, 61 + }; 62 + 63 + static struct regmap_irq_chip max77705_charger_irq_chip = { 64 + .name = "max77705-charger", 65 + .status_base = MAX77705_CHG_REG_INT, 66 + .mask_base = MAX77705_CHG_REG_INT_MASK, 67 + .handle_post_irq = max77705_chgin_irq, 68 + .num_regs = 1, 69 + .irqs = max77705_charger_irqs, 70 + .num_irqs = ARRAY_SIZE(max77705_charger_irqs), 71 + }; 72 + 73 + static int max77705_charger_enable(struct max77705_charger_data *chg) 74 + { 75 + int rv; 76 + 77 + rv = regmap_update_bits(chg->regmap, MAX77705_CHG_REG_CNFG_09, 78 + MAX77705_CHG_EN_MASK, MAX77705_CHG_EN_MASK); 79 + if (rv) 80 + dev_err(chg->dev, "unable to enable the charger: %d\n", rv); 81 + 82 + return rv; 83 + } 84 + 85 + static void max77705_charger_disable(void *data) 86 + { 87 + struct max77705_charger_data *chg = data; 88 + int rv; 89 + 90 + rv = regmap_update_bits(chg->regmap, 91 + MAX77705_CHG_REG_CNFG_09, 92 + MAX77705_CHG_EN_MASK, 93 + MAX77705_CHG_DISABLE); 94 + if (rv) 95 + dev_err(chg->dev, "unable to disable the charger: %d\n", rv); 96 + } 97 + 98 + static int max77705_get_online(struct regmap *regmap, int *val) 99 + { 100 + unsigned int data; 101 + int ret; 102 + 103 + ret = regmap_read(regmap, MAX77705_CHG_REG_INT_OK, &data); 104 + if (ret < 0) 105 + return ret; 106 + 107 + *val = !!(data & MAX77705_CHGIN_OK); 108 + 109 + return 0; 110 + } 111 + 112 + static int max77705_check_battery(struct max77705_charger_data *charger, int *val) 113 + { 114 + unsigned int reg_data; 115 + unsigned int reg_data2; 116 + struct regmap *regmap = charger->regmap; 117 + 118 + regmap_read(regmap, MAX77705_CHG_REG_INT_OK, &reg_data); 119 + 120 + dev_dbg(charger->dev, "CHG_INT_OK(0x%x)\n", reg_data); 121 + 122 + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, &reg_data2); 123 + 124 + dev_dbg(charger->dev, "CHG_DETAILS00(0x%x)\n", reg_data2); 125 + 126 + if ((reg_data & MAX77705_BATP_OK) || !(reg_data2 & MAX77705_BATP_DTLS)) 127 + *val = true; 128 + else 129 + *val = false; 130 + 131 + return 0; 132 + } 133 + 134 + static int max77705_get_charge_type(struct max77705_charger_data *charger, int *val) 135 + { 136 + struct regmap *regmap = charger->regmap; 137 + unsigned int reg_data; 138 + 139 + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, &reg_data); 140 + if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) { 141 + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; 142 + return 0; 143 + } 144 + 145 + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &reg_data); 146 + reg_data &= MAX77705_CHG_DTLS; 147 + 148 + switch (reg_data) { 149 + case 0x0: 150 + case MAX77705_CHARGER_CONSTANT_CURRENT: 151 + case MAX77705_CHARGER_CONSTANT_VOLTAGE: 152 + *val = POWER_SUPPLY_CHARGE_TYPE_FAST; 153 + return 0; 154 + default: 155 + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; 156 + return 0; 157 + } 158 + 159 + return 0; 160 + } 161 + 162 + static int max77705_get_status(struct max77705_charger_data *charger, int *val) 163 + { 164 + struct regmap *regmap = charger->regmap; 165 + unsigned int reg_data; 166 + 167 + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, &reg_data); 168 + if (!MAX77705_CHARGER_CHG_CHARGING(reg_data)) { 169 + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; 170 + return 0; 171 + } 172 + 173 + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &reg_data); 174 + reg_data &= MAX77705_CHG_DTLS; 175 + 176 + switch (reg_data) { 177 + case 0x0: 178 + case MAX77705_CHARGER_CONSTANT_CURRENT: 179 + case MAX77705_CHARGER_CONSTANT_VOLTAGE: 180 + *val = POWER_SUPPLY_STATUS_CHARGING; 181 + return 0; 182 + case MAX77705_CHARGER_END_OF_CHARGE: 183 + case MAX77705_CHARGER_DONE: 184 + *val = POWER_SUPPLY_STATUS_FULL; 185 + return 0; 186 + /* those values hard coded as in vendor kernel, because of */ 187 + /* failure to determine it's actual meaning. */ 188 + case 0x05: 189 + case 0x06: 190 + case 0x07: 191 + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; 192 + return 0; 193 + case 0x08: 194 + case 0xA: 195 + case 0xB: 196 + *val = POWER_SUPPLY_STATUS_DISCHARGING; 197 + return 0; 198 + default: 199 + *val = POWER_SUPPLY_STATUS_UNKNOWN; 200 + return 0; 201 + } 202 + 203 + return 0; 204 + } 205 + 206 + static int max77705_get_vbus_state(struct regmap *regmap, int *value) 207 + { 208 + int ret; 209 + unsigned int charge_dtls; 210 + 211 + ret = regmap_read(regmap, MAX77705_CHG_REG_DETAILS_00, &charge_dtls); 212 + if (ret) 213 + return ret; 214 + 215 + charge_dtls = ((charge_dtls & MAX77705_CHGIN_DTLS) >> 216 + MAX77705_CHGIN_DTLS_SHIFT); 217 + 218 + switch (charge_dtls) { 219 + case 0x00: 220 + *value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; 221 + break; 222 + case 0x01: 223 + *value = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; 224 + break; 225 + case 0x02: 226 + *value = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 227 + break; 228 + case 0x03: 229 + *value = POWER_SUPPLY_HEALTH_GOOD; 230 + break; 231 + default: 232 + return 0; 233 + } 234 + return 0; 235 + } 236 + 237 + static int max77705_get_battery_health(struct max77705_charger_data *charger, 238 + int *value) 239 + { 240 + struct regmap *regmap = charger->regmap; 241 + unsigned int bat_dtls; 242 + 243 + regmap_read(regmap, MAX77705_CHG_REG_DETAILS_01, &bat_dtls); 244 + bat_dtls = ((bat_dtls & MAX77705_BAT_DTLS) >> MAX77705_BAT_DTLS_SHIFT); 245 + 246 + switch (bat_dtls) { 247 + case MAX77705_BATTERY_NOBAT: 248 + dev_dbg(charger->dev, "%s: No battery and the charger is suspended\n", 249 + __func__); 250 + *value = POWER_SUPPLY_HEALTH_NO_BATTERY; 251 + break; 252 + case MAX77705_BATTERY_PREQUALIFICATION: 253 + dev_dbg(charger->dev, "%s: battery is okay but its voltage is low(~VPQLB)\n", 254 + __func__); 255 + break; 256 + case MAX77705_BATTERY_DEAD: 257 + dev_dbg(charger->dev, "%s: battery dead\n", __func__); 258 + *value = POWER_SUPPLY_HEALTH_DEAD; 259 + break; 260 + case MAX77705_BATTERY_GOOD: 261 + case MAX77705_BATTERY_LOWVOLTAGE: 262 + *value = POWER_SUPPLY_HEALTH_GOOD; 263 + break; 264 + case MAX77705_BATTERY_OVERVOLTAGE: 265 + dev_dbg(charger->dev, "%s: battery ovp\n", __func__); 266 + *value = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 267 + break; 268 + default: 269 + dev_dbg(charger->dev, "%s: battery unknown\n", __func__); 270 + *value = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 271 + break; 272 + } 273 + 274 + return 0; 275 + } 276 + 277 + static int max77705_get_health(struct max77705_charger_data *charger, int *val) 278 + { 279 + struct regmap *regmap = charger->regmap; 280 + int ret, is_online = 0; 281 + 282 + ret = max77705_get_online(regmap, &is_online); 283 + if (ret) 284 + return ret; 285 + if (is_online) { 286 + ret = max77705_get_vbus_state(regmap, val); 287 + if (ret || (*val != POWER_SUPPLY_HEALTH_GOOD)) 288 + return ret; 289 + } 290 + return max77705_get_battery_health(charger, val); 291 + } 292 + 293 + static int max77705_get_input_current(struct max77705_charger_data *charger, 294 + int *val) 295 + { 296 + unsigned int reg_data; 297 + int get_current = 0; 298 + struct regmap *regmap = charger->regmap; 299 + 300 + regmap_read(regmap, MAX77705_CHG_REG_CNFG_09, &reg_data); 301 + 302 + reg_data &= MAX77705_CHG_CHGIN_LIM_MASK; 303 + 304 + if (reg_data <= 3) 305 + get_current = MAX77705_CURRENT_CHGIN_MIN; 306 + else if (reg_data >= MAX77705_CHG_CHGIN_LIM_MASK) 307 + get_current = MAX77705_CURRENT_CHGIN_MAX; 308 + else 309 + get_current = (reg_data + 1) * MAX77705_CURRENT_CHGIN_STEP; 310 + 311 + *val = get_current; 312 + 313 + return 0; 314 + } 315 + 316 + static int max77705_get_charge_current(struct max77705_charger_data *charger, 317 + int *val) 318 + { 319 + unsigned int reg_data; 320 + struct regmap *regmap = charger->regmap; 321 + 322 + regmap_read(regmap, MAX77705_CHG_REG_CNFG_02, &reg_data); 323 + reg_data &= MAX77705_CHG_CC; 324 + 325 + *val = reg_data <= 0x2 ? MAX77705_CURRENT_CHGIN_MIN : reg_data * MAX77705_CURRENT_CHG_STEP; 326 + 327 + return 0; 328 + } 329 + 330 + static int max77705_set_float_voltage(struct max77705_charger_data *charger, 331 + int float_voltage) 332 + { 333 + int float_voltage_mv; 334 + unsigned int reg_data = 0; 335 + struct regmap *regmap = charger->regmap; 336 + 337 + float_voltage_mv = float_voltage / 1000; 338 + reg_data = float_voltage_mv <= 4000 ? 0x0 : 339 + float_voltage_mv >= 4500 ? 0x23 : 340 + (float_voltage_mv <= 4200) ? (float_voltage_mv - 4000) / 50 : 341 + (((float_voltage_mv - 4200) / 10) + 0x04); 342 + 343 + return regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_04, 344 + MAX77705_CHG_CV_PRM_MASK, 345 + (reg_data << MAX77705_CHG_CV_PRM_SHIFT)); 346 + } 347 + 348 + static int max77705_get_float_voltage(struct max77705_charger_data *charger, 349 + int *val) 350 + { 351 + unsigned int reg_data = 0; 352 + int voltage_mv; 353 + struct regmap *regmap = charger->regmap; 354 + 355 + regmap_read(regmap, MAX77705_CHG_REG_CNFG_04, &reg_data); 356 + reg_data &= MAX77705_CHG_PRM_MASK; 357 + voltage_mv = reg_data <= 0x04 ? reg_data * 50 + 4000 : 358 + (reg_data - 4) * 10 + 4200; 359 + *val = voltage_mv * 1000; 360 + 361 + return 0; 362 + } 363 + 364 + static int max77705_chg_get_property(struct power_supply *psy, 365 + enum power_supply_property psp, 366 + union power_supply_propval *val) 367 + { 368 + struct max77705_charger_data *charger = power_supply_get_drvdata(psy); 369 + struct regmap *regmap = charger->regmap; 370 + 371 + switch (psp) { 372 + case POWER_SUPPLY_PROP_ONLINE: 373 + return max77705_get_online(regmap, &val->intval); 374 + case POWER_SUPPLY_PROP_PRESENT: 375 + return max77705_check_battery(charger, &val->intval); 376 + case POWER_SUPPLY_PROP_STATUS: 377 + return max77705_get_status(charger, &val->intval); 378 + case POWER_SUPPLY_PROP_CHARGE_TYPE: 379 + return max77705_get_charge_type(charger, &val->intval); 380 + case POWER_SUPPLY_PROP_HEALTH: 381 + return max77705_get_health(charger, &val->intval); 382 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 383 + return max77705_get_input_current(charger, &val->intval); 384 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 385 + return max77705_get_charge_current(charger, &val->intval); 386 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 387 + return max77705_get_float_voltage(charger, &val->intval); 388 + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 389 + val->intval = charger->bat_info->voltage_max_design_uv; 390 + break; 391 + case POWER_SUPPLY_PROP_MODEL_NAME: 392 + val->strval = max77705_charger_model; 393 + break; 394 + case POWER_SUPPLY_PROP_MANUFACTURER: 395 + val->strval = max77705_charger_manufacturer; 396 + break; 397 + default: 398 + return -EINVAL; 399 + } 400 + return 0; 401 + } 402 + 403 + static const struct power_supply_desc max77705_charger_psy_desc = { 404 + .name = "max77705-charger", 405 + .type = POWER_SUPPLY_TYPE_USB, 406 + .properties = max77705_charger_props, 407 + .num_properties = ARRAY_SIZE(max77705_charger_props), 408 + .get_property = max77705_chg_get_property, 409 + }; 410 + 411 + static void max77705_chgin_isr_work(struct work_struct *work) 412 + { 413 + struct max77705_charger_data *charger = 414 + container_of(work, struct max77705_charger_data, chgin_work); 415 + 416 + power_supply_changed(charger->psy_chg); 417 + } 418 + 419 + static void max77705_charger_initialize(struct max77705_charger_data *chg) 420 + { 421 + u8 reg_data; 422 + struct power_supply_battery_info *info; 423 + struct regmap *regmap = chg->regmap; 424 + 425 + if (power_supply_get_battery_info(chg->psy_chg, &info) < 0) 426 + return; 427 + 428 + chg->bat_info = info; 429 + 430 + /* unlock charger setting protect */ 431 + /* slowest LX slope */ 432 + reg_data = MAX77705_CHGPROT_MASK | MAX77705_SLOWEST_LX_SLOPE; 433 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_06, reg_data, 434 + reg_data); 435 + 436 + /* fast charge timer disable */ 437 + /* restart threshold disable */ 438 + /* pre-qual charge disable */ 439 + reg_data = (MAX77705_FCHGTIME_DISABLE << MAX77705_FCHGTIME_SHIFT) | 440 + (MAX77705_CHG_RSTRT_DISABLE << MAX77705_CHG_RSTRT_SHIFT) | 441 + (MAX77705_CHG_PQEN_DISABLE << MAX77705_PQEN_SHIFT); 442 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_01, 443 + (MAX77705_FCHGTIME_MASK | 444 + MAX77705_CHG_RSTRT_MASK | 445 + MAX77705_PQEN_MASK), 446 + reg_data); 447 + 448 + /* OTG off(UNO on), boost off */ 449 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00, 450 + MAX77705_OTG_CTRL, 0); 451 + 452 + /* charge current 450mA(default) */ 453 + /* otg current limit 900mA */ 454 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_02, 455 + MAX77705_OTG_ILIM_MASK, 456 + MAX77705_OTG_ILIM_900 << MAX77705_OTG_ILIM_SHIFT); 457 + 458 + /* BAT to SYS OCP 4.80A */ 459 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_05, 460 + MAX77705_REG_B2SOVRC_MASK, 461 + MAX77705_B2SOVRC_4_8A << MAX77705_REG_B2SOVRC_SHIFT); 462 + /* top off current 150mA */ 463 + /* top off timer 30min */ 464 + reg_data = (MAX77705_TO_ITH_150MA << MAX77705_TO_ITH_SHIFT) | 465 + (MAX77705_TO_TIME_30M << MAX77705_TO_TIME_SHIFT) | 466 + (MAX77705_SYS_TRACK_DISABLE << MAX77705_SYS_TRACK_DIS_SHIFT); 467 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_03, 468 + (MAX77705_TO_ITH_MASK | 469 + MAX77705_TO_TIME_MASK | 470 + MAX77705_SYS_TRACK_DIS_MASK), reg_data); 471 + 472 + /* cv voltage 4.2V or 4.35V */ 473 + /* MINVSYS 3.6V(default) */ 474 + if (info->voltage_max_design_uv < 0) { 475 + dev_warn(chg->dev, "missing battery:voltage-max-design-microvolt\n"); 476 + max77705_set_float_voltage(chg, 4200000); 477 + } else { 478 + max77705_set_float_voltage(chg, info->voltage_max_design_uv); 479 + } 480 + 481 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, 482 + MAX77705_VCHGIN_REG_MASK, MAX77705_VCHGIN_4_5); 483 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, 484 + MAX77705_WCIN_REG_MASK, MAX77705_WCIN_4_5); 485 + 486 + /* Watchdog timer */ 487 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_00, 488 + MAX77705_WDTEN_MASK, 0); 489 + 490 + /* Active Discharge Enable */ 491 + regmap_update_bits(regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1); 492 + 493 + /* VBYPSET=5.0V */ 494 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_11, MAX77705_VBYPSET_MASK, 0); 495 + 496 + /* Switching Frequency : 1.5MHz */ 497 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_08, MAX77705_REG_FSW_MASK, 498 + (MAX77705_CHG_FSW_1_5MHz << MAX77705_REG_FSW_SHIFT)); 499 + 500 + /* Auto skip mode */ 501 + regmap_update_bits(regmap, MAX77705_CHG_REG_CNFG_12, MAX77705_REG_DISKIP_MASK, 502 + (MAX77705_AUTO_SKIP << MAX77705_REG_DISKIP_SHIFT)); 503 + } 504 + 505 + static int max77705_charger_probe(struct i2c_client *i2c) 506 + { 507 + struct power_supply_config pscfg = {}; 508 + struct max77705_charger_data *chg; 509 + struct device *dev; 510 + struct regmap_irq_chip_data *irq_data; 511 + int ret; 512 + 513 + dev = &i2c->dev; 514 + 515 + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); 516 + if (!chg) 517 + return -ENOMEM; 518 + 519 + chg->dev = dev; 520 + i2c_set_clientdata(i2c, chg); 521 + 522 + chg->regmap = devm_regmap_init_i2c(i2c, &max77705_chg_regmap_config); 523 + if (IS_ERR(chg->regmap)) 524 + return PTR_ERR(chg->regmap); 525 + 526 + ret = regmap_update_bits(chg->regmap, 527 + MAX77705_CHG_REG_INT_MASK, 528 + MAX77705_CHGIN_IM, 0); 529 + if (ret) 530 + return ret; 531 + 532 + pscfg.fwnode = dev_fwnode(dev); 533 + pscfg.drv_data = chg; 534 + 535 + chg->psy_chg = devm_power_supply_register(dev, &max77705_charger_psy_desc, &pscfg); 536 + if (IS_ERR(chg->psy_chg)) 537 + return PTR_ERR(chg->psy_chg); 538 + 539 + max77705_charger_irq_chip.irq_drv_data = chg; 540 + ret = devm_regmap_add_irq_chip(chg->dev, chg->regmap, i2c->irq, 541 + IRQF_ONESHOT | IRQF_SHARED, 0, 542 + &max77705_charger_irq_chip, 543 + &irq_data); 544 + if (ret) 545 + return dev_err_probe(dev, ret, "failed to add irq chip\n"); 546 + 547 + chg->wqueue = create_singlethread_workqueue(dev_name(dev)); 548 + if (IS_ERR(chg->wqueue)) 549 + return dev_err_probe(dev, PTR_ERR(chg->wqueue), "failed to create workqueue\n"); 550 + 551 + ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work); 552 + if (ret) 553 + return dev_err_probe(dev, ret, "failed to initialize interrupt work\n"); 554 + 555 + max77705_charger_initialize(chg); 556 + 557 + ret = max77705_charger_enable(chg); 558 + if (ret) 559 + return dev_err_probe(dev, ret, "failed to enable charge\n"); 560 + 561 + return devm_add_action_or_reset(dev, max77705_charger_disable, chg); 562 + } 563 + 564 + static const struct of_device_id max77705_charger_of_match[] = { 565 + { .compatible = "maxim,max77705-charger" }, 566 + { } 567 + }; 568 + MODULE_DEVICE_TABLE(of, max77705_charger_of_match); 569 + 570 + static struct i2c_driver max77705_charger_driver = { 571 + .driver = { 572 + .name = "max77705-charger", 573 + .of_match_table = max77705_charger_of_match, 574 + }, 575 + .probe = max77705_charger_probe, 576 + }; 577 + module_i2c_driver(max77705_charger_driver); 578 + 579 + MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>"); 580 + MODULE_DESCRIPTION("Maxim MAX77705 charger driver"); 581 + MODULE_LICENSE("GPL");
+195
include/linux/power/max77705_charger.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Maxim MAX77705 definitions. 4 + * 5 + * Copyright (C) 2015 Samsung Electronics, Inc. 6 + * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com> 7 + */ 8 + 9 + #ifndef __MAX77705_CHARGER_H 10 + #define __MAX77705_CHARGER_H __FILE__ 11 + 12 + /* MAX77705_CHG_REG_CHG_INT */ 13 + #define MAX77705_BYP_I BIT(0) 14 + #define MAX77705_INP_LIMIT_I BIT(1) 15 + #define MAX77705_BATP_I BIT(2) 16 + #define MAX77705_BAT_I BIT(3) 17 + #define MAX77705_CHG_I BIT(4) 18 + #define MAX77705_WCIN_I BIT(5) 19 + #define MAX77705_CHGIN_I BIT(6) 20 + #define MAX77705_AICL_I BIT(7) 21 + 22 + /* MAX77705_CHG_REG_CHG_INT_MASK */ 23 + #define MAX77705_BYP_IM BIT(0) 24 + #define MAX77705_INP_LIMIT_IM BIT(1) 25 + #define MAX77705_BATP_IM BIT(2) 26 + #define MAX77705_BAT_IM BIT(3) 27 + #define MAX77705_CHG_IM BIT(4) 28 + #define MAX77705_WCIN_IM BIT(5) 29 + #define MAX77705_CHGIN_IM BIT(6) 30 + #define MAX77705_AICL_IM BIT(7) 31 + 32 + /* MAX77705_CHG_REG_CHG_INT_OK */ 33 + #define MAX77705_BYP_OK BIT(0) 34 + #define MAX77705_DISQBAT_OK BIT(1) 35 + #define MAX77705_BATP_OK BIT(2) 36 + #define MAX77705_BAT_OK BIT(3) 37 + #define MAX77705_CHG_OK BIT(4) 38 + #define MAX77705_WCIN_OK BIT(5) 39 + #define MAX77705_CHGIN_OK BIT(6) 40 + #define MAX77705_AICL_OK BIT(7) 41 + 42 + /* MAX77705_CHG_REG_DETAILS_00 */ 43 + #define MAX77705_BATP_DTLS BIT(0) 44 + #define MAX77705_WCIN_DTLS GENMASK(4, 3) 45 + #define MAX77705_WCIN_DTLS_SHIFT 3 46 + #define MAX77705_CHGIN_DTLS GENMASK(6, 5) 47 + #define MAX77705_CHGIN_DTLS_SHIFT 5 48 + 49 + /* MAX77705_CHG_REG_DETAILS_01 */ 50 + #define MAX77705_CHG_DTLS GENMASK(3, 0) 51 + #define MAX77705_CHG_DTLS_SHIFT 0 52 + #define MAX77705_BAT_DTLS GENMASK(6, 4) 53 + #define MAX77705_BAT_DTLS_SHIFT 4 54 + 55 + /* MAX77705_CHG_REG_DETAILS_02 */ 56 + #define MAX77705_BYP_DTLS GENMASK(3, 0) 57 + #define MAX77705_BYP_DTLS_SHIFT 0 58 + 59 + /* MAX77705_CHG_REG_CNFG_00 */ 60 + #define MAX77705_CHG_SHIFT 0 61 + #define MAX77705_UNO_SHIFT 1 62 + #define MAX77705_OTG_SHIFT 1 63 + #define MAX77705_BUCK_SHIFT 2 64 + #define MAX77705_BOOST_SHIFT 3 65 + #define MAX77705_WDTEN_SHIFT 4 66 + #define MAX77705_MODE_MASK GENMASK(3, 0) 67 + #define MAX77705_CHG_MASK BIT(MAX77705_CHG_SHIFT) 68 + #define MAX77705_UNO_MASK BIT(MAX77705_UNO_SHIFT) 69 + #define MAX77705_OTG_MASK BIT(MAX77705_OTG_SHIFT) 70 + #define MAX77705_BUCK_MASK BIT(MAX77705_BUCK_SHIFT) 71 + #define MAX77705_BOOST_MASK BIT(MAX77705_BOOST_SHIFT) 72 + #define MAX77705_WDTEN_MASK BIT(MAX77705_WDTEN_SHIFT) 73 + #define MAX77705_UNO_CTRL (MAX77705_UNO_MASK | MAX77705_BOOST_MASK) 74 + #define MAX77705_OTG_CTRL (MAX77705_OTG_MASK | MAX77705_BOOST_MASK) 75 + 76 + /* MAX77705_CHG_REG_CNFG_01 */ 77 + #define MAX77705_FCHGTIME_SHIFT 0 78 + #define MAX77705_FCHGTIME_MASK GENMASK(2, 0) 79 + #define MAX77705_CHG_RSTRT_SHIFT 4 80 + #define MAX77705_CHG_RSTRT_MASK GENMASK(5, 4) 81 + #define MAX77705_FCHGTIME_DISABLE 0 82 + #define MAX77705_CHG_RSTRT_DISABLE 0x3 83 + 84 + #define MAX77705_PQEN_SHIFT 7 85 + #define MAX77705_PQEN_MASK BIT(7) 86 + #define MAX77705_CHG_PQEN_DISABLE 0 87 + #define MAX77705_CHG_PQEN_ENABLE 1 88 + 89 + /* MAX77705_CHG_REG_CNFG_02 */ 90 + #define MAX77705_OTG_ILIM_SHIFT 6 91 + #define MAX77705_OTG_ILIM_MASK GENMASK(7, 6) 92 + #define MAX77705_OTG_ILIM_500 0 93 + #define MAX77705_OTG_ILIM_900 1 94 + #define MAX77705_OTG_ILIM_1200 2 95 + #define MAX77705_OTG_ILIM_1500 3 96 + #define MAX77705_CHG_CC GENMASK(5, 0) 97 + 98 + /* MAX77705_CHG_REG_CNFG_03 */ 99 + #define MAX77705_TO_ITH_SHIFT 0 100 + #define MAX77705_TO_ITH_MASK GENMASK(2, 0) 101 + #define MAX77705_TO_TIME_SHIFT 3 102 + #define MAX77705_TO_TIME_MASK GENMASK(5, 3) 103 + #define MAX77705_SYS_TRACK_DIS_SHIFT 7 104 + #define MAX77705_SYS_TRACK_DIS_MASK BIT(7) 105 + #define MAX77705_TO_ITH_150MA 0 106 + #define MAX77705_TO_TIME_30M 3 107 + #define MAX77705_SYS_TRACK_ENABLE 0 108 + #define MAX77705_SYS_TRACK_DISABLE 1 109 + 110 + /* MAX77705_CHG_REG_CNFG_04 */ 111 + #define MAX77705_CHG_MINVSYS_SHIFT 6 112 + #define MAX77705_CHG_MINVSYS_MASK GENMASK(7, 6) 113 + #define MAX77705_CHG_PRM_SHIFT 0 114 + #define MAX77705_CHG_PRM_MASK GENMASK(5, 0) 115 + 116 + #define MAX77705_CHG_CV_PRM_SHIFT 0 117 + #define MAX77705_CHG_CV_PRM_MASK GENMASK(5, 0) 118 + 119 + /* MAX77705_CHG_REG_CNFG_05 */ 120 + #define MAX77705_REG_B2SOVRC_SHIFT 0 121 + #define MAX77705_REG_B2SOVRC_MASK GENMASK(3, 0) 122 + #define MAX77705_B2SOVRC_DISABLE 0 123 + #define MAX77705_B2SOVRC_4_5A 6 124 + #define MAX77705_B2SOVRC_4_8A 8 125 + #define MAX77705_B2SOVRC_5_0A 9 126 + 127 + /* MAX77705_CHG_CNFG_06 */ 128 + #define MAX77705_WDTCLR_SHIFT 0 129 + #define MAX77705_WDTCLR_MASK GENMASK(1, 0) 130 + #define MAX77705_WDTCLR 1 131 + #define MAX77705_CHGPROT_MASK GENMASK(3, 2) 132 + #define MAX77705_CHGPROT_UNLOCKED GENMASK(3, 2) 133 + #define MAX77705_SLOWEST_LX_SLOPE GENMASK(6, 5) 134 + 135 + /* MAX77705_CHG_REG_CNFG_07 */ 136 + #define MAX77705_CHG_FMBST 4 137 + #define MAX77705_REG_FMBST_SHIFT 2 138 + #define MAX77705_REG_FMBST_MASK BIT(MAX77705_REG_FMBST_SHIFT) 139 + #define MAX77705_REG_FGSRC_SHIFT 1 140 + #define MAX77705_REG_FGSRC_MASK BIT(MAX77705_REG_FGSRC_SHIFT) 141 + 142 + /* MAX77705_CHG_REG_CNFG_08 */ 143 + #define MAX77705_REG_FSW_SHIFT 0 144 + #define MAX77705_REG_FSW_MASK GENMASK(1, 0) 145 + #define MAX77705_CHG_FSW_3MHz 0 146 + #define MAX77705_CHG_FSW_2MHz 1 147 + #define MAX77705_CHG_FSW_1_5MHz 2 148 + 149 + /* MAX77705_CHG_REG_CNFG_09 */ 150 + #define MAX77705_CHG_CHGIN_LIM_MASK GENMASK(6, 0) 151 + #define MAX77705_CHG_EN_MASK BIT(7) 152 + #define MAX77705_CHG_DISABLE 0 153 + #define MAX77705_CHARGER_CHG_CHARGING(_reg) \ 154 + (((_reg) & MAX77705_CHG_EN_MASK) > 1) 155 + 156 + 157 + /* MAX77705_CHG_REG_CNFG_10 */ 158 + #define MAX77705_CHG_WCIN_LIM GENMASK(5, 0) 159 + 160 + /* MAX77705_CHG_REG_CNFG_11 */ 161 + #define MAX77705_VBYPSET_SHIFT 0 162 + #define MAX77705_VBYPSET_MASK GENMASK(6, 0) 163 + 164 + /* MAX77705_CHG_REG_CNFG_12 */ 165 + #define MAX77705_CHGINSEL_SHIFT 5 166 + #define MAX77705_CHGINSEL_MASK BIT(MAX77705_CHGINSEL_SHIFT) 167 + #define MAX77705_WCINSEL_SHIFT 6 168 + #define MAX77705_WCINSEL_MASK BIT(MAX77705_WCINSEL_SHIFT) 169 + #define MAX77705_VCHGIN_REG_MASK GENMASK(4, 3) 170 + #define MAX77705_WCIN_REG_MASK GENMASK(2, 1) 171 + #define MAX77705_REG_DISKIP_SHIFT 0 172 + #define MAX77705_REG_DISKIP_MASK BIT(MAX77705_REG_DISKIP_SHIFT) 173 + /* REG=4.5V, UVLO=4.7V */ 174 + #define MAX77705_VCHGIN_4_5 0 175 + /* REG=4.5V, UVLO=4.7V */ 176 + #define MAX77705_WCIN_4_5 0 177 + #define MAX77705_DISABLE_SKIP 1 178 + #define MAX77705_AUTO_SKIP 0 179 + 180 + /* uA */ 181 + #define MAX77705_CURRENT_CHGIN_STEP 25000 182 + #define MAX77705_CURRENT_CHG_STEP 50000 183 + #define MAX77705_CURRENT_CHGIN_MIN 100000 184 + #define MAX77705_CURRENT_CHGIN_MAX 3200000 185 + 186 + struct max77705_charger_data { 187 + struct device *dev; 188 + struct regmap *regmap; 189 + struct power_supply_battery_info *bat_info; 190 + struct workqueue_struct *wqueue; 191 + struct work_struct chgin_work; 192 + struct power_supply *psy_chg; 193 + }; 194 + 195 + #endif /* __MAX77705_CHARGER_H */