162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// max17040_battery.c 462306a36Sopenharmony_ci// fuel-gauge systems for lithium-ion (Li+) batteries 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright (C) 2009 Samsung Electronics 762306a36Sopenharmony_ci// Minkyu Kang <mk7.kang@samsung.com> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/mutex.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/i2c.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/power_supply.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/regmap.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MAX17040_VCELL 0x02 2362306a36Sopenharmony_ci#define MAX17040_SOC 0x04 2462306a36Sopenharmony_ci#define MAX17040_MODE 0x06 2562306a36Sopenharmony_ci#define MAX17040_VER 0x08 2662306a36Sopenharmony_ci#define MAX17040_CONFIG 0x0C 2762306a36Sopenharmony_ci#define MAX17040_STATUS 0x1A 2862306a36Sopenharmony_ci#define MAX17040_CMD 0xFE 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MAX17040_DELAY 1000 3262306a36Sopenharmony_ci#define MAX17040_BATTERY_FULL 95 3362306a36Sopenharmony_ci#define MAX17040_RCOMP_DEFAULT 0x9700 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MAX17040_ATHD_MASK 0x3f 3662306a36Sopenharmony_ci#define MAX17040_ALSC_MASK 0x40 3762306a36Sopenharmony_ci#define MAX17040_ATHD_DEFAULT_POWER_UP 4 3862306a36Sopenharmony_ci#define MAX17040_STATUS_HD_MASK 0x1000 3962306a36Sopenharmony_ci#define MAX17040_STATUS_SC_MASK 0x2000 4062306a36Sopenharmony_ci#define MAX17040_CFG_RCOMP_MASK 0xff00 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cienum chip_id { 4362306a36Sopenharmony_ci ID_MAX17040, 4462306a36Sopenharmony_ci ID_MAX17041, 4562306a36Sopenharmony_ci ID_MAX17043, 4662306a36Sopenharmony_ci ID_MAX17044, 4762306a36Sopenharmony_ci ID_MAX17048, 4862306a36Sopenharmony_ci ID_MAX17049, 4962306a36Sopenharmony_ci ID_MAX17058, 5062306a36Sopenharmony_ci ID_MAX17059, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* values that differ by chip_id */ 5462306a36Sopenharmony_cistruct chip_data { 5562306a36Sopenharmony_ci u16 reset_val; 5662306a36Sopenharmony_ci u16 vcell_shift; 5762306a36Sopenharmony_ci u16 vcell_mul; 5862306a36Sopenharmony_ci u16 vcell_div; 5962306a36Sopenharmony_ci u8 has_low_soc_alert; 6062306a36Sopenharmony_ci u8 rcomp_bytes; 6162306a36Sopenharmony_ci u8 has_soc_alert; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct chip_data max17040_family[] = { 6562306a36Sopenharmony_ci [ID_MAX17040] = { 6662306a36Sopenharmony_ci .reset_val = 0x0054, 6762306a36Sopenharmony_ci .vcell_shift = 4, 6862306a36Sopenharmony_ci .vcell_mul = 1250, 6962306a36Sopenharmony_ci .vcell_div = 1, 7062306a36Sopenharmony_ci .has_low_soc_alert = 0, 7162306a36Sopenharmony_ci .rcomp_bytes = 2, 7262306a36Sopenharmony_ci .has_soc_alert = 0, 7362306a36Sopenharmony_ci }, 7462306a36Sopenharmony_ci [ID_MAX17041] = { 7562306a36Sopenharmony_ci .reset_val = 0x0054, 7662306a36Sopenharmony_ci .vcell_shift = 4, 7762306a36Sopenharmony_ci .vcell_mul = 2500, 7862306a36Sopenharmony_ci .vcell_div = 1, 7962306a36Sopenharmony_ci .has_low_soc_alert = 0, 8062306a36Sopenharmony_ci .rcomp_bytes = 2, 8162306a36Sopenharmony_ci .has_soc_alert = 0, 8262306a36Sopenharmony_ci }, 8362306a36Sopenharmony_ci [ID_MAX17043] = { 8462306a36Sopenharmony_ci .reset_val = 0x0054, 8562306a36Sopenharmony_ci .vcell_shift = 4, 8662306a36Sopenharmony_ci .vcell_mul = 1250, 8762306a36Sopenharmony_ci .vcell_div = 1, 8862306a36Sopenharmony_ci .has_low_soc_alert = 1, 8962306a36Sopenharmony_ci .rcomp_bytes = 1, 9062306a36Sopenharmony_ci .has_soc_alert = 0, 9162306a36Sopenharmony_ci }, 9262306a36Sopenharmony_ci [ID_MAX17044] = { 9362306a36Sopenharmony_ci .reset_val = 0x0054, 9462306a36Sopenharmony_ci .vcell_shift = 4, 9562306a36Sopenharmony_ci .vcell_mul = 2500, 9662306a36Sopenharmony_ci .vcell_div = 1, 9762306a36Sopenharmony_ci .has_low_soc_alert = 1, 9862306a36Sopenharmony_ci .rcomp_bytes = 1, 9962306a36Sopenharmony_ci .has_soc_alert = 0, 10062306a36Sopenharmony_ci }, 10162306a36Sopenharmony_ci [ID_MAX17048] = { 10262306a36Sopenharmony_ci .reset_val = 0x5400, 10362306a36Sopenharmony_ci .vcell_shift = 0, 10462306a36Sopenharmony_ci .vcell_mul = 625, 10562306a36Sopenharmony_ci .vcell_div = 8, 10662306a36Sopenharmony_ci .has_low_soc_alert = 1, 10762306a36Sopenharmony_ci .rcomp_bytes = 1, 10862306a36Sopenharmony_ci .has_soc_alert = 1, 10962306a36Sopenharmony_ci }, 11062306a36Sopenharmony_ci [ID_MAX17049] = { 11162306a36Sopenharmony_ci .reset_val = 0x5400, 11262306a36Sopenharmony_ci .vcell_shift = 0, 11362306a36Sopenharmony_ci .vcell_mul = 625, 11462306a36Sopenharmony_ci .vcell_div = 4, 11562306a36Sopenharmony_ci .has_low_soc_alert = 1, 11662306a36Sopenharmony_ci .rcomp_bytes = 1, 11762306a36Sopenharmony_ci .has_soc_alert = 1, 11862306a36Sopenharmony_ci }, 11962306a36Sopenharmony_ci [ID_MAX17058] = { 12062306a36Sopenharmony_ci .reset_val = 0x5400, 12162306a36Sopenharmony_ci .vcell_shift = 0, 12262306a36Sopenharmony_ci .vcell_mul = 625, 12362306a36Sopenharmony_ci .vcell_div = 8, 12462306a36Sopenharmony_ci .has_low_soc_alert = 1, 12562306a36Sopenharmony_ci .rcomp_bytes = 1, 12662306a36Sopenharmony_ci .has_soc_alert = 0, 12762306a36Sopenharmony_ci }, 12862306a36Sopenharmony_ci [ID_MAX17059] = { 12962306a36Sopenharmony_ci .reset_val = 0x5400, 13062306a36Sopenharmony_ci .vcell_shift = 0, 13162306a36Sopenharmony_ci .vcell_mul = 625, 13262306a36Sopenharmony_ci .vcell_div = 4, 13362306a36Sopenharmony_ci .has_low_soc_alert = 1, 13462306a36Sopenharmony_ci .rcomp_bytes = 1, 13562306a36Sopenharmony_ci .has_soc_alert = 0, 13662306a36Sopenharmony_ci }, 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistruct max17040_chip { 14062306a36Sopenharmony_ci struct i2c_client *client; 14162306a36Sopenharmony_ci struct regmap *regmap; 14262306a36Sopenharmony_ci struct delayed_work work; 14362306a36Sopenharmony_ci struct power_supply *battery; 14462306a36Sopenharmony_ci struct chip_data data; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* battery capacity */ 14762306a36Sopenharmony_ci int soc; 14862306a36Sopenharmony_ci /* Low alert threshold from 32% to 1% of the State of Charge */ 14962306a36Sopenharmony_ci u32 low_soc_alert; 15062306a36Sopenharmony_ci /* some devices return twice the capacity */ 15162306a36Sopenharmony_ci bool quirk_double_soc; 15262306a36Sopenharmony_ci /* higher 8 bits for 17043+, 16 bits for 17040,41 */ 15362306a36Sopenharmony_ci u16 rcomp; 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int max17040_reset(struct max17040_chip *chip) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci level = 32 - level * (chip->quirk_double_soc ? 2 : 1); 16462306a36Sopenharmony_ci return regmap_update_bits(chip->regmap, MAX17040_CONFIG, 16562306a36Sopenharmony_ci MAX17040_ATHD_MASK, level); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int max17040_set_soc_alert(struct max17040_chip *chip, bool enable) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci return regmap_update_bits(chip->regmap, MAX17040_CONFIG, 17162306a36Sopenharmony_ci MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci u16 mask = chip->data.rcomp_bytes == 2 ? 17762306a36Sopenharmony_ci 0xffff : MAX17040_CFG_RCOMP_MASK; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return regmap_update_bits(chip->regmap, MAX17040_CONFIG, mask, rcomp); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct chip_data *d = &chip->data; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int max17040_get_vcell(struct max17040_chip *chip) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci u32 vcell; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci regmap_read(chip->regmap, MAX17040_VCELL, &vcell); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return max17040_raw_vcell_to_uvolts(chip, vcell); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int max17040_get_soc(struct max17040_chip *chip) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci u32 soc; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci regmap_read(chip->regmap, MAX17040_SOC, &soc); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return soc >> (chip->quirk_double_soc ? 9 : 8); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int max17040_get_version(struct max17040_chip *chip) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci int ret; 21162306a36Sopenharmony_ci u32 version; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci ret = regmap_read(chip->regmap, MAX17040_VER, &version); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return ret ? ret : version; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int max17040_get_online(struct max17040_chip *chip) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci return 1; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int max17040_get_of_data(struct max17040_chip *chip) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct device *dev = &chip->client->dev; 22662306a36Sopenharmony_ci struct chip_data *data = &max17040_family[ 22762306a36Sopenharmony_ci (uintptr_t) of_device_get_match_data(dev)]; 22862306a36Sopenharmony_ci int rcomp_len; 22962306a36Sopenharmony_ci u8 rcomp[2]; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci chip->quirk_double_soc = device_property_read_bool(dev, 23262306a36Sopenharmony_ci "maxim,double-soc"); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP; 23562306a36Sopenharmony_ci device_property_read_u32(dev, 23662306a36Sopenharmony_ci "maxim,alert-low-soc-level", 23762306a36Sopenharmony_ci &chip->low_soc_alert); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (chip->low_soc_alert <= 0 || 24062306a36Sopenharmony_ci chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) { 24162306a36Sopenharmony_ci dev_err(dev, "maxim,alert-low-soc-level out of bounds\n"); 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci rcomp_len = device_property_count_u8(dev, "maxim,rcomp"); 24662306a36Sopenharmony_ci chip->rcomp = MAX17040_RCOMP_DEFAULT; 24762306a36Sopenharmony_ci if (rcomp_len == data->rcomp_bytes) { 24862306a36Sopenharmony_ci if (!device_property_read_u8_array(dev, "maxim,rcomp", 24962306a36Sopenharmony_ci rcomp, rcomp_len)) 25062306a36Sopenharmony_ci chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] : 25162306a36Sopenharmony_ci rcomp[0] << 8; 25262306a36Sopenharmony_ci } else if (rcomp_len > 0) { 25362306a36Sopenharmony_ci dev_err(dev, "maxim,rcomp has incorrect length\n"); 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void max17040_check_changes(struct max17040_chip *chip) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci chip->soc = max17040_get_soc(chip); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void max17040_queue_work(struct max17040_chip *chip) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &chip->work, 26862306a36Sopenharmony_ci MAX17040_DELAY); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void max17040_stop_work(void *data) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct max17040_chip *chip = data; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci cancel_delayed_work_sync(&chip->work); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void max17040_work(struct work_struct *work) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct max17040_chip *chip; 28162306a36Sopenharmony_ci int last_soc; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci chip = container_of(work, struct max17040_chip, work.work); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* store SOC to check changes */ 28662306a36Sopenharmony_ci last_soc = chip->soc; 28762306a36Sopenharmony_ci max17040_check_changes(chip); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* check changes and send uevent */ 29062306a36Sopenharmony_ci if (last_soc != chip->soc) 29162306a36Sopenharmony_ci power_supply_changed(chip->battery); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci max17040_queue_work(chip); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* Returns true if alert cause was SOC change, not low SOC */ 29762306a36Sopenharmony_cistatic bool max17040_handle_soc_alert(struct max17040_chip *chip) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci bool ret = true; 30062306a36Sopenharmony_ci u32 data; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci regmap_read(chip->regmap, MAX17040_STATUS, &data); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (data & MAX17040_STATUS_HD_MASK) { 30562306a36Sopenharmony_ci // this alert was caused by low soc 30662306a36Sopenharmony_ci ret = false; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci if (data & MAX17040_STATUS_SC_MASK) { 30962306a36Sopenharmony_ci // soc change bit -- deassert to mark as handled 31062306a36Sopenharmony_ci regmap_write(chip->regmap, MAX17040_STATUS, 31162306a36Sopenharmony_ci data & ~MAX17040_STATUS_SC_MASK); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic irqreturn_t max17040_thread_handler(int id, void *dev) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct max17040_chip *chip = dev; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip))) 32262306a36Sopenharmony_ci dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n"); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* read registers */ 32562306a36Sopenharmony_ci max17040_check_changes(chip); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* send uevent */ 32862306a36Sopenharmony_ci power_supply_changed(chip->battery); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* reset alert bit */ 33162306a36Sopenharmony_ci max17040_set_low_soc_alert(chip, chip->low_soc_alert); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return IRQ_HANDLED; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int max17040_enable_alert_irq(struct max17040_chip *chip) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct i2c_client *client = chip->client; 33962306a36Sopenharmony_ci int ret; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, 34262306a36Sopenharmony_ci max17040_thread_handler, IRQF_ONESHOT, 34362306a36Sopenharmony_ci chip->battery->desc->name, chip); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return ret; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic int max17040_prop_writeable(struct power_supply *psy, 34962306a36Sopenharmony_ci enum power_supply_property psp) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci switch (psp) { 35262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 35362306a36Sopenharmony_ci return 1; 35462306a36Sopenharmony_ci default: 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int max17040_set_property(struct power_supply *psy, 36062306a36Sopenharmony_ci enum power_supply_property psp, 36162306a36Sopenharmony_ci const union power_supply_propval *val) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct max17040_chip *chip = power_supply_get_drvdata(psy); 36462306a36Sopenharmony_ci int ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci switch (psp) { 36762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 36862306a36Sopenharmony_ci /* alert threshold can be programmed from 1% up to 16/32% */ 36962306a36Sopenharmony_ci if ((val->intval < 1) || 37062306a36Sopenharmony_ci (val->intval > (chip->quirk_double_soc ? 16 : 32))) { 37162306a36Sopenharmony_ci ret = -EINVAL; 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci ret = max17040_set_low_soc_alert(chip, val->intval); 37562306a36Sopenharmony_ci chip->low_soc_alert = val->intval; 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci default: 37862306a36Sopenharmony_ci ret = -EINVAL; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int max17040_get_property(struct power_supply *psy, 38562306a36Sopenharmony_ci enum power_supply_property psp, 38662306a36Sopenharmony_ci union power_supply_propval *val) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct max17040_chip *chip = power_supply_get_drvdata(psy); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci switch (psp) { 39162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 39262306a36Sopenharmony_ci val->intval = max17040_get_online(chip); 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 39562306a36Sopenharmony_ci val->intval = max17040_get_vcell(chip); 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 39862306a36Sopenharmony_ci val->intval = max17040_get_soc(chip); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 40162306a36Sopenharmony_ci val->intval = chip->low_soc_alert; 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci default: 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic const struct regmap_config max17040_regmap = { 41062306a36Sopenharmony_ci .reg_bits = 8, 41162306a36Sopenharmony_ci .reg_stride = 2, 41262306a36Sopenharmony_ci .val_bits = 16, 41362306a36Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_BIG, 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic enum power_supply_property max17040_battery_props[] = { 41762306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 41862306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 41962306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 42062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, 42162306a36Sopenharmony_ci}; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic const struct power_supply_desc max17040_battery_desc = { 42462306a36Sopenharmony_ci .name = "battery", 42562306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 42662306a36Sopenharmony_ci .get_property = max17040_get_property, 42762306a36Sopenharmony_ci .set_property = max17040_set_property, 42862306a36Sopenharmony_ci .property_is_writeable = max17040_prop_writeable, 42962306a36Sopenharmony_ci .properties = max17040_battery_props, 43062306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(max17040_battery_props), 43162306a36Sopenharmony_ci}; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int max17040_probe(struct i2c_client *client) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 43662306a36Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 43762306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 43862306a36Sopenharmony_ci struct max17040_chip *chip; 43962306a36Sopenharmony_ci enum chip_id chip_id; 44062306a36Sopenharmony_ci bool enable_irq = false; 44162306a36Sopenharmony_ci int ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 44462306a36Sopenharmony_ci return -EIO; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 44762306a36Sopenharmony_ci if (!chip) 44862306a36Sopenharmony_ci return -ENOMEM; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci chip->client = client; 45162306a36Sopenharmony_ci chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); 45262306a36Sopenharmony_ci if (IS_ERR(chip->regmap)) 45362306a36Sopenharmony_ci return PTR_ERR(chip->regmap); 45462306a36Sopenharmony_ci chip_id = (enum chip_id) id->driver_data; 45562306a36Sopenharmony_ci if (client->dev.of_node) { 45662306a36Sopenharmony_ci ret = max17040_get_of_data(chip); 45762306a36Sopenharmony_ci if (ret) 45862306a36Sopenharmony_ci return ret; 45962306a36Sopenharmony_ci chip_id = (uintptr_t)of_device_get_match_data(&client->dev); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci chip->data = max17040_family[chip_id]; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci i2c_set_clientdata(client, chip); 46462306a36Sopenharmony_ci psy_cfg.drv_data = chip; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci chip->battery = devm_power_supply_register(&client->dev, 46762306a36Sopenharmony_ci &max17040_battery_desc, &psy_cfg); 46862306a36Sopenharmony_ci if (IS_ERR(chip->battery)) { 46962306a36Sopenharmony_ci dev_err(&client->dev, "failed: power supply register\n"); 47062306a36Sopenharmony_ci return PTR_ERR(chip->battery); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ret = max17040_get_version(chip); 47462306a36Sopenharmony_ci if (ret < 0) 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) 47962306a36Sopenharmony_ci max17040_reset(chip); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci max17040_set_rcomp(chip, chip->rcomp); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* check interrupt */ 48462306a36Sopenharmony_ci if (client->irq && chip->data.has_low_soc_alert) { 48562306a36Sopenharmony_ci ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); 48662306a36Sopenharmony_ci if (ret) { 48762306a36Sopenharmony_ci dev_err(&client->dev, 48862306a36Sopenharmony_ci "Failed to set low SOC alert: err %d\n", ret); 48962306a36Sopenharmony_ci return ret; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci enable_irq = true; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (client->irq && chip->data.has_soc_alert) { 49662306a36Sopenharmony_ci ret = max17040_set_soc_alert(chip, 1); 49762306a36Sopenharmony_ci if (ret) { 49862306a36Sopenharmony_ci dev_err(&client->dev, 49962306a36Sopenharmony_ci "Failed to set SOC alert: err %d\n", ret); 50062306a36Sopenharmony_ci return ret; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci enable_irq = true; 50362306a36Sopenharmony_ci } else { 50462306a36Sopenharmony_ci /* soc alerts negate the need for polling */ 50562306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&chip->work, max17040_work); 50662306a36Sopenharmony_ci ret = devm_add_action(&client->dev, max17040_stop_work, chip); 50762306a36Sopenharmony_ci if (ret) 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci max17040_queue_work(chip); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (enable_irq) { 51362306a36Sopenharmony_ci ret = max17040_enable_alert_irq(chip); 51462306a36Sopenharmony_ci if (ret) { 51562306a36Sopenharmony_ci client->irq = 0; 51662306a36Sopenharmony_ci dev_warn(&client->dev, 51762306a36Sopenharmony_ci "Failed to get IRQ err %d\n", ret); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int max17040_suspend(struct device *dev) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 52962306a36Sopenharmony_ci struct max17040_chip *chip = i2c_get_clientdata(client); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (client->irq && chip->data.has_soc_alert) 53262306a36Sopenharmony_ci // disable soc alert to prevent wakeup 53362306a36Sopenharmony_ci max17040_set_soc_alert(chip, 0); 53462306a36Sopenharmony_ci else 53562306a36Sopenharmony_ci cancel_delayed_work(&chip->work); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (client->irq && device_may_wakeup(dev)) 53862306a36Sopenharmony_ci enable_irq_wake(client->irq); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int max17040_resume(struct device *dev) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 54662306a36Sopenharmony_ci struct max17040_chip *chip = i2c_get_clientdata(client); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (client->irq && device_may_wakeup(dev)) 54962306a36Sopenharmony_ci disable_irq_wake(client->irq); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (client->irq && chip->data.has_soc_alert) 55262306a36Sopenharmony_ci max17040_set_soc_alert(chip, 1); 55362306a36Sopenharmony_ci else 55462306a36Sopenharmony_ci max17040_queue_work(chip); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); 56062306a36Sopenharmony_ci#define MAX17040_PM_OPS (&max17040_pm_ops) 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci#else 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci#define MAX17040_PM_OPS NULL 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic const struct i2c_device_id max17040_id[] = { 56962306a36Sopenharmony_ci { "max17040", ID_MAX17040 }, 57062306a36Sopenharmony_ci { "max17041", ID_MAX17041 }, 57162306a36Sopenharmony_ci { "max17043", ID_MAX17043 }, 57262306a36Sopenharmony_ci { "max77836-battery", ID_MAX17043 }, 57362306a36Sopenharmony_ci { "max17044", ID_MAX17044 }, 57462306a36Sopenharmony_ci { "max17048", ID_MAX17048 }, 57562306a36Sopenharmony_ci { "max17049", ID_MAX17049 }, 57662306a36Sopenharmony_ci { "max17058", ID_MAX17058 }, 57762306a36Sopenharmony_ci { "max17059", ID_MAX17059 }, 57862306a36Sopenharmony_ci { /* sentinel */ } 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max17040_id); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic const struct of_device_id max17040_of_match[] = { 58362306a36Sopenharmony_ci { .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 }, 58462306a36Sopenharmony_ci { .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 }, 58562306a36Sopenharmony_ci { .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 }, 58662306a36Sopenharmony_ci { .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 }, 58762306a36Sopenharmony_ci { .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 }, 58862306a36Sopenharmony_ci { .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 }, 58962306a36Sopenharmony_ci { .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 }, 59062306a36Sopenharmony_ci { .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 }, 59162306a36Sopenharmony_ci { .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 }, 59262306a36Sopenharmony_ci { /* sentinel */ }, 59362306a36Sopenharmony_ci}; 59462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, max17040_of_match); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic struct i2c_driver max17040_i2c_driver = { 59762306a36Sopenharmony_ci .driver = { 59862306a36Sopenharmony_ci .name = "max17040", 59962306a36Sopenharmony_ci .of_match_table = max17040_of_match, 60062306a36Sopenharmony_ci .pm = MAX17040_PM_OPS, 60162306a36Sopenharmony_ci }, 60262306a36Sopenharmony_ci .probe = max17040_probe, 60362306a36Sopenharmony_ci .id_table = max17040_id, 60462306a36Sopenharmony_ci}; 60562306a36Sopenharmony_cimodule_i2c_driver(max17040_i2c_driver); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciMODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); 60862306a36Sopenharmony_ciMODULE_DESCRIPTION("MAX17040 Fuel Gauge"); 60962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 610