162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Battery driver for Maxim MAX8925 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2009-2010 Marvell International Ltd. 662306a36Sopenharmony_ci * Haojian Zhuang <haojian.zhuang@marvell.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/power_supply.h> 1762306a36Sopenharmony_ci#include <linux/mfd/max8925.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* registers in GPM */ 2062306a36Sopenharmony_ci#define MAX8925_OUT5VEN 0x54 2162306a36Sopenharmony_ci#define MAX8925_OUT3VEN 0x58 2262306a36Sopenharmony_ci#define MAX8925_CHG_CNTL1 0x7c 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* bits definition */ 2562306a36Sopenharmony_ci#define MAX8925_CHG_STAT_VSYSLOW (1 << 0) 2662306a36Sopenharmony_ci#define MAX8925_CHG_STAT_MODE_MASK (3 << 2) 2762306a36Sopenharmony_ci#define MAX8925_CHG_STAT_EN_MASK (1 << 4) 2862306a36Sopenharmony_ci#define MAX8925_CHG_MBDET (1 << 1) 2962306a36Sopenharmony_ci#define MAX8925_CHG_AC_RANGE_MASK (3 << 6) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* registers in ADC */ 3262306a36Sopenharmony_ci#define MAX8925_ADC_RES_CNFG1 0x06 3362306a36Sopenharmony_ci#define MAX8925_ADC_AVG_CNFG1 0x07 3462306a36Sopenharmony_ci#define MAX8925_ADC_ACQ_CNFG1 0x08 3562306a36Sopenharmony_ci#define MAX8925_ADC_ACQ_CNFG2 0x09 3662306a36Sopenharmony_ci/* 2 bytes registers in below. MSB is 1st, LSB is 2nd. */ 3762306a36Sopenharmony_ci#define MAX8925_ADC_AUX2 0x62 3862306a36Sopenharmony_ci#define MAX8925_ADC_VCHG 0x64 3962306a36Sopenharmony_ci#define MAX8925_ADC_VBBATT 0x66 4062306a36Sopenharmony_ci#define MAX8925_ADC_VMBATT 0x68 4162306a36Sopenharmony_ci#define MAX8925_ADC_ISNS 0x6a 4262306a36Sopenharmony_ci#define MAX8925_ADC_THM 0x6c 4362306a36Sopenharmony_ci#define MAX8925_ADC_TDIE 0x6e 4462306a36Sopenharmony_ci#define MAX8925_CMD_AUX2 0xc8 4562306a36Sopenharmony_ci#define MAX8925_CMD_VCHG 0xd0 4662306a36Sopenharmony_ci#define MAX8925_CMD_VBBATT 0xd8 4762306a36Sopenharmony_ci#define MAX8925_CMD_VMBATT 0xe0 4862306a36Sopenharmony_ci#define MAX8925_CMD_ISNS 0xe8 4962306a36Sopenharmony_ci#define MAX8925_CMD_THM 0xf0 5062306a36Sopenharmony_ci#define MAX8925_CMD_TDIE 0xf8 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cienum { 5362306a36Sopenharmony_ci MEASURE_AUX2, 5462306a36Sopenharmony_ci MEASURE_VCHG, 5562306a36Sopenharmony_ci MEASURE_VBBATT, 5662306a36Sopenharmony_ci MEASURE_VMBATT, 5762306a36Sopenharmony_ci MEASURE_ISNS, 5862306a36Sopenharmony_ci MEASURE_THM, 5962306a36Sopenharmony_ci MEASURE_TDIE, 6062306a36Sopenharmony_ci MEASURE_MAX, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct max8925_power_info { 6462306a36Sopenharmony_ci struct max8925_chip *chip; 6562306a36Sopenharmony_ci struct i2c_client *gpm; 6662306a36Sopenharmony_ci struct i2c_client *adc; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci struct power_supply *ac; 6962306a36Sopenharmony_ci struct power_supply *usb; 7062306a36Sopenharmony_ci struct power_supply *battery; 7162306a36Sopenharmony_ci int irq_base; 7262306a36Sopenharmony_ci unsigned ac_online:1; 7362306a36Sopenharmony_ci unsigned usb_online:1; 7462306a36Sopenharmony_ci unsigned bat_online:1; 7562306a36Sopenharmony_ci unsigned chg_mode:2; 7662306a36Sopenharmony_ci unsigned batt_detect:1; /* detecing MB by ID pin */ 7762306a36Sopenharmony_ci unsigned topoff_threshold:2; 7862306a36Sopenharmony_ci unsigned fast_charge:3; 7962306a36Sopenharmony_ci unsigned no_temp_support:1; 8062306a36Sopenharmony_ci unsigned no_insert_detect:1; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci int (*set_charger) (int); 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int __set_charger(struct max8925_power_info *info, int enable) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct max8925_chip *chip = info->chip; 8862306a36Sopenharmony_ci if (enable) { 8962306a36Sopenharmony_ci /* enable charger in platform */ 9062306a36Sopenharmony_ci if (info->set_charger) 9162306a36Sopenharmony_ci info->set_charger(1); 9262306a36Sopenharmony_ci /* enable charger */ 9362306a36Sopenharmony_ci max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 0); 9462306a36Sopenharmony_ci } else { 9562306a36Sopenharmony_ci /* disable charge */ 9662306a36Sopenharmony_ci max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7); 9762306a36Sopenharmony_ci if (info->set_charger) 9862306a36Sopenharmony_ci info->set_charger(0); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci dev_dbg(chip->dev, "%s\n", (enable) ? "Enable charger" 10162306a36Sopenharmony_ci : "Disable charger"); 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic irqreturn_t max8925_charger_handler(int irq, void *data) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct max8925_power_info *info = (struct max8925_power_info *)data; 10862306a36Sopenharmony_ci struct max8925_chip *chip = info->chip; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci switch (irq - chip->irq_base) { 11162306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_DC_R: 11262306a36Sopenharmony_ci info->ac_online = 1; 11362306a36Sopenharmony_ci __set_charger(info, 1); 11462306a36Sopenharmony_ci dev_dbg(chip->dev, "Adapter inserted\n"); 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_DC_F: 11762306a36Sopenharmony_ci info->ac_online = 0; 11862306a36Sopenharmony_ci __set_charger(info, 0); 11962306a36Sopenharmony_ci dev_dbg(chip->dev, "Adapter removed\n"); 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_THM_OK_F: 12262306a36Sopenharmony_ci /* Battery is not ready yet */ 12362306a36Sopenharmony_ci dev_dbg(chip->dev, "Battery temperature is out of range\n"); 12462306a36Sopenharmony_ci fallthrough; 12562306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_DC_OVP: 12662306a36Sopenharmony_ci dev_dbg(chip->dev, "Error detection\n"); 12762306a36Sopenharmony_ci __set_charger(info, 0); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_THM_OK_R: 13062306a36Sopenharmony_ci /* Battery is ready now */ 13162306a36Sopenharmony_ci dev_dbg(chip->dev, "Battery temperature is in range\n"); 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_SYSLOW_R: 13462306a36Sopenharmony_ci /* VSYS is low */ 13562306a36Sopenharmony_ci dev_info(chip->dev, "Sys power is too low\n"); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_SYSLOW_F: 13862306a36Sopenharmony_ci dev_dbg(chip->dev, "Sys power is above low threshold\n"); 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_DONE: 14162306a36Sopenharmony_ci __set_charger(info, 0); 14262306a36Sopenharmony_ci dev_dbg(chip->dev, "Charging is done\n"); 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_TOPOFF: 14562306a36Sopenharmony_ci dev_dbg(chip->dev, "Charging in top-off mode\n"); 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_TMR_FAULT: 14862306a36Sopenharmony_ci __set_charger(info, 0); 14962306a36Sopenharmony_ci dev_dbg(chip->dev, "Safe timer is expired\n"); 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci case MAX8925_IRQ_VCHG_RST: 15262306a36Sopenharmony_ci __set_charger(info, 0); 15362306a36Sopenharmony_ci dev_dbg(chip->dev, "Charger is reset\n"); 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci return IRQ_HANDLED; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int start_measure(struct max8925_power_info *info, int type) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci unsigned char buf[2] = {0, 0}; 16262306a36Sopenharmony_ci int meas_cmd; 16362306a36Sopenharmony_ci int meas_reg = 0, ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci switch (type) { 16662306a36Sopenharmony_ci case MEASURE_VCHG: 16762306a36Sopenharmony_ci meas_cmd = MAX8925_CMD_VCHG; 16862306a36Sopenharmony_ci meas_reg = MAX8925_ADC_VCHG; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case MEASURE_VBBATT: 17162306a36Sopenharmony_ci meas_cmd = MAX8925_CMD_VBBATT; 17262306a36Sopenharmony_ci meas_reg = MAX8925_ADC_VBBATT; 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci case MEASURE_VMBATT: 17562306a36Sopenharmony_ci meas_cmd = MAX8925_CMD_VMBATT; 17662306a36Sopenharmony_ci meas_reg = MAX8925_ADC_VMBATT; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci case MEASURE_ISNS: 17962306a36Sopenharmony_ci meas_cmd = MAX8925_CMD_ISNS; 18062306a36Sopenharmony_ci meas_reg = MAX8925_ADC_ISNS; 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci default: 18362306a36Sopenharmony_ci return -EINVAL; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci max8925_reg_write(info->adc, meas_cmd, 0); 18762306a36Sopenharmony_ci max8925_bulk_read(info->adc, meas_reg, 2, buf); 18862306a36Sopenharmony_ci ret = ((buf[0]<<8) | buf[1]) >> 4; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int max8925_ac_get_prop(struct power_supply *psy, 19462306a36Sopenharmony_ci enum power_supply_property psp, 19562306a36Sopenharmony_ci union power_supply_propval *val) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent); 19862306a36Sopenharmony_ci int ret = 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci switch (psp) { 20162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 20262306a36Sopenharmony_ci val->intval = info->ac_online; 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 20562306a36Sopenharmony_ci if (info->ac_online) { 20662306a36Sopenharmony_ci ret = start_measure(info, MEASURE_VCHG); 20762306a36Sopenharmony_ci if (ret >= 0) { 20862306a36Sopenharmony_ci val->intval = ret * 2000; /* unit is uV */ 20962306a36Sopenharmony_ci goto out; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci ret = -ENODATA; 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci default: 21562306a36Sopenharmony_ci ret = -ENODEV; 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ciout: 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic enum power_supply_property max8925_ac_props[] = { 22362306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 22462306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int max8925_usb_get_prop(struct power_supply *psy, 22862306a36Sopenharmony_ci enum power_supply_property psp, 22962306a36Sopenharmony_ci union power_supply_propval *val) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent); 23262306a36Sopenharmony_ci int ret = 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci switch (psp) { 23562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 23662306a36Sopenharmony_ci val->intval = info->usb_online; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 23962306a36Sopenharmony_ci if (info->usb_online) { 24062306a36Sopenharmony_ci ret = start_measure(info, MEASURE_VCHG); 24162306a36Sopenharmony_ci if (ret >= 0) { 24262306a36Sopenharmony_ci val->intval = ret * 2000; /* unit is uV */ 24362306a36Sopenharmony_ci goto out; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci ret = -ENODATA; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci default: 24962306a36Sopenharmony_ci ret = -ENODEV; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ciout: 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic enum power_supply_property max8925_usb_props[] = { 25762306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 25862306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int max8925_bat_get_prop(struct power_supply *psy, 26262306a36Sopenharmony_ci enum power_supply_property psp, 26362306a36Sopenharmony_ci union power_supply_propval *val) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent); 26662306a36Sopenharmony_ci int ret = 0; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci switch (psp) { 26962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 27062306a36Sopenharmony_ci val->intval = info->bat_online; 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 27362306a36Sopenharmony_ci if (info->bat_online) { 27462306a36Sopenharmony_ci ret = start_measure(info, MEASURE_VMBATT); 27562306a36Sopenharmony_ci if (ret >= 0) { 27662306a36Sopenharmony_ci val->intval = ret * 2000; /* unit is uV */ 27762306a36Sopenharmony_ci ret = 0; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci ret = -ENODATA; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 28462306a36Sopenharmony_ci if (info->bat_online) { 28562306a36Sopenharmony_ci ret = start_measure(info, MEASURE_ISNS); 28662306a36Sopenharmony_ci if (ret >= 0) { 28762306a36Sopenharmony_ci /* assume r_sns is 0.02 */ 28862306a36Sopenharmony_ci ret = ((ret * 6250) - 3125) /* uA */; 28962306a36Sopenharmony_ci val->intval = 0; 29062306a36Sopenharmony_ci if (ret > 0) 29162306a36Sopenharmony_ci val->intval = ret; /* unit is mA */ 29262306a36Sopenharmony_ci ret = 0; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci ret = -ENODATA; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 29962306a36Sopenharmony_ci if (!info->bat_online) { 30062306a36Sopenharmony_ci ret = -ENODATA; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS); 30462306a36Sopenharmony_ci ret = (ret & MAX8925_CHG_STAT_MODE_MASK) >> 2; 30562306a36Sopenharmony_ci switch (ret) { 30662306a36Sopenharmony_ci case 1: 30762306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case 0: 31062306a36Sopenharmony_ci case 2: 31162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case 3: 31462306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci ret = 0; 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 32062306a36Sopenharmony_ci if (!info->bat_online) { 32162306a36Sopenharmony_ci ret = -ENODATA; 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS); 32562306a36Sopenharmony_ci if (info->usb_online || info->ac_online) { 32662306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 32762306a36Sopenharmony_ci if (ret & MAX8925_CHG_STAT_EN_MASK) 32862306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 32962306a36Sopenharmony_ci } else 33062306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 33162306a36Sopenharmony_ci ret = 0; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci default: 33462306a36Sopenharmony_ci ret = -ENODEV; 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic enum power_supply_property max8925_battery_props[] = { 34162306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 34262306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 34362306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 34462306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 34562306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic const struct power_supply_desc ac_desc = { 34962306a36Sopenharmony_ci .name = "max8925-ac", 35062306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 35162306a36Sopenharmony_ci .properties = max8925_ac_props, 35262306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(max8925_ac_props), 35362306a36Sopenharmony_ci .get_property = max8925_ac_get_prop, 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic const struct power_supply_desc usb_desc = { 35762306a36Sopenharmony_ci .name = "max8925-usb", 35862306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 35962306a36Sopenharmony_ci .properties = max8925_usb_props, 36062306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(max8925_usb_props), 36162306a36Sopenharmony_ci .get_property = max8925_usb_get_prop, 36262306a36Sopenharmony_ci}; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic const struct power_supply_desc battery_desc = { 36562306a36Sopenharmony_ci .name = "max8925-battery", 36662306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 36762306a36Sopenharmony_ci .properties = max8925_battery_props, 36862306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(max8925_battery_props), 36962306a36Sopenharmony_ci .get_property = max8925_bat_get_prop, 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci#define REQUEST_IRQ(_irq, _name) \ 37362306a36Sopenharmony_cido { \ 37462306a36Sopenharmony_ci ret = request_threaded_irq(chip->irq_base + _irq, NULL, \ 37562306a36Sopenharmony_ci max8925_charger_handler, \ 37662306a36Sopenharmony_ci IRQF_ONESHOT, _name, info); \ 37762306a36Sopenharmony_ci if (ret) \ 37862306a36Sopenharmony_ci dev_err(chip->dev, "Failed to request IRQ #%d: %d\n", \ 37962306a36Sopenharmony_ci _irq, ret); \ 38062306a36Sopenharmony_ci} while (0) 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int max8925_init_charger(struct max8925_chip *chip, 38362306a36Sopenharmony_ci struct max8925_power_info *info) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp"); 38862306a36Sopenharmony_ci if (!info->no_insert_detect) { 38962306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove"); 39062306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert"); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci if (!info->no_temp_support) { 39362306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range"); 39462306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range"); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high"); 39762306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low"); 39862306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset"); 39962306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done"); 40062306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff"); 40162306a36Sopenharmony_ci REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire"); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci info->usb_online = 0; 40462306a36Sopenharmony_ci info->bat_online = 0; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* check for power - can miss interrupt at boot time */ 40762306a36Sopenharmony_ci if (start_measure(info, MEASURE_VCHG) * 2000 > 500000) 40862306a36Sopenharmony_ci info->ac_online = 1; 40962306a36Sopenharmony_ci else 41062306a36Sopenharmony_ci info->ac_online = 0; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS); 41362306a36Sopenharmony_ci if (ret >= 0) { 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * If battery detection is enabled, ID pin of battery is 41662306a36Sopenharmony_ci * connected to MBDET pin of MAX8925. It could be used to 41762306a36Sopenharmony_ci * detect battery presence. 41862306a36Sopenharmony_ci * Otherwise, we have to assume that battery is always on. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci if (info->batt_detect) 42162306a36Sopenharmony_ci info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1; 42262306a36Sopenharmony_ci else 42362306a36Sopenharmony_ci info->bat_online = 1; 42462306a36Sopenharmony_ci if (ret & MAX8925_CHG_AC_RANGE_MASK) 42562306a36Sopenharmony_ci info->ac_online = 1; 42662306a36Sopenharmony_ci else 42762306a36Sopenharmony_ci info->ac_online = 0; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci /* disable charge */ 43062306a36Sopenharmony_ci max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7); 43162306a36Sopenharmony_ci /* set charging current in charge topoff mode */ 43262306a36Sopenharmony_ci max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5, 43362306a36Sopenharmony_ci info->topoff_threshold << 5); 43462306a36Sopenharmony_ci /* set charing current in fast charge mode */ 43562306a36Sopenharmony_ci max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int max8925_deinit_charger(struct max8925_power_info *info) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct max8925_chip *chip = info->chip; 44362306a36Sopenharmony_ci int irq; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci irq = chip->irq_base + MAX8925_IRQ_VCHG_DC_OVP; 44662306a36Sopenharmony_ci for (; irq <= chip->irq_base + MAX8925_IRQ_VCHG_TMR_FAULT; irq++) 44762306a36Sopenharmony_ci free_irq(irq, info); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci#ifdef CONFIG_OF 45362306a36Sopenharmony_cistatic struct max8925_power_pdata * 45462306a36Sopenharmony_cimax8925_power_dt_init(struct platform_device *pdev) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct device_node *nproot = pdev->dev.parent->of_node; 45762306a36Sopenharmony_ci struct device_node *np; 45862306a36Sopenharmony_ci int batt_detect; 45962306a36Sopenharmony_ci int topoff_threshold; 46062306a36Sopenharmony_ci int fast_charge; 46162306a36Sopenharmony_ci int no_temp_support; 46262306a36Sopenharmony_ci int no_insert_detect; 46362306a36Sopenharmony_ci struct max8925_power_pdata *pdata; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!nproot) 46662306a36Sopenharmony_ci return pdev->dev.platform_data; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci np = of_get_child_by_name(nproot, "charger"); 46962306a36Sopenharmony_ci if (!np) { 47062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to find charger node\n"); 47162306a36Sopenharmony_ci return NULL; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, 47562306a36Sopenharmony_ci sizeof(struct max8925_power_pdata), 47662306a36Sopenharmony_ci GFP_KERNEL); 47762306a36Sopenharmony_ci if (!pdata) 47862306a36Sopenharmony_ci goto ret; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci of_property_read_u32(np, "topoff-threshold", &topoff_threshold); 48162306a36Sopenharmony_ci of_property_read_u32(np, "batt-detect", &batt_detect); 48262306a36Sopenharmony_ci of_property_read_u32(np, "fast-charge", &fast_charge); 48362306a36Sopenharmony_ci of_property_read_u32(np, "no-insert-detect", &no_insert_detect); 48462306a36Sopenharmony_ci of_property_read_u32(np, "no-temp-support", &no_temp_support); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci pdata->batt_detect = batt_detect; 48762306a36Sopenharmony_ci pdata->fast_charge = fast_charge; 48862306a36Sopenharmony_ci pdata->topoff_threshold = topoff_threshold; 48962306a36Sopenharmony_ci pdata->no_insert_detect = no_insert_detect; 49062306a36Sopenharmony_ci pdata->no_temp_support = no_temp_support; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ciret: 49362306a36Sopenharmony_ci of_node_put(np); 49462306a36Sopenharmony_ci return pdata; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci#else 49762306a36Sopenharmony_cistatic struct max8925_power_pdata * 49862306a36Sopenharmony_cimax8925_power_dt_init(struct platform_device *pdev) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci return pdev->dev.platform_data; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci#endif 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int max8925_power_probe(struct platform_device *pdev) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); 50762306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; /* Only for ac and usb */ 50862306a36Sopenharmony_ci struct max8925_power_pdata *pdata = NULL; 50962306a36Sopenharmony_ci struct max8925_power_info *info; 51062306a36Sopenharmony_ci int ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci pdata = max8925_power_dt_init(pdev); 51362306a36Sopenharmony_ci if (!pdata) { 51462306a36Sopenharmony_ci dev_err(&pdev->dev, "platform data isn't assigned to " 51562306a36Sopenharmony_ci "power supply\n"); 51662306a36Sopenharmony_ci return -EINVAL; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info), 52062306a36Sopenharmony_ci GFP_KERNEL); 52162306a36Sopenharmony_ci if (!info) 52262306a36Sopenharmony_ci return -ENOMEM; 52362306a36Sopenharmony_ci info->chip = chip; 52462306a36Sopenharmony_ci info->gpm = chip->i2c; 52562306a36Sopenharmony_ci info->adc = chip->adc; 52662306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci psy_cfg.supplied_to = pdata->supplied_to; 52962306a36Sopenharmony_ci psy_cfg.num_supplicants = pdata->num_supplicants; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg); 53262306a36Sopenharmony_ci if (IS_ERR(info->ac)) { 53362306a36Sopenharmony_ci ret = PTR_ERR(info->ac); 53462306a36Sopenharmony_ci goto out; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci info->ac->dev.parent = &pdev->dev; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci info->usb = power_supply_register(&pdev->dev, &usb_desc, &psy_cfg); 53962306a36Sopenharmony_ci if (IS_ERR(info->usb)) { 54062306a36Sopenharmony_ci ret = PTR_ERR(info->usb); 54162306a36Sopenharmony_ci goto out_unregister_ac; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci info->usb->dev.parent = &pdev->dev; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci info->battery = power_supply_register(&pdev->dev, &battery_desc, NULL); 54662306a36Sopenharmony_ci if (IS_ERR(info->battery)) { 54762306a36Sopenharmony_ci ret = PTR_ERR(info->battery); 54862306a36Sopenharmony_ci goto out_unregister_usb; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci info->battery->dev.parent = &pdev->dev; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci info->batt_detect = pdata->batt_detect; 55362306a36Sopenharmony_ci info->topoff_threshold = pdata->topoff_threshold; 55462306a36Sopenharmony_ci info->fast_charge = pdata->fast_charge; 55562306a36Sopenharmony_ci info->set_charger = pdata->set_charger; 55662306a36Sopenharmony_ci info->no_temp_support = pdata->no_temp_support; 55762306a36Sopenharmony_ci info->no_insert_detect = pdata->no_insert_detect; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci max8925_init_charger(chip, info); 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ciout_unregister_usb: 56262306a36Sopenharmony_ci power_supply_unregister(info->usb); 56362306a36Sopenharmony_ciout_unregister_ac: 56462306a36Sopenharmony_ci power_supply_unregister(info->ac); 56562306a36Sopenharmony_ciout: 56662306a36Sopenharmony_ci return ret; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int max8925_power_remove(struct platform_device *pdev) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct max8925_power_info *info = platform_get_drvdata(pdev); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (info) { 57462306a36Sopenharmony_ci power_supply_unregister(info->ac); 57562306a36Sopenharmony_ci power_supply_unregister(info->usb); 57662306a36Sopenharmony_ci power_supply_unregister(info->battery); 57762306a36Sopenharmony_ci max8925_deinit_charger(info); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic struct platform_driver max8925_power_driver = { 58362306a36Sopenharmony_ci .probe = max8925_power_probe, 58462306a36Sopenharmony_ci .remove = max8925_power_remove, 58562306a36Sopenharmony_ci .driver = { 58662306a36Sopenharmony_ci .name = "max8925-power", 58762306a36Sopenharmony_ci }, 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cimodule_platform_driver(max8925_power_driver); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 59362306a36Sopenharmony_ciMODULE_DESCRIPTION("Power supply driver for MAX8925"); 59462306a36Sopenharmony_ciMODULE_ALIAS("platform:max8925-power"); 595