18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for LP8727 Micro/Mini USB IC with integrated charger 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 National Semiconductor 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_data/lp8727.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define LP8788_NUM_INTREGS 2 188c2ecf20Sopenharmony_ci#define DEFAULT_DEBOUNCE_MSEC 270 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Registers */ 218c2ecf20Sopenharmony_ci#define LP8727_CTRL1 0x1 228c2ecf20Sopenharmony_ci#define LP8727_CTRL2 0x2 238c2ecf20Sopenharmony_ci#define LP8727_SWCTRL 0x3 248c2ecf20Sopenharmony_ci#define LP8727_INT1 0x4 258c2ecf20Sopenharmony_ci#define LP8727_INT2 0x5 268c2ecf20Sopenharmony_ci#define LP8727_STATUS1 0x6 278c2ecf20Sopenharmony_ci#define LP8727_STATUS2 0x7 288c2ecf20Sopenharmony_ci#define LP8727_CHGCTRL2 0x9 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* CTRL1 register */ 318c2ecf20Sopenharmony_ci#define LP8727_CP_EN BIT(0) 328c2ecf20Sopenharmony_ci#define LP8727_ADC_EN BIT(1) 338c2ecf20Sopenharmony_ci#define LP8727_ID200_EN BIT(4) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* CTRL2 register */ 368c2ecf20Sopenharmony_ci#define LP8727_CHGDET_EN BIT(1) 378c2ecf20Sopenharmony_ci#define LP8727_INT_EN BIT(6) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* SWCTRL register */ 408c2ecf20Sopenharmony_ci#define LP8727_SW_DM1_DM (0x0 << 0) 418c2ecf20Sopenharmony_ci#define LP8727_SW_DM1_HiZ (0x7 << 0) 428c2ecf20Sopenharmony_ci#define LP8727_SW_DP2_DP (0x0 << 3) 438c2ecf20Sopenharmony_ci#define LP8727_SW_DP2_HiZ (0x7 << 3) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* INT1 register */ 468c2ecf20Sopenharmony_ci#define LP8727_IDNO (0xF << 0) 478c2ecf20Sopenharmony_ci#define LP8727_VBUS BIT(4) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* STATUS1 register */ 508c2ecf20Sopenharmony_ci#define LP8727_CHGSTAT (3 << 4) 518c2ecf20Sopenharmony_ci#define LP8727_CHPORT BIT(6) 528c2ecf20Sopenharmony_ci#define LP8727_DCPORT BIT(7) 538c2ecf20Sopenharmony_ci#define LP8727_STAT_EOC 0x30 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* STATUS2 register */ 568c2ecf20Sopenharmony_ci#define LP8727_TEMP_STAT (3 << 5) 578c2ecf20Sopenharmony_ci#define LP8727_TEMP_SHIFT 5 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* CHGCTRL2 register */ 608c2ecf20Sopenharmony_ci#define LP8727_ICHG_SHIFT 4 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cienum lp8727_dev_id { 638c2ecf20Sopenharmony_ci LP8727_ID_NONE, 648c2ecf20Sopenharmony_ci LP8727_ID_TA, 658c2ecf20Sopenharmony_ci LP8727_ID_DEDICATED_CHG, 668c2ecf20Sopenharmony_ci LP8727_ID_USB_CHG, 678c2ecf20Sopenharmony_ci LP8727_ID_USB_DS, 688c2ecf20Sopenharmony_ci LP8727_ID_MAX, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cienum lp8727_die_temp { 728c2ecf20Sopenharmony_ci LP8788_TEMP_75C, 738c2ecf20Sopenharmony_ci LP8788_TEMP_95C, 748c2ecf20Sopenharmony_ci LP8788_TEMP_115C, 758c2ecf20Sopenharmony_ci LP8788_TEMP_135C, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct lp8727_psy { 798c2ecf20Sopenharmony_ci struct power_supply *ac; 808c2ecf20Sopenharmony_ci struct power_supply *usb; 818c2ecf20Sopenharmony_ci struct power_supply *batt; 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct lp8727_chg { 858c2ecf20Sopenharmony_ci struct device *dev; 868c2ecf20Sopenharmony_ci struct i2c_client *client; 878c2ecf20Sopenharmony_ci struct mutex xfer_lock; 888c2ecf20Sopenharmony_ci struct lp8727_psy *psy; 898c2ecf20Sopenharmony_ci struct lp8727_platform_data *pdata; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Charger Data */ 928c2ecf20Sopenharmony_ci enum lp8727_dev_id devid; 938c2ecf20Sopenharmony_ci struct lp8727_chg_param *chg_param; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Interrupt Handling */ 968c2ecf20Sopenharmony_ci int irq; 978c2ecf20Sopenharmony_ci struct delayed_work work; 988c2ecf20Sopenharmony_ci unsigned long debounce_jiffies; 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci s32 ret; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci mutex_lock(&pchg->xfer_lock); 1068c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data); 1078c2ecf20Sopenharmony_ci mutex_unlock(&pchg->xfer_lock); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return (ret != len) ? -EIO : 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci return lp8727_read_bytes(pchg, reg, data, 1); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int ret; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci mutex_lock(&pchg->xfer_lock); 1228c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(pchg->client, reg, data); 1238c2ecf20Sopenharmony_ci mutex_unlock(&pchg->xfer_lock); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic bool lp8727_is_charger_attached(const char *name, int id) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci if (!strcmp(name, "ac")) 1318c2ecf20Sopenharmony_ci return id == LP8727_ID_TA || id == LP8727_ID_DEDICATED_CHG; 1328c2ecf20Sopenharmony_ci else if (!strcmp(name, "usb")) 1338c2ecf20Sopenharmony_ci return id == LP8727_ID_USB_CHG; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return id >= LP8727_ID_TA && id <= LP8727_ID_USB_CHG; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int lp8727_init_device(struct lp8727_chg *pchg) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u8 val; 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci u8 intstat[LP8788_NUM_INTREGS]; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* clear interrupts */ 1458c2ecf20Sopenharmony_ci ret = lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS); 1468c2ecf20Sopenharmony_ci if (ret) 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci val = LP8727_ID200_EN | LP8727_ADC_EN | LP8727_CP_EN; 1508c2ecf20Sopenharmony_ci ret = lp8727_write_byte(pchg, LP8727_CTRL1, val); 1518c2ecf20Sopenharmony_ci if (ret) 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci val = LP8727_INT_EN | LP8727_CHGDET_EN; 1558c2ecf20Sopenharmony_ci return lp8727_write_byte(pchg, LP8727_CTRL2, val); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int lp8727_is_dedicated_charger(struct lp8727_chg *pchg) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci u8 val; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci lp8727_read_byte(pchg, LP8727_STATUS1, &val); 1638c2ecf20Sopenharmony_ci return val & LP8727_DCPORT; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int lp8727_is_usb_charger(struct lp8727_chg *pchg) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci u8 val; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci lp8727_read_byte(pchg, LP8727_STATUS1, &val); 1718c2ecf20Sopenharmony_ci return val & LP8727_CHPORT; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic inline void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci lp8727_write_byte(pchg, LP8727_SWCTRL, sw); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct lp8727_platform_data *pdata = pchg->pdata; 1828c2ecf20Sopenharmony_ci u8 devid = LP8727_ID_NONE; 1838c2ecf20Sopenharmony_ci u8 swctrl = LP8727_SW_DM1_HiZ | LP8727_SW_DP2_HiZ; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci switch (id) { 1868c2ecf20Sopenharmony_ci case 0x5: 1878c2ecf20Sopenharmony_ci devid = LP8727_ID_TA; 1888c2ecf20Sopenharmony_ci pchg->chg_param = pdata ? pdata->ac : NULL; 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci case 0xB: 1918c2ecf20Sopenharmony_ci if (lp8727_is_dedicated_charger(pchg)) { 1928c2ecf20Sopenharmony_ci pchg->chg_param = pdata ? pdata->ac : NULL; 1938c2ecf20Sopenharmony_ci devid = LP8727_ID_DEDICATED_CHG; 1948c2ecf20Sopenharmony_ci } else if (lp8727_is_usb_charger(pchg)) { 1958c2ecf20Sopenharmony_ci pchg->chg_param = pdata ? pdata->usb : NULL; 1968c2ecf20Sopenharmony_ci devid = LP8727_ID_USB_CHG; 1978c2ecf20Sopenharmony_ci swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP; 1988c2ecf20Sopenharmony_ci } else if (vbusin) { 1998c2ecf20Sopenharmony_ci devid = LP8727_ID_USB_DS; 2008c2ecf20Sopenharmony_ci swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci default: 2048c2ecf20Sopenharmony_ci devid = LP8727_ID_NONE; 2058c2ecf20Sopenharmony_ci pchg->chg_param = NULL; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci pchg->devid = devid; 2108c2ecf20Sopenharmony_ci lp8727_ctrl_switch(pchg, swctrl); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void lp8727_enable_chgdet(struct lp8727_chg *pchg) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci u8 val; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci lp8727_read_byte(pchg, LP8727_CTRL2, &val); 2188c2ecf20Sopenharmony_ci val |= LP8727_CHGDET_EN; 2198c2ecf20Sopenharmony_ci lp8727_write_byte(pchg, LP8727_CTRL2, val); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void lp8727_delayed_func(struct work_struct *_work) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct lp8727_chg *pchg = container_of(_work, struct lp8727_chg, 2258c2ecf20Sopenharmony_ci work.work); 2268c2ecf20Sopenharmony_ci u8 intstat[LP8788_NUM_INTREGS]; 2278c2ecf20Sopenharmony_ci u8 idno; 2288c2ecf20Sopenharmony_ci u8 vbus; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS)) { 2318c2ecf20Sopenharmony_ci dev_err(pchg->dev, "can not read INT registers\n"); 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci idno = intstat[0] & LP8727_IDNO; 2368c2ecf20Sopenharmony_ci vbus = intstat[0] & LP8727_VBUS; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci lp8727_id_detection(pchg, idno, vbus); 2398c2ecf20Sopenharmony_ci lp8727_enable_chgdet(pchg); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci power_supply_changed(pchg->psy->ac); 2428c2ecf20Sopenharmony_ci power_supply_changed(pchg->psy->usb); 2438c2ecf20Sopenharmony_ci power_supply_changed(pchg->psy->batt); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic irqreturn_t lp8727_isr_func(int irq, void *ptr) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct lp8727_chg *pchg = ptr; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci schedule_delayed_work(&pchg->work, pchg->debounce_jiffies); 2518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int lp8727_setup_irq(struct lp8727_chg *pchg) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci int irq = pchg->client->irq; 2588c2ecf20Sopenharmony_ci unsigned delay_msec = pchg->pdata ? pchg->pdata->debounce_msec : 2598c2ecf20Sopenharmony_ci DEFAULT_DEBOUNCE_MSEC; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (irq <= 0) { 2648c2ecf20Sopenharmony_ci dev_warn(pchg->dev, "invalid irq number: %d\n", irq); 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ret = request_threaded_irq(irq, NULL, lp8727_isr_func, 2698c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 2708c2ecf20Sopenharmony_ci "lp8727_irq", pchg); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (ret) 2738c2ecf20Sopenharmony_ci return ret; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci pchg->irq = irq; 2768c2ecf20Sopenharmony_ci pchg->debounce_jiffies = msecs_to_jiffies(delay_msec); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void lp8727_release_irq(struct lp8727_chg *pchg) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&pchg->work); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (pchg->irq) 2868c2ecf20Sopenharmony_ci free_irq(pchg->irq, pchg); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic enum power_supply_property lp8727_charger_prop[] = { 2908c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic enum power_supply_property lp8727_battery_prop[] = { 2948c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 2958c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 2968c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 2978c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 2988c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 2998c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic char *battery_supplied_to[] = { 3038c2ecf20Sopenharmony_ci "main_batt", 3048c2ecf20Sopenharmony_ci}; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int lp8727_charger_get_property(struct power_supply *psy, 3078c2ecf20Sopenharmony_ci enum power_supply_property psp, 3088c2ecf20Sopenharmony_ci union power_supply_propval *val) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (psp != POWER_SUPPLY_PROP_ONLINE) 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci val->intval = lp8727_is_charger_attached(psy->desc->name, pchg->devid); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic bool lp8727_is_high_temperature(enum lp8727_die_temp temp) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci switch (temp) { 3238c2ecf20Sopenharmony_ci case LP8788_TEMP_95C: 3248c2ecf20Sopenharmony_ci case LP8788_TEMP_115C: 3258c2ecf20Sopenharmony_ci case LP8788_TEMP_135C: 3268c2ecf20Sopenharmony_ci return true; 3278c2ecf20Sopenharmony_ci default: 3288c2ecf20Sopenharmony_ci return false; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int lp8727_battery_get_property(struct power_supply *psy, 3338c2ecf20Sopenharmony_ci enum power_supply_property psp, 3348c2ecf20Sopenharmony_ci union power_supply_propval *val) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent); 3378c2ecf20Sopenharmony_ci struct lp8727_platform_data *pdata = pchg->pdata; 3388c2ecf20Sopenharmony_ci enum lp8727_die_temp temp; 3398c2ecf20Sopenharmony_ci u8 read; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci switch (psp) { 3428c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 3438c2ecf20Sopenharmony_ci if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid)) { 3448c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci lp8727_read_byte(pchg, LP8727_STATUS1, &read); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci val->intval = (read & LP8727_CHGSTAT) == LP8727_STAT_EOC ? 3518c2ecf20Sopenharmony_ci POWER_SUPPLY_STATUS_FULL : 3528c2ecf20Sopenharmony_ci POWER_SUPPLY_STATUS_CHARGING; 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 3558c2ecf20Sopenharmony_ci lp8727_read_byte(pchg, LP8727_STATUS2, &read); 3568c2ecf20Sopenharmony_ci temp = (read & LP8727_TEMP_STAT) >> LP8727_TEMP_SHIFT; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci val->intval = lp8727_is_high_temperature(temp) ? 3598c2ecf20Sopenharmony_ci POWER_SUPPLY_HEALTH_OVERHEAT : 3608c2ecf20Sopenharmony_ci POWER_SUPPLY_HEALTH_GOOD; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 3638c2ecf20Sopenharmony_ci if (!pdata) 3648c2ecf20Sopenharmony_ci return -EINVAL; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (pdata->get_batt_present) 3678c2ecf20Sopenharmony_ci val->intval = pdata->get_batt_present(); 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 3708c2ecf20Sopenharmony_ci if (!pdata) 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (pdata->get_batt_level) 3748c2ecf20Sopenharmony_ci val->intval = pdata->get_batt_level(); 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 3778c2ecf20Sopenharmony_ci if (!pdata) 3788c2ecf20Sopenharmony_ci return -EINVAL; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (pdata->get_batt_capacity) 3818c2ecf20Sopenharmony_ci val->intval = pdata->get_batt_capacity(); 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 3848c2ecf20Sopenharmony_ci if (!pdata) 3858c2ecf20Sopenharmony_ci return -EINVAL; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (pdata->get_batt_temp) 3888c2ecf20Sopenharmony_ci val->intval = pdata->get_batt_temp(); 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci default: 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void lp8727_charger_changed(struct power_supply *psy) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent); 4008c2ecf20Sopenharmony_ci u8 eoc_level; 4018c2ecf20Sopenharmony_ci u8 ichg; 4028c2ecf20Sopenharmony_ci u8 val; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* skip if no charger exists */ 4058c2ecf20Sopenharmony_ci if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid)) 4068c2ecf20Sopenharmony_ci return; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* update charging parameters */ 4098c2ecf20Sopenharmony_ci if (pchg->chg_param) { 4108c2ecf20Sopenharmony_ci eoc_level = pchg->chg_param->eoc_level; 4118c2ecf20Sopenharmony_ci ichg = pchg->chg_param->ichg; 4128c2ecf20Sopenharmony_ci val = (ichg << LP8727_ICHG_SHIFT) | eoc_level; 4138c2ecf20Sopenharmony_ci lp8727_write_byte(pchg, LP8727_CHGCTRL2, val); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic const struct power_supply_desc lp8727_ac_desc = { 4188c2ecf20Sopenharmony_ci .name = "ac", 4198c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 4208c2ecf20Sopenharmony_ci .properties = lp8727_charger_prop, 4218c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(lp8727_charger_prop), 4228c2ecf20Sopenharmony_ci .get_property = lp8727_charger_get_property, 4238c2ecf20Sopenharmony_ci}; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic const struct power_supply_desc lp8727_usb_desc = { 4268c2ecf20Sopenharmony_ci .name = "usb", 4278c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 4288c2ecf20Sopenharmony_ci .properties = lp8727_charger_prop, 4298c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(lp8727_charger_prop), 4308c2ecf20Sopenharmony_ci .get_property = lp8727_charger_get_property, 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic const struct power_supply_desc lp8727_batt_desc = { 4348c2ecf20Sopenharmony_ci .name = "main_batt", 4358c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 4368c2ecf20Sopenharmony_ci .properties = lp8727_battery_prop, 4378c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(lp8727_battery_prop), 4388c2ecf20Sopenharmony_ci .get_property = lp8727_battery_get_property, 4398c2ecf20Sopenharmony_ci .external_power_changed = lp8727_charger_changed, 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int lp8727_register_psy(struct lp8727_chg *pchg) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; /* Only for ac and usb */ 4458c2ecf20Sopenharmony_ci struct lp8727_psy *psy; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci psy = devm_kzalloc(pchg->dev, sizeof(*psy), GFP_KERNEL); 4488c2ecf20Sopenharmony_ci if (!psy) 4498c2ecf20Sopenharmony_ci return -ENOMEM; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci pchg->psy = psy; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci psy_cfg.supplied_to = battery_supplied_to; 4548c2ecf20Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci psy->ac = power_supply_register(pchg->dev, &lp8727_ac_desc, &psy_cfg); 4578c2ecf20Sopenharmony_ci if (IS_ERR(psy->ac)) 4588c2ecf20Sopenharmony_ci goto err_psy_ac; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci psy->usb = power_supply_register(pchg->dev, &lp8727_usb_desc, 4618c2ecf20Sopenharmony_ci &psy_cfg); 4628c2ecf20Sopenharmony_ci if (IS_ERR(psy->usb)) 4638c2ecf20Sopenharmony_ci goto err_psy_usb; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci psy->batt = power_supply_register(pchg->dev, &lp8727_batt_desc, NULL); 4668c2ecf20Sopenharmony_ci if (IS_ERR(psy->batt)) 4678c2ecf20Sopenharmony_ci goto err_psy_batt; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cierr_psy_batt: 4728c2ecf20Sopenharmony_ci power_supply_unregister(psy->usb); 4738c2ecf20Sopenharmony_cierr_psy_usb: 4748c2ecf20Sopenharmony_ci power_supply_unregister(psy->ac); 4758c2ecf20Sopenharmony_cierr_psy_ac: 4768c2ecf20Sopenharmony_ci return -EPERM; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic void lp8727_unregister_psy(struct lp8727_chg *pchg) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct lp8727_psy *psy = pchg->psy; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!psy) 4848c2ecf20Sopenharmony_ci return; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci power_supply_unregister(psy->ac); 4878c2ecf20Sopenharmony_ci power_supply_unregister(psy->usb); 4888c2ecf20Sopenharmony_ci power_supply_unregister(psy->batt); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 4928c2ecf20Sopenharmony_cistatic struct lp8727_chg_param 4938c2ecf20Sopenharmony_ci*lp8727_parse_charge_pdata(struct device *dev, struct device_node *np) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct lp8727_chg_param *param; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci param = devm_kzalloc(dev, sizeof(*param), GFP_KERNEL); 4988c2ecf20Sopenharmony_ci if (!param) 4998c2ecf20Sopenharmony_ci goto out; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci of_property_read_u8(np, "eoc-level", (u8 *)¶m->eoc_level); 5028c2ecf20Sopenharmony_ci of_property_read_u8(np, "charging-current", (u8 *)¶m->ichg); 5038c2ecf20Sopenharmony_ciout: 5048c2ecf20Sopenharmony_ci return param; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic struct lp8727_platform_data *lp8727_parse_dt(struct device *dev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 5108c2ecf20Sopenharmony_ci struct device_node *child; 5118c2ecf20Sopenharmony_ci struct lp8727_platform_data *pdata; 5128c2ecf20Sopenharmony_ci const char *type; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 5158c2ecf20Sopenharmony_ci if (!pdata) 5168c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci of_property_read_u32(np, "debounce-ms", &pdata->debounce_msec); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* If charging parameter is not defined, just skip parsing the dt */ 5218c2ecf20Sopenharmony_ci if (of_get_child_count(np) == 0) 5228c2ecf20Sopenharmony_ci return pdata; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 5258c2ecf20Sopenharmony_ci of_property_read_string(child, "charger-type", &type); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (!strcmp(type, "ac")) 5288c2ecf20Sopenharmony_ci pdata->ac = lp8727_parse_charge_pdata(dev, child); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (!strcmp(type, "usb")) 5318c2ecf20Sopenharmony_ci pdata->usb = lp8727_parse_charge_pdata(dev, child); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return pdata; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci#else 5378c2ecf20Sopenharmony_cistatic struct lp8727_platform_data *lp8727_parse_dt(struct device *dev) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci return NULL; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci#endif 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct lp8727_chg *pchg; 5468c2ecf20Sopenharmony_ci struct lp8727_platform_data *pdata; 5478c2ecf20Sopenharmony_ci int ret; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) 5508c2ecf20Sopenharmony_ci return -EIO; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (cl->dev.of_node) { 5538c2ecf20Sopenharmony_ci pdata = lp8727_parse_dt(&cl->dev); 5548c2ecf20Sopenharmony_ci if (IS_ERR(pdata)) 5558c2ecf20Sopenharmony_ci return PTR_ERR(pdata); 5568c2ecf20Sopenharmony_ci } else { 5578c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&cl->dev); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL); 5618c2ecf20Sopenharmony_ci if (!pchg) 5628c2ecf20Sopenharmony_ci return -ENOMEM; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci pchg->client = cl; 5658c2ecf20Sopenharmony_ci pchg->dev = &cl->dev; 5668c2ecf20Sopenharmony_ci pchg->pdata = pdata; 5678c2ecf20Sopenharmony_ci i2c_set_clientdata(cl, pchg); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci mutex_init(&pchg->xfer_lock); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci ret = lp8727_init_device(pchg); 5728c2ecf20Sopenharmony_ci if (ret) { 5738c2ecf20Sopenharmony_ci dev_err(pchg->dev, "i2c communication err: %d", ret); 5748c2ecf20Sopenharmony_ci return ret; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = lp8727_register_psy(pchg); 5788c2ecf20Sopenharmony_ci if (ret) { 5798c2ecf20Sopenharmony_ci dev_err(pchg->dev, "power supplies register err: %d", ret); 5808c2ecf20Sopenharmony_ci return ret; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ret = lp8727_setup_irq(pchg); 5848c2ecf20Sopenharmony_ci if (ret) { 5858c2ecf20Sopenharmony_ci dev_err(pchg->dev, "irq handler err: %d", ret); 5868c2ecf20Sopenharmony_ci lp8727_unregister_psy(pchg); 5878c2ecf20Sopenharmony_ci return ret; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int lp8727_remove(struct i2c_client *cl) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct lp8727_chg *pchg = i2c_get_clientdata(cl); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci lp8727_release_irq(pchg); 5988c2ecf20Sopenharmony_ci lp8727_unregister_psy(pchg); 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic const struct of_device_id lp8727_dt_ids[] = { 6038c2ecf20Sopenharmony_ci { .compatible = "ti,lp8727", }, 6048c2ecf20Sopenharmony_ci { } 6058c2ecf20Sopenharmony_ci}; 6068c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lp8727_dt_ids); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic const struct i2c_device_id lp8727_ids[] = { 6098c2ecf20Sopenharmony_ci {"lp8727", 0}, 6108c2ecf20Sopenharmony_ci { } 6118c2ecf20Sopenharmony_ci}; 6128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lp8727_ids); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic struct i2c_driver lp8727_driver = { 6158c2ecf20Sopenharmony_ci .driver = { 6168c2ecf20Sopenharmony_ci .name = "lp8727", 6178c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(lp8727_dt_ids), 6188c2ecf20Sopenharmony_ci }, 6198c2ecf20Sopenharmony_ci .probe = lp8727_probe, 6208c2ecf20Sopenharmony_ci .remove = lp8727_remove, 6218c2ecf20Sopenharmony_ci .id_table = lp8727_ids, 6228c2ecf20Sopenharmony_ci}; 6238c2ecf20Sopenharmony_cimodule_i2c_driver(lp8727_driver); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver"); 6268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Milo Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>"); 6278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 628