18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Battery driver for Marvell 88PM860x PMIC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Marvell International Ltd. 68c2ecf20Sopenharmony_ci * Author: Jett Zhou <jtzhou@marvell.com> 78c2ecf20Sopenharmony_ci * Haojian Zhuang <haojian.zhuang@marvell.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/88pm860x.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 188c2ecf20Sopenharmony_ci#include <asm/div64.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* bit definitions of Status Query Interface 2 */ 218c2ecf20Sopenharmony_ci#define STATUS2_CHG (1 << 2) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* bit definitions of Reset Out Register */ 248c2ecf20Sopenharmony_ci#define RESET_SW_PD (1 << 7) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* bit definitions of PreReg 1 */ 278c2ecf20Sopenharmony_ci#define PREREG1_90MA (0x0) 288c2ecf20Sopenharmony_ci#define PREREG1_180MA (0x1) 298c2ecf20Sopenharmony_ci#define PREREG1_450MA (0x4) 308c2ecf20Sopenharmony_ci#define PREREG1_540MA (0x5) 318c2ecf20Sopenharmony_ci#define PREREG1_1350MA (0xE) 328c2ecf20Sopenharmony_ci#define PREREG1_VSYS_4_5V (3 << 4) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* bit definitions of Charger Control 1 Register */ 358c2ecf20Sopenharmony_ci#define CC1_MODE_OFF (0) 368c2ecf20Sopenharmony_ci#define CC1_MODE_PRECHARGE (1) 378c2ecf20Sopenharmony_ci#define CC1_MODE_FASTCHARGE (2) 388c2ecf20Sopenharmony_ci#define CC1_MODE_PULSECHARGE (3) 398c2ecf20Sopenharmony_ci#define CC1_ITERM_20MA (0 << 2) 408c2ecf20Sopenharmony_ci#define CC1_ITERM_60MA (2 << 2) 418c2ecf20Sopenharmony_ci#define CC1_VFCHG_4_2V (9 << 4) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* bit definitions of Charger Control 2 Register */ 448c2ecf20Sopenharmony_ci#define CC2_ICHG_100MA (0x1) 458c2ecf20Sopenharmony_ci#define CC2_ICHG_500MA (0x9) 468c2ecf20Sopenharmony_ci#define CC2_ICHG_1000MA (0x13) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* bit definitions of Charger Control 3 Register */ 498c2ecf20Sopenharmony_ci#define CC3_180MIN_TIMEOUT (0x6 << 4) 508c2ecf20Sopenharmony_ci#define CC3_270MIN_TIMEOUT (0x7 << 4) 518c2ecf20Sopenharmony_ci#define CC3_360MIN_TIMEOUT (0xA << 4) 528c2ecf20Sopenharmony_ci#define CC3_DISABLE_TIMEOUT (0xF << 4) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* bit definitions of Charger Control 4 Register */ 558c2ecf20Sopenharmony_ci#define CC4_IPRE_40MA (7) 568c2ecf20Sopenharmony_ci#define CC4_VPCHG_3_2V (3 << 4) 578c2ecf20Sopenharmony_ci#define CC4_IFCHG_MON_EN (1 << 6) 588c2ecf20Sopenharmony_ci#define CC4_BTEMP_MON_EN (1 << 7) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* bit definitions of Charger Control 6 Register */ 618c2ecf20Sopenharmony_ci#define CC6_BAT_OV_EN (1 << 2) 628c2ecf20Sopenharmony_ci#define CC6_BAT_UV_EN (1 << 3) 638c2ecf20Sopenharmony_ci#define CC6_UV_VBAT_SET (0x3 << 6) /* 2.8v */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* bit definitions of Charger Control 7 Register */ 668c2ecf20Sopenharmony_ci#define CC7_BAT_REM_EN (1 << 3) 678c2ecf20Sopenharmony_ci#define CC7_IFSM_EN (1 << 7) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* bit definitions of Measurement Enable 1 Register */ 708c2ecf20Sopenharmony_ci#define MEAS1_VBAT (1 << 0) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* bit definitions of Measurement Enable 3 Register */ 738c2ecf20Sopenharmony_ci#define MEAS3_IBAT_EN (1 << 0) 748c2ecf20Sopenharmony_ci#define MEAS3_CC_EN (1 << 2) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define FSM_INIT 0 778c2ecf20Sopenharmony_ci#define FSM_DISCHARGE 1 788c2ecf20Sopenharmony_ci#define FSM_PRECHARGE 2 798c2ecf20Sopenharmony_ci#define FSM_FASTCHARGE 3 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define PRECHARGE_THRESHOLD 3100 828c2ecf20Sopenharmony_ci#define POWEROFF_THRESHOLD 3400 838c2ecf20Sopenharmony_ci#define CHARGE_THRESHOLD 4000 848c2ecf20Sopenharmony_ci#define DISCHARGE_THRESHOLD 4180 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* over-temperature on PM8606 setting */ 878c2ecf20Sopenharmony_ci#define OVER_TEMP_FLAG (1 << 6) 888c2ecf20Sopenharmony_ci#define OVTEMP_AUTORECOVER (1 << 3) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* over-voltage protect on vchg setting mv */ 918c2ecf20Sopenharmony_ci#define VCHG_NORMAL_LOW 4200 928c2ecf20Sopenharmony_ci#define VCHG_NORMAL_CHECK 5800 938c2ecf20Sopenharmony_ci#define VCHG_NORMAL_HIGH 6000 948c2ecf20Sopenharmony_ci#define VCHG_OVP_LOW 5500 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct pm860x_charger_info { 978c2ecf20Sopenharmony_ci struct pm860x_chip *chip; 988c2ecf20Sopenharmony_ci struct i2c_client *i2c; 998c2ecf20Sopenharmony_ci struct i2c_client *i2c_8606; 1008c2ecf20Sopenharmony_ci struct device *dev; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci struct power_supply *usb; 1038c2ecf20Sopenharmony_ci struct mutex lock; 1048c2ecf20Sopenharmony_ci int irq_nums; 1058c2ecf20Sopenharmony_ci int irq[7]; 1068c2ecf20Sopenharmony_ci unsigned state:3; /* fsm state */ 1078c2ecf20Sopenharmony_ci unsigned online:1; /* usb charger */ 1088c2ecf20Sopenharmony_ci unsigned present:1; /* battery present */ 1098c2ecf20Sopenharmony_ci unsigned allowed:1; 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic char *pm860x_supplied_to[] = { 1138c2ecf20Sopenharmony_ci "battery-monitor", 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int measure_vchg(struct pm860x_charger_info *info, int *data) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci unsigned char buf[2]; 1198c2ecf20Sopenharmony_ci int ret = 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ret = pm860x_bulk_read(info->i2c, PM8607_VCHG_MEAS1, 2, buf); 1228c2ecf20Sopenharmony_ci if (ret < 0) 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f); 1268c2ecf20Sopenharmony_ci /* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */ 1278c2ecf20Sopenharmony_ci *data = ((*data & 0xfff) * 9 * 125) >> 9; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s, vchg: %d mv\n", __func__, *data); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void set_vchg_threshold(struct pm860x_charger_info *info, 1358c2ecf20Sopenharmony_ci int min, int max) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci int data; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* (tmp << 8) * / 5 / 1800 */ 1408c2ecf20Sopenharmony_ci if (min <= 0) 1418c2ecf20Sopenharmony_ci data = 0; 1428c2ecf20Sopenharmony_ci else 1438c2ecf20Sopenharmony_ci data = (min << 5) / 1125; 1448c2ecf20Sopenharmony_ci pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data); 1458c2ecf20Sopenharmony_ci dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (max <= 0) 1488c2ecf20Sopenharmony_ci data = 0xff; 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci data = (max << 5) / 1125; 1518c2ecf20Sopenharmony_ci pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data); 1528c2ecf20Sopenharmony_ci dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void set_vbatt_threshold(struct pm860x_charger_info *info, 1578c2ecf20Sopenharmony_ci int min, int max) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int data; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* (tmp << 8) * 3 / 1800 */ 1628c2ecf20Sopenharmony_ci if (min <= 0) 1638c2ecf20Sopenharmony_ci data = 0; 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci data = (min << 5) / 675; 1668c2ecf20Sopenharmony_ci pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data); 1678c2ecf20Sopenharmony_ci dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (max <= 0) 1708c2ecf20Sopenharmony_ci data = 0xff; 1718c2ecf20Sopenharmony_ci else 1728c2ecf20Sopenharmony_ci data = (max << 5) / 675; 1738c2ecf20Sopenharmony_ci pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data); 1748c2ecf20Sopenharmony_ci dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int start_precharge(struct pm860x_charger_info *info) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci int ret; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci dev_dbg(info->dev, "Start Pre-charging!\n"); 1848c2ecf20Sopenharmony_ci set_vbatt_threshold(info, 0, 0); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, 1878c2ecf20Sopenharmony_ci PREREG1_1350MA | PREREG1_VSYS_4_5V); 1888c2ecf20Sopenharmony_ci if (ret < 0) 1898c2ecf20Sopenharmony_ci goto out; 1908c2ecf20Sopenharmony_ci /* stop charging */ 1918c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 1928c2ecf20Sopenharmony_ci CC1_MODE_OFF); 1938c2ecf20Sopenharmony_ci if (ret < 0) 1948c2ecf20Sopenharmony_ci goto out; 1958c2ecf20Sopenharmony_ci /* set 270 minutes timeout */ 1968c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), 1978c2ecf20Sopenharmony_ci CC3_270MIN_TIMEOUT); 1988c2ecf20Sopenharmony_ci if (ret < 0) 1998c2ecf20Sopenharmony_ci goto out; 2008c2ecf20Sopenharmony_ci /* set precharge current, termination voltage, IBAT & TBAT monitor */ 2018c2ecf20Sopenharmony_ci ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4, 2028c2ecf20Sopenharmony_ci CC4_IPRE_40MA | CC4_VPCHG_3_2V | 2038c2ecf20Sopenharmony_ci CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); 2048c2ecf20Sopenharmony_ci if (ret < 0) 2058c2ecf20Sopenharmony_ci goto out; 2068c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, 2078c2ecf20Sopenharmony_ci CC7_BAT_REM_EN | CC7_IFSM_EN, 2088c2ecf20Sopenharmony_ci CC7_BAT_REM_EN | CC7_IFSM_EN); 2098c2ecf20Sopenharmony_ci if (ret < 0) 2108c2ecf20Sopenharmony_ci goto out; 2118c2ecf20Sopenharmony_ci /* trigger precharge */ 2128c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 2138c2ecf20Sopenharmony_ci CC1_MODE_PRECHARGE); 2148c2ecf20Sopenharmony_ciout: 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int start_fastcharge(struct pm860x_charger_info *info) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int ret; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci dev_dbg(info->dev, "Start Fast-charging!\n"); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* set fastcharge termination current & voltage, disable charging */ 2258c2ecf20Sopenharmony_ci ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1, 2268c2ecf20Sopenharmony_ci CC1_MODE_OFF | CC1_ITERM_60MA | 2278c2ecf20Sopenharmony_ci CC1_VFCHG_4_2V); 2288c2ecf20Sopenharmony_ci if (ret < 0) 2298c2ecf20Sopenharmony_ci goto out; 2308c2ecf20Sopenharmony_ci ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, 2318c2ecf20Sopenharmony_ci PREREG1_540MA | PREREG1_VSYS_4_5V); 2328c2ecf20Sopenharmony_ci if (ret < 0) 2338c2ecf20Sopenharmony_ci goto out; 2348c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, 2358c2ecf20Sopenharmony_ci CC2_ICHG_500MA); 2368c2ecf20Sopenharmony_ci if (ret < 0) 2378c2ecf20Sopenharmony_ci goto out; 2388c2ecf20Sopenharmony_ci /* set 270 minutes timeout */ 2398c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), 2408c2ecf20Sopenharmony_ci CC3_270MIN_TIMEOUT); 2418c2ecf20Sopenharmony_ci if (ret < 0) 2428c2ecf20Sopenharmony_ci goto out; 2438c2ecf20Sopenharmony_ci /* set IBAT & TBAT monitor */ 2448c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4, 2458c2ecf20Sopenharmony_ci CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN, 2468c2ecf20Sopenharmony_ci CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); 2478c2ecf20Sopenharmony_ci if (ret < 0) 2488c2ecf20Sopenharmony_ci goto out; 2498c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6, 2508c2ecf20Sopenharmony_ci CC6_BAT_OV_EN | CC6_BAT_UV_EN | 2518c2ecf20Sopenharmony_ci CC6_UV_VBAT_SET, 2528c2ecf20Sopenharmony_ci CC6_BAT_OV_EN | CC6_BAT_UV_EN | 2538c2ecf20Sopenharmony_ci CC6_UV_VBAT_SET); 2548c2ecf20Sopenharmony_ci if (ret < 0) 2558c2ecf20Sopenharmony_ci goto out; 2568c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, 2578c2ecf20Sopenharmony_ci CC7_BAT_REM_EN | CC7_IFSM_EN, 2588c2ecf20Sopenharmony_ci CC7_BAT_REM_EN | CC7_IFSM_EN); 2598c2ecf20Sopenharmony_ci if (ret < 0) 2608c2ecf20Sopenharmony_ci goto out; 2618c2ecf20Sopenharmony_ci /* launch fast-charge */ 2628c2ecf20Sopenharmony_ci ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 2638c2ecf20Sopenharmony_ci CC1_MODE_FASTCHARGE); 2648c2ecf20Sopenharmony_ci /* vchg threshold setting */ 2658c2ecf20Sopenharmony_ci set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH); 2668c2ecf20Sopenharmony_ciout: 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void stop_charge(struct pm860x_charger_info *info, int vbatt) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci dev_dbg(info->dev, "Stop charging!\n"); 2738c2ecf20Sopenharmony_ci pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF); 2748c2ecf20Sopenharmony_ci if (vbatt > CHARGE_THRESHOLD && info->online) 2758c2ecf20Sopenharmony_ci set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic void power_off_notification(struct pm860x_charger_info *info) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci dev_dbg(info->dev, "Power-off notification!\n"); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int set_charging_fsm(struct pm860x_charger_info *info) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct power_supply *psy; 2868c2ecf20Sopenharmony_ci union power_supply_propval data; 2878c2ecf20Sopenharmony_ci unsigned char fsm_state[][16] = { "init", "discharge", "precharge", 2888c2ecf20Sopenharmony_ci "fastcharge", 2898c2ecf20Sopenharmony_ci }; 2908c2ecf20Sopenharmony_ci int ret; 2918c2ecf20Sopenharmony_ci int vbatt; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci psy = power_supply_get_by_name(pm860x_supplied_to[0]); 2948c2ecf20Sopenharmony_ci if (!psy) 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, 2978c2ecf20Sopenharmony_ci &data); 2988c2ecf20Sopenharmony_ci if (ret) { 2998c2ecf20Sopenharmony_ci power_supply_put(psy); 3008c2ecf20Sopenharmony_ci return ret; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci vbatt = data.intval / 1000; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data); 3058c2ecf20Sopenharmony_ci if (ret) { 3068c2ecf20Sopenharmony_ci power_supply_put(psy); 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci power_supply_put(psy); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 3128c2ecf20Sopenharmony_ci info->present = data.intval; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, " 3158c2ecf20Sopenharmony_ci "Allowed:%d\n", 3168c2ecf20Sopenharmony_ci &fsm_state[info->state][0], 3178c2ecf20Sopenharmony_ci (info->online) ? "online" : "N/A", 3188c2ecf20Sopenharmony_ci (info->present) ? "present" : "N/A", info->allowed); 3198c2ecf20Sopenharmony_ci dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci switch (info->state) { 3228c2ecf20Sopenharmony_ci case FSM_INIT: 3238c2ecf20Sopenharmony_ci if (info->online && info->present && info->allowed) { 3248c2ecf20Sopenharmony_ci if (vbatt < PRECHARGE_THRESHOLD) { 3258c2ecf20Sopenharmony_ci info->state = FSM_PRECHARGE; 3268c2ecf20Sopenharmony_ci start_precharge(info); 3278c2ecf20Sopenharmony_ci } else if (vbatt > DISCHARGE_THRESHOLD) { 3288c2ecf20Sopenharmony_ci info->state = FSM_DISCHARGE; 3298c2ecf20Sopenharmony_ci stop_charge(info, vbatt); 3308c2ecf20Sopenharmony_ci } else if (vbatt < DISCHARGE_THRESHOLD) { 3318c2ecf20Sopenharmony_ci info->state = FSM_FASTCHARGE; 3328c2ecf20Sopenharmony_ci start_fastcharge(info); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci } else { 3358c2ecf20Sopenharmony_ci if (vbatt < POWEROFF_THRESHOLD) { 3368c2ecf20Sopenharmony_ci power_off_notification(info); 3378c2ecf20Sopenharmony_ci } else { 3388c2ecf20Sopenharmony_ci info->state = FSM_DISCHARGE; 3398c2ecf20Sopenharmony_ci stop_charge(info, vbatt); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci case FSM_PRECHARGE: 3448c2ecf20Sopenharmony_ci if (info->online && info->present && info->allowed) { 3458c2ecf20Sopenharmony_ci if (vbatt > PRECHARGE_THRESHOLD) { 3468c2ecf20Sopenharmony_ci info->state = FSM_FASTCHARGE; 3478c2ecf20Sopenharmony_ci start_fastcharge(info); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } else { 3508c2ecf20Sopenharmony_ci info->state = FSM_DISCHARGE; 3518c2ecf20Sopenharmony_ci stop_charge(info, vbatt); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case FSM_FASTCHARGE: 3558c2ecf20Sopenharmony_ci if (info->online && info->present && info->allowed) { 3568c2ecf20Sopenharmony_ci if (vbatt < PRECHARGE_THRESHOLD) { 3578c2ecf20Sopenharmony_ci info->state = FSM_PRECHARGE; 3588c2ecf20Sopenharmony_ci start_precharge(info); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci } else { 3618c2ecf20Sopenharmony_ci info->state = FSM_DISCHARGE; 3628c2ecf20Sopenharmony_ci stop_charge(info, vbatt); 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci case FSM_DISCHARGE: 3668c2ecf20Sopenharmony_ci if (info->online && info->present && info->allowed) { 3678c2ecf20Sopenharmony_ci if (vbatt < PRECHARGE_THRESHOLD) { 3688c2ecf20Sopenharmony_ci info->state = FSM_PRECHARGE; 3698c2ecf20Sopenharmony_ci start_precharge(info); 3708c2ecf20Sopenharmony_ci } else if (vbatt < DISCHARGE_THRESHOLD) { 3718c2ecf20Sopenharmony_ci info->state = FSM_FASTCHARGE; 3728c2ecf20Sopenharmony_ci start_fastcharge(info); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci } else { 3758c2ecf20Sopenharmony_ci if (vbatt < POWEROFF_THRESHOLD) 3768c2ecf20Sopenharmony_ci power_off_notification(info); 3778c2ecf20Sopenharmony_ci else if (vbatt > CHARGE_THRESHOLD && info->online) 3788c2ecf20Sopenharmony_ci set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci default: 3828c2ecf20Sopenharmony_ci dev_warn(info->dev, "FSM meets wrong state:%d\n", 3838c2ecf20Sopenharmony_ci info->state); 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci dev_dbg(info->dev, 3878c2ecf20Sopenharmony_ci "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n", 3888c2ecf20Sopenharmony_ci &fsm_state[info->state][0], 3898c2ecf20Sopenharmony_ci (info->online) ? "online" : "N/A", 3908c2ecf20Sopenharmony_ci (info->present) ? "present" : "N/A", info->allowed); 3918c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic irqreturn_t pm860x_charger_handler(int irq, void *data) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct pm860x_charger_info *info = data; 3998c2ecf20Sopenharmony_ci int ret; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 4028c2ecf20Sopenharmony_ci ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 4038c2ecf20Sopenharmony_ci if (ret < 0) { 4048c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 4058c2ecf20Sopenharmony_ci goto out; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci if (ret & STATUS2_CHG) { 4088c2ecf20Sopenharmony_ci info->online = 1; 4098c2ecf20Sopenharmony_ci info->allowed = 1; 4108c2ecf20Sopenharmony_ci } else { 4118c2ecf20Sopenharmony_ci info->online = 0; 4128c2ecf20Sopenharmony_ci info->allowed = 0; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 4158c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__, 4168c2ecf20Sopenharmony_ci (info->online) ? "online" : "N/A", info->allowed); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci set_charging_fsm(info); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci power_supply_changed(info->usb); 4218c2ecf20Sopenharmony_ciout: 4228c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic irqreturn_t pm860x_temp_handler(int irq, void *data) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct power_supply *psy; 4288c2ecf20Sopenharmony_ci struct pm860x_charger_info *info = data; 4298c2ecf20Sopenharmony_ci union power_supply_propval temp; 4308c2ecf20Sopenharmony_ci int value; 4318c2ecf20Sopenharmony_ci int ret; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci psy = power_supply_get_by_name(pm860x_supplied_to[0]); 4348c2ecf20Sopenharmony_ci if (!psy) 4358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4368c2ecf20Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp); 4378c2ecf20Sopenharmony_ci if (ret) 4388c2ecf20Sopenharmony_ci goto out; 4398c2ecf20Sopenharmony_ci value = temp.intval / 10; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 4428c2ecf20Sopenharmony_ci /* Temperature < -10 C or >40 C, Will not allow charge */ 4438c2ecf20Sopenharmony_ci if (value < -10 || value > 40) 4448c2ecf20Sopenharmony_ci info->allowed = 0; 4458c2ecf20Sopenharmony_ci else 4468c2ecf20Sopenharmony_ci info->allowed = 1; 4478c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 4488c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci set_charging_fsm(info); 4518c2ecf20Sopenharmony_ciout: 4528c2ecf20Sopenharmony_ci power_supply_put(psy); 4538c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic irqreturn_t pm860x_exception_handler(int irq, void *data) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct pm860x_charger_info *info = data; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 4618c2ecf20Sopenharmony_ci info->allowed = 0; 4628c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 4638c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci set_charging_fsm(info); 4668c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic irqreturn_t pm860x_done_handler(int irq, void *data) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct pm860x_charger_info *info = data; 4728c2ecf20Sopenharmony_ci struct power_supply *psy; 4738c2ecf20Sopenharmony_ci union power_supply_propval val; 4748c2ecf20Sopenharmony_ci int ret; 4758c2ecf20Sopenharmony_ci int vbatt; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 4788c2ecf20Sopenharmony_ci /* pre-charge done, will transimit to fast-charge stage */ 4798c2ecf20Sopenharmony_ci if (info->state == FSM_PRECHARGE) { 4808c2ecf20Sopenharmony_ci info->allowed = 1; 4818c2ecf20Sopenharmony_ci goto out; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci /* 4848c2ecf20Sopenharmony_ci * Fast charge done, delay to read 4858c2ecf20Sopenharmony_ci * the correct status of CHG_DET. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci mdelay(5); 4888c2ecf20Sopenharmony_ci info->allowed = 0; 4898c2ecf20Sopenharmony_ci psy = power_supply_get_by_name(pm860x_supplied_to[0]); 4908c2ecf20Sopenharmony_ci if (!psy) 4918c2ecf20Sopenharmony_ci goto out; 4928c2ecf20Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, 4938c2ecf20Sopenharmony_ci &val); 4948c2ecf20Sopenharmony_ci if (ret) 4958c2ecf20Sopenharmony_ci goto out_psy_put; 4968c2ecf20Sopenharmony_ci vbatt = val.intval / 1000; 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * CHG_DONE interrupt is faster than CHG_DET interrupt when 4998c2ecf20Sopenharmony_ci * plug in/out usb, So we can not rely on info->online, we 5008c2ecf20Sopenharmony_ci * need check pm8607 status register to check usb is online 5018c2ecf20Sopenharmony_ci * or not, then we can decide it is real charge done 5028c2ecf20Sopenharmony_ci * automatically or it is triggered by usb plug out; 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 5058c2ecf20Sopenharmony_ci if (ret < 0) 5068c2ecf20Sopenharmony_ci goto out_psy_put; 5078c2ecf20Sopenharmony_ci if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG) 5088c2ecf20Sopenharmony_ci power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL, 5098c2ecf20Sopenharmony_ci &val); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ciout_psy_put: 5128c2ecf20Sopenharmony_ci power_supply_put(psy); 5138c2ecf20Sopenharmony_ciout: 5148c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 5158c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 5168c2ecf20Sopenharmony_ci set_charging_fsm(info); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic irqreturn_t pm860x_vbattery_handler(int irq, void *data) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct pm860x_charger_info *info = data; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci set_vbatt_threshold(info, 0, 0); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (info->present && info->online) 5308c2ecf20Sopenharmony_ci info->allowed = 1; 5318c2ecf20Sopenharmony_ci else 5328c2ecf20Sopenharmony_ci info->allowed = 0; 5338c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 5348c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci set_charging_fsm(info); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic irqreturn_t pm860x_vchg_handler(int irq, void *data) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct pm860x_charger_info *info = data; 5448c2ecf20Sopenharmony_ci int vchg = 0; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (info->present) 5478c2ecf20Sopenharmony_ci goto out; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci measure_vchg(info, &vchg); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 5528c2ecf20Sopenharmony_ci if (!info->online) { 5538c2ecf20Sopenharmony_ci int status; 5548c2ecf20Sopenharmony_ci /* check if over-temp on pm8606 or not */ 5558c2ecf20Sopenharmony_ci status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS); 5568c2ecf20Sopenharmony_ci if (status & OVER_TEMP_FLAG) { 5578c2ecf20Sopenharmony_ci /* clear over temp flag and set auto recover */ 5588c2ecf20Sopenharmony_ci pm860x_set_bits(info->i2c_8606, PM8606_FLAGS, 5598c2ecf20Sopenharmony_ci OVER_TEMP_FLAG, OVER_TEMP_FLAG); 5608c2ecf20Sopenharmony_ci pm860x_set_bits(info->i2c_8606, 5618c2ecf20Sopenharmony_ci PM8606_VSYS, 5628c2ecf20Sopenharmony_ci OVTEMP_AUTORECOVER, 5638c2ecf20Sopenharmony_ci OVTEMP_AUTORECOVER); 5648c2ecf20Sopenharmony_ci dev_dbg(info->dev, 5658c2ecf20Sopenharmony_ci "%s, pm8606 over-temp occurred\n", __func__); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (vchg > VCHG_NORMAL_CHECK) { 5708c2ecf20Sopenharmony_ci set_vchg_threshold(info, VCHG_OVP_LOW, 0); 5718c2ecf20Sopenharmony_ci info->allowed = 0; 5728c2ecf20Sopenharmony_ci dev_dbg(info->dev, 5738c2ecf20Sopenharmony_ci "%s,pm8607 over-vchg occurred,vchg = %dmv\n", 5748c2ecf20Sopenharmony_ci __func__, vchg); 5758c2ecf20Sopenharmony_ci } else if (vchg < VCHG_OVP_LOW) { 5768c2ecf20Sopenharmony_ci set_vchg_threshold(info, VCHG_NORMAL_LOW, 5778c2ecf20Sopenharmony_ci VCHG_NORMAL_HIGH); 5788c2ecf20Sopenharmony_ci info->allowed = 1; 5798c2ecf20Sopenharmony_ci dev_dbg(info->dev, 5808c2ecf20Sopenharmony_ci "%s,pm8607 over-vchg recover,vchg = %dmv\n", 5818c2ecf20Sopenharmony_ci __func__, vchg); 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 5868c2ecf20Sopenharmony_ci set_charging_fsm(info); 5878c2ecf20Sopenharmony_ciout: 5888c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic int pm860x_usb_get_prop(struct power_supply *psy, 5928c2ecf20Sopenharmony_ci enum power_supply_property psp, 5938c2ecf20Sopenharmony_ci union power_supply_propval *val) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct pm860x_charger_info *info = power_supply_get_drvdata(psy); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci switch (psp) { 5988c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 5998c2ecf20Sopenharmony_ci if (info->state == FSM_FASTCHARGE || 6008c2ecf20Sopenharmony_ci info->state == FSM_PRECHARGE) 6018c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 6028c2ecf20Sopenharmony_ci else 6038c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 6068c2ecf20Sopenharmony_ci val->intval = info->online; 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci default: 6098c2ecf20Sopenharmony_ci return -ENODEV; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic enum power_supply_property pm860x_usb_props[] = { 6158c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 6168c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 6178c2ecf20Sopenharmony_ci}; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int pm860x_init_charger(struct pm860x_charger_info *info) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci int ret; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 6248c2ecf20Sopenharmony_ci if (ret < 0) 6258c2ecf20Sopenharmony_ci return ret; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 6288c2ecf20Sopenharmony_ci info->state = FSM_INIT; 6298c2ecf20Sopenharmony_ci if (ret & STATUS2_CHG) { 6308c2ecf20Sopenharmony_ci info->online = 1; 6318c2ecf20Sopenharmony_ci info->allowed = 1; 6328c2ecf20Sopenharmony_ci } else { 6338c2ecf20Sopenharmony_ci info->online = 0; 6348c2ecf20Sopenharmony_ci info->allowed = 0; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci set_charging_fsm(info); 6398c2ecf20Sopenharmony_ci return 0; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic struct pm860x_irq_desc { 6438c2ecf20Sopenharmony_ci const char *name; 6448c2ecf20Sopenharmony_ci irqreturn_t (*handler)(int irq, void *data); 6458c2ecf20Sopenharmony_ci} pm860x_irq_descs[] = { 6468c2ecf20Sopenharmony_ci { "usb supply detect", pm860x_charger_handler }, 6478c2ecf20Sopenharmony_ci { "charge done", pm860x_done_handler }, 6488c2ecf20Sopenharmony_ci { "charge timeout", pm860x_exception_handler }, 6498c2ecf20Sopenharmony_ci { "charge fault", pm860x_exception_handler }, 6508c2ecf20Sopenharmony_ci { "temperature", pm860x_temp_handler }, 6518c2ecf20Sopenharmony_ci { "vbatt", pm860x_vbattery_handler }, 6528c2ecf20Sopenharmony_ci { "vchg", pm860x_vchg_handler }, 6538c2ecf20Sopenharmony_ci}; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic const struct power_supply_desc pm860x_charger_desc = { 6568c2ecf20Sopenharmony_ci .name = "usb", 6578c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 6588c2ecf20Sopenharmony_ci .properties = pm860x_usb_props, 6598c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(pm860x_usb_props), 6608c2ecf20Sopenharmony_ci .get_property = pm860x_usb_get_prop, 6618c2ecf20Sopenharmony_ci}; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int pm860x_charger_probe(struct platform_device *pdev) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); 6668c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 6678c2ecf20Sopenharmony_ci struct pm860x_charger_info *info; 6688c2ecf20Sopenharmony_ci int ret; 6698c2ecf20Sopenharmony_ci int count; 6708c2ecf20Sopenharmony_ci int i; 6718c2ecf20Sopenharmony_ci int j; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 6748c2ecf20Sopenharmony_ci if (!info) 6758c2ecf20Sopenharmony_ci return -ENOMEM; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci count = pdev->num_resources; 6788c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < count; i++) { 6798c2ecf20Sopenharmony_ci info->irq[j] = platform_get_irq(pdev, i); 6808c2ecf20Sopenharmony_ci if (info->irq[j] < 0) 6818c2ecf20Sopenharmony_ci continue; 6828c2ecf20Sopenharmony_ci j++; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci info->irq_nums = j; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci info->chip = chip; 6878c2ecf20Sopenharmony_ci info->i2c = 6888c2ecf20Sopenharmony_ci (chip->id == CHIP_PM8607) ? chip->client : chip->companion; 6898c2ecf20Sopenharmony_ci info->i2c_8606 = 6908c2ecf20Sopenharmony_ci (chip->id == CHIP_PM8607) ? chip->companion : chip->client; 6918c2ecf20Sopenharmony_ci if (!info->i2c_8606) { 6928c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n"); 6938c2ecf20Sopenharmony_ci ret = -EINVAL; 6948c2ecf20Sopenharmony_ci goto out; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* set init value for the case we are not using battery */ 6998c2ecf20Sopenharmony_ci set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci mutex_init(&info->lock); 7028c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci psy_cfg.drv_data = info; 7058c2ecf20Sopenharmony_ci psy_cfg.supplied_to = pm860x_supplied_to; 7068c2ecf20Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); 7078c2ecf20Sopenharmony_ci info->usb = power_supply_register(&pdev->dev, &pm860x_charger_desc, 7088c2ecf20Sopenharmony_ci &psy_cfg); 7098c2ecf20Sopenharmony_ci if (IS_ERR(info->usb)) { 7108c2ecf20Sopenharmony_ci ret = PTR_ERR(info->usb); 7118c2ecf20Sopenharmony_ci goto out; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci pm860x_init_charger(info); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(info->irq); i++) { 7178c2ecf20Sopenharmony_ci ret = request_threaded_irq(info->irq[i], NULL, 7188c2ecf20Sopenharmony_ci pm860x_irq_descs[i].handler, 7198c2ecf20Sopenharmony_ci IRQF_ONESHOT, pm860x_irq_descs[i].name, info); 7208c2ecf20Sopenharmony_ci if (ret < 0) { 7218c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", 7228c2ecf20Sopenharmony_ci info->irq[i], ret); 7238c2ecf20Sopenharmony_ci goto out_irq; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ciout_irq: 7298c2ecf20Sopenharmony_ci power_supply_unregister(info->usb); 7308c2ecf20Sopenharmony_ci while (--i >= 0) 7318c2ecf20Sopenharmony_ci free_irq(info->irq[i], info); 7328c2ecf20Sopenharmony_ciout: 7338c2ecf20Sopenharmony_ci return ret; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int pm860x_charger_remove(struct platform_device *pdev) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct pm860x_charger_info *info = platform_get_drvdata(pdev); 7398c2ecf20Sopenharmony_ci int i; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci power_supply_unregister(info->usb); 7428c2ecf20Sopenharmony_ci for (i = 0; i < info->irq_nums; i++) 7438c2ecf20Sopenharmony_ci free_irq(info->irq[i], info); 7448c2ecf20Sopenharmony_ci return 0; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic struct platform_driver pm860x_charger_driver = { 7488c2ecf20Sopenharmony_ci .driver = { 7498c2ecf20Sopenharmony_ci .name = "88pm860x-charger", 7508c2ecf20Sopenharmony_ci }, 7518c2ecf20Sopenharmony_ci .probe = pm860x_charger_probe, 7528c2ecf20Sopenharmony_ci .remove = pm860x_charger_remove, 7538c2ecf20Sopenharmony_ci}; 7548c2ecf20Sopenharmony_cimodule_platform_driver(pm860x_charger_driver); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell 88PM860x Charger driver"); 7578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 758