18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * axp288_charger.c - X-power AXP288 PMIC Charger driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2014 Intel Corporation 78c2ecf20Sopenharmony_ci * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/bitops.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/usb/otg.h> 198c2ecf20Sopenharmony_ci#include <linux/notifier.h> 208c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 218c2ecf20Sopenharmony_ci#include <linux/property.h> 228c2ecf20Sopenharmony_ci#include <linux/mfd/axp20x.h> 238c2ecf20Sopenharmony_ci#include <linux/extcon.h> 248c2ecf20Sopenharmony_ci#include <linux/dmi.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define PS_STAT_VBUS_TRIGGER BIT(0) 278c2ecf20Sopenharmony_ci#define PS_STAT_BAT_CHRG_DIR BIT(2) 288c2ecf20Sopenharmony_ci#define PS_STAT_VBAT_ABOVE_VHOLD BIT(3) 298c2ecf20Sopenharmony_ci#define PS_STAT_VBUS_VALID BIT(4) 308c2ecf20Sopenharmony_ci#define PS_STAT_VBUS_PRESENT BIT(5) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define CHRG_STAT_BAT_SAFE_MODE BIT(3) 338c2ecf20Sopenharmony_ci#define CHRG_STAT_BAT_VALID BIT(4) 348c2ecf20Sopenharmony_ci#define CHRG_STAT_BAT_PRESENT BIT(5) 358c2ecf20Sopenharmony_ci#define CHRG_STAT_CHARGING BIT(6) 368c2ecf20Sopenharmony_ci#define CHRG_STAT_PMIC_OTP BIT(7) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_MASK 0x03 398c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_BIT_POS 0 408c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_900MA 0x0 /* 900mA */ 418c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_1500MA 0x1 /* 1500mA */ 428c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_2000MA 0x2 /* 2000mA */ 438c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_NO_LIM 0x3 /* 2500mA */ 448c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_MASK 0x38 458c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_BIT_POS 0x3 468c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_OFFSET 4000 /* 4000mV */ 478c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_LSB_RES 100 /* 100mV */ 488c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_4400MV 0x4 /* 4400mV */ 498c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VBUS_PATH_DIS BIT(7) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */ 528c2ecf20Sopenharmony_ci#define CHRG_CCCV_CC_BIT_POS 0 538c2ecf20Sopenharmony_ci#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */ 548c2ecf20Sopenharmony_ci#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */ 558c2ecf20Sopenharmony_ci#define CHRG_CCCV_ITERM_20P BIT(4) /* 20% of CC */ 568c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */ 578c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_BIT_POS 5 588c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */ 598c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */ 608c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */ 618c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */ 628c2ecf20Sopenharmony_ci#define CHRG_CCCV_CHG_EN BIT(7) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define CNTL2_CC_TIMEOUT_MASK 0x3 /* 2 bits */ 658c2ecf20Sopenharmony_ci#define CNTL2_CC_TIMEOUT_OFFSET 6 /* 6 Hrs */ 668c2ecf20Sopenharmony_ci#define CNTL2_CC_TIMEOUT_LSB_RES 2 /* 2 Hrs */ 678c2ecf20Sopenharmony_ci#define CNTL2_CC_TIMEOUT_12HRS 0x3 /* 12 Hrs */ 688c2ecf20Sopenharmony_ci#define CNTL2_CHGLED_TYPEB BIT(4) 698c2ecf20Sopenharmony_ci#define CNTL2_CHG_OUT_TURNON BIT(5) 708c2ecf20Sopenharmony_ci#define CNTL2_PC_TIMEOUT_MASK 0xC0 718c2ecf20Sopenharmony_ci#define CNTL2_PC_TIMEOUT_OFFSET 40 /* 40 mins */ 728c2ecf20Sopenharmony_ci#define CNTL2_PC_TIMEOUT_LSB_RES 10 /* 10 mins */ 738c2ecf20Sopenharmony_ci#define CNTL2_PC_TIMEOUT_70MINS 0x3 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define CHRG_ILIM_TEMP_LOOP_EN BIT(3) 768c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_MASK 0xf0 778c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_BIT_POS 4 788c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_100MA 0x0 /* 100mA */ 798c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_500MA 0x1 /* 500mA */ 808c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_900MA 0x2 /* 900mA */ 818c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_1500MA 0x3 /* 1500mA */ 828c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_2000MA 0x4 /* 2000mA */ 838c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_2500MA 0x5 /* 2500mA */ 848c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_3000MA 0x6 /* 3000mA */ 858c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_3500MA 0x7 /* 3500mA */ 868c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_4000MA 0x8 /* 4000mA */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define CHRG_VLTFC_0C 0xA5 /* 0 DegC */ 898c2ecf20Sopenharmony_ci#define CHRG_VHTFC_45C 0x1F /* 45 DegC */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define FG_CNTL_OCV_ADJ_EN BIT(3) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define CV_4100MV 4100 /* 4100mV */ 948c2ecf20Sopenharmony_ci#define CV_4150MV 4150 /* 4150mV */ 958c2ecf20Sopenharmony_ci#define CV_4200MV 4200 /* 4200mV */ 968c2ecf20Sopenharmony_ci#define CV_4350MV 4350 /* 4350mV */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define AXP288_EXTCON_DEV_NAME "axp288_extcon" 998c2ecf20Sopenharmony_ci#define USB_HOST_EXTCON_HID "INT3496" 1008c2ecf20Sopenharmony_ci#define USB_HOST_EXTCON_NAME "INT3496:00" 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cienum { 1038c2ecf20Sopenharmony_ci VBUS_OV_IRQ = 0, 1048c2ecf20Sopenharmony_ci CHARGE_DONE_IRQ, 1058c2ecf20Sopenharmony_ci CHARGE_CHARGING_IRQ, 1068c2ecf20Sopenharmony_ci BAT_SAFE_QUIT_IRQ, 1078c2ecf20Sopenharmony_ci BAT_SAFE_ENTER_IRQ, 1088c2ecf20Sopenharmony_ci QCBTU_IRQ, 1098c2ecf20Sopenharmony_ci CBTU_IRQ, 1108c2ecf20Sopenharmony_ci QCBTO_IRQ, 1118c2ecf20Sopenharmony_ci CBTO_IRQ, 1128c2ecf20Sopenharmony_ci CHRG_INTR_END, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistruct axp288_chrg_info { 1168c2ecf20Sopenharmony_ci struct platform_device *pdev; 1178c2ecf20Sopenharmony_ci struct regmap *regmap; 1188c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *regmap_irqc; 1198c2ecf20Sopenharmony_ci int irq[CHRG_INTR_END]; 1208c2ecf20Sopenharmony_ci struct power_supply *psy_usb; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* OTG/Host mode */ 1238c2ecf20Sopenharmony_ci struct { 1248c2ecf20Sopenharmony_ci struct work_struct work; 1258c2ecf20Sopenharmony_ci struct extcon_dev *cable; 1268c2ecf20Sopenharmony_ci struct notifier_block id_nb; 1278c2ecf20Sopenharmony_ci bool id_short; 1288c2ecf20Sopenharmony_ci } otg; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* SDP/CDP/DCP USB charging cable notifications */ 1318c2ecf20Sopenharmony_ci struct { 1328c2ecf20Sopenharmony_ci struct extcon_dev *edev; 1338c2ecf20Sopenharmony_ci struct notifier_block nb; 1348c2ecf20Sopenharmony_ci struct work_struct work; 1358c2ecf20Sopenharmony_ci } cable; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci int cc; 1388c2ecf20Sopenharmony_ci int cv; 1398c2ecf20Sopenharmony_ci int max_cc; 1408c2ecf20Sopenharmony_ci int max_cv; 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci u8 reg_val; 1468c2ecf20Sopenharmony_ci int ret; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (cc < CHRG_CCCV_CC_OFFSET) 1498c2ecf20Sopenharmony_ci cc = CHRG_CCCV_CC_OFFSET; 1508c2ecf20Sopenharmony_ci else if (cc > info->max_cc) 1518c2ecf20Sopenharmony_ci cc = info->max_cc; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES; 1548c2ecf20Sopenharmony_ci cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET; 1558c2ecf20Sopenharmony_ci reg_val = reg_val << CHRG_CCCV_CC_BIT_POS; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, 1588c2ecf20Sopenharmony_ci AXP20X_CHRG_CTRL1, 1598c2ecf20Sopenharmony_ci CHRG_CCCV_CC_MASK, reg_val); 1608c2ecf20Sopenharmony_ci if (ret >= 0) 1618c2ecf20Sopenharmony_ci info->cc = cc; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci u8 reg_val; 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (cv <= CV_4100MV) { 1728c2ecf20Sopenharmony_ci reg_val = CHRG_CCCV_CV_4100MV; 1738c2ecf20Sopenharmony_ci cv = CV_4100MV; 1748c2ecf20Sopenharmony_ci } else if (cv <= CV_4150MV) { 1758c2ecf20Sopenharmony_ci reg_val = CHRG_CCCV_CV_4150MV; 1768c2ecf20Sopenharmony_ci cv = CV_4150MV; 1778c2ecf20Sopenharmony_ci } else if (cv <= CV_4200MV) { 1788c2ecf20Sopenharmony_ci reg_val = CHRG_CCCV_CV_4200MV; 1798c2ecf20Sopenharmony_ci cv = CV_4200MV; 1808c2ecf20Sopenharmony_ci } else { 1818c2ecf20Sopenharmony_ci reg_val = CHRG_CCCV_CV_4350MV; 1828c2ecf20Sopenharmony_ci cv = CV_4350MV; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci reg_val = reg_val << CHRG_CCCV_CV_BIT_POS; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, 1888c2ecf20Sopenharmony_ci AXP20X_CHRG_CTRL1, 1898c2ecf20Sopenharmony_ci CHRG_CCCV_CV_MASK, reg_val); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (ret >= 0) 1928c2ecf20Sopenharmony_ci info->cv = cv; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci unsigned int val; 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val); 2038c2ecf20Sopenharmony_ci if (ret < 0) 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci val >>= CHRG_VBUS_ILIM_BIT_POS; 2078c2ecf20Sopenharmony_ci switch (val) { 2088c2ecf20Sopenharmony_ci case CHRG_VBUS_ILIM_100MA: 2098c2ecf20Sopenharmony_ci return 100000; 2108c2ecf20Sopenharmony_ci case CHRG_VBUS_ILIM_500MA: 2118c2ecf20Sopenharmony_ci return 500000; 2128c2ecf20Sopenharmony_ci case CHRG_VBUS_ILIM_900MA: 2138c2ecf20Sopenharmony_ci return 900000; 2148c2ecf20Sopenharmony_ci case CHRG_VBUS_ILIM_1500MA: 2158c2ecf20Sopenharmony_ci return 1500000; 2168c2ecf20Sopenharmony_ci case CHRG_VBUS_ILIM_2000MA: 2178c2ecf20Sopenharmony_ci return 2000000; 2188c2ecf20Sopenharmony_ci case CHRG_VBUS_ILIM_2500MA: 2198c2ecf20Sopenharmony_ci return 2500000; 2208c2ecf20Sopenharmony_ci case CHRG_VBUS_ILIM_3000MA: 2218c2ecf20Sopenharmony_ci return 3000000; 2228c2ecf20Sopenharmony_ci case CHRG_VBUS_ILIM_3500MA: 2238c2ecf20Sopenharmony_ci return 3500000; 2248c2ecf20Sopenharmony_ci default: 2258c2ecf20Sopenharmony_ci /* All b1xxx values map to 4000 mA */ 2268c2ecf20Sopenharmony_ci return 4000000; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, 2318c2ecf20Sopenharmony_ci int inlmt) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci u8 reg_val; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (inlmt >= 4000000) 2378c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_4000MA << CHRG_VBUS_ILIM_BIT_POS; 2388c2ecf20Sopenharmony_ci else if (inlmt >= 3500000) 2398c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_3500MA << CHRG_VBUS_ILIM_BIT_POS; 2408c2ecf20Sopenharmony_ci else if (inlmt >= 3000000) 2418c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS; 2428c2ecf20Sopenharmony_ci else if (inlmt >= 2500000) 2438c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS; 2448c2ecf20Sopenharmony_ci else if (inlmt >= 2000000) 2458c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS; 2468c2ecf20Sopenharmony_ci else if (inlmt >= 1500000) 2478c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS; 2488c2ecf20Sopenharmony_ci else if (inlmt >= 900000) 2498c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS; 2508c2ecf20Sopenharmony_ci else if (inlmt >= 500000) 2518c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS; 2528c2ecf20Sopenharmony_ci else 2538c2ecf20Sopenharmony_ci reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL, 2568c2ecf20Sopenharmony_ci CHRG_VBUS_ILIM_MASK, reg_val); 2578c2ecf20Sopenharmony_ci if (ret < 0) 2588c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "charger BAK control %d\n", ret); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int axp288_charger_vbus_path_select(struct axp288_chrg_info *info, 2648c2ecf20Sopenharmony_ci bool enable) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci int ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (enable) 2698c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT, 2708c2ecf20Sopenharmony_ci VBUS_ISPOUT_VBUS_PATH_DIS, 0); 2718c2ecf20Sopenharmony_ci else 2728c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT, 2738c2ecf20Sopenharmony_ci VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (ret < 0) 2768c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return ret; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int axp288_charger_enable_charger(struct axp288_chrg_info *info, 2828c2ecf20Sopenharmony_ci bool enable) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci int ret; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (enable) 2878c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1, 2888c2ecf20Sopenharmony_ci CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN); 2898c2ecf20Sopenharmony_ci else 2908c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1, 2918c2ecf20Sopenharmony_ci CHRG_CCCV_CHG_EN, 0); 2928c2ecf20Sopenharmony_ci if (ret < 0) 2938c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return ret; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int axp288_charger_is_present(struct axp288_chrg_info *info) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci int ret, present = 0; 3018c2ecf20Sopenharmony_ci unsigned int val; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); 3048c2ecf20Sopenharmony_ci if (ret < 0) 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (val & PS_STAT_VBUS_PRESENT) 3088c2ecf20Sopenharmony_ci present = 1; 3098c2ecf20Sopenharmony_ci return present; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int axp288_charger_is_online(struct axp288_chrg_info *info) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int ret, online = 0; 3158c2ecf20Sopenharmony_ci unsigned int val; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); 3188c2ecf20Sopenharmony_ci if (ret < 0) 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (val & PS_STAT_VBUS_VALID) 3228c2ecf20Sopenharmony_ci online = 1; 3238c2ecf20Sopenharmony_ci return online; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int axp288_get_charger_health(struct axp288_chrg_info *info) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci int ret, pwr_stat, chrg_stat; 3298c2ecf20Sopenharmony_ci int health = POWER_SUPPLY_HEALTH_UNKNOWN; 3308c2ecf20Sopenharmony_ci unsigned int val; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); 3338c2ecf20Sopenharmony_ci if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT)) 3348c2ecf20Sopenharmony_ci goto health_read_fail; 3358c2ecf20Sopenharmony_ci else 3368c2ecf20Sopenharmony_ci pwr_stat = val; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val); 3398c2ecf20Sopenharmony_ci if (ret < 0) 3408c2ecf20Sopenharmony_ci goto health_read_fail; 3418c2ecf20Sopenharmony_ci else 3428c2ecf20Sopenharmony_ci chrg_stat = val; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!(pwr_stat & PS_STAT_VBUS_VALID)) 3458c2ecf20Sopenharmony_ci health = POWER_SUPPLY_HEALTH_DEAD; 3468c2ecf20Sopenharmony_ci else if (chrg_stat & CHRG_STAT_PMIC_OTP) 3478c2ecf20Sopenharmony_ci health = POWER_SUPPLY_HEALTH_OVERHEAT; 3488c2ecf20Sopenharmony_ci else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE) 3498c2ecf20Sopenharmony_ci health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; 3508c2ecf20Sopenharmony_ci else 3518c2ecf20Sopenharmony_ci health = POWER_SUPPLY_HEALTH_GOOD; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cihealth_read_fail: 3548c2ecf20Sopenharmony_ci return health; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int axp288_charger_usb_set_property(struct power_supply *psy, 3588c2ecf20Sopenharmony_ci enum power_supply_property psp, 3598c2ecf20Sopenharmony_ci const union power_supply_propval *val) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct axp288_chrg_info *info = power_supply_get_drvdata(psy); 3628c2ecf20Sopenharmony_ci int ret = 0; 3638c2ecf20Sopenharmony_ci int scaled_val; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci switch (psp) { 3668c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 3678c2ecf20Sopenharmony_ci scaled_val = min(val->intval, info->max_cc); 3688c2ecf20Sopenharmony_ci scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); 3698c2ecf20Sopenharmony_ci ret = axp288_charger_set_cc(info, scaled_val); 3708c2ecf20Sopenharmony_ci if (ret < 0) 3718c2ecf20Sopenharmony_ci dev_warn(&info->pdev->dev, "set charge current failed\n"); 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 3748c2ecf20Sopenharmony_ci scaled_val = min(val->intval, info->max_cv); 3758c2ecf20Sopenharmony_ci scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); 3768c2ecf20Sopenharmony_ci ret = axp288_charger_set_cv(info, scaled_val); 3778c2ecf20Sopenharmony_ci if (ret < 0) 3788c2ecf20Sopenharmony_ci dev_warn(&info->pdev->dev, "set charge voltage failed\n"); 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 3818c2ecf20Sopenharmony_ci ret = axp288_charger_set_vbus_inlmt(info, val->intval); 3828c2ecf20Sopenharmony_ci if (ret < 0) 3838c2ecf20Sopenharmony_ci dev_warn(&info->pdev->dev, "set input current limit failed\n"); 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci default: 3868c2ecf20Sopenharmony_ci ret = -EINVAL; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int axp288_charger_usb_get_property(struct power_supply *psy, 3938c2ecf20Sopenharmony_ci enum power_supply_property psp, 3948c2ecf20Sopenharmony_ci union power_supply_propval *val) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct axp288_chrg_info *info = power_supply_get_drvdata(psy); 3978c2ecf20Sopenharmony_ci int ret; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci switch (psp) { 4008c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 4018c2ecf20Sopenharmony_ci /* Check for OTG case first */ 4028c2ecf20Sopenharmony_ci if (info->otg.id_short) { 4038c2ecf20Sopenharmony_ci val->intval = 0; 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci ret = axp288_charger_is_present(info); 4078c2ecf20Sopenharmony_ci if (ret < 0) 4088c2ecf20Sopenharmony_ci return ret; 4098c2ecf20Sopenharmony_ci val->intval = ret; 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 4128c2ecf20Sopenharmony_ci /* Check for OTG case first */ 4138c2ecf20Sopenharmony_ci if (info->otg.id_short) { 4148c2ecf20Sopenharmony_ci val->intval = 0; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci ret = axp288_charger_is_online(info); 4188c2ecf20Sopenharmony_ci if (ret < 0) 4198c2ecf20Sopenharmony_ci return ret; 4208c2ecf20Sopenharmony_ci val->intval = ret; 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 4238c2ecf20Sopenharmony_ci val->intval = axp288_get_charger_health(info); 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 4268c2ecf20Sopenharmony_ci val->intval = info->cc * 1000; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 4298c2ecf20Sopenharmony_ci val->intval = info->max_cc * 1000; 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 4328c2ecf20Sopenharmony_ci val->intval = info->cv * 1000; 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 4358c2ecf20Sopenharmony_ci val->intval = info->max_cv * 1000; 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 4388c2ecf20Sopenharmony_ci ret = axp288_charger_get_vbus_inlmt(info); 4398c2ecf20Sopenharmony_ci if (ret < 0) 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci val->intval = ret; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci default: 4448c2ecf20Sopenharmony_ci return -EINVAL; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int axp288_charger_property_is_writeable(struct power_supply *psy, 4518c2ecf20Sopenharmony_ci enum power_supply_property psp) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci int ret; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci switch (psp) { 4568c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 4578c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 4588c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 4598c2ecf20Sopenharmony_ci ret = 1; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci default: 4628c2ecf20Sopenharmony_ci ret = 0; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic enum power_supply_property axp288_usb_props[] = { 4698c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 4708c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 4718c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TYPE, 4728c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 4738c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 4748c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 4758c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 4768c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 4778c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic const struct power_supply_desc axp288_charger_desc = { 4818c2ecf20Sopenharmony_ci .name = "axp288_charger", 4828c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 4838c2ecf20Sopenharmony_ci .properties = axp288_usb_props, 4848c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(axp288_usb_props), 4858c2ecf20Sopenharmony_ci .get_property = axp288_charger_usb_get_property, 4868c2ecf20Sopenharmony_ci .set_property = axp288_charger_usb_set_property, 4878c2ecf20Sopenharmony_ci .property_is_writeable = axp288_charger_property_is_writeable, 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct axp288_chrg_info *info = dev; 4938c2ecf20Sopenharmony_ci int i; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci for (i = 0; i < CHRG_INTR_END; i++) { 4968c2ecf20Sopenharmony_ci if (info->irq[i] == irq) 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (i >= CHRG_INTR_END) { 5018c2ecf20Sopenharmony_ci dev_warn(&info->pdev->dev, "spurious interrupt!!\n"); 5028c2ecf20Sopenharmony_ci return IRQ_NONE; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci switch (i) { 5068c2ecf20Sopenharmony_ci case VBUS_OV_IRQ: 5078c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n"); 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci case CHARGE_DONE_IRQ: 5108c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "Charging Done INTR\n"); 5118c2ecf20Sopenharmony_ci break; 5128c2ecf20Sopenharmony_ci case CHARGE_CHARGING_IRQ: 5138c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "Start Charging IRQ\n"); 5148c2ecf20Sopenharmony_ci break; 5158c2ecf20Sopenharmony_ci case BAT_SAFE_QUIT_IRQ: 5168c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, 5178c2ecf20Sopenharmony_ci "Quit Safe Mode(restart timer) Charging IRQ\n"); 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci case BAT_SAFE_ENTER_IRQ: 5208c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, 5218c2ecf20Sopenharmony_ci "Enter Safe Mode(timer expire) Charging IRQ\n"); 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci case QCBTU_IRQ: 5248c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, 5258c2ecf20Sopenharmony_ci "Quit Battery Under Temperature(CHRG) INTR\n"); 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci case CBTU_IRQ: 5288c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, 5298c2ecf20Sopenharmony_ci "Hit Battery Under Temperature(CHRG) INTR\n"); 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci case QCBTO_IRQ: 5328c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, 5338c2ecf20Sopenharmony_ci "Quit Battery Over Temperature(CHRG) INTR\n"); 5348c2ecf20Sopenharmony_ci break; 5358c2ecf20Sopenharmony_ci case CBTO_IRQ: 5368c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, 5378c2ecf20Sopenharmony_ci "Hit Battery Over Temperature(CHRG) INTR\n"); 5388c2ecf20Sopenharmony_ci break; 5398c2ecf20Sopenharmony_ci default: 5408c2ecf20Sopenharmony_ci dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n"); 5418c2ecf20Sopenharmony_ci goto out; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci power_supply_changed(info->psy_usb); 5458c2ecf20Sopenharmony_ciout: 5468c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* 5508c2ecf20Sopenharmony_ci * The HP Pavilion x2 10 series comes in a number of variants: 5518c2ecf20Sopenharmony_ci * Bay Trail SoC + AXP288 PMIC, Micro-USB, DMI_BOARD_NAME: "8021" 5528c2ecf20Sopenharmony_ci * Bay Trail SoC + AXP288 PMIC, Type-C, DMI_BOARD_NAME: "815D" 5538c2ecf20Sopenharmony_ci * Cherry Trail SoC + AXP288 PMIC, Type-C, DMI_BOARD_NAME: "813E" 5548c2ecf20Sopenharmony_ci * Cherry Trail SoC + TI PMIC, Type-C, DMI_BOARD_NAME: "827C" or "82F4" 5558c2ecf20Sopenharmony_ci * 5568c2ecf20Sopenharmony_ci * The variants with the AXP288 + Type-C connector are all kinds of special: 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * 1. They use a Type-C connector which the AXP288 does not support, so when 5598c2ecf20Sopenharmony_ci * using a Type-C charger it is not recognized. Unlike most AXP288 devices, 5608c2ecf20Sopenharmony_ci * this model actually has mostly working ACPI AC / Battery code, the ACPI code 5618c2ecf20Sopenharmony_ci * "solves" this by simply setting the input_current_limit to 3A. 5628c2ecf20Sopenharmony_ci * There are still some issues with the ACPI code, so we use this native driver, 5638c2ecf20Sopenharmony_ci * and to solve the charging not working (500mA is not enough) issue we hardcode 5648c2ecf20Sopenharmony_ci * the 3A input_current_limit like the ACPI code does. 5658c2ecf20Sopenharmony_ci * 5668c2ecf20Sopenharmony_ci * 2. If no charger is connected the machine boots with the vbus-path disabled. 5678c2ecf20Sopenharmony_ci * Normally this is done when a 5V boost converter is active to avoid the PMIC 5688c2ecf20Sopenharmony_ci * trying to charge from the 5V boost converter's output. This is done when 5698c2ecf20Sopenharmony_ci * an OTG host cable is inserted and the ID pin on the micro-B receptacle is 5708c2ecf20Sopenharmony_ci * pulled low and the ID pin has an ACPI event handler associated with it 5718c2ecf20Sopenharmony_ci * which re-enables the vbus-path when the ID pin is pulled high when the 5728c2ecf20Sopenharmony_ci * OTG host cable is removed. The Type-C connector has no ID pin, there is 5738c2ecf20Sopenharmony_ci * no ID pin handler and there appears to be no 5V boost converter, so we 5748c2ecf20Sopenharmony_ci * end up not charging because the vbus-path is disabled, until we unplug 5758c2ecf20Sopenharmony_ci * the charger which automatically clears the vbus-path disable bit and then 5768c2ecf20Sopenharmony_ci * on the second plug-in of the adapter we start charging. To solve the not 5778c2ecf20Sopenharmony_ci * charging on first charger plugin we unconditionally enable the vbus-path at 5788c2ecf20Sopenharmony_ci * probe on this model, which is safe since there is no 5V boost converter. 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_cistatic const struct dmi_system_id axp288_hp_x2_dmi_ids[] = { 5818c2ecf20Sopenharmony_ci { 5828c2ecf20Sopenharmony_ci .matches = { 5838c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 5848c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), 5858c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_BOARD_NAME, "815D"), 5868c2ecf20Sopenharmony_ci }, 5878c2ecf20Sopenharmony_ci }, 5888c2ecf20Sopenharmony_ci { 5898c2ecf20Sopenharmony_ci .matches = { 5908c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "HP"), 5918c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), 5928c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_BOARD_NAME, "813E"), 5938c2ecf20Sopenharmony_ci }, 5948c2ecf20Sopenharmony_ci }, 5958c2ecf20Sopenharmony_ci {} /* Terminating entry */ 5968c2ecf20Sopenharmony_ci}; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void axp288_charger_extcon_evt_worker(struct work_struct *work) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct axp288_chrg_info *info = 6018c2ecf20Sopenharmony_ci container_of(work, struct axp288_chrg_info, cable.work); 6028c2ecf20Sopenharmony_ci int ret, current_limit; 6038c2ecf20Sopenharmony_ci struct extcon_dev *edev = info->cable.edev; 6048c2ecf20Sopenharmony_ci unsigned int val; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); 6078c2ecf20Sopenharmony_ci if (ret < 0) { 6088c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret); 6098c2ecf20Sopenharmony_ci return; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Offline? Disable charging and bail */ 6138c2ecf20Sopenharmony_ci if (!(val & PS_STAT_VBUS_VALID)) { 6148c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "USB charger disconnected\n"); 6158c2ecf20Sopenharmony_ci axp288_charger_enable_charger(info, false); 6168c2ecf20Sopenharmony_ci power_supply_changed(info->psy_usb); 6178c2ecf20Sopenharmony_ci return; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* Determine cable/charger type */ 6218c2ecf20Sopenharmony_ci if (dmi_check_system(axp288_hp_x2_dmi_ids)) { 6228c2ecf20Sopenharmony_ci /* See comment above axp288_hp_x2_dmi_ids declaration */ 6238c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n"); 6248c2ecf20Sopenharmony_ci current_limit = 3000000; 6258c2ecf20Sopenharmony_ci } else if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) { 6268c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n"); 6278c2ecf20Sopenharmony_ci current_limit = 500000; 6288c2ecf20Sopenharmony_ci } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) { 6298c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n"); 6308c2ecf20Sopenharmony_ci current_limit = 1500000; 6318c2ecf20Sopenharmony_ci } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) { 6328c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n"); 6338c2ecf20Sopenharmony_ci current_limit = 2000000; 6348c2ecf20Sopenharmony_ci } else { 6358c2ecf20Sopenharmony_ci /* Charger type detection still in progress, bail. */ 6368c2ecf20Sopenharmony_ci return; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* Set vbus current limit first, then enable charger */ 6408c2ecf20Sopenharmony_ci ret = axp288_charger_set_vbus_inlmt(info, current_limit); 6418c2ecf20Sopenharmony_ci if (ret == 0) 6428c2ecf20Sopenharmony_ci axp288_charger_enable_charger(info, true); 6438c2ecf20Sopenharmony_ci else 6448c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, 6458c2ecf20Sopenharmony_ci "error setting current limit (%d)\n", ret); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci power_supply_changed(info->psy_usb); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic int axp288_charger_handle_cable_evt(struct notifier_block *nb, 6518c2ecf20Sopenharmony_ci unsigned long event, void *param) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct axp288_chrg_info *info = 6548c2ecf20Sopenharmony_ci container_of(nb, struct axp288_chrg_info, cable.nb); 6558c2ecf20Sopenharmony_ci schedule_work(&info->cable.work); 6568c2ecf20Sopenharmony_ci return NOTIFY_OK; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic void axp288_charger_otg_evt_worker(struct work_struct *work) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci struct axp288_chrg_info *info = 6628c2ecf20Sopenharmony_ci container_of(work, struct axp288_chrg_info, otg.work); 6638c2ecf20Sopenharmony_ci struct extcon_dev *edev = info->otg.cable; 6648c2ecf20Sopenharmony_ci int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n", 6678c2ecf20Sopenharmony_ci usb_host ? "attached" : "detached"); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* 6708c2ecf20Sopenharmony_ci * Set usb_id_short flag to avoid running charger detection logic 6718c2ecf20Sopenharmony_ci * in case usb host. 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_ci info->otg.id_short = usb_host; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* Disable VBUS path before enabling the 5V boost */ 6768c2ecf20Sopenharmony_ci ret = axp288_charger_vbus_path_select(info, !info->otg.id_short); 6778c2ecf20Sopenharmony_ci if (ret < 0) 6788c2ecf20Sopenharmony_ci dev_warn(&info->pdev->dev, "vbus path disable failed\n"); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic int axp288_charger_handle_otg_evt(struct notifier_block *nb, 6828c2ecf20Sopenharmony_ci unsigned long event, void *param) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct axp288_chrg_info *info = 6858c2ecf20Sopenharmony_ci container_of(nb, struct axp288_chrg_info, otg.id_nb); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci schedule_work(&info->otg.work); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return NOTIFY_OK; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int charger_init_hw_regs(struct axp288_chrg_info *info) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci int ret, cc, cv; 6958c2ecf20Sopenharmony_ci unsigned int val; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* Program temperature thresholds */ 6988c2ecf20Sopenharmony_ci ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C); 6998c2ecf20Sopenharmony_ci if (ret < 0) { 7008c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", 7018c2ecf20Sopenharmony_ci AXP20X_V_LTF_CHRG, ret); 7028c2ecf20Sopenharmony_ci return ret; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C); 7068c2ecf20Sopenharmony_ci if (ret < 0) { 7078c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", 7088c2ecf20Sopenharmony_ci AXP20X_V_HTF_CHRG, ret); 7098c2ecf20Sopenharmony_ci return ret; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* Do not turn-off charger o/p after charge cycle ends */ 7138c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, 7148c2ecf20Sopenharmony_ci AXP20X_CHRG_CTRL2, 7158c2ecf20Sopenharmony_ci CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON); 7168c2ecf20Sopenharmony_ci if (ret < 0) { 7178c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", 7188c2ecf20Sopenharmony_ci AXP20X_CHRG_CTRL2, ret); 7198c2ecf20Sopenharmony_ci return ret; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Setup ending condition for charging to be 10% of I(chrg) */ 7238c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, 7248c2ecf20Sopenharmony_ci AXP20X_CHRG_CTRL1, 7258c2ecf20Sopenharmony_ci CHRG_CCCV_ITERM_20P, 0); 7268c2ecf20Sopenharmony_ci if (ret < 0) { 7278c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", 7288c2ecf20Sopenharmony_ci AXP20X_CHRG_CTRL1, ret); 7298c2ecf20Sopenharmony_ci return ret; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* Disable OCV-SOC curve calibration */ 7338c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, 7348c2ecf20Sopenharmony_ci AXP20X_CC_CTRL, 7358c2ecf20Sopenharmony_ci FG_CNTL_OCV_ADJ_EN, 0); 7368c2ecf20Sopenharmony_ci if (ret < 0) { 7378c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", 7388c2ecf20Sopenharmony_ci AXP20X_CC_CTRL, ret); 7398c2ecf20Sopenharmony_ci return ret; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (dmi_check_system(axp288_hp_x2_dmi_ids)) { 7438c2ecf20Sopenharmony_ci /* See comment above axp288_hp_x2_dmi_ids declaration */ 7448c2ecf20Sopenharmony_ci ret = axp288_charger_vbus_path_select(info, true); 7458c2ecf20Sopenharmony_ci if (ret < 0) 7468c2ecf20Sopenharmony_ci return ret; 7478c2ecf20Sopenharmony_ci } else { 7488c2ecf20Sopenharmony_ci /* Set Vhold to the factory default / recommended 4.4V */ 7498c2ecf20Sopenharmony_ci val = VBUS_ISPOUT_VHOLD_SET_4400MV << VBUS_ISPOUT_VHOLD_SET_BIT_POS; 7508c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT, 7518c2ecf20Sopenharmony_ci VBUS_ISPOUT_VHOLD_SET_MASK, val); 7528c2ecf20Sopenharmony_ci if (ret < 0) { 7538c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", 7548c2ecf20Sopenharmony_ci AXP20X_VBUS_IPSOUT_MGMT, ret); 7558c2ecf20Sopenharmony_ci return ret; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* Read current charge voltage and current limit */ 7608c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val); 7618c2ecf20Sopenharmony_ci if (ret < 0) { 7628c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "register(%x) read error(%d)\n", 7638c2ecf20Sopenharmony_ci AXP20X_CHRG_CTRL1, ret); 7648c2ecf20Sopenharmony_ci return ret; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* Determine charge voltage */ 7688c2ecf20Sopenharmony_ci cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS; 7698c2ecf20Sopenharmony_ci switch (cv) { 7708c2ecf20Sopenharmony_ci case CHRG_CCCV_CV_4100MV: 7718c2ecf20Sopenharmony_ci info->cv = CV_4100MV; 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci case CHRG_CCCV_CV_4150MV: 7748c2ecf20Sopenharmony_ci info->cv = CV_4150MV; 7758c2ecf20Sopenharmony_ci break; 7768c2ecf20Sopenharmony_ci case CHRG_CCCV_CV_4200MV: 7778c2ecf20Sopenharmony_ci info->cv = CV_4200MV; 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci case CHRG_CCCV_CV_4350MV: 7808c2ecf20Sopenharmony_ci info->cv = CV_4350MV; 7818c2ecf20Sopenharmony_ci break; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* Determine charge current limit */ 7858c2ecf20Sopenharmony_ci cc = (val & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS; 7868c2ecf20Sopenharmony_ci cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET; 7878c2ecf20Sopenharmony_ci info->cc = cc; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* 7908c2ecf20Sopenharmony_ci * Do not allow the user to configure higher settings then those 7918c2ecf20Sopenharmony_ci * set by the firmware 7928c2ecf20Sopenharmony_ci */ 7938c2ecf20Sopenharmony_ci info->max_cv = info->cv; 7948c2ecf20Sopenharmony_ci info->max_cc = info->cc; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return 0; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic void axp288_charger_cancel_work(void *data) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct axp288_chrg_info *info = data; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci cancel_work_sync(&info->otg.work); 8048c2ecf20Sopenharmony_ci cancel_work_sync(&info->cable.work); 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic int axp288_charger_probe(struct platform_device *pdev) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci int ret, i, pirq; 8108c2ecf20Sopenharmony_ci struct axp288_chrg_info *info; 8118c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 8128c2ecf20Sopenharmony_ci struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 8138c2ecf20Sopenharmony_ci struct power_supply_config charger_cfg = {}; 8148c2ecf20Sopenharmony_ci unsigned int val; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* 8178c2ecf20Sopenharmony_ci * On some devices the fuelgauge and charger parts of the axp288 are 8188c2ecf20Sopenharmony_ci * not used, check that the fuelgauge is enabled (CC_CTRL != 0). 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_ci ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val); 8218c2ecf20Sopenharmony_ci if (ret < 0) 8228c2ecf20Sopenharmony_ci return ret; 8238c2ecf20Sopenharmony_ci if (val == 0) 8248c2ecf20Sopenharmony_ci return -ENODEV; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 8278c2ecf20Sopenharmony_ci if (!info) 8288c2ecf20Sopenharmony_ci return -ENOMEM; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci info->pdev = pdev; 8318c2ecf20Sopenharmony_ci info->regmap = axp20x->regmap; 8328c2ecf20Sopenharmony_ci info->regmap_irqc = axp20x->regmap_irqc; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); 8358c2ecf20Sopenharmony_ci if (info->cable.edev == NULL) { 8368c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n", 8378c2ecf20Sopenharmony_ci AXP288_EXTCON_DEV_NAME); 8388c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (acpi_dev_present(USB_HOST_EXTCON_HID, NULL, -1)) { 8428c2ecf20Sopenharmony_ci info->otg.cable = extcon_get_extcon_dev(USB_HOST_EXTCON_NAME); 8438c2ecf20Sopenharmony_ci if (info->otg.cable == NULL) { 8448c2ecf20Sopenharmony_ci dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n"); 8458c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 8488c2ecf20Sopenharmony_ci "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n"); 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci ret = charger_init_hw_regs(info); 8548c2ecf20Sopenharmony_ci if (ret) 8558c2ecf20Sopenharmony_ci return ret; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* Register with power supply class */ 8588c2ecf20Sopenharmony_ci charger_cfg.drv_data = info; 8598c2ecf20Sopenharmony_ci info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc, 8608c2ecf20Sopenharmony_ci &charger_cfg); 8618c2ecf20Sopenharmony_ci if (IS_ERR(info->psy_usb)) { 8628c2ecf20Sopenharmony_ci ret = PTR_ERR(info->psy_usb); 8638c2ecf20Sopenharmony_ci dev_err(dev, "failed to register power supply: %d\n", ret); 8648c2ecf20Sopenharmony_ci return ret; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* Cancel our work on cleanup, register this before the notifiers */ 8688c2ecf20Sopenharmony_ci ret = devm_add_action(dev, axp288_charger_cancel_work, info); 8698c2ecf20Sopenharmony_ci if (ret) 8708c2ecf20Sopenharmony_ci return ret; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* Register for extcon notification */ 8738c2ecf20Sopenharmony_ci INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); 8748c2ecf20Sopenharmony_ci info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; 8758c2ecf20Sopenharmony_ci ret = devm_extcon_register_notifier_all(dev, info->cable.edev, 8768c2ecf20Sopenharmony_ci &info->cable.nb); 8778c2ecf20Sopenharmony_ci if (ret) { 8788c2ecf20Sopenharmony_ci dev_err(dev, "failed to register cable extcon notifier\n"); 8798c2ecf20Sopenharmony_ci return ret; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci schedule_work(&info->cable.work); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* Register for OTG notification */ 8848c2ecf20Sopenharmony_ci INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker); 8858c2ecf20Sopenharmony_ci info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt; 8868c2ecf20Sopenharmony_ci if (info->otg.cable) { 8878c2ecf20Sopenharmony_ci ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable, 8888c2ecf20Sopenharmony_ci EXTCON_USB_HOST, &info->otg.id_nb); 8898c2ecf20Sopenharmony_ci if (ret) { 8908c2ecf20Sopenharmony_ci dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n"); 8918c2ecf20Sopenharmony_ci return ret; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci schedule_work(&info->otg.work); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci /* Register charger interrupts */ 8978c2ecf20Sopenharmony_ci for (i = 0; i < CHRG_INTR_END; i++) { 8988c2ecf20Sopenharmony_ci pirq = platform_get_irq(info->pdev, i); 8998c2ecf20Sopenharmony_ci if (pirq < 0) 9008c2ecf20Sopenharmony_ci return pirq; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); 9038c2ecf20Sopenharmony_ci if (info->irq[i] < 0) { 9048c2ecf20Sopenharmony_ci dev_warn(&info->pdev->dev, 9058c2ecf20Sopenharmony_ci "failed to get virtual interrupt=%d\n", pirq); 9068c2ecf20Sopenharmony_ci return info->irq[i]; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i], 9098c2ecf20Sopenharmony_ci NULL, axp288_charger_irq_thread_handler, 9108c2ecf20Sopenharmony_ci IRQF_ONESHOT, info->pdev->name, info); 9118c2ecf20Sopenharmony_ci if (ret) { 9128c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to request interrupt=%d\n", 9138c2ecf20Sopenharmony_ci info->irq[i]); 9148c2ecf20Sopenharmony_ci return ret; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci return 0; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic const struct platform_device_id axp288_charger_id_table[] = { 9228c2ecf20Sopenharmony_ci { .name = "axp288_charger" }, 9238c2ecf20Sopenharmony_ci {}, 9248c2ecf20Sopenharmony_ci}; 9258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, axp288_charger_id_table); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic struct platform_driver axp288_charger_driver = { 9288c2ecf20Sopenharmony_ci .probe = axp288_charger_probe, 9298c2ecf20Sopenharmony_ci .id_table = axp288_charger_id_table, 9308c2ecf20Sopenharmony_ci .driver = { 9318c2ecf20Sopenharmony_ci .name = "axp288_charger", 9328c2ecf20Sopenharmony_ci }, 9338c2ecf20Sopenharmony_ci}; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cimodule_platform_driver(axp288_charger_driver); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>"); 9388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("X-power AXP288 Charger Driver"); 9398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 940