162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// max14577_charger.c - Battery charger driver for the Maxim 14577/77836 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2013,2014 Samsung Electronics 662306a36Sopenharmony_ci// Krzysztof Kozlowski <krzk@kernel.org> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/platform_device.h> 1062306a36Sopenharmony_ci#include <linux/power_supply.h> 1162306a36Sopenharmony_ci#include <linux/mfd/max14577-private.h> 1262306a36Sopenharmony_ci#include <linux/mfd/max14577.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistruct max14577_charger { 1562306a36Sopenharmony_ci struct device *dev; 1662306a36Sopenharmony_ci struct max14577 *max14577; 1762306a36Sopenharmony_ci struct power_supply *charger; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci struct max14577_charger_platform_data *pdata; 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * Helper function for mapping values of STATUS2/CHGTYP register on max14577 2462306a36Sopenharmony_ci * and max77836 chipsets to enum maxim_muic_charger_type. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_cistatic enum max14577_muic_charger_type maxim_get_charger_type( 2762306a36Sopenharmony_ci enum maxim_device_type dev_type, u8 val) { 2862306a36Sopenharmony_ci switch (val) { 2962306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_NONE: 3062306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_USB: 3162306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: 3262306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DEDICATED_CHG: 3362306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_SPECIAL_500MA: 3462306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_SPECIAL_1A: 3562306a36Sopenharmony_ci return val; 3662306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DEAD_BATTERY: 3762306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_RESERVED: 3862306a36Sopenharmony_ci if (dev_type == MAXIM_DEVICE_TYPE_MAX77836) 3962306a36Sopenharmony_ci val |= 0x8; 4062306a36Sopenharmony_ci return val; 4162306a36Sopenharmony_ci default: 4262306a36Sopenharmony_ci WARN_ONCE(1, "max14577: Unsupported chgtyp register value 0x%02x", val); 4362306a36Sopenharmony_ci return val; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int max14577_get_charger_state(struct max14577_charger *chg, int *val) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct regmap *rmap = chg->max14577->regmap; 5062306a36Sopenharmony_ci int ret; 5162306a36Sopenharmony_ci u8 reg_data; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * Charging occurs only if: 5562306a36Sopenharmony_ci * - CHGCTRL2/MBCHOSTEN == 1 5662306a36Sopenharmony_ci * - STATUS2/CGMBC == 1 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * TODO: 5962306a36Sopenharmony_ci * - handle FULL after Top-off timer (EOC register may be off 6062306a36Sopenharmony_ci * and the charger won't be charging although MBCHOSTEN is on) 6162306a36Sopenharmony_ci * - handle properly dead-battery charging (respect timer) 6262306a36Sopenharmony_ci * - handle timers (fast-charge and prequal) /MBCCHGERR/ 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci ret = max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL2, ®_data); 6562306a36Sopenharmony_ci if (ret < 0) 6662306a36Sopenharmony_ci goto out; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0) { 6962306a36Sopenharmony_ci *val = POWER_SUPPLY_STATUS_DISCHARGING; 7062306a36Sopenharmony_ci goto out; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, ®_data); 7462306a36Sopenharmony_ci if (ret < 0) 7562306a36Sopenharmony_ci goto out; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (reg_data & STATUS3_CGMBC_MASK) { 7862306a36Sopenharmony_ci /* Charger or USB-cable is connected */ 7962306a36Sopenharmony_ci if (reg_data & STATUS3_EOC_MASK) 8062306a36Sopenharmony_ci *val = POWER_SUPPLY_STATUS_FULL; 8162306a36Sopenharmony_ci else 8262306a36Sopenharmony_ci *val = POWER_SUPPLY_STATUS_CHARGING; 8362306a36Sopenharmony_ci goto out; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci *val = POWER_SUPPLY_STATUS_DISCHARGING; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciout: 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * Supported charge types: 9462306a36Sopenharmony_ci * - POWER_SUPPLY_CHARGE_TYPE_NONE 9562306a36Sopenharmony_ci * - POWER_SUPPLY_CHARGE_TYPE_FAST 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic int max14577_get_charge_type(struct max14577_charger *chg, int *val) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci int ret, charging; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * TODO: CHARGE_TYPE_TRICKLE (VCHGR_RC or EOC)? 10362306a36Sopenharmony_ci * As spec says: 10462306a36Sopenharmony_ci * [after reaching EOC interrupt] 10562306a36Sopenharmony_ci * "When the battery is fully charged, the 30-minute (typ) 10662306a36Sopenharmony_ci * top-off timer starts. The device continues to trickle 10762306a36Sopenharmony_ci * charge the battery until the top-off timer runs out." 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci ret = max14577_get_charger_state(chg, &charging); 11062306a36Sopenharmony_ci if (ret < 0) 11162306a36Sopenharmony_ci return ret; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (charging == POWER_SUPPLY_STATUS_CHARGING) 11462306a36Sopenharmony_ci *val = POWER_SUPPLY_CHARGE_TYPE_FAST; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci *val = POWER_SUPPLY_CHARGE_TYPE_NONE; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int max14577_get_online(struct max14577_charger *chg, int *val) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct regmap *rmap = chg->max14577->regmap; 12462306a36Sopenharmony_ci u8 reg_data; 12562306a36Sopenharmony_ci int ret; 12662306a36Sopenharmony_ci enum max14577_muic_charger_type chg_type; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); 12962306a36Sopenharmony_ci if (ret < 0) 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT); 13362306a36Sopenharmony_ci chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data); 13462306a36Sopenharmony_ci switch (chg_type) { 13562306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_USB: 13662306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DEDICATED_CHG: 13762306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_SPECIAL_500MA: 13862306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_SPECIAL_1A: 13962306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DEAD_BATTERY: 14062306a36Sopenharmony_ci case MAX77836_CHARGER_TYPE_SPECIAL_BIAS: 14162306a36Sopenharmony_ci *val = 1; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_NONE: 14462306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: 14562306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_RESERVED: 14662306a36Sopenharmony_ci case MAX77836_CHARGER_TYPE_RESERVED: 14762306a36Sopenharmony_ci default: 14862306a36Sopenharmony_ci *val = 0; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * Supported health statuses: 15662306a36Sopenharmony_ci * - POWER_SUPPLY_HEALTH_DEAD 15762306a36Sopenharmony_ci * - POWER_SUPPLY_HEALTH_OVERVOLTAGE 15862306a36Sopenharmony_ci * - POWER_SUPPLY_HEALTH_GOOD 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_cistatic int max14577_get_battery_health(struct max14577_charger *chg, int *val) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct regmap *rmap = chg->max14577->regmap; 16362306a36Sopenharmony_ci int ret; 16462306a36Sopenharmony_ci u8 reg_data; 16562306a36Sopenharmony_ci enum max14577_muic_charger_type chg_type; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); 16862306a36Sopenharmony_ci if (ret < 0) 16962306a36Sopenharmony_ci goto out; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT); 17262306a36Sopenharmony_ci chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data); 17362306a36Sopenharmony_ci if (chg_type == MAX14577_CHARGER_TYPE_DEAD_BATTERY) { 17462306a36Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_DEAD; 17562306a36Sopenharmony_ci goto out; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, ®_data); 17962306a36Sopenharmony_ci if (ret < 0) 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (reg_data & STATUS3_OVP_MASK) { 18362306a36Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Not dead, not overvoltage */ 18862306a36Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_GOOD; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciout: 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * Always returns 1. 19662306a36Sopenharmony_ci * The max14577 chip doesn't report any status of battery presence. 19762306a36Sopenharmony_ci * Lets assume that it will always be used with some battery. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_cistatic int max14577_get_present(struct max14577_charger *chg, int *val) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci *val = 1; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int max14577_set_fast_charge_timer(struct max14577_charger *chg, 20762306a36Sopenharmony_ci unsigned long hours) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci u8 reg_data; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci switch (hours) { 21262306a36Sopenharmony_ci case 5 ... 7: 21362306a36Sopenharmony_ci reg_data = hours - 3; 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci case 0: 21662306a36Sopenharmony_ci /* Disable */ 21762306a36Sopenharmony_ci reg_data = 0x7; 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci default: 22062306a36Sopenharmony_ci dev_err(chg->dev, "Wrong value for Fast-Charge Timer: %lu\n", 22162306a36Sopenharmony_ci hours); 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci reg_data <<= CHGCTRL1_TCHW_SHIFT; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return max14577_update_reg(chg->max14577->regmap, 22762306a36Sopenharmony_ci MAX14577_REG_CHGCTRL1, CHGCTRL1_TCHW_MASK, reg_data); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int max14577_init_constant_voltage(struct max14577_charger *chg, 23162306a36Sopenharmony_ci unsigned int uvolt) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci u8 reg_data; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (uvolt < MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN || 23662306a36Sopenharmony_ci uvolt > MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX) 23762306a36Sopenharmony_ci return -EINVAL; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (uvolt == 4200000) 24062306a36Sopenharmony_ci reg_data = 0x0; 24162306a36Sopenharmony_ci else if (uvolt == MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX) 24262306a36Sopenharmony_ci reg_data = 0x1f; 24362306a36Sopenharmony_ci else if (uvolt <= 4280000) { 24462306a36Sopenharmony_ci unsigned int val = uvolt; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci val -= MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN; 24762306a36Sopenharmony_ci val /= MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP; 24862306a36Sopenharmony_ci if (uvolt <= 4180000) 24962306a36Sopenharmony_ci reg_data = 0x1 + val; 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci reg_data = val; /* Fix for gap between 4.18V and 4.22V */ 25262306a36Sopenharmony_ci } else 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci reg_data <<= CHGCTRL3_MBCCVWRC_SHIFT; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return max14577_write_reg(chg->max14577->regmap, 25862306a36Sopenharmony_ci MAX14577_CHG_REG_CHG_CTRL3, reg_data); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int max14577_init_eoc(struct max14577_charger *chg, 26262306a36Sopenharmony_ci unsigned int uamp) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci unsigned int current_bits; 26562306a36Sopenharmony_ci u8 reg_data; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci switch (chg->max14577->dev_type) { 26862306a36Sopenharmony_ci case MAXIM_DEVICE_TYPE_MAX77836: 26962306a36Sopenharmony_ci if (uamp < 5000) 27062306a36Sopenharmony_ci return -EINVAL; /* Requested current is too low */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (uamp >= 7500 && uamp < 10000) 27362306a36Sopenharmony_ci current_bits = 0x0; 27462306a36Sopenharmony_ci else if (uamp <= 50000) { 27562306a36Sopenharmony_ci /* <5000, 7499> and <10000, 50000> */ 27662306a36Sopenharmony_ci current_bits = uamp / 5000; 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci uamp = min(uamp, 100000U) - 50000U; 27962306a36Sopenharmony_ci current_bits = 0xa + uamp / 10000; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci case MAXIM_DEVICE_TYPE_MAX14577: 28462306a36Sopenharmony_ci default: 28562306a36Sopenharmony_ci if (uamp < MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN) 28662306a36Sopenharmony_ci return -EINVAL; /* Requested current is too low */ 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci uamp = min(uamp, MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX); 28962306a36Sopenharmony_ci uamp -= MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN; 29062306a36Sopenharmony_ci current_bits = uamp / MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP; 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci reg_data = current_bits << CHGCTRL5_EOCS_SHIFT; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return max14577_update_reg(chg->max14577->regmap, 29762306a36Sopenharmony_ci MAX14577_CHG_REG_CHG_CTRL5, CHGCTRL5_EOCS_MASK, 29862306a36Sopenharmony_ci reg_data); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int max14577_init_fast_charge(struct max14577_charger *chg, 30262306a36Sopenharmony_ci unsigned int uamp) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci u8 reg_data; 30562306a36Sopenharmony_ci int ret; 30662306a36Sopenharmony_ci const struct maxim_charger_current *limits = 30762306a36Sopenharmony_ci &maxim_charger_currents[chg->max14577->dev_type]; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ret = maxim_charger_calc_reg_current(limits, uamp, uamp, ®_data); 31062306a36Sopenharmony_ci if (ret) { 31162306a36Sopenharmony_ci dev_err(chg->dev, "Wrong value for fast charge: %u\n", uamp); 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return max14577_update_reg(chg->max14577->regmap, 31662306a36Sopenharmony_ci MAX14577_CHG_REG_CHG_CTRL4, 31762306a36Sopenharmony_ci CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK, 31862306a36Sopenharmony_ci reg_data); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* 32262306a36Sopenharmony_ci * Sets charger registers to proper and safe default values. 32362306a36Sopenharmony_ci * Some of these values are equal to defaults in MAX14577E 32462306a36Sopenharmony_ci * data sheet but there are minor differences. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_cistatic int max14577_charger_reg_init(struct max14577_charger *chg) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct regmap *rmap = chg->max14577->regmap; 32962306a36Sopenharmony_ci u8 reg_data; 33062306a36Sopenharmony_ci int ret; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* 33362306a36Sopenharmony_ci * Charger-Type Manual Detection, default off (set CHGTYPMAN to 0) 33462306a36Sopenharmony_ci * Charger-Detection Enable, default on (set CHGDETEN to 1) 33562306a36Sopenharmony_ci * Combined mask of CHGDETEN and CHGTYPMAN will zero the CHGTYPMAN bit 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci reg_data = 0x1 << CDETCTRL1_CHGDETEN_SHIFT; 33862306a36Sopenharmony_ci max14577_update_reg(rmap, MAX14577_REG_CDETCTRL1, 33962306a36Sopenharmony_ci CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK, 34062306a36Sopenharmony_ci reg_data); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * Wall-Adapter Rapid Charge, default on 34462306a36Sopenharmony_ci * Battery-Charger, default on 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci reg_data = 0x1 << CHGCTRL2_VCHGR_RC_SHIFT; 34762306a36Sopenharmony_ci reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT; 34862306a36Sopenharmony_ci max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Auto Charging Stop, default off */ 35162306a36Sopenharmony_ci reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT; 35262306a36Sopenharmony_ci max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ret = max14577_init_constant_voltage(chg, chg->pdata->constant_uvolt); 35562306a36Sopenharmony_ci if (ret) 35662306a36Sopenharmony_ci return ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = max14577_init_eoc(chg, chg->pdata->eoc_uamp); 35962306a36Sopenharmony_ci if (ret) 36062306a36Sopenharmony_ci return ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret = max14577_init_fast_charge(chg, chg->pdata->fast_charge_uamp); 36362306a36Sopenharmony_ci if (ret) 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci ret = max14577_set_fast_charge_timer(chg, 36762306a36Sopenharmony_ci MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT); 36862306a36Sopenharmony_ci if (ret) 36962306a36Sopenharmony_ci return ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Initialize Overvoltage-Protection Threshold */ 37262306a36Sopenharmony_ci switch (chg->pdata->ovp_uvolt) { 37362306a36Sopenharmony_ci case 7500000: 37462306a36Sopenharmony_ci reg_data = 0x0; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci case 6000000: 37762306a36Sopenharmony_ci case 6500000: 37862306a36Sopenharmony_ci case 7000000: 37962306a36Sopenharmony_ci reg_data = 0x1 + (chg->pdata->ovp_uvolt - 6000000) / 500000; 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci default: 38262306a36Sopenharmony_ci dev_err(chg->dev, "Wrong value for OVP: %u\n", 38362306a36Sopenharmony_ci chg->pdata->ovp_uvolt); 38462306a36Sopenharmony_ci return -EINVAL; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci reg_data <<= CHGCTRL7_OTPCGHCVS_SHIFT; 38762306a36Sopenharmony_ci max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* Support property from charger */ 39362306a36Sopenharmony_cistatic enum power_supply_property max14577_charger_props[] = { 39462306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 39562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 39662306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 39762306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 39862306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 39962306a36Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 40062306a36Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 40162306a36Sopenharmony_ci}; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic const char * const model_names[] = { 40462306a36Sopenharmony_ci [MAXIM_DEVICE_TYPE_UNKNOWN] = "MAX14577-like", 40562306a36Sopenharmony_ci [MAXIM_DEVICE_TYPE_MAX14577] = "MAX14577", 40662306a36Sopenharmony_ci [MAXIM_DEVICE_TYPE_MAX77836] = "MAX77836", 40762306a36Sopenharmony_ci}; 40862306a36Sopenharmony_cistatic const char *manufacturer = "Maxim Integrated"; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int max14577_charger_get_property(struct power_supply *psy, 41162306a36Sopenharmony_ci enum power_supply_property psp, 41262306a36Sopenharmony_ci union power_supply_propval *val) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct max14577_charger *chg = power_supply_get_drvdata(psy); 41562306a36Sopenharmony_ci int ret = 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci switch (psp) { 41862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 41962306a36Sopenharmony_ci ret = max14577_get_charger_state(chg, &val->intval); 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 42262306a36Sopenharmony_ci ret = max14577_get_charge_type(chg, &val->intval); 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 42562306a36Sopenharmony_ci ret = max14577_get_battery_health(chg, &val->intval); 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 42862306a36Sopenharmony_ci ret = max14577_get_present(chg, &val->intval); 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 43162306a36Sopenharmony_ci ret = max14577_get_online(chg, &val->intval); 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME: 43462306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(model_names) != MAXIM_DEVICE_TYPE_NUM); 43562306a36Sopenharmony_ci val->strval = model_names[chg->max14577->dev_type]; 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_MANUFACTURER: 43862306a36Sopenharmony_ci val->strval = manufacturer; 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci default: 44162306a36Sopenharmony_ci return -EINVAL; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic const struct power_supply_desc max14577_charger_desc = { 44862306a36Sopenharmony_ci .name = "max14577-charger", 44962306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 45062306a36Sopenharmony_ci .properties = max14577_charger_props, 45162306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(max14577_charger_props), 45262306a36Sopenharmony_ci .get_property = max14577_charger_get_property, 45362306a36Sopenharmony_ci}; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci#ifdef CONFIG_OF 45662306a36Sopenharmony_cistatic struct max14577_charger_platform_data *max14577_charger_dt_init( 45762306a36Sopenharmony_ci struct platform_device *pdev) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct max14577_charger_platform_data *pdata; 46062306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 46162306a36Sopenharmony_ci int ret; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (!np) { 46462306a36Sopenharmony_ci dev_err(&pdev->dev, "No charger OF node\n"); 46562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 46962306a36Sopenharmony_ci if (!pdata) 47062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ret = of_property_read_u32(np, "maxim,constant-uvolt", 47362306a36Sopenharmony_ci &pdata->constant_uvolt); 47462306a36Sopenharmony_ci if (ret) { 47562306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot parse maxim,constant-uvolt field from DT\n"); 47662306a36Sopenharmony_ci return ERR_PTR(ret); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci ret = of_property_read_u32(np, "maxim,fast-charge-uamp", 48062306a36Sopenharmony_ci &pdata->fast_charge_uamp); 48162306a36Sopenharmony_ci if (ret) { 48262306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot parse maxim,fast-charge-uamp field from DT\n"); 48362306a36Sopenharmony_ci return ERR_PTR(ret); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci ret = of_property_read_u32(np, "maxim,eoc-uamp", &pdata->eoc_uamp); 48762306a36Sopenharmony_ci if (ret) { 48862306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot parse maxim,eoc-uamp field from DT\n"); 48962306a36Sopenharmony_ci return ERR_PTR(ret); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret = of_property_read_u32(np, "maxim,ovp-uvolt", &pdata->ovp_uvolt); 49362306a36Sopenharmony_ci if (ret) { 49462306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot parse maxim,ovp-uvolt field from DT\n"); 49562306a36Sopenharmony_ci return ERR_PTR(ret); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return pdata; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci#else /* CONFIG_OF */ 50162306a36Sopenharmony_cistatic struct max14577_charger_platform_data *max14577_charger_dt_init( 50262306a36Sopenharmony_ci struct platform_device *pdev) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci return NULL; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci#endif /* CONFIG_OF */ 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic ssize_t show_fast_charge_timer(struct device *dev, 50962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct max14577_charger *chg = dev_get_drvdata(dev); 51262306a36Sopenharmony_ci u8 reg_data; 51362306a36Sopenharmony_ci int ret; 51462306a36Sopenharmony_ci unsigned int val; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ret = max14577_read_reg(chg->max14577->regmap, MAX14577_REG_CHGCTRL1, 51762306a36Sopenharmony_ci ®_data); 51862306a36Sopenharmony_ci if (ret) 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci reg_data &= CHGCTRL1_TCHW_MASK; 52262306a36Sopenharmony_ci reg_data >>= CHGCTRL1_TCHW_SHIFT; 52362306a36Sopenharmony_ci switch (reg_data) { 52462306a36Sopenharmony_ci case 0x2 ... 0x4: 52562306a36Sopenharmony_ci val = reg_data + 3; 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci case 0x7: 52862306a36Sopenharmony_ci val = 0; 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci default: 53162306a36Sopenharmony_ci val = 5; 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", val); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic ssize_t store_fast_charge_timer(struct device *dev, 53962306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct max14577_charger *chg = dev_get_drvdata(dev); 54262306a36Sopenharmony_ci unsigned long val; 54362306a36Sopenharmony_ci int ret; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci ret = kstrtoul(buf, 10, &val); 54662306a36Sopenharmony_ci if (ret) 54762306a36Sopenharmony_ci return ret; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci ret = max14577_set_fast_charge_timer(chg, val); 55062306a36Sopenharmony_ci if (ret) 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return count; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic DEVICE_ATTR(fast_charge_timer, S_IRUGO | S_IWUSR, 55762306a36Sopenharmony_ci show_fast_charge_timer, store_fast_charge_timer); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int max14577_charger_probe(struct platform_device *pdev) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct max14577_charger *chg; 56262306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 56362306a36Sopenharmony_ci struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent); 56462306a36Sopenharmony_ci int ret; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); 56762306a36Sopenharmony_ci if (!chg) 56862306a36Sopenharmony_ci return -ENOMEM; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci platform_set_drvdata(pdev, chg); 57162306a36Sopenharmony_ci chg->dev = &pdev->dev; 57262306a36Sopenharmony_ci chg->max14577 = max14577; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci chg->pdata = max14577_charger_dt_init(pdev); 57562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(chg->pdata)) 57662306a36Sopenharmony_ci return PTR_ERR(chg->pdata); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci ret = max14577_charger_reg_init(chg); 57962306a36Sopenharmony_ci if (ret) 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer); 58362306a36Sopenharmony_ci if (ret) { 58462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed: create sysfs entry\n"); 58562306a36Sopenharmony_ci return ret; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci psy_cfg.drv_data = chg; 58962306a36Sopenharmony_ci chg->charger = power_supply_register(&pdev->dev, &max14577_charger_desc, 59062306a36Sopenharmony_ci &psy_cfg); 59162306a36Sopenharmony_ci if (IS_ERR(chg->charger)) { 59262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed: power supply register\n"); 59362306a36Sopenharmony_ci ret = PTR_ERR(chg->charger); 59462306a36Sopenharmony_ci goto err; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Check for valid values for charger */ 59862306a36Sopenharmony_ci BUILD_BUG_ON(MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN + 59962306a36Sopenharmony_ci MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP * 0xf != 60062306a36Sopenharmony_ci MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX); 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cierr: 60462306a36Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return ret; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int max14577_charger_remove(struct platform_device *pdev) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct max14577_charger *chg = platform_get_drvdata(pdev); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); 61462306a36Sopenharmony_ci power_supply_unregister(chg->charger); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic const struct platform_device_id max14577_charger_id[] = { 62062306a36Sopenharmony_ci { "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, }, 62162306a36Sopenharmony_ci { "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, }, 62262306a36Sopenharmony_ci { } 62362306a36Sopenharmony_ci}; 62462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, max14577_charger_id); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic const struct of_device_id of_max14577_charger_dt_match[] = { 62762306a36Sopenharmony_ci { .compatible = "maxim,max14577-charger", 62862306a36Sopenharmony_ci .data = (void *)MAXIM_DEVICE_TYPE_MAX14577, }, 62962306a36Sopenharmony_ci { .compatible = "maxim,max77836-charger", 63062306a36Sopenharmony_ci .data = (void *)MAXIM_DEVICE_TYPE_MAX77836, }, 63162306a36Sopenharmony_ci { }, 63262306a36Sopenharmony_ci}; 63362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_max14577_charger_dt_match); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic struct platform_driver max14577_charger_driver = { 63662306a36Sopenharmony_ci .driver = { 63762306a36Sopenharmony_ci .name = "max14577-charger", 63862306a36Sopenharmony_ci .of_match_table = of_max14577_charger_dt_match, 63962306a36Sopenharmony_ci }, 64062306a36Sopenharmony_ci .probe = max14577_charger_probe, 64162306a36Sopenharmony_ci .remove = max14577_charger_remove, 64262306a36Sopenharmony_ci .id_table = max14577_charger_id, 64362306a36Sopenharmony_ci}; 64462306a36Sopenharmony_cimodule_platform_driver(max14577_charger_driver); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ciMODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); 64762306a36Sopenharmony_ciMODULE_DESCRIPTION("Maxim 14577/77836 charger driver"); 64862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 649