18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TWL4030/TPS65950 BCI (Battery Charger Interface) driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * based on twl4030_bci_battery.c by TI 88c2ecf20Sopenharmony_ci * Copyright (C) 2008 Texas Instruments, Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/twl.h> 188c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 198c2ecf20Sopenharmony_ci#include <linux/notifier.h> 208c2ecf20Sopenharmony_ci#include <linux/usb/otg.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define TWL4030_BCIMDEN 0x00 248c2ecf20Sopenharmony_ci#define TWL4030_BCIMDKEY 0x01 258c2ecf20Sopenharmony_ci#define TWL4030_BCIMSTATEC 0x02 268c2ecf20Sopenharmony_ci#define TWL4030_BCIICHG 0x08 278c2ecf20Sopenharmony_ci#define TWL4030_BCIVAC 0x0a 288c2ecf20Sopenharmony_ci#define TWL4030_BCIVBUS 0x0c 298c2ecf20Sopenharmony_ci#define TWL4030_BCIMFSTS3 0x0F 308c2ecf20Sopenharmony_ci#define TWL4030_BCIMFSTS4 0x10 318c2ecf20Sopenharmony_ci#define TWL4030_BCICTL1 0x23 328c2ecf20Sopenharmony_ci#define TWL4030_BB_CFG 0x12 338c2ecf20Sopenharmony_ci#define TWL4030_BCIIREF1 0x27 348c2ecf20Sopenharmony_ci#define TWL4030_BCIIREF2 0x28 358c2ecf20Sopenharmony_ci#define TWL4030_BCIMFKEY 0x11 368c2ecf20Sopenharmony_ci#define TWL4030_BCIMFEN3 0x14 378c2ecf20Sopenharmony_ci#define TWL4030_BCIMFTH8 0x1d 388c2ecf20Sopenharmony_ci#define TWL4030_BCIMFTH9 0x1e 398c2ecf20Sopenharmony_ci#define TWL4030_BCIWDKEY 0x21 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define TWL4030_BCIMFSTS1 0x01 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define TWL4030_BCIAUTOWEN BIT(5) 448c2ecf20Sopenharmony_ci#define TWL4030_CONFIG_DONE BIT(4) 458c2ecf20Sopenharmony_ci#define TWL4030_CVENAC BIT(2) 468c2ecf20Sopenharmony_ci#define TWL4030_BCIAUTOUSB BIT(1) 478c2ecf20Sopenharmony_ci#define TWL4030_BCIAUTOAC BIT(0) 488c2ecf20Sopenharmony_ci#define TWL4030_CGAIN BIT(5) 498c2ecf20Sopenharmony_ci#define TWL4030_USBFASTMCHG BIT(2) 508c2ecf20Sopenharmony_ci#define TWL4030_STS_VBUS BIT(7) 518c2ecf20Sopenharmony_ci#define TWL4030_STS_USB_ID BIT(2) 528c2ecf20Sopenharmony_ci#define TWL4030_BBCHEN BIT(4) 538c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_MASK 0x0c 548c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_2V5 0x00 558c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_3V0 0x04 568c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_3V1 0x08 578c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_3V2 0x0c 588c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_MASK 0x03 598c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_25uA 0x00 608c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_150uA 0x01 618c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_500uA 0x02 628c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_1000uA 0x03 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define TWL4030_BATSTSPCHG BIT(2) 658c2ecf20Sopenharmony_ci#define TWL4030_BATSTSMCHG BIT(6) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* BCI interrupts */ 688c2ecf20Sopenharmony_ci#define TWL4030_WOVF BIT(0) /* Watchdog overflow */ 698c2ecf20Sopenharmony_ci#define TWL4030_TMOVF BIT(1) /* Timer overflow */ 708c2ecf20Sopenharmony_ci#define TWL4030_ICHGHIGH BIT(2) /* Battery charge current high */ 718c2ecf20Sopenharmony_ci#define TWL4030_ICHGLOW BIT(3) /* Battery cc. low / FSM state change */ 728c2ecf20Sopenharmony_ci#define TWL4030_ICHGEOC BIT(4) /* Battery current end-of-charge */ 738c2ecf20Sopenharmony_ci#define TWL4030_TBATOR2 BIT(5) /* Battery temperature out of range 2 */ 748c2ecf20Sopenharmony_ci#define TWL4030_TBATOR1 BIT(6) /* Battery temperature out of range 1 */ 758c2ecf20Sopenharmony_ci#define TWL4030_BATSTS BIT(7) /* Battery status */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define TWL4030_VBATLVL BIT(0) /* VBAT level */ 788c2ecf20Sopenharmony_ci#define TWL4030_VBATOV BIT(1) /* VBAT overvoltage */ 798c2ecf20Sopenharmony_ci#define TWL4030_VBUSOV BIT(2) /* VBUS overvoltage */ 808c2ecf20Sopenharmony_ci#define TWL4030_ACCHGOV BIT(3) /* Ac charger overvoltage */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_USB BIT(4) 838c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_AC BIT(5) 848c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_MASK 0x0f 858c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_QUICK1 0x02 868c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_QUICK7 0x07 878c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_COMPLETE1 0x0b 888c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_COMPLETE4 0x0e 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11) 928c2ecf20Sopenharmony_ci * then AC is available. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic inline int ac_available(struct iio_channel *channel_vac) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci int val, err; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (!channel_vac) 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci err = iio_read_channel_processed(channel_vac, &val); 1028c2ecf20Sopenharmony_ci if (err < 0) 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci return val > 4500; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic bool allow_usb; 1088c2ecf20Sopenharmony_cimodule_param(allow_usb, bool, 0644); 1098c2ecf20Sopenharmony_ciMODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current"); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistruct twl4030_bci { 1128c2ecf20Sopenharmony_ci struct device *dev; 1138c2ecf20Sopenharmony_ci struct power_supply *ac; 1148c2ecf20Sopenharmony_ci struct power_supply *usb; 1158c2ecf20Sopenharmony_ci struct usb_phy *transceiver; 1168c2ecf20Sopenharmony_ci struct notifier_block usb_nb; 1178c2ecf20Sopenharmony_ci struct work_struct work; 1188c2ecf20Sopenharmony_ci int irq_chg; 1198c2ecf20Sopenharmony_ci int irq_bci; 1208c2ecf20Sopenharmony_ci int usb_enabled; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* 1238c2ecf20Sopenharmony_ci * ichg_* and *_cur values in uA. If any are 'large', we set 1248c2ecf20Sopenharmony_ci * CGAIN to '1' which doubles the range for half the 1258c2ecf20Sopenharmony_ci * precision. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci unsigned int ichg_eoc, ichg_lo, ichg_hi; 1288c2ecf20Sopenharmony_ci unsigned int usb_cur, ac_cur; 1298c2ecf20Sopenharmony_ci struct iio_channel *channel_vac; 1308c2ecf20Sopenharmony_ci bool ac_is_active; 1318c2ecf20Sopenharmony_ci int usb_mode, ac_mode; /* charging mode requested */ 1328c2ecf20Sopenharmony_ci#define CHARGE_OFF 0 1338c2ecf20Sopenharmony_ci#define CHARGE_AUTO 1 1348c2ecf20Sopenharmony_ci#define CHARGE_LINEAR 2 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* When setting the USB current we slowly increase the 1378c2ecf20Sopenharmony_ci * requested current until target is reached or the voltage 1388c2ecf20Sopenharmony_ci * drops below 4.75V. In the latter case we step back one 1398c2ecf20Sopenharmony_ci * step. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci unsigned int usb_cur_target; 1428c2ecf20Sopenharmony_ci struct delayed_work current_worker; 1438c2ecf20Sopenharmony_ci#define USB_CUR_STEP 20000 /* 20mA at a time */ 1448c2ecf20Sopenharmony_ci#define USB_MIN_VOLT 4750000 /* 4.75V */ 1458c2ecf20Sopenharmony_ci#define USB_CUR_DELAY msecs_to_jiffies(100) 1468c2ecf20Sopenharmony_ci#define USB_MAX_CURRENT 1700000 /* TWL4030 caps at 1.7A */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci unsigned long event; 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* strings for 'usb_mode' values */ 1528c2ecf20Sopenharmony_cistatic const char *modes[] = { "off", "auto", "continuous" }; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * clear and set bits on an given register on a given module 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistatic int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci u8 val = 0; 1608c2ecf20Sopenharmony_ci int ret; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci ret = twl_i2c_read_u8(mod_no, &val, reg); 1638c2ecf20Sopenharmony_ci if (ret) 1648c2ecf20Sopenharmony_ci return ret; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci val &= ~clear; 1678c2ecf20Sopenharmony_ci val |= set; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return twl_i2c_write_u8(mod_no, val, reg); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int twl4030_bci_read(u8 reg, u8 *val) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int twl4030_clear_set_boot_bci(u8 clear, u8 set) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci return twl4030_clear_set(TWL_MODULE_PM_MASTER, clear, 1808c2ecf20Sopenharmony_ci TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set, 1818c2ecf20Sopenharmony_ci TWL4030_PM_MASTER_BOOT_BCI); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int twl4030bci_read_adc_val(u8 reg) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int ret, temp; 1878c2ecf20Sopenharmony_ci u8 val; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* read MSB */ 1908c2ecf20Sopenharmony_ci ret = twl4030_bci_read(reg + 1, &val); 1918c2ecf20Sopenharmony_ci if (ret) 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci temp = (int)(val & 0x03) << 8; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* read LSB */ 1978c2ecf20Sopenharmony_ci ret = twl4030_bci_read(reg, &val); 1988c2ecf20Sopenharmony_ci if (ret) 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return temp | val; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* 2058c2ecf20Sopenharmony_ci * TI provided formulas: 2068c2ecf20Sopenharmony_ci * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85 2078c2ecf20Sopenharmony_ci * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7 2088c2ecf20Sopenharmony_ci * Here we use integer approximation of: 2098c2ecf20Sopenharmony_ci * CGAIN == 0: val * 1.6618 - 0.85 * 1000 2108c2ecf20Sopenharmony_ci * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * convert twl register value for currents into uA 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistatic int regval2ua(int regval, bool cgain) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci if (cgain) 2188c2ecf20Sopenharmony_ci return (regval * 16618 - 8500 * 1000) / 5; 2198c2ecf20Sopenharmony_ci else 2208c2ecf20Sopenharmony_ci return (regval * 16618 - 8500 * 1000) / 10; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* 2248c2ecf20Sopenharmony_ci * convert uA currents into twl register value 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistatic int ua2regval(int ua, bool cgain) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int ret; 2298c2ecf20Sopenharmony_ci if (cgain) 2308c2ecf20Sopenharmony_ci ua /= 2; 2318c2ecf20Sopenharmony_ci ret = (ua * 10 + 8500 * 1000) / 16618; 2328c2ecf20Sopenharmony_ci /* rounding problems */ 2338c2ecf20Sopenharmony_ci if (ret < 512) 2348c2ecf20Sopenharmony_ci ret = 512; 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int twl4030_charger_update_current(struct twl4030_bci *bci) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int status; 2418c2ecf20Sopenharmony_ci int cur; 2428c2ecf20Sopenharmony_ci unsigned reg, cur_reg; 2438c2ecf20Sopenharmony_ci u8 bcictl1, oldreg, fullreg; 2448c2ecf20Sopenharmony_ci bool cgain = false; 2458c2ecf20Sopenharmony_ci u8 boot_bci; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11) 2498c2ecf20Sopenharmony_ci * and AC is enabled, set current for 'ac' 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci if (ac_available(bci->channel_vac)) { 2528c2ecf20Sopenharmony_ci cur = bci->ac_cur; 2538c2ecf20Sopenharmony_ci bci->ac_is_active = true; 2548c2ecf20Sopenharmony_ci } else { 2558c2ecf20Sopenharmony_ci cur = bci->usb_cur; 2568c2ecf20Sopenharmony_ci bci->ac_is_active = false; 2578c2ecf20Sopenharmony_ci if (cur > bci->usb_cur_target) { 2588c2ecf20Sopenharmony_ci cur = bci->usb_cur_target; 2598c2ecf20Sopenharmony_ci bci->usb_cur = cur; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci if (cur < bci->usb_cur_target) 2628c2ecf20Sopenharmony_ci schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* First, check thresholds and see if cgain is needed */ 2668c2ecf20Sopenharmony_ci if (bci->ichg_eoc >= 200000) 2678c2ecf20Sopenharmony_ci cgain = true; 2688c2ecf20Sopenharmony_ci if (bci->ichg_lo >= 400000) 2698c2ecf20Sopenharmony_ci cgain = true; 2708c2ecf20Sopenharmony_ci if (bci->ichg_hi >= 820000) 2718c2ecf20Sopenharmony_ci cgain = true; 2728c2ecf20Sopenharmony_ci if (cur > 852000) 2738c2ecf20Sopenharmony_ci cgain = true; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); 2768c2ecf20Sopenharmony_ci if (status < 0) 2778c2ecf20Sopenharmony_ci return status; 2788c2ecf20Sopenharmony_ci if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci, 2798c2ecf20Sopenharmony_ci TWL4030_PM_MASTER_BOOT_BCI) < 0) 2808c2ecf20Sopenharmony_ci boot_bci = 0; 2818c2ecf20Sopenharmony_ci boot_bci &= 7; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) 2848c2ecf20Sopenharmony_ci /* Need to turn for charging while we change the 2858c2ecf20Sopenharmony_ci * CGAIN bit. Leave it off while everything is 2868c2ecf20Sopenharmony_ci * updated. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci twl4030_clear_set_boot_bci(boot_bci, 0); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * For ichg_eoc, the hardware only supports reg values matching 2928c2ecf20Sopenharmony_ci * 100XXXX000, and requires the XXXX be stored in the high nibble 2938c2ecf20Sopenharmony_ci * of TWL4030_BCIMFTH8. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci reg = ua2regval(bci->ichg_eoc, cgain); 2968c2ecf20Sopenharmony_ci if (reg > 0x278) 2978c2ecf20Sopenharmony_ci reg = 0x278; 2988c2ecf20Sopenharmony_ci if (reg < 0x200) 2998c2ecf20Sopenharmony_ci reg = 0x200; 3008c2ecf20Sopenharmony_ci reg = (reg >> 3) & 0xf; 3018c2ecf20Sopenharmony_ci fullreg = reg << 4; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * For ichg_lo, reg value must match 10XXXX0000. 3058c2ecf20Sopenharmony_ci * XXXX is stored in low nibble of TWL4030_BCIMFTH8. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci reg = ua2regval(bci->ichg_lo, cgain); 3088c2ecf20Sopenharmony_ci if (reg > 0x2F0) 3098c2ecf20Sopenharmony_ci reg = 0x2F0; 3108c2ecf20Sopenharmony_ci if (reg < 0x200) 3118c2ecf20Sopenharmony_ci reg = 0x200; 3128c2ecf20Sopenharmony_ci reg = (reg >> 4) & 0xf; 3138c2ecf20Sopenharmony_ci fullreg |= reg; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* ichg_eoc and ichg_lo live in same register */ 3168c2ecf20Sopenharmony_ci status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg); 3178c2ecf20Sopenharmony_ci if (status < 0) 3188c2ecf20Sopenharmony_ci return status; 3198c2ecf20Sopenharmony_ci if (oldreg != fullreg) { 3208c2ecf20Sopenharmony_ci status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4, 3218c2ecf20Sopenharmony_ci TWL4030_BCIMFKEY); 3228c2ecf20Sopenharmony_ci if (status < 0) 3238c2ecf20Sopenharmony_ci return status; 3248c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 3258c2ecf20Sopenharmony_ci fullreg, TWL4030_BCIMFTH8); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* ichg_hi threshold must be 1XXXX01100 (I think) */ 3298c2ecf20Sopenharmony_ci reg = ua2regval(bci->ichg_hi, cgain); 3308c2ecf20Sopenharmony_ci if (reg > 0x3E0) 3318c2ecf20Sopenharmony_ci reg = 0x3E0; 3328c2ecf20Sopenharmony_ci if (reg < 0x200) 3338c2ecf20Sopenharmony_ci reg = 0x200; 3348c2ecf20Sopenharmony_ci fullreg = (reg >> 5) & 0xF; 3358c2ecf20Sopenharmony_ci fullreg <<= 4; 3368c2ecf20Sopenharmony_ci status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg); 3378c2ecf20Sopenharmony_ci if (status < 0) 3388c2ecf20Sopenharmony_ci return status; 3398c2ecf20Sopenharmony_ci if ((oldreg & 0xF0) != fullreg) { 3408c2ecf20Sopenharmony_ci fullreg |= (oldreg & 0x0F); 3418c2ecf20Sopenharmony_ci status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7, 3428c2ecf20Sopenharmony_ci TWL4030_BCIMFKEY); 3438c2ecf20Sopenharmony_ci if (status < 0) 3448c2ecf20Sopenharmony_ci return status; 3458c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 3468c2ecf20Sopenharmony_ci fullreg, TWL4030_BCIMFTH9); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* 3508c2ecf20Sopenharmony_ci * And finally, set the current. This is stored in 3518c2ecf20Sopenharmony_ci * two registers. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_ci reg = ua2regval(cur, cgain); 3548c2ecf20Sopenharmony_ci /* we have only 10 bits */ 3558c2ecf20Sopenharmony_ci if (reg > 0x3ff) 3568c2ecf20Sopenharmony_ci reg = 0x3ff; 3578c2ecf20Sopenharmony_ci status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg); 3588c2ecf20Sopenharmony_ci if (status < 0) 3598c2ecf20Sopenharmony_ci return status; 3608c2ecf20Sopenharmony_ci cur_reg = oldreg; 3618c2ecf20Sopenharmony_ci status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg); 3628c2ecf20Sopenharmony_ci if (status < 0) 3638c2ecf20Sopenharmony_ci return status; 3648c2ecf20Sopenharmony_ci cur_reg |= oldreg << 8; 3658c2ecf20Sopenharmony_ci if (reg != oldreg) { 3668c2ecf20Sopenharmony_ci /* disable write protection for one write access for 3678c2ecf20Sopenharmony_ci * BCIIREF */ 3688c2ecf20Sopenharmony_ci status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7, 3698c2ecf20Sopenharmony_ci TWL4030_BCIMFKEY); 3708c2ecf20Sopenharmony_ci if (status < 0) 3718c2ecf20Sopenharmony_ci return status; 3728c2ecf20Sopenharmony_ci status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 3738c2ecf20Sopenharmony_ci (reg & 0x100) ? 3 : 2, 3748c2ecf20Sopenharmony_ci TWL4030_BCIIREF2); 3758c2ecf20Sopenharmony_ci if (status < 0) 3768c2ecf20Sopenharmony_ci return status; 3778c2ecf20Sopenharmony_ci /* disable write protection for one write access for 3788c2ecf20Sopenharmony_ci * BCIIREF */ 3798c2ecf20Sopenharmony_ci status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7, 3808c2ecf20Sopenharmony_ci TWL4030_BCIMFKEY); 3818c2ecf20Sopenharmony_ci if (status < 0) 3828c2ecf20Sopenharmony_ci return status; 3838c2ecf20Sopenharmony_ci status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 3848c2ecf20Sopenharmony_ci reg & 0xff, 3858c2ecf20Sopenharmony_ci TWL4030_BCIIREF1); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) { 3888c2ecf20Sopenharmony_ci /* Flip CGAIN and re-enable charging */ 3898c2ecf20Sopenharmony_ci bcictl1 ^= TWL4030_CGAIN; 3908c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 3918c2ecf20Sopenharmony_ci bcictl1, TWL4030_BCICTL1); 3928c2ecf20Sopenharmony_ci twl4030_clear_set_boot_bci(0, boot_bci); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int twl4030_charger_get_current(void); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void twl4030_current_worker(struct work_struct *data) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci int v, curr; 4028c2ecf20Sopenharmony_ci int res; 4038c2ecf20Sopenharmony_ci struct twl4030_bci *bci = container_of(data, struct twl4030_bci, 4048c2ecf20Sopenharmony_ci current_worker.work); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci res = twl4030bci_read_adc_val(TWL4030_BCIVBUS); 4078c2ecf20Sopenharmony_ci if (res < 0) 4088c2ecf20Sopenharmony_ci v = 0; 4098c2ecf20Sopenharmony_ci else 4108c2ecf20Sopenharmony_ci /* BCIVBUS uses ADCIN8, 7/1023 V/step */ 4118c2ecf20Sopenharmony_ci v = res * 6843; 4128c2ecf20Sopenharmony_ci curr = twl4030_charger_get_current(); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci dev_dbg(bci->dev, "v=%d cur=%d limit=%d target=%d\n", v, curr, 4158c2ecf20Sopenharmony_ci bci->usb_cur, bci->usb_cur_target); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (v < USB_MIN_VOLT) { 4188c2ecf20Sopenharmony_ci /* Back up and stop adjusting. */ 4198c2ecf20Sopenharmony_ci if (bci->usb_cur >= USB_CUR_STEP) 4208c2ecf20Sopenharmony_ci bci->usb_cur -= USB_CUR_STEP; 4218c2ecf20Sopenharmony_ci bci->usb_cur_target = bci->usb_cur; 4228c2ecf20Sopenharmony_ci } else if (bci->usb_cur >= bci->usb_cur_target || 4238c2ecf20Sopenharmony_ci bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) { 4248c2ecf20Sopenharmony_ci /* Reached target and voltage is OK - stop */ 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci } else { 4278c2ecf20Sopenharmony_ci bci->usb_cur += USB_CUR_STEP; 4288c2ecf20Sopenharmony_ci schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci twl4030_charger_update_current(bci); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* 4348c2ecf20Sopenharmony_ci * Enable/Disable USB Charge functionality. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_cistatic int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci int ret; 4398c2ecf20Sopenharmony_ci u32 reg; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (bci->usb_mode == CHARGE_OFF) 4428c2ecf20Sopenharmony_ci enable = false; 4438c2ecf20Sopenharmony_ci if (enable && !IS_ERR_OR_NULL(bci->transceiver)) { 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci twl4030_charger_update_current(bci); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* Need to keep phy powered */ 4488c2ecf20Sopenharmony_ci if (!bci->usb_enabled) { 4498c2ecf20Sopenharmony_ci pm_runtime_get_sync(bci->transceiver->dev); 4508c2ecf20Sopenharmony_ci bci->usb_enabled = 1; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (bci->usb_mode == CHARGE_AUTO) { 4548c2ecf20Sopenharmony_ci /* Enable interrupts now. */ 4558c2ecf20Sopenharmony_ci reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | 4568c2ecf20Sopenharmony_ci TWL4030_TBATOR2 | TWL4030_TBATOR1 | 4578c2ecf20Sopenharmony_ci TWL4030_BATSTS); 4588c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, 4598c2ecf20Sopenharmony_ci TWL4030_INTERRUPTS_BCIIMR1A); 4608c2ecf20Sopenharmony_ci if (ret < 0) { 4618c2ecf20Sopenharmony_ci dev_err(bci->dev, 4628c2ecf20Sopenharmony_ci "failed to unmask interrupts: %d\n", 4638c2ecf20Sopenharmony_ci ret); 4648c2ecf20Sopenharmony_ci return ret; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ 4678c2ecf20Sopenharmony_ci ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ 4718c2ecf20Sopenharmony_ci ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0, 4728c2ecf20Sopenharmony_ci TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); 4738c2ecf20Sopenharmony_ci if (bci->usb_mode == CHARGE_LINEAR) { 4748c2ecf20Sopenharmony_ci /* Enable interrupts now. */ 4758c2ecf20Sopenharmony_ci reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_TBATOR2 | 4768c2ecf20Sopenharmony_ci TWL4030_TBATOR1 | TWL4030_BATSTS); 4778c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, 4788c2ecf20Sopenharmony_ci TWL4030_INTERRUPTS_BCIIMR1A); 4798c2ecf20Sopenharmony_ci if (ret < 0) { 4808c2ecf20Sopenharmony_ci dev_err(bci->dev, 4818c2ecf20Sopenharmony_ci "failed to unmask interrupts: %d\n", 4828c2ecf20Sopenharmony_ci ret); 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC|TWL4030_CVENAC, 0); 4868c2ecf20Sopenharmony_ci /* Watch dog key: WOVF acknowledge */ 4878c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x33, 4888c2ecf20Sopenharmony_ci TWL4030_BCIWDKEY); 4898c2ecf20Sopenharmony_ci /* 0x24 + EKEY6: off mode */ 4908c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a, 4918c2ecf20Sopenharmony_ci TWL4030_BCIMDKEY); 4928c2ecf20Sopenharmony_ci /* EKEY2: Linear charge: USB path */ 4938c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x26, 4948c2ecf20Sopenharmony_ci TWL4030_BCIMDKEY); 4958c2ecf20Sopenharmony_ci /* WDKEY5: stop watchdog count */ 4968c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf3, 4978c2ecf20Sopenharmony_ci TWL4030_BCIWDKEY); 4988c2ecf20Sopenharmony_ci /* enable MFEN3 access */ 4998c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x9c, 5008c2ecf20Sopenharmony_ci TWL4030_BCIMFKEY); 5018c2ecf20Sopenharmony_ci /* ICHGEOCEN - end-of-charge monitor (current < 80mA) 5028c2ecf20Sopenharmony_ci * (charging continues) 5038c2ecf20Sopenharmony_ci * ICHGLOWEN - current level monitor (charge continues) 5048c2ecf20Sopenharmony_ci * don't monitor over-current or heat save 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf0, 5078c2ecf20Sopenharmony_ci TWL4030_BCIMFEN3); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci } else { 5108c2ecf20Sopenharmony_ci ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); 5118c2ecf20Sopenharmony_ci ret |= twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a, 5128c2ecf20Sopenharmony_ci TWL4030_BCIMDKEY); 5138c2ecf20Sopenharmony_ci if (bci->usb_enabled) { 5148c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(bci->transceiver->dev); 5158c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(bci->transceiver->dev); 5168c2ecf20Sopenharmony_ci bci->usb_enabled = 0; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci bci->usb_cur = 0; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return ret; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/* 5258c2ecf20Sopenharmony_ci * Enable/Disable AC Charge funtionality. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_cistatic int twl4030_charger_enable_ac(struct twl4030_bci *bci, bool enable) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int ret; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (bci->ac_mode == CHARGE_OFF) 5328c2ecf20Sopenharmony_ci enable = false; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (enable) 5358c2ecf20Sopenharmony_ci ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC); 5368c2ecf20Sopenharmony_ci else 5378c2ecf20Sopenharmony_ci ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return ret; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci/* 5438c2ecf20Sopenharmony_ci * Enable/Disable charging of Backup Battery. 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_cistatic int twl4030_charger_enable_backup(int uvolt, int uamp) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci int ret; 5488c2ecf20Sopenharmony_ci u8 flags; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (uvolt < 2500000 || 5518c2ecf20Sopenharmony_ci uamp < 25) { 5528c2ecf20Sopenharmony_ci /* disable charging of backup battery */ 5538c2ecf20Sopenharmony_ci ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER, 5548c2ecf20Sopenharmony_ci TWL4030_BBCHEN, 0, TWL4030_BB_CFG); 5558c2ecf20Sopenharmony_ci return ret; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci flags = TWL4030_BBCHEN; 5598c2ecf20Sopenharmony_ci if (uvolt >= 3200000) 5608c2ecf20Sopenharmony_ci flags |= TWL4030_BBSEL_3V2; 5618c2ecf20Sopenharmony_ci else if (uvolt >= 3100000) 5628c2ecf20Sopenharmony_ci flags |= TWL4030_BBSEL_3V1; 5638c2ecf20Sopenharmony_ci else if (uvolt >= 3000000) 5648c2ecf20Sopenharmony_ci flags |= TWL4030_BBSEL_3V0; 5658c2ecf20Sopenharmony_ci else 5668c2ecf20Sopenharmony_ci flags |= TWL4030_BBSEL_2V5; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (uamp >= 1000) 5698c2ecf20Sopenharmony_ci flags |= TWL4030_BBISEL_1000uA; 5708c2ecf20Sopenharmony_ci else if (uamp >= 500) 5718c2ecf20Sopenharmony_ci flags |= TWL4030_BBISEL_500uA; 5728c2ecf20Sopenharmony_ci else if (uamp >= 150) 5738c2ecf20Sopenharmony_ci flags |= TWL4030_BBISEL_150uA; 5748c2ecf20Sopenharmony_ci else 5758c2ecf20Sopenharmony_ci flags |= TWL4030_BBISEL_25uA; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER, 5788c2ecf20Sopenharmony_ci TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK, 5798c2ecf20Sopenharmony_ci flags, 5808c2ecf20Sopenharmony_ci TWL4030_BB_CFG); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return ret; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* 5868c2ecf20Sopenharmony_ci * TWL4030 CHG_PRES (AC charger presence) events 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_cistatic irqreturn_t twl4030_charger_interrupt(int irq, void *arg) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct twl4030_bci *bci = arg; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci dev_dbg(bci->dev, "CHG_PRES irq\n"); 5938c2ecf20Sopenharmony_ci /* reset current on each 'plug' event */ 5948c2ecf20Sopenharmony_ci bci->ac_cur = 500000; 5958c2ecf20Sopenharmony_ci twl4030_charger_update_current(bci); 5968c2ecf20Sopenharmony_ci power_supply_changed(bci->ac); 5978c2ecf20Sopenharmony_ci power_supply_changed(bci->usb); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci/* 6038c2ecf20Sopenharmony_ci * TWL4030 BCI monitoring events 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_cistatic irqreturn_t twl4030_bci_interrupt(int irq, void *arg) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct twl4030_bci *bci = arg; 6088c2ecf20Sopenharmony_ci u8 irqs1, irqs2; 6098c2ecf20Sopenharmony_ci int ret; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1, 6128c2ecf20Sopenharmony_ci TWL4030_INTERRUPTS_BCIISR1A); 6138c2ecf20Sopenharmony_ci if (ret < 0) 6148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2, 6178c2ecf20Sopenharmony_ci TWL4030_INTERRUPTS_BCIISR2A); 6188c2ecf20Sopenharmony_ci if (ret < 0) 6198c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) { 6248c2ecf20Sopenharmony_ci /* charger state change, inform the core */ 6258c2ecf20Sopenharmony_ci power_supply_changed(bci->ac); 6268c2ecf20Sopenharmony_ci power_supply_changed(bci->usb); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci twl4030_charger_update_current(bci); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* various monitoring events, for now we just log them here */ 6318c2ecf20Sopenharmony_ci if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1)) 6328c2ecf20Sopenharmony_ci dev_warn(bci->dev, "battery temperature out of range\n"); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (irqs1 & TWL4030_BATSTS) 6358c2ecf20Sopenharmony_ci dev_crit(bci->dev, "battery disconnected\n"); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (irqs2 & TWL4030_VBATOV) 6388c2ecf20Sopenharmony_ci dev_crit(bci->dev, "VBAT overvoltage\n"); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (irqs2 & TWL4030_VBUSOV) 6418c2ecf20Sopenharmony_ci dev_crit(bci->dev, "VBUS overvoltage\n"); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (irqs2 & TWL4030_ACCHGOV) 6448c2ecf20Sopenharmony_ci dev_crit(bci->dev, "Ac charger overvoltage\n"); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic void twl4030_bci_usb_work(struct work_struct *data) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci switch (bci->event) { 6548c2ecf20Sopenharmony_ci case USB_EVENT_VBUS: 6558c2ecf20Sopenharmony_ci case USB_EVENT_CHARGER: 6568c2ecf20Sopenharmony_ci twl4030_charger_enable_usb(bci, true); 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci case USB_EVENT_NONE: 6598c2ecf20Sopenharmony_ci twl4030_charger_enable_usb(bci, false); 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, 6658c2ecf20Sopenharmony_ci void *priv) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci dev_dbg(bci->dev, "OTG notify %lu\n", val); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* reset current on each 'plug' event */ 6728c2ecf20Sopenharmony_ci if (allow_usb) 6738c2ecf20Sopenharmony_ci bci->usb_cur_target = 500000; 6748c2ecf20Sopenharmony_ci else 6758c2ecf20Sopenharmony_ci bci->usb_cur_target = 100000; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci bci->event = val; 6788c2ecf20Sopenharmony_ci schedule_work(&bci->work); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci return NOTIFY_OK; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci/* 6848c2ecf20Sopenharmony_ci * sysfs charger enabled store 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_cistatic ssize_t 6878c2ecf20Sopenharmony_citwl4030_bci_mode_store(struct device *dev, struct device_attribute *attr, 6888c2ecf20Sopenharmony_ci const char *buf, size_t n) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct twl4030_bci *bci = dev_get_drvdata(dev->parent); 6918c2ecf20Sopenharmony_ci int mode; 6928c2ecf20Sopenharmony_ci int status; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci mode = sysfs_match_string(modes, buf); 6958c2ecf20Sopenharmony_ci if (mode < 0) 6968c2ecf20Sopenharmony_ci return mode; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (dev == &bci->ac->dev) { 6998c2ecf20Sopenharmony_ci if (mode == 2) 7008c2ecf20Sopenharmony_ci return -EINVAL; 7018c2ecf20Sopenharmony_ci twl4030_charger_enable_ac(bci, false); 7028c2ecf20Sopenharmony_ci bci->ac_mode = mode; 7038c2ecf20Sopenharmony_ci status = twl4030_charger_enable_ac(bci, true); 7048c2ecf20Sopenharmony_ci } else { 7058c2ecf20Sopenharmony_ci twl4030_charger_enable_usb(bci, false); 7068c2ecf20Sopenharmony_ci bci->usb_mode = mode; 7078c2ecf20Sopenharmony_ci status = twl4030_charger_enable_usb(bci, true); 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci return (status == 0) ? n : status; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/* 7138c2ecf20Sopenharmony_ci * sysfs charger enabled show 7148c2ecf20Sopenharmony_ci */ 7158c2ecf20Sopenharmony_cistatic ssize_t 7168c2ecf20Sopenharmony_citwl4030_bci_mode_show(struct device *dev, 7178c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct twl4030_bci *bci = dev_get_drvdata(dev->parent); 7208c2ecf20Sopenharmony_ci int len = 0; 7218c2ecf20Sopenharmony_ci int i; 7228c2ecf20Sopenharmony_ci int mode = bci->usb_mode; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (dev == &bci->ac->dev) 7258c2ecf20Sopenharmony_ci mode = bci->ac_mode; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(modes); i++) 7288c2ecf20Sopenharmony_ci if (mode == i) 7298c2ecf20Sopenharmony_ci len += scnprintf(buf+len, PAGE_SIZE-len, 7308c2ecf20Sopenharmony_ci "[%s] ", modes[i]); 7318c2ecf20Sopenharmony_ci else 7328c2ecf20Sopenharmony_ci len += scnprintf(buf+len, PAGE_SIZE-len, 7338c2ecf20Sopenharmony_ci "%s ", modes[i]); 7348c2ecf20Sopenharmony_ci buf[len-1] = '\n'; 7358c2ecf20Sopenharmony_ci return len; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show, 7388c2ecf20Sopenharmony_ci twl4030_bci_mode_store); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic int twl4030_charger_get_current(void) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci int curr; 7438c2ecf20Sopenharmony_ci int ret; 7448c2ecf20Sopenharmony_ci u8 bcictl1; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci curr = twl4030bci_read_adc_val(TWL4030_BCIICHG); 7478c2ecf20Sopenharmony_ci if (curr < 0) 7488c2ecf20Sopenharmony_ci return curr; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); 7518c2ecf20Sopenharmony_ci if (ret) 7528c2ecf20Sopenharmony_ci return ret; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return regval2ua(curr, bcictl1 & TWL4030_CGAIN); 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci/* 7588c2ecf20Sopenharmony_ci * Returns the main charge FSM state 7598c2ecf20Sopenharmony_ci * Or < 0 on failure. 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_cistatic int twl4030bci_state(struct twl4030_bci *bci) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci int ret; 7648c2ecf20Sopenharmony_ci u8 state; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state); 7678c2ecf20Sopenharmony_ci if (ret) { 7688c2ecf20Sopenharmony_ci dev_err(bci->dev, "error reading BCIMSTATEC\n"); 7698c2ecf20Sopenharmony_ci return ret; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci dev_dbg(bci->dev, "state: %02x\n", state); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci return state; 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic int twl4030_bci_state_to_status(int state) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci state &= TWL4030_MSTATEC_MASK; 7808c2ecf20Sopenharmony_ci if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7) 7818c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_CHARGING; 7828c2ecf20Sopenharmony_ci else if (TWL4030_MSTATEC_COMPLETE1 <= state && 7838c2ecf20Sopenharmony_ci state <= TWL4030_MSTATEC_COMPLETE4) 7848c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_FULL; 7858c2ecf20Sopenharmony_ci else 7868c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_NOT_CHARGING; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic int twl4030_bci_get_property(struct power_supply *psy, 7908c2ecf20Sopenharmony_ci enum power_supply_property psp, 7918c2ecf20Sopenharmony_ci union power_supply_propval *val) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent); 7948c2ecf20Sopenharmony_ci int is_charging; 7958c2ecf20Sopenharmony_ci int state; 7968c2ecf20Sopenharmony_ci int ret; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci state = twl4030bci_state(bci); 7998c2ecf20Sopenharmony_ci if (state < 0) 8008c2ecf20Sopenharmony_ci return state; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (psy->desc->type == POWER_SUPPLY_TYPE_USB) 8038c2ecf20Sopenharmony_ci is_charging = state & TWL4030_MSTATEC_USB; 8048c2ecf20Sopenharmony_ci else 8058c2ecf20Sopenharmony_ci is_charging = state & TWL4030_MSTATEC_AC; 8068c2ecf20Sopenharmony_ci if (!is_charging) { 8078c2ecf20Sopenharmony_ci u8 s; 8088c2ecf20Sopenharmony_ci ret = twl4030_bci_read(TWL4030_BCIMDEN, &s); 8098c2ecf20Sopenharmony_ci if (ret < 0) 8108c2ecf20Sopenharmony_ci return ret; 8118c2ecf20Sopenharmony_ci if (psy->desc->type == POWER_SUPPLY_TYPE_USB) 8128c2ecf20Sopenharmony_ci is_charging = s & 1; 8138c2ecf20Sopenharmony_ci else 8148c2ecf20Sopenharmony_ci is_charging = s & 2; 8158c2ecf20Sopenharmony_ci if (is_charging) 8168c2ecf20Sopenharmony_ci /* A little white lie */ 8178c2ecf20Sopenharmony_ci state = TWL4030_MSTATEC_QUICK1; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci switch (psp) { 8218c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 8228c2ecf20Sopenharmony_ci if (is_charging) 8238c2ecf20Sopenharmony_ci val->intval = twl4030_bci_state_to_status(state); 8248c2ecf20Sopenharmony_ci else 8258c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 8288c2ecf20Sopenharmony_ci /* charging must be active for meaningful result */ 8298c2ecf20Sopenharmony_ci if (!is_charging) 8308c2ecf20Sopenharmony_ci return -ENODATA; 8318c2ecf20Sopenharmony_ci if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { 8328c2ecf20Sopenharmony_ci ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS); 8338c2ecf20Sopenharmony_ci if (ret < 0) 8348c2ecf20Sopenharmony_ci return ret; 8358c2ecf20Sopenharmony_ci /* BCIVBUS uses ADCIN8, 7/1023 V/step */ 8368c2ecf20Sopenharmony_ci val->intval = ret * 6843; 8378c2ecf20Sopenharmony_ci } else { 8388c2ecf20Sopenharmony_ci ret = twl4030bci_read_adc_val(TWL4030_BCIVAC); 8398c2ecf20Sopenharmony_ci if (ret < 0) 8408c2ecf20Sopenharmony_ci return ret; 8418c2ecf20Sopenharmony_ci /* BCIVAC uses ADCIN11, 10/1023 V/step */ 8428c2ecf20Sopenharmony_ci val->intval = ret * 9775; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci break; 8458c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 8468c2ecf20Sopenharmony_ci if (!is_charging) 8478c2ecf20Sopenharmony_ci return -ENODATA; 8488c2ecf20Sopenharmony_ci /* current measurement is shared between AC and USB */ 8498c2ecf20Sopenharmony_ci ret = twl4030_charger_get_current(); 8508c2ecf20Sopenharmony_ci if (ret < 0) 8518c2ecf20Sopenharmony_ci return ret; 8528c2ecf20Sopenharmony_ci val->intval = ret; 8538c2ecf20Sopenharmony_ci break; 8548c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 8558c2ecf20Sopenharmony_ci val->intval = is_charging && 8568c2ecf20Sopenharmony_ci twl4030_bci_state_to_status(state) != 8578c2ecf20Sopenharmony_ci POWER_SUPPLY_STATUS_NOT_CHARGING; 8588c2ecf20Sopenharmony_ci break; 8598c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 8608c2ecf20Sopenharmony_ci val->intval = -1; 8618c2ecf20Sopenharmony_ci if (psy->desc->type != POWER_SUPPLY_TYPE_USB) { 8628c2ecf20Sopenharmony_ci if (!bci->ac_is_active) 8638c2ecf20Sopenharmony_ci val->intval = bci->ac_cur; 8648c2ecf20Sopenharmony_ci } else { 8658c2ecf20Sopenharmony_ci if (bci->ac_is_active) 8668c2ecf20Sopenharmony_ci val->intval = bci->usb_cur_target; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci if (val->intval < 0) { 8698c2ecf20Sopenharmony_ci u8 bcictl1; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci val->intval = twl4030bci_read_adc_val(TWL4030_BCIIREF1); 8728c2ecf20Sopenharmony_ci if (val->intval < 0) 8738c2ecf20Sopenharmony_ci return val->intval; 8748c2ecf20Sopenharmony_ci ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); 8758c2ecf20Sopenharmony_ci if (ret < 0) 8768c2ecf20Sopenharmony_ci return ret; 8778c2ecf20Sopenharmony_ci val->intval = regval2ua(val->intval, bcictl1 & 8788c2ecf20Sopenharmony_ci TWL4030_CGAIN); 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci default: 8828c2ecf20Sopenharmony_ci return -EINVAL; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci return 0; 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic int twl4030_bci_set_property(struct power_supply *psy, 8898c2ecf20Sopenharmony_ci enum power_supply_property psp, 8908c2ecf20Sopenharmony_ci const union power_supply_propval *val) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci switch (psp) { 8958c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 8968c2ecf20Sopenharmony_ci if (psy->desc->type == POWER_SUPPLY_TYPE_USB) 8978c2ecf20Sopenharmony_ci bci->usb_cur_target = val->intval; 8988c2ecf20Sopenharmony_ci else 8998c2ecf20Sopenharmony_ci bci->ac_cur = val->intval; 9008c2ecf20Sopenharmony_ci twl4030_charger_update_current(bci); 9018c2ecf20Sopenharmony_ci break; 9028c2ecf20Sopenharmony_ci default: 9038c2ecf20Sopenharmony_ci return -EINVAL; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci return 0; 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic int twl4030_bci_property_is_writeable(struct power_supply *psy, 9108c2ecf20Sopenharmony_ci enum power_supply_property psp) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci switch (psp) { 9138c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 9148c2ecf20Sopenharmony_ci return true; 9158c2ecf20Sopenharmony_ci default: 9168c2ecf20Sopenharmony_ci return false; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic enum power_supply_property twl4030_charger_props[] = { 9218c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 9228c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 9238c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 9248c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 9258c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 9268c2ecf20Sopenharmony_ci}; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 9298c2ecf20Sopenharmony_cistatic const struct twl4030_bci_platform_data * 9308c2ecf20Sopenharmony_citwl4030_bci_parse_dt(struct device *dev) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 9338c2ecf20Sopenharmony_ci struct twl4030_bci_platform_data *pdata; 9348c2ecf20Sopenharmony_ci u32 num; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (!np) 9378c2ecf20Sopenharmony_ci return NULL; 9388c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 9398c2ecf20Sopenharmony_ci if (!pdata) 9408c2ecf20Sopenharmony_ci return pdata; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "ti,bb-uvolt", &num) == 0) 9438c2ecf20Sopenharmony_ci pdata->bb_uvolt = num; 9448c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "ti,bb-uamp", &num) == 0) 9458c2ecf20Sopenharmony_ci pdata->bb_uamp = num; 9468c2ecf20Sopenharmony_ci return pdata; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci#else 9498c2ecf20Sopenharmony_cistatic inline const struct twl4030_bci_platform_data * 9508c2ecf20Sopenharmony_citwl4030_bci_parse_dt(struct device *dev) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci return NULL; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci#endif 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic const struct power_supply_desc twl4030_bci_ac_desc = { 9578c2ecf20Sopenharmony_ci .name = "twl4030_ac", 9588c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 9598c2ecf20Sopenharmony_ci .properties = twl4030_charger_props, 9608c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(twl4030_charger_props), 9618c2ecf20Sopenharmony_ci .get_property = twl4030_bci_get_property, 9628c2ecf20Sopenharmony_ci .set_property = twl4030_bci_set_property, 9638c2ecf20Sopenharmony_ci .property_is_writeable = twl4030_bci_property_is_writeable, 9648c2ecf20Sopenharmony_ci}; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic const struct power_supply_desc twl4030_bci_usb_desc = { 9678c2ecf20Sopenharmony_ci .name = "twl4030_usb", 9688c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 9698c2ecf20Sopenharmony_ci .properties = twl4030_charger_props, 9708c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(twl4030_charger_props), 9718c2ecf20Sopenharmony_ci .get_property = twl4030_bci_get_property, 9728c2ecf20Sopenharmony_ci .set_property = twl4030_bci_set_property, 9738c2ecf20Sopenharmony_ci .property_is_writeable = twl4030_bci_property_is_writeable, 9748c2ecf20Sopenharmony_ci}; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic int twl4030_bci_probe(struct platform_device *pdev) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct twl4030_bci *bci; 9798c2ecf20Sopenharmony_ci const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; 9808c2ecf20Sopenharmony_ci int ret; 9818c2ecf20Sopenharmony_ci u32 reg; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL); 9848c2ecf20Sopenharmony_ci if (bci == NULL) 9858c2ecf20Sopenharmony_ci return -ENOMEM; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (!pdata) 9888c2ecf20Sopenharmony_ci pdata = twl4030_bci_parse_dt(&pdev->dev); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci bci->ichg_eoc = 80100; /* Stop charging when current drops to here */ 9918c2ecf20Sopenharmony_ci bci->ichg_lo = 241000; /* Low threshold */ 9928c2ecf20Sopenharmony_ci bci->ichg_hi = 500000; /* High threshold */ 9938c2ecf20Sopenharmony_ci bci->ac_cur = 500000; /* 500mA */ 9948c2ecf20Sopenharmony_ci if (allow_usb) 9958c2ecf20Sopenharmony_ci bci->usb_cur_target = 500000; /* 500mA */ 9968c2ecf20Sopenharmony_ci else 9978c2ecf20Sopenharmony_ci bci->usb_cur_target = 100000; /* 100mA */ 9988c2ecf20Sopenharmony_ci bci->usb_mode = CHARGE_AUTO; 9998c2ecf20Sopenharmony_ci bci->ac_mode = CHARGE_AUTO; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci bci->dev = &pdev->dev; 10028c2ecf20Sopenharmony_ci bci->irq_chg = platform_get_irq(pdev, 0); 10038c2ecf20Sopenharmony_ci bci->irq_bci = platform_get_irq(pdev, 1); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, bci); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci INIT_WORK(&bci->work, twl4030_bci_usb_work); 10088c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci bci->channel_vac = devm_iio_channel_get(&pdev->dev, "vac"); 10118c2ecf20Sopenharmony_ci if (IS_ERR(bci->channel_vac)) { 10128c2ecf20Sopenharmony_ci ret = PTR_ERR(bci->channel_vac); 10138c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 10148c2ecf20Sopenharmony_ci return ret; /* iio not ready */ 10158c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "could not request vac iio channel (%d)", 10168c2ecf20Sopenharmony_ci ret); 10178c2ecf20Sopenharmony_ci bci->channel_vac = NULL; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (bci->dev->of_node) { 10218c2ecf20Sopenharmony_ci struct device_node *phynode; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci phynode = of_get_compatible_child(bci->dev->of_node->parent, 10248c2ecf20Sopenharmony_ci "ti,twl4030-usb"); 10258c2ecf20Sopenharmony_ci if (phynode) { 10268c2ecf20Sopenharmony_ci bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; 10278c2ecf20Sopenharmony_ci bci->transceiver = devm_usb_get_phy_by_node( 10288c2ecf20Sopenharmony_ci bci->dev, phynode, &bci->usb_nb); 10298c2ecf20Sopenharmony_ci of_node_put(phynode); 10308c2ecf20Sopenharmony_ci if (IS_ERR(bci->transceiver)) { 10318c2ecf20Sopenharmony_ci ret = PTR_ERR(bci->transceiver); 10328c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 10338c2ecf20Sopenharmony_ci return ret; /* phy not ready */ 10348c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "could not request transceiver (%d)", 10358c2ecf20Sopenharmony_ci ret); 10368c2ecf20Sopenharmony_ci bci->transceiver = NULL; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc, 10428c2ecf20Sopenharmony_ci NULL); 10438c2ecf20Sopenharmony_ci if (IS_ERR(bci->ac)) { 10448c2ecf20Sopenharmony_ci ret = PTR_ERR(bci->ac); 10458c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register ac: %d\n", ret); 10468c2ecf20Sopenharmony_ci return ret; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc, 10508c2ecf20Sopenharmony_ci NULL); 10518c2ecf20Sopenharmony_ci if (IS_ERR(bci->usb)) { 10528c2ecf20Sopenharmony_ci ret = PTR_ERR(bci->usb); 10538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register usb: %d\n", ret); 10548c2ecf20Sopenharmony_ci return ret; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL, 10588c2ecf20Sopenharmony_ci twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name, 10598c2ecf20Sopenharmony_ci bci); 10608c2ecf20Sopenharmony_ci if (ret < 0) { 10618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not request irq %d, status %d\n", 10628c2ecf20Sopenharmony_ci bci->irq_chg, ret); 10638c2ecf20Sopenharmony_ci return ret; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL, 10678c2ecf20Sopenharmony_ci twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci); 10688c2ecf20Sopenharmony_ci if (ret < 0) { 10698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not request irq %d, status %d\n", 10708c2ecf20Sopenharmony_ci bci->irq_bci, ret); 10718c2ecf20Sopenharmony_ci return ret; 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* Enable interrupts now. */ 10758c2ecf20Sopenharmony_ci reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 | 10768c2ecf20Sopenharmony_ci TWL4030_TBATOR1 | TWL4030_BATSTS); 10778c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, 10788c2ecf20Sopenharmony_ci TWL4030_INTERRUPTS_BCIIMR1A); 10798c2ecf20Sopenharmony_ci if (ret < 0) { 10808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret); 10818c2ecf20Sopenharmony_ci return ret; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); 10858c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, 10868c2ecf20Sopenharmony_ci TWL4030_INTERRUPTS_BCIIMR2A); 10878c2ecf20Sopenharmony_ci if (ret < 0) 10888c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci twl4030_charger_update_current(bci); 10918c2ecf20Sopenharmony_ci if (device_create_file(&bci->usb->dev, &dev_attr_mode)) 10928c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "could not create sysfs file\n"); 10938c2ecf20Sopenharmony_ci if (device_create_file(&bci->ac->dev, &dev_attr_mode)) 10948c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "could not create sysfs file\n"); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci twl4030_charger_enable_ac(bci, true); 10978c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(bci->transceiver)) 10988c2ecf20Sopenharmony_ci twl4030_bci_usb_ncb(&bci->usb_nb, 10998c2ecf20Sopenharmony_ci bci->transceiver->last_event, 11008c2ecf20Sopenharmony_ci NULL); 11018c2ecf20Sopenharmony_ci else 11028c2ecf20Sopenharmony_ci twl4030_charger_enable_usb(bci, false); 11038c2ecf20Sopenharmony_ci if (pdata) 11048c2ecf20Sopenharmony_ci twl4030_charger_enable_backup(pdata->bb_uvolt, 11058c2ecf20Sopenharmony_ci pdata->bb_uamp); 11068c2ecf20Sopenharmony_ci else 11078c2ecf20Sopenharmony_ci twl4030_charger_enable_backup(0, 0); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci return 0; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic int twl4030_bci_remove(struct platform_device *pdev) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci struct twl4030_bci *bci = platform_get_drvdata(pdev); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci twl4030_charger_enable_ac(bci, false); 11178c2ecf20Sopenharmony_ci twl4030_charger_enable_usb(bci, false); 11188c2ecf20Sopenharmony_ci twl4030_charger_enable_backup(0, 0); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci device_remove_file(&bci->usb->dev, &dev_attr_mode); 11218c2ecf20Sopenharmony_ci device_remove_file(&bci->ac->dev, &dev_attr_mode); 11228c2ecf20Sopenharmony_ci /* mask interrupts */ 11238c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, 11248c2ecf20Sopenharmony_ci TWL4030_INTERRUPTS_BCIIMR1A); 11258c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, 11268c2ecf20Sopenharmony_ci TWL4030_INTERRUPTS_BCIIMR2A); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci return 0; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic const struct of_device_id twl_bci_of_match[] = { 11328c2ecf20Sopenharmony_ci {.compatible = "ti,twl4030-bci", }, 11338c2ecf20Sopenharmony_ci { } 11348c2ecf20Sopenharmony_ci}; 11358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, twl_bci_of_match); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic struct platform_driver twl4030_bci_driver = { 11388c2ecf20Sopenharmony_ci .probe = twl4030_bci_probe, 11398c2ecf20Sopenharmony_ci .remove = twl4030_bci_remove, 11408c2ecf20Sopenharmony_ci .driver = { 11418c2ecf20Sopenharmony_ci .name = "twl4030_bci", 11428c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(twl_bci_of_match), 11438c2ecf20Sopenharmony_ci }, 11448c2ecf20Sopenharmony_ci}; 11458c2ecf20Sopenharmony_cimodule_platform_driver(twl4030_bci_driver); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gražvydas Ignotas"); 11488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); 11498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11508c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:twl4030_bci"); 1151