162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Battery driver for Marvell 88PM860x PMIC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2012 Marvell International Ltd. 662306a36Sopenharmony_ci * Author: Jett Zhou <jtzhou@marvell.com> 762306a36Sopenharmony_ci * Haojian Zhuang <haojian.zhuang@marvell.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/power_supply.h> 1562306a36Sopenharmony_ci#include <linux/mfd/88pm860x.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/uaccess.h> 1862306a36Sopenharmony_ci#include <asm/div64.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* bit definitions of Status Query Interface 2 */ 2162306a36Sopenharmony_ci#define STATUS2_CHG (1 << 2) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* bit definitions of Reset Out Register */ 2462306a36Sopenharmony_ci#define RESET_SW_PD (1 << 7) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* bit definitions of PreReg 1 */ 2762306a36Sopenharmony_ci#define PREREG1_90MA (0x0) 2862306a36Sopenharmony_ci#define PREREG1_180MA (0x1) 2962306a36Sopenharmony_ci#define PREREG1_450MA (0x4) 3062306a36Sopenharmony_ci#define PREREG1_540MA (0x5) 3162306a36Sopenharmony_ci#define PREREG1_1350MA (0xE) 3262306a36Sopenharmony_ci#define PREREG1_VSYS_4_5V (3 << 4) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* bit definitions of Charger Control 1 Register */ 3562306a36Sopenharmony_ci#define CC1_MODE_OFF (0) 3662306a36Sopenharmony_ci#define CC1_MODE_PRECHARGE (1) 3762306a36Sopenharmony_ci#define CC1_MODE_FASTCHARGE (2) 3862306a36Sopenharmony_ci#define CC1_MODE_PULSECHARGE (3) 3962306a36Sopenharmony_ci#define CC1_ITERM_20MA (0 << 2) 4062306a36Sopenharmony_ci#define CC1_ITERM_60MA (2 << 2) 4162306a36Sopenharmony_ci#define CC1_VFCHG_4_2V (9 << 4) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* bit definitions of Charger Control 2 Register */ 4462306a36Sopenharmony_ci#define CC2_ICHG_100MA (0x1) 4562306a36Sopenharmony_ci#define CC2_ICHG_500MA (0x9) 4662306a36Sopenharmony_ci#define CC2_ICHG_1000MA (0x13) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* bit definitions of Charger Control 3 Register */ 4962306a36Sopenharmony_ci#define CC3_180MIN_TIMEOUT (0x6 << 4) 5062306a36Sopenharmony_ci#define CC3_270MIN_TIMEOUT (0x7 << 4) 5162306a36Sopenharmony_ci#define CC3_360MIN_TIMEOUT (0xA << 4) 5262306a36Sopenharmony_ci#define CC3_DISABLE_TIMEOUT (0xF << 4) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* bit definitions of Charger Control 4 Register */ 5562306a36Sopenharmony_ci#define CC4_IPRE_40MA (7) 5662306a36Sopenharmony_ci#define CC4_VPCHG_3_2V (3 << 4) 5762306a36Sopenharmony_ci#define CC4_IFCHG_MON_EN (1 << 6) 5862306a36Sopenharmony_ci#define CC4_BTEMP_MON_EN (1 << 7) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* bit definitions of Charger Control 6 Register */ 6162306a36Sopenharmony_ci#define CC6_BAT_OV_EN (1 << 2) 6262306a36Sopenharmony_ci#define CC6_BAT_UV_EN (1 << 3) 6362306a36Sopenharmony_ci#define CC6_UV_VBAT_SET (0x3 << 6) /* 2.8v */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* bit definitions of Charger Control 7 Register */ 6662306a36Sopenharmony_ci#define CC7_BAT_REM_EN (1 << 3) 6762306a36Sopenharmony_ci#define CC7_IFSM_EN (1 << 7) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* bit definitions of Measurement Enable 1 Register */ 7062306a36Sopenharmony_ci#define MEAS1_VBAT (1 << 0) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* bit definitions of Measurement Enable 3 Register */ 7362306a36Sopenharmony_ci#define MEAS3_IBAT_EN (1 << 0) 7462306a36Sopenharmony_ci#define MEAS3_CC_EN (1 << 2) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define FSM_INIT 0 7762306a36Sopenharmony_ci#define FSM_DISCHARGE 1 7862306a36Sopenharmony_ci#define FSM_PRECHARGE 2 7962306a36Sopenharmony_ci#define FSM_FASTCHARGE 3 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define PRECHARGE_THRESHOLD 3100 8262306a36Sopenharmony_ci#define POWEROFF_THRESHOLD 3400 8362306a36Sopenharmony_ci#define CHARGE_THRESHOLD 4000 8462306a36Sopenharmony_ci#define DISCHARGE_THRESHOLD 4180 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* over-temperature on PM8606 setting */ 8762306a36Sopenharmony_ci#define OVER_TEMP_FLAG (1 << 6) 8862306a36Sopenharmony_ci#define OVTEMP_AUTORECOVER (1 << 3) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* over-voltage protect on vchg setting mv */ 9162306a36Sopenharmony_ci#define VCHG_NORMAL_LOW 4200 9262306a36Sopenharmony_ci#define VCHG_NORMAL_CHECK 5800 9362306a36Sopenharmony_ci#define VCHG_NORMAL_HIGH 6000 9462306a36Sopenharmony_ci#define VCHG_OVP_LOW 5500 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct pm860x_charger_info { 9762306a36Sopenharmony_ci struct pm860x_chip *chip; 9862306a36Sopenharmony_ci struct i2c_client *i2c; 9962306a36Sopenharmony_ci struct i2c_client *i2c_8606; 10062306a36Sopenharmony_ci struct device *dev; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci struct power_supply *usb; 10362306a36Sopenharmony_ci struct mutex lock; 10462306a36Sopenharmony_ci int irq_nums; 10562306a36Sopenharmony_ci int irq[7]; 10662306a36Sopenharmony_ci unsigned state:3; /* fsm state */ 10762306a36Sopenharmony_ci unsigned online:1; /* usb charger */ 10862306a36Sopenharmony_ci unsigned present:1; /* battery present */ 10962306a36Sopenharmony_ci unsigned allowed:1; 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic char *pm860x_supplied_to[] = { 11362306a36Sopenharmony_ci "battery-monitor", 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int measure_vchg(struct pm860x_charger_info *info, int *data) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned char buf[2]; 11962306a36Sopenharmony_ci int ret = 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = pm860x_bulk_read(info->i2c, PM8607_VCHG_MEAS1, 2, buf); 12262306a36Sopenharmony_ci if (ret < 0) 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f); 12662306a36Sopenharmony_ci /* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */ 12762306a36Sopenharmony_ci *data = ((*data & 0xfff) * 9 * 125) >> 9; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci dev_dbg(info->dev, "%s, vchg: %d mv\n", __func__, *data); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return ret; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void set_vchg_threshold(struct pm860x_charger_info *info, 13562306a36Sopenharmony_ci int min, int max) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int data; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* (tmp << 8) * / 5 / 1800 */ 14062306a36Sopenharmony_ci if (min <= 0) 14162306a36Sopenharmony_ci data = 0; 14262306a36Sopenharmony_ci else 14362306a36Sopenharmony_ci data = (min << 5) / 1125; 14462306a36Sopenharmony_ci pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data); 14562306a36Sopenharmony_ci dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (max <= 0) 14862306a36Sopenharmony_ci data = 0xff; 14962306a36Sopenharmony_ci else 15062306a36Sopenharmony_ci data = (max << 5) / 1125; 15162306a36Sopenharmony_ci pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data); 15262306a36Sopenharmony_ci dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void set_vbatt_threshold(struct pm860x_charger_info *info, 15762306a36Sopenharmony_ci int min, int max) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci int data; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* (tmp << 8) * 3 / 1800 */ 16262306a36Sopenharmony_ci if (min <= 0) 16362306a36Sopenharmony_ci data = 0; 16462306a36Sopenharmony_ci else 16562306a36Sopenharmony_ci data = (min << 5) / 675; 16662306a36Sopenharmony_ci pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data); 16762306a36Sopenharmony_ci dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (max <= 0) 17062306a36Sopenharmony_ci data = 0xff; 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci data = (max << 5) / 675; 17362306a36Sopenharmony_ci pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data); 17462306a36Sopenharmony_ci dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int start_precharge(struct pm860x_charger_info *info) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int ret; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci dev_dbg(info->dev, "Start Pre-charging!\n"); 18462306a36Sopenharmony_ci set_vbatt_threshold(info, 0, 0); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, 18762306a36Sopenharmony_ci PREREG1_1350MA | PREREG1_VSYS_4_5V); 18862306a36Sopenharmony_ci if (ret < 0) 18962306a36Sopenharmony_ci goto out; 19062306a36Sopenharmony_ci /* stop charging */ 19162306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 19262306a36Sopenharmony_ci CC1_MODE_OFF); 19362306a36Sopenharmony_ci if (ret < 0) 19462306a36Sopenharmony_ci goto out; 19562306a36Sopenharmony_ci /* set 270 minutes timeout */ 19662306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), 19762306a36Sopenharmony_ci CC3_270MIN_TIMEOUT); 19862306a36Sopenharmony_ci if (ret < 0) 19962306a36Sopenharmony_ci goto out; 20062306a36Sopenharmony_ci /* set precharge current, termination voltage, IBAT & TBAT monitor */ 20162306a36Sopenharmony_ci ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4, 20262306a36Sopenharmony_ci CC4_IPRE_40MA | CC4_VPCHG_3_2V | 20362306a36Sopenharmony_ci CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); 20462306a36Sopenharmony_ci if (ret < 0) 20562306a36Sopenharmony_ci goto out; 20662306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, 20762306a36Sopenharmony_ci CC7_BAT_REM_EN | CC7_IFSM_EN, 20862306a36Sopenharmony_ci CC7_BAT_REM_EN | CC7_IFSM_EN); 20962306a36Sopenharmony_ci if (ret < 0) 21062306a36Sopenharmony_ci goto out; 21162306a36Sopenharmony_ci /* trigger precharge */ 21262306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 21362306a36Sopenharmony_ci CC1_MODE_PRECHARGE); 21462306a36Sopenharmony_ciout: 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int start_fastcharge(struct pm860x_charger_info *info) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci int ret; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci dev_dbg(info->dev, "Start Fast-charging!\n"); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* set fastcharge termination current & voltage, disable charging */ 22562306a36Sopenharmony_ci ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1, 22662306a36Sopenharmony_ci CC1_MODE_OFF | CC1_ITERM_60MA | 22762306a36Sopenharmony_ci CC1_VFCHG_4_2V); 22862306a36Sopenharmony_ci if (ret < 0) 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, 23162306a36Sopenharmony_ci PREREG1_540MA | PREREG1_VSYS_4_5V); 23262306a36Sopenharmony_ci if (ret < 0) 23362306a36Sopenharmony_ci goto out; 23462306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, 23562306a36Sopenharmony_ci CC2_ICHG_500MA); 23662306a36Sopenharmony_ci if (ret < 0) 23762306a36Sopenharmony_ci goto out; 23862306a36Sopenharmony_ci /* set 270 minutes timeout */ 23962306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), 24062306a36Sopenharmony_ci CC3_270MIN_TIMEOUT); 24162306a36Sopenharmony_ci if (ret < 0) 24262306a36Sopenharmony_ci goto out; 24362306a36Sopenharmony_ci /* set IBAT & TBAT monitor */ 24462306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4, 24562306a36Sopenharmony_ci CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN, 24662306a36Sopenharmony_ci CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); 24762306a36Sopenharmony_ci if (ret < 0) 24862306a36Sopenharmony_ci goto out; 24962306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6, 25062306a36Sopenharmony_ci CC6_BAT_OV_EN | CC6_BAT_UV_EN | 25162306a36Sopenharmony_ci CC6_UV_VBAT_SET, 25262306a36Sopenharmony_ci CC6_BAT_OV_EN | CC6_BAT_UV_EN | 25362306a36Sopenharmony_ci CC6_UV_VBAT_SET); 25462306a36Sopenharmony_ci if (ret < 0) 25562306a36Sopenharmony_ci goto out; 25662306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, 25762306a36Sopenharmony_ci CC7_BAT_REM_EN | CC7_IFSM_EN, 25862306a36Sopenharmony_ci CC7_BAT_REM_EN | CC7_IFSM_EN); 25962306a36Sopenharmony_ci if (ret < 0) 26062306a36Sopenharmony_ci goto out; 26162306a36Sopenharmony_ci /* launch fast-charge */ 26262306a36Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 26362306a36Sopenharmony_ci CC1_MODE_FASTCHARGE); 26462306a36Sopenharmony_ci /* vchg threshold setting */ 26562306a36Sopenharmony_ci set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH); 26662306a36Sopenharmony_ciout: 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void stop_charge(struct pm860x_charger_info *info, int vbatt) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci dev_dbg(info->dev, "Stop charging!\n"); 27362306a36Sopenharmony_ci pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF); 27462306a36Sopenharmony_ci if (vbatt > CHARGE_THRESHOLD && info->online) 27562306a36Sopenharmony_ci set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void power_off_notification(struct pm860x_charger_info *info) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci dev_dbg(info->dev, "Power-off notification!\n"); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int set_charging_fsm(struct pm860x_charger_info *info) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct power_supply *psy; 28662306a36Sopenharmony_ci union power_supply_propval data; 28762306a36Sopenharmony_ci unsigned char fsm_state[][16] = { "init", "discharge", "precharge", 28862306a36Sopenharmony_ci "fastcharge", 28962306a36Sopenharmony_ci }; 29062306a36Sopenharmony_ci int ret; 29162306a36Sopenharmony_ci int vbatt; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci psy = power_supply_get_by_name(pm860x_supplied_to[0]); 29462306a36Sopenharmony_ci if (!psy) 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, 29762306a36Sopenharmony_ci &data); 29862306a36Sopenharmony_ci if (ret) { 29962306a36Sopenharmony_ci power_supply_put(psy); 30062306a36Sopenharmony_ci return ret; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci vbatt = data.intval / 1000; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data); 30562306a36Sopenharmony_ci if (ret) { 30662306a36Sopenharmony_ci power_supply_put(psy); 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci power_supply_put(psy); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci mutex_lock(&info->lock); 31262306a36Sopenharmony_ci info->present = data.intval; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, " 31562306a36Sopenharmony_ci "Allowed:%d\n", 31662306a36Sopenharmony_ci &fsm_state[info->state][0], 31762306a36Sopenharmony_ci (info->online) ? "online" : "N/A", 31862306a36Sopenharmony_ci (info->present) ? "present" : "N/A", info->allowed); 31962306a36Sopenharmony_ci dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci switch (info->state) { 32262306a36Sopenharmony_ci case FSM_INIT: 32362306a36Sopenharmony_ci if (info->online && info->present && info->allowed) { 32462306a36Sopenharmony_ci if (vbatt < PRECHARGE_THRESHOLD) { 32562306a36Sopenharmony_ci info->state = FSM_PRECHARGE; 32662306a36Sopenharmony_ci start_precharge(info); 32762306a36Sopenharmony_ci } else if (vbatt > DISCHARGE_THRESHOLD) { 32862306a36Sopenharmony_ci info->state = FSM_DISCHARGE; 32962306a36Sopenharmony_ci stop_charge(info, vbatt); 33062306a36Sopenharmony_ci } else if (vbatt < DISCHARGE_THRESHOLD) { 33162306a36Sopenharmony_ci info->state = FSM_FASTCHARGE; 33262306a36Sopenharmony_ci start_fastcharge(info); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci if (vbatt < POWEROFF_THRESHOLD) { 33662306a36Sopenharmony_ci power_off_notification(info); 33762306a36Sopenharmony_ci } else { 33862306a36Sopenharmony_ci info->state = FSM_DISCHARGE; 33962306a36Sopenharmony_ci stop_charge(info, vbatt); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci case FSM_PRECHARGE: 34462306a36Sopenharmony_ci if (info->online && info->present && info->allowed) { 34562306a36Sopenharmony_ci if (vbatt > PRECHARGE_THRESHOLD) { 34662306a36Sopenharmony_ci info->state = FSM_FASTCHARGE; 34762306a36Sopenharmony_ci start_fastcharge(info); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } else { 35062306a36Sopenharmony_ci info->state = FSM_DISCHARGE; 35162306a36Sopenharmony_ci stop_charge(info, vbatt); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci case FSM_FASTCHARGE: 35562306a36Sopenharmony_ci if (info->online && info->present && info->allowed) { 35662306a36Sopenharmony_ci if (vbatt < PRECHARGE_THRESHOLD) { 35762306a36Sopenharmony_ci info->state = FSM_PRECHARGE; 35862306a36Sopenharmony_ci start_precharge(info); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } else { 36162306a36Sopenharmony_ci info->state = FSM_DISCHARGE; 36262306a36Sopenharmony_ci stop_charge(info, vbatt); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case FSM_DISCHARGE: 36662306a36Sopenharmony_ci if (info->online && info->present && info->allowed) { 36762306a36Sopenharmony_ci if (vbatt < PRECHARGE_THRESHOLD) { 36862306a36Sopenharmony_ci info->state = FSM_PRECHARGE; 36962306a36Sopenharmony_ci start_precharge(info); 37062306a36Sopenharmony_ci } else if (vbatt < DISCHARGE_THRESHOLD) { 37162306a36Sopenharmony_ci info->state = FSM_FASTCHARGE; 37262306a36Sopenharmony_ci start_fastcharge(info); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci if (vbatt < POWEROFF_THRESHOLD) 37662306a36Sopenharmony_ci power_off_notification(info); 37762306a36Sopenharmony_ci else if (vbatt > CHARGE_THRESHOLD && info->online) 37862306a36Sopenharmony_ci set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci default: 38262306a36Sopenharmony_ci dev_warn(info->dev, "FSM meets wrong state:%d\n", 38362306a36Sopenharmony_ci info->state); 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci dev_dbg(info->dev, 38762306a36Sopenharmony_ci "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n", 38862306a36Sopenharmony_ci &fsm_state[info->state][0], 38962306a36Sopenharmony_ci (info->online) ? "online" : "N/A", 39062306a36Sopenharmony_ci (info->present) ? "present" : "N/A", info->allowed); 39162306a36Sopenharmony_ci mutex_unlock(&info->lock); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic irqreturn_t pm860x_charger_handler(int irq, void *data) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct pm860x_charger_info *info = data; 39962306a36Sopenharmony_ci int ret; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci mutex_lock(&info->lock); 40262306a36Sopenharmony_ci ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 40362306a36Sopenharmony_ci if (ret < 0) { 40462306a36Sopenharmony_ci mutex_unlock(&info->lock); 40562306a36Sopenharmony_ci goto out; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci if (ret & STATUS2_CHG) { 40862306a36Sopenharmony_ci info->online = 1; 40962306a36Sopenharmony_ci info->allowed = 1; 41062306a36Sopenharmony_ci } else { 41162306a36Sopenharmony_ci info->online = 0; 41262306a36Sopenharmony_ci info->allowed = 0; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci mutex_unlock(&info->lock); 41562306a36Sopenharmony_ci dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__, 41662306a36Sopenharmony_ci (info->online) ? "online" : "N/A", info->allowed); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci set_charging_fsm(info); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci power_supply_changed(info->usb); 42162306a36Sopenharmony_ciout: 42262306a36Sopenharmony_ci return IRQ_HANDLED; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic irqreturn_t pm860x_temp_handler(int irq, void *data) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct power_supply *psy; 42862306a36Sopenharmony_ci struct pm860x_charger_info *info = data; 42962306a36Sopenharmony_ci union power_supply_propval temp; 43062306a36Sopenharmony_ci int value; 43162306a36Sopenharmony_ci int ret; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci psy = power_supply_get_by_name(pm860x_supplied_to[0]); 43462306a36Sopenharmony_ci if (!psy) 43562306a36Sopenharmony_ci return IRQ_HANDLED; 43662306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp); 43762306a36Sopenharmony_ci if (ret) 43862306a36Sopenharmony_ci goto out; 43962306a36Sopenharmony_ci value = temp.intval / 10; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci mutex_lock(&info->lock); 44262306a36Sopenharmony_ci /* Temperature < -10 C or >40 C, Will not allow charge */ 44362306a36Sopenharmony_ci if (value < -10 || value > 40) 44462306a36Sopenharmony_ci info->allowed = 0; 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci info->allowed = 1; 44762306a36Sopenharmony_ci dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 44862306a36Sopenharmony_ci mutex_unlock(&info->lock); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci set_charging_fsm(info); 45162306a36Sopenharmony_ciout: 45262306a36Sopenharmony_ci power_supply_put(psy); 45362306a36Sopenharmony_ci return IRQ_HANDLED; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic irqreturn_t pm860x_exception_handler(int irq, void *data) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct pm860x_charger_info *info = data; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci mutex_lock(&info->lock); 46162306a36Sopenharmony_ci info->allowed = 0; 46262306a36Sopenharmony_ci mutex_unlock(&info->lock); 46362306a36Sopenharmony_ci dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci set_charging_fsm(info); 46662306a36Sopenharmony_ci return IRQ_HANDLED; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic irqreturn_t pm860x_done_handler(int irq, void *data) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct pm860x_charger_info *info = data; 47262306a36Sopenharmony_ci struct power_supply *psy; 47362306a36Sopenharmony_ci union power_supply_propval val; 47462306a36Sopenharmony_ci int ret; 47562306a36Sopenharmony_ci int vbatt; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci mutex_lock(&info->lock); 47862306a36Sopenharmony_ci /* pre-charge done, will transimit to fast-charge stage */ 47962306a36Sopenharmony_ci if (info->state == FSM_PRECHARGE) { 48062306a36Sopenharmony_ci info->allowed = 1; 48162306a36Sopenharmony_ci goto out; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * Fast charge done, delay to read 48562306a36Sopenharmony_ci * the correct status of CHG_DET. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci mdelay(5); 48862306a36Sopenharmony_ci info->allowed = 0; 48962306a36Sopenharmony_ci psy = power_supply_get_by_name(pm860x_supplied_to[0]); 49062306a36Sopenharmony_ci if (!psy) 49162306a36Sopenharmony_ci goto out; 49262306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, 49362306a36Sopenharmony_ci &val); 49462306a36Sopenharmony_ci if (ret) 49562306a36Sopenharmony_ci goto out_psy_put; 49662306a36Sopenharmony_ci vbatt = val.intval / 1000; 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * CHG_DONE interrupt is faster than CHG_DET interrupt when 49962306a36Sopenharmony_ci * plug in/out usb, So we can not rely on info->online, we 50062306a36Sopenharmony_ci * need check pm8607 status register to check usb is online 50162306a36Sopenharmony_ci * or not, then we can decide it is real charge done 50262306a36Sopenharmony_ci * automatically or it is triggered by usb plug out; 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 50562306a36Sopenharmony_ci if (ret < 0) 50662306a36Sopenharmony_ci goto out_psy_put; 50762306a36Sopenharmony_ci if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG) 50862306a36Sopenharmony_ci power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL, 50962306a36Sopenharmony_ci &val); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ciout_psy_put: 51262306a36Sopenharmony_ci power_supply_put(psy); 51362306a36Sopenharmony_ciout: 51462306a36Sopenharmony_ci mutex_unlock(&info->lock); 51562306a36Sopenharmony_ci dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 51662306a36Sopenharmony_ci set_charging_fsm(info); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return IRQ_HANDLED; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic irqreturn_t pm860x_vbattery_handler(int irq, void *data) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct pm860x_charger_info *info = data; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci mutex_lock(&info->lock); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci set_vbatt_threshold(info, 0, 0); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (info->present && info->online) 53062306a36Sopenharmony_ci info->allowed = 1; 53162306a36Sopenharmony_ci else 53262306a36Sopenharmony_ci info->allowed = 0; 53362306a36Sopenharmony_ci mutex_unlock(&info->lock); 53462306a36Sopenharmony_ci dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci set_charging_fsm(info); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return IRQ_HANDLED; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic irqreturn_t pm860x_vchg_handler(int irq, void *data) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct pm860x_charger_info *info = data; 54462306a36Sopenharmony_ci int vchg = 0; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (info->present) 54762306a36Sopenharmony_ci goto out; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci measure_vchg(info, &vchg); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci mutex_lock(&info->lock); 55262306a36Sopenharmony_ci if (!info->online) { 55362306a36Sopenharmony_ci int status; 55462306a36Sopenharmony_ci /* check if over-temp on pm8606 or not */ 55562306a36Sopenharmony_ci status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS); 55662306a36Sopenharmony_ci if (status & OVER_TEMP_FLAG) { 55762306a36Sopenharmony_ci /* clear over temp flag and set auto recover */ 55862306a36Sopenharmony_ci pm860x_set_bits(info->i2c_8606, PM8606_FLAGS, 55962306a36Sopenharmony_ci OVER_TEMP_FLAG, OVER_TEMP_FLAG); 56062306a36Sopenharmony_ci pm860x_set_bits(info->i2c_8606, 56162306a36Sopenharmony_ci PM8606_VSYS, 56262306a36Sopenharmony_ci OVTEMP_AUTORECOVER, 56362306a36Sopenharmony_ci OVTEMP_AUTORECOVER); 56462306a36Sopenharmony_ci dev_dbg(info->dev, 56562306a36Sopenharmony_ci "%s, pm8606 over-temp occurred\n", __func__); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (vchg > VCHG_NORMAL_CHECK) { 57062306a36Sopenharmony_ci set_vchg_threshold(info, VCHG_OVP_LOW, 0); 57162306a36Sopenharmony_ci info->allowed = 0; 57262306a36Sopenharmony_ci dev_dbg(info->dev, 57362306a36Sopenharmony_ci "%s,pm8607 over-vchg occurred,vchg = %dmv\n", 57462306a36Sopenharmony_ci __func__, vchg); 57562306a36Sopenharmony_ci } else if (vchg < VCHG_OVP_LOW) { 57662306a36Sopenharmony_ci set_vchg_threshold(info, VCHG_NORMAL_LOW, 57762306a36Sopenharmony_ci VCHG_NORMAL_HIGH); 57862306a36Sopenharmony_ci info->allowed = 1; 57962306a36Sopenharmony_ci dev_dbg(info->dev, 58062306a36Sopenharmony_ci "%s,pm8607 over-vchg recover,vchg = %dmv\n", 58162306a36Sopenharmony_ci __func__, vchg); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci mutex_unlock(&info->lock); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 58662306a36Sopenharmony_ci set_charging_fsm(info); 58762306a36Sopenharmony_ciout: 58862306a36Sopenharmony_ci return IRQ_HANDLED; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int pm860x_usb_get_prop(struct power_supply *psy, 59262306a36Sopenharmony_ci enum power_supply_property psp, 59362306a36Sopenharmony_ci union power_supply_propval *val) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct pm860x_charger_info *info = power_supply_get_drvdata(psy); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci switch (psp) { 59862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 59962306a36Sopenharmony_ci if (info->state == FSM_FASTCHARGE || 60062306a36Sopenharmony_ci info->state == FSM_PRECHARGE) 60162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 60262306a36Sopenharmony_ci else 60362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 60662306a36Sopenharmony_ci val->intval = info->online; 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci default: 60962306a36Sopenharmony_ci return -ENODEV; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic enum power_supply_property pm860x_usb_props[] = { 61562306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 61662306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 61762306a36Sopenharmony_ci}; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int pm860x_init_charger(struct pm860x_charger_info *info) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 62462306a36Sopenharmony_ci if (ret < 0) 62562306a36Sopenharmony_ci return ret; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci mutex_lock(&info->lock); 62862306a36Sopenharmony_ci info->state = FSM_INIT; 62962306a36Sopenharmony_ci if (ret & STATUS2_CHG) { 63062306a36Sopenharmony_ci info->online = 1; 63162306a36Sopenharmony_ci info->allowed = 1; 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci info->online = 0; 63462306a36Sopenharmony_ci info->allowed = 0; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci mutex_unlock(&info->lock); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci set_charging_fsm(info); 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic struct pm860x_irq_desc { 64362306a36Sopenharmony_ci const char *name; 64462306a36Sopenharmony_ci irqreturn_t (*handler)(int irq, void *data); 64562306a36Sopenharmony_ci} pm860x_irq_descs[] = { 64662306a36Sopenharmony_ci { "usb supply detect", pm860x_charger_handler }, 64762306a36Sopenharmony_ci { "charge done", pm860x_done_handler }, 64862306a36Sopenharmony_ci { "charge timeout", pm860x_exception_handler }, 64962306a36Sopenharmony_ci { "charge fault", pm860x_exception_handler }, 65062306a36Sopenharmony_ci { "temperature", pm860x_temp_handler }, 65162306a36Sopenharmony_ci { "vbatt", pm860x_vbattery_handler }, 65262306a36Sopenharmony_ci { "vchg", pm860x_vchg_handler }, 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic const struct power_supply_desc pm860x_charger_desc = { 65662306a36Sopenharmony_ci .name = "usb", 65762306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 65862306a36Sopenharmony_ci .properties = pm860x_usb_props, 65962306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(pm860x_usb_props), 66062306a36Sopenharmony_ci .get_property = pm860x_usb_get_prop, 66162306a36Sopenharmony_ci}; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int pm860x_charger_probe(struct platform_device *pdev) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); 66662306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 66762306a36Sopenharmony_ci struct pm860x_charger_info *info; 66862306a36Sopenharmony_ci int ret; 66962306a36Sopenharmony_ci int count; 67062306a36Sopenharmony_ci int i; 67162306a36Sopenharmony_ci int j; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 67462306a36Sopenharmony_ci if (!info) 67562306a36Sopenharmony_ci return -ENOMEM; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci count = pdev->num_resources; 67862306a36Sopenharmony_ci for (i = 0, j = 0; i < count; i++) { 67962306a36Sopenharmony_ci info->irq[j] = platform_get_irq(pdev, i); 68062306a36Sopenharmony_ci if (info->irq[j] < 0) 68162306a36Sopenharmony_ci continue; 68262306a36Sopenharmony_ci j++; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci info->irq_nums = j; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci info->chip = chip; 68762306a36Sopenharmony_ci info->i2c = 68862306a36Sopenharmony_ci (chip->id == CHIP_PM8607) ? chip->client : chip->companion; 68962306a36Sopenharmony_ci info->i2c_8606 = 69062306a36Sopenharmony_ci (chip->id == CHIP_PM8607) ? chip->companion : chip->client; 69162306a36Sopenharmony_ci if (!info->i2c_8606) { 69262306a36Sopenharmony_ci dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n"); 69362306a36Sopenharmony_ci return -EINVAL; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci info->dev = &pdev->dev; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* set init value for the case we are not using battery */ 69862306a36Sopenharmony_ci set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci mutex_init(&info->lock); 70162306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci psy_cfg.drv_data = info; 70462306a36Sopenharmony_ci psy_cfg.supplied_to = pm860x_supplied_to; 70562306a36Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); 70662306a36Sopenharmony_ci info->usb = devm_power_supply_register(&pdev->dev, &pm860x_charger_desc, 70762306a36Sopenharmony_ci &psy_cfg); 70862306a36Sopenharmony_ci if (IS_ERR(info->usb)) { 70962306a36Sopenharmony_ci return PTR_ERR(info->usb); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci pm860x_init_charger(info); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(info->irq); i++) { 71562306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, info->irq[i], NULL, 71662306a36Sopenharmony_ci pm860x_irq_descs[i].handler, 71762306a36Sopenharmony_ci IRQF_ONESHOT, 71862306a36Sopenharmony_ci pm860x_irq_descs[i].name, info); 71962306a36Sopenharmony_ci if (ret < 0) { 72062306a36Sopenharmony_ci dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", 72162306a36Sopenharmony_ci info->irq[i], ret); 72262306a36Sopenharmony_ci return ret; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic struct platform_driver pm860x_charger_driver = { 72962306a36Sopenharmony_ci .driver = { 73062306a36Sopenharmony_ci .name = "88pm860x-charger", 73162306a36Sopenharmony_ci }, 73262306a36Sopenharmony_ci .probe = pm860x_charger_probe, 73362306a36Sopenharmony_ci}; 73462306a36Sopenharmony_cimodule_platform_driver(pm860x_charger_driver); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell 88PM860x Charger driver"); 73762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 738