18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Summit Microelectronics SMB347 Battery Charger Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011, Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com> 88c2ecf20Sopenharmony_ci * Mika Westerberg <mika.westerberg@linux.intel.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 208c2ecf20Sopenharmony_ci#include <linux/property.h> 218c2ecf20Sopenharmony_ci#include <linux/regmap.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <dt-bindings/power/summit,smb347-charger.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Use the default compensation method */ 268c2ecf20Sopenharmony_ci#define SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT -1 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Use default factory programmed value for hard/soft temperature limit */ 298c2ecf20Sopenharmony_ci#define SMB3XX_TEMP_USE_DEFAULT -273 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * Configuration registers. These are mirrored to volatile RAM and can be 338c2ecf20Sopenharmony_ci * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be 348c2ecf20Sopenharmony_ci * reloaded from non-volatile registers after POR. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#define CFG_CHARGE_CURRENT 0x00 378c2ecf20Sopenharmony_ci#define CFG_CHARGE_CURRENT_FCC_MASK 0xe0 388c2ecf20Sopenharmony_ci#define CFG_CHARGE_CURRENT_FCC_SHIFT 5 398c2ecf20Sopenharmony_ci#define CFG_CHARGE_CURRENT_PCC_MASK 0x18 408c2ecf20Sopenharmony_ci#define CFG_CHARGE_CURRENT_PCC_SHIFT 3 418c2ecf20Sopenharmony_ci#define CFG_CHARGE_CURRENT_TC_MASK 0x07 428c2ecf20Sopenharmony_ci#define CFG_CURRENT_LIMIT 0x01 438c2ecf20Sopenharmony_ci#define CFG_CURRENT_LIMIT_DC_MASK 0xf0 448c2ecf20Sopenharmony_ci#define CFG_CURRENT_LIMIT_DC_SHIFT 4 458c2ecf20Sopenharmony_ci#define CFG_CURRENT_LIMIT_USB_MASK 0x0f 468c2ecf20Sopenharmony_ci#define CFG_FLOAT_VOLTAGE 0x03 478c2ecf20Sopenharmony_ci#define CFG_FLOAT_VOLTAGE_FLOAT_MASK 0x3f 488c2ecf20Sopenharmony_ci#define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0 498c2ecf20Sopenharmony_ci#define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6 508c2ecf20Sopenharmony_ci#define CFG_STAT 0x05 518c2ecf20Sopenharmony_ci#define CFG_STAT_DISABLED BIT(5) 528c2ecf20Sopenharmony_ci#define CFG_STAT_ACTIVE_HIGH BIT(7) 538c2ecf20Sopenharmony_ci#define CFG_PIN 0x06 548c2ecf20Sopenharmony_ci#define CFG_PIN_EN_CTRL_MASK 0x60 558c2ecf20Sopenharmony_ci#define CFG_PIN_EN_CTRL_ACTIVE_HIGH 0x40 568c2ecf20Sopenharmony_ci#define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60 578c2ecf20Sopenharmony_ci#define CFG_PIN_EN_APSD_IRQ BIT(1) 588c2ecf20Sopenharmony_ci#define CFG_PIN_EN_CHARGER_ERROR BIT(2) 598c2ecf20Sopenharmony_ci#define CFG_PIN_EN_CTRL BIT(4) 608c2ecf20Sopenharmony_ci#define CFG_THERM 0x07 618c2ecf20Sopenharmony_ci#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03 628c2ecf20Sopenharmony_ci#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0 638c2ecf20Sopenharmony_ci#define CFG_THERM_SOFT_COLD_COMPENSATION_MASK 0x0c 648c2ecf20Sopenharmony_ci#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2 658c2ecf20Sopenharmony_ci#define CFG_THERM_MONITOR_DISABLED BIT(4) 668c2ecf20Sopenharmony_ci#define CFG_SYSOK 0x08 678c2ecf20Sopenharmony_ci#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2) 688c2ecf20Sopenharmony_ci#define CFG_OTHER 0x09 698c2ecf20Sopenharmony_ci#define CFG_OTHER_RID_MASK 0xc0 708c2ecf20Sopenharmony_ci#define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0 718c2ecf20Sopenharmony_ci#define CFG_OTG 0x0a 728c2ecf20Sopenharmony_ci#define CFG_OTG_TEMP_THRESHOLD_MASK 0x30 738c2ecf20Sopenharmony_ci#define CFG_OTG_TEMP_THRESHOLD_SHIFT 4 748c2ecf20Sopenharmony_ci#define CFG_OTG_CC_COMPENSATION_MASK 0xc0 758c2ecf20Sopenharmony_ci#define CFG_OTG_CC_COMPENSATION_SHIFT 6 768c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT 0x0b 778c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT_SOFT_HOT_MASK 0x03 788c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT 0 798c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT_SOFT_COLD_MASK 0x0c 808c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT 2 818c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT_HARD_HOT_MASK 0x30 828c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT_HARD_HOT_SHIFT 4 838c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT_HARD_COLD_MASK 0xc0 848c2ecf20Sopenharmony_ci#define CFG_TEMP_LIMIT_HARD_COLD_SHIFT 6 858c2ecf20Sopenharmony_ci#define CFG_FAULT_IRQ 0x0c 868c2ecf20Sopenharmony_ci#define CFG_FAULT_IRQ_DCIN_UV BIT(2) 878c2ecf20Sopenharmony_ci#define CFG_STATUS_IRQ 0x0d 888c2ecf20Sopenharmony_ci#define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4) 898c2ecf20Sopenharmony_ci#define CFG_STATUS_IRQ_CHARGE_TIMEOUT BIT(7) 908c2ecf20Sopenharmony_ci#define CFG_ADDRESS 0x0e 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Command registers */ 938c2ecf20Sopenharmony_ci#define CMD_A 0x30 948c2ecf20Sopenharmony_ci#define CMD_A_CHG_ENABLED BIT(1) 958c2ecf20Sopenharmony_ci#define CMD_A_SUSPEND_ENABLED BIT(2) 968c2ecf20Sopenharmony_ci#define CMD_A_ALLOW_WRITE BIT(7) 978c2ecf20Sopenharmony_ci#define CMD_B 0x31 988c2ecf20Sopenharmony_ci#define CMD_C 0x33 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Interrupt Status registers */ 1018c2ecf20Sopenharmony_ci#define IRQSTAT_A 0x35 1028c2ecf20Sopenharmony_ci#define IRQSTAT_C 0x37 1038c2ecf20Sopenharmony_ci#define IRQSTAT_C_TERMINATION_STAT BIT(0) 1048c2ecf20Sopenharmony_ci#define IRQSTAT_C_TERMINATION_IRQ BIT(1) 1058c2ecf20Sopenharmony_ci#define IRQSTAT_C_TAPER_IRQ BIT(3) 1068c2ecf20Sopenharmony_ci#define IRQSTAT_D 0x38 1078c2ecf20Sopenharmony_ci#define IRQSTAT_D_CHARGE_TIMEOUT_STAT BIT(2) 1088c2ecf20Sopenharmony_ci#define IRQSTAT_D_CHARGE_TIMEOUT_IRQ BIT(3) 1098c2ecf20Sopenharmony_ci#define IRQSTAT_E 0x39 1108c2ecf20Sopenharmony_ci#define IRQSTAT_E_USBIN_UV_STAT BIT(0) 1118c2ecf20Sopenharmony_ci#define IRQSTAT_E_USBIN_UV_IRQ BIT(1) 1128c2ecf20Sopenharmony_ci#define IRQSTAT_E_DCIN_UV_STAT BIT(4) 1138c2ecf20Sopenharmony_ci#define IRQSTAT_E_DCIN_UV_IRQ BIT(5) 1148c2ecf20Sopenharmony_ci#define IRQSTAT_F 0x3a 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* Status registers */ 1178c2ecf20Sopenharmony_ci#define STAT_A 0x3b 1188c2ecf20Sopenharmony_ci#define STAT_A_FLOAT_VOLTAGE_MASK 0x3f 1198c2ecf20Sopenharmony_ci#define STAT_B 0x3c 1208c2ecf20Sopenharmony_ci#define STAT_C 0x3d 1218c2ecf20Sopenharmony_ci#define STAT_C_CHG_ENABLED BIT(0) 1228c2ecf20Sopenharmony_ci#define STAT_C_HOLDOFF_STAT BIT(3) 1238c2ecf20Sopenharmony_ci#define STAT_C_CHG_MASK 0x06 1248c2ecf20Sopenharmony_ci#define STAT_C_CHG_SHIFT 1 1258c2ecf20Sopenharmony_ci#define STAT_C_CHG_TERM BIT(5) 1268c2ecf20Sopenharmony_ci#define STAT_C_CHARGER_ERROR BIT(6) 1278c2ecf20Sopenharmony_ci#define STAT_E 0x3f 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define SMB347_MAX_REGISTER 0x3f 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/** 1328c2ecf20Sopenharmony_ci * struct smb347_charger - smb347 charger instance 1338c2ecf20Sopenharmony_ci * @dev: pointer to device 1348c2ecf20Sopenharmony_ci * @regmap: pointer to driver regmap 1358c2ecf20Sopenharmony_ci * @mains: power_supply instance for AC/DC power 1368c2ecf20Sopenharmony_ci * @usb: power_supply instance for USB power 1378c2ecf20Sopenharmony_ci * @id: SMB charger ID 1388c2ecf20Sopenharmony_ci * @mains_online: is AC/DC input connected 1398c2ecf20Sopenharmony_ci * @usb_online: is USB input connected 1408c2ecf20Sopenharmony_ci * @charging_enabled: is charging enabled 1418c2ecf20Sopenharmony_ci * @irq_unsupported: is interrupt unsupported by SMB hardware 1428c2ecf20Sopenharmony_ci * @max_charge_current: maximum current (in uA) the battery can be charged 1438c2ecf20Sopenharmony_ci * @max_charge_voltage: maximum voltage (in uV) the battery can be charged 1448c2ecf20Sopenharmony_ci * @pre_charge_current: current (in uA) to use in pre-charging phase 1458c2ecf20Sopenharmony_ci * @termination_current: current (in uA) used to determine when the 1468c2ecf20Sopenharmony_ci * charging cycle terminates 1478c2ecf20Sopenharmony_ci * @pre_to_fast_voltage: voltage (in uV) treshold used for transitioning to 1488c2ecf20Sopenharmony_ci * pre-charge to fast charge mode 1498c2ecf20Sopenharmony_ci * @mains_current_limit: maximum input current drawn from AC/DC input (in uA) 1508c2ecf20Sopenharmony_ci * @usb_hc_current_limit: maximum input high current (in uA) drawn from USB 1518c2ecf20Sopenharmony_ci * input 1528c2ecf20Sopenharmony_ci * @chip_temp_threshold: die temperature where device starts limiting charge 1538c2ecf20Sopenharmony_ci * current [%100 - %130] (in degree C) 1548c2ecf20Sopenharmony_ci * @soft_cold_temp_limit: soft cold temperature limit [%0 - %15] (in degree C), 1558c2ecf20Sopenharmony_ci * granularity is 5 deg C. 1568c2ecf20Sopenharmony_ci * @soft_hot_temp_limit: soft hot temperature limit [%40 - %55] (in degree C), 1578c2ecf20Sopenharmony_ci * granularity is 5 deg C. 1588c2ecf20Sopenharmony_ci * @hard_cold_temp_limit: hard cold temperature limit [%-5 - %10] (in degree C), 1598c2ecf20Sopenharmony_ci * granularity is 5 deg C. 1608c2ecf20Sopenharmony_ci * @hard_hot_temp_limit: hard hot temperature limit [%50 - %65] (in degree C), 1618c2ecf20Sopenharmony_ci * granularity is 5 deg C. 1628c2ecf20Sopenharmony_ci * @suspend_on_hard_temp_limit: suspend charging when hard limit is hit 1638c2ecf20Sopenharmony_ci * @soft_temp_limit_compensation: compensation method when soft temperature 1648c2ecf20Sopenharmony_ci * limit is hit 1658c2ecf20Sopenharmony_ci * @charge_current_compensation: current (in uA) for charging compensation 1668c2ecf20Sopenharmony_ci * current when temperature hits soft limits 1678c2ecf20Sopenharmony_ci * @use_mains: AC/DC input can be used 1688c2ecf20Sopenharmony_ci * @use_usb: USB input can be used 1698c2ecf20Sopenharmony_ci * @use_usb_otg: USB OTG output can be used (not implemented yet) 1708c2ecf20Sopenharmony_ci * @enable_control: how charging enable/disable is controlled 1718c2ecf20Sopenharmony_ci * (driver/pin controls) 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * @use_main, @use_usb, and @use_usb_otg are means to enable/disable 1748c2ecf20Sopenharmony_ci * hardware support for these. This is useful when we want to have for 1758c2ecf20Sopenharmony_ci * example OTG charging controlled via OTG transceiver driver and not by 1768c2ecf20Sopenharmony_ci * the SMB347 hardware. 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * Hard and soft temperature limit values are given as described in the 1798c2ecf20Sopenharmony_ci * device data sheet and assuming NTC beta value is %3750. Even if this is 1808c2ecf20Sopenharmony_ci * not the case, these values should be used. They can be mapped to the 1818c2ecf20Sopenharmony_ci * corresponding NTC beta values with the help of table %2 in the data 1828c2ecf20Sopenharmony_ci * sheet. So for example if NTC beta is %3375 and we want to program hard 1838c2ecf20Sopenharmony_ci * hot limit to be %53 deg C, @hard_hot_temp_limit should be set to %50. 1848c2ecf20Sopenharmony_ci * 1858c2ecf20Sopenharmony_ci * If zero value is given in any of the current and voltage values, the 1868c2ecf20Sopenharmony_ci * factory programmed default will be used. For soft/hard temperature 1878c2ecf20Sopenharmony_ci * values, pass in %SMB3XX_TEMP_USE_DEFAULT instead. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistruct smb347_charger { 1908c2ecf20Sopenharmony_ci struct device *dev; 1918c2ecf20Sopenharmony_ci struct regmap *regmap; 1928c2ecf20Sopenharmony_ci struct power_supply *mains; 1938c2ecf20Sopenharmony_ci struct power_supply *usb; 1948c2ecf20Sopenharmony_ci unsigned int id; 1958c2ecf20Sopenharmony_ci bool mains_online; 1968c2ecf20Sopenharmony_ci bool usb_online; 1978c2ecf20Sopenharmony_ci bool charging_enabled; 1988c2ecf20Sopenharmony_ci bool irq_unsupported; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci unsigned int max_charge_current; 2018c2ecf20Sopenharmony_ci unsigned int max_charge_voltage; 2028c2ecf20Sopenharmony_ci unsigned int pre_charge_current; 2038c2ecf20Sopenharmony_ci unsigned int termination_current; 2048c2ecf20Sopenharmony_ci unsigned int pre_to_fast_voltage; 2058c2ecf20Sopenharmony_ci unsigned int mains_current_limit; 2068c2ecf20Sopenharmony_ci unsigned int usb_hc_current_limit; 2078c2ecf20Sopenharmony_ci unsigned int chip_temp_threshold; 2088c2ecf20Sopenharmony_ci int soft_cold_temp_limit; 2098c2ecf20Sopenharmony_ci int soft_hot_temp_limit; 2108c2ecf20Sopenharmony_ci int hard_cold_temp_limit; 2118c2ecf20Sopenharmony_ci int hard_hot_temp_limit; 2128c2ecf20Sopenharmony_ci bool suspend_on_hard_temp_limit; 2138c2ecf20Sopenharmony_ci unsigned int soft_temp_limit_compensation; 2148c2ecf20Sopenharmony_ci unsigned int charge_current_compensation; 2158c2ecf20Sopenharmony_ci bool use_mains; 2168c2ecf20Sopenharmony_ci bool use_usb; 2178c2ecf20Sopenharmony_ci bool use_usb_otg; 2188c2ecf20Sopenharmony_ci unsigned int enable_control; 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cienum smb_charger_chipid { 2228c2ecf20Sopenharmony_ci SMB345, 2238c2ecf20Sopenharmony_ci SMB347, 2248c2ecf20Sopenharmony_ci SMB358, 2258c2ecf20Sopenharmony_ci NUM_CHIP_TYPES, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* Fast charge current in uA */ 2298c2ecf20Sopenharmony_cistatic const unsigned int fcc_tbl[NUM_CHIP_TYPES][8] = { 2308c2ecf20Sopenharmony_ci [SMB345] = { 200000, 450000, 600000, 900000, 2318c2ecf20Sopenharmony_ci 1300000, 1500000, 1800000, 2000000 }, 2328c2ecf20Sopenharmony_ci [SMB347] = { 700000, 900000, 1200000, 1500000, 2338c2ecf20Sopenharmony_ci 1800000, 2000000, 2200000, 2500000 }, 2348c2ecf20Sopenharmony_ci [SMB358] = { 200000, 450000, 600000, 900000, 2358c2ecf20Sopenharmony_ci 1300000, 1500000, 1800000, 2000000 }, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci/* Pre-charge current in uA */ 2388c2ecf20Sopenharmony_cistatic const unsigned int pcc_tbl[NUM_CHIP_TYPES][4] = { 2398c2ecf20Sopenharmony_ci [SMB345] = { 150000, 250000, 350000, 450000 }, 2408c2ecf20Sopenharmony_ci [SMB347] = { 100000, 150000, 200000, 250000 }, 2418c2ecf20Sopenharmony_ci [SMB358] = { 150000, 250000, 350000, 450000 }, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* Termination current in uA */ 2458c2ecf20Sopenharmony_cistatic const unsigned int tc_tbl[NUM_CHIP_TYPES][8] = { 2468c2ecf20Sopenharmony_ci [SMB345] = { 30000, 40000, 60000, 80000, 2478c2ecf20Sopenharmony_ci 100000, 125000, 150000, 200000 }, 2488c2ecf20Sopenharmony_ci [SMB347] = { 37500, 50000, 100000, 150000, 2498c2ecf20Sopenharmony_ci 200000, 250000, 500000, 600000 }, 2508c2ecf20Sopenharmony_ci [SMB358] = { 30000, 40000, 60000, 80000, 2518c2ecf20Sopenharmony_ci 100000, 125000, 150000, 200000 }, 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* Input current limit in uA */ 2558c2ecf20Sopenharmony_cistatic const unsigned int icl_tbl[NUM_CHIP_TYPES][10] = { 2568c2ecf20Sopenharmony_ci [SMB345] = { 300000, 500000, 700000, 1000000, 1500000, 2578c2ecf20Sopenharmony_ci 1800000, 2000000, 2000000, 2000000, 2000000 }, 2588c2ecf20Sopenharmony_ci [SMB347] = { 300000, 500000, 700000, 900000, 1200000, 2598c2ecf20Sopenharmony_ci 1500000, 1800000, 2000000, 2200000, 2500000 }, 2608c2ecf20Sopenharmony_ci [SMB358] = { 300000, 500000, 700000, 1000000, 1500000, 2618c2ecf20Sopenharmony_ci 1800000, 2000000, 2000000, 2000000, 2000000 }, 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/* Charge current compensation in uA */ 2658c2ecf20Sopenharmony_cistatic const unsigned int ccc_tbl[NUM_CHIP_TYPES][4] = { 2668c2ecf20Sopenharmony_ci [SMB345] = { 200000, 450000, 600000, 900000 }, 2678c2ecf20Sopenharmony_ci [SMB347] = { 250000, 700000, 900000, 1200000 }, 2688c2ecf20Sopenharmony_ci [SMB358] = { 200000, 450000, 600000, 900000 }, 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* Convert register value to current using lookup table */ 2728c2ecf20Sopenharmony_cistatic int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci if (val >= size) 2758c2ecf20Sopenharmony_ci return -EINVAL; 2768c2ecf20Sopenharmony_ci return tbl[val]; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* Convert current to register value using lookup table */ 2808c2ecf20Sopenharmony_cistatic int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci size_t i; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) 2858c2ecf20Sopenharmony_ci if (val < tbl[i]) 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci return i > 0 ? i - 1 : -EINVAL; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/** 2918c2ecf20Sopenharmony_ci * smb347_update_ps_status - refreshes the power source status 2928c2ecf20Sopenharmony_ci * @smb: pointer to smb347 charger instance 2938c2ecf20Sopenharmony_ci * 2948c2ecf20Sopenharmony_ci * Function checks whether any power source is connected to the charger and 2958c2ecf20Sopenharmony_ci * updates internal state accordingly. If there is a change to previous state 2968c2ecf20Sopenharmony_ci * function returns %1, otherwise %0 and negative errno in case of errror. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_cistatic int smb347_update_ps_status(struct smb347_charger *smb) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci bool usb = false; 3018c2ecf20Sopenharmony_ci bool dc = false; 3028c2ecf20Sopenharmony_ci unsigned int val; 3038c2ecf20Sopenharmony_ci int ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, IRQSTAT_E, &val); 3068c2ecf20Sopenharmony_ci if (ret < 0) 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * Dc and usb are set depending on whether they are enabled in 3118c2ecf20Sopenharmony_ci * platform data _and_ whether corresponding undervoltage is set. 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci if (smb->use_mains) 3148c2ecf20Sopenharmony_ci dc = !(val & IRQSTAT_E_DCIN_UV_STAT); 3158c2ecf20Sopenharmony_ci if (smb->use_usb) 3168c2ecf20Sopenharmony_ci usb = !(val & IRQSTAT_E_USBIN_UV_STAT); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci ret = smb->mains_online != dc || smb->usb_online != usb; 3198c2ecf20Sopenharmony_ci smb->mains_online = dc; 3208c2ecf20Sopenharmony_ci smb->usb_online = usb; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * smb347_is_ps_online - returns whether input power source is connected 3278c2ecf20Sopenharmony_ci * @smb: pointer to smb347 charger instance 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * Returns %true if input power source is connected. Note that this is 3308c2ecf20Sopenharmony_ci * dependent on what platform has configured for usable power sources. For 3318c2ecf20Sopenharmony_ci * example if USB is disabled, this will return %false even if the USB cable 3328c2ecf20Sopenharmony_ci * is connected. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_cistatic bool smb347_is_ps_online(struct smb347_charger *smb) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci return smb->usb_online || smb->mains_online; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/** 3408c2ecf20Sopenharmony_ci * smb347_charging_status - returns status of charging 3418c2ecf20Sopenharmony_ci * @smb: pointer to smb347 charger instance 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * Function returns charging status. %0 means no charging is in progress, 3448c2ecf20Sopenharmony_ci * %1 means pre-charging, %2 fast-charging and %3 taper-charging. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_cistatic int smb347_charging_status(struct smb347_charger *smb) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci unsigned int val; 3498c2ecf20Sopenharmony_ci int ret; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!smb347_is_ps_online(smb)) 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, STAT_C, &val); 3558c2ecf20Sopenharmony_ci if (ret < 0) 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int smb347_charging_set(struct smb347_charger *smb, bool enable) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci int ret = 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) { 3668c2ecf20Sopenharmony_ci dev_dbg(smb->dev, "charging enable/disable in SW disabled\n"); 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (smb->charging_enabled != enable) { 3718c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, 3728c2ecf20Sopenharmony_ci enable ? CMD_A_CHG_ENABLED : 0); 3738c2ecf20Sopenharmony_ci if (!ret) 3748c2ecf20Sopenharmony_ci smb->charging_enabled = enable; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic inline int smb347_charging_enable(struct smb347_charger *smb) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci return smb347_charging_set(smb, true); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic inline int smb347_charging_disable(struct smb347_charger *smb) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci return smb347_charging_set(smb, false); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int smb347_start_stop_charging(struct smb347_charger *smb) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci int ret; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* 3958c2ecf20Sopenharmony_ci * Depending on whether valid power source is connected or not, we 3968c2ecf20Sopenharmony_ci * disable or enable the charging. We do it manually because it 3978c2ecf20Sopenharmony_ci * depends on how the platform has configured the valid inputs. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci if (smb347_is_ps_online(smb)) { 4008c2ecf20Sopenharmony_ci ret = smb347_charging_enable(smb); 4018c2ecf20Sopenharmony_ci if (ret < 0) 4028c2ecf20Sopenharmony_ci dev_err(smb->dev, "failed to enable charging\n"); 4038c2ecf20Sopenharmony_ci } else { 4048c2ecf20Sopenharmony_ci ret = smb347_charging_disable(smb); 4058c2ecf20Sopenharmony_ci if (ret < 0) 4068c2ecf20Sopenharmony_ci dev_err(smb->dev, "failed to disable charging\n"); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return ret; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int smb347_set_charge_current(struct smb347_charger *smb) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci unsigned int id = smb->id; 4158c2ecf20Sopenharmony_ci int ret; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (smb->max_charge_current) { 4188c2ecf20Sopenharmony_ci ret = current_to_hw(fcc_tbl[id], ARRAY_SIZE(fcc_tbl[id]), 4198c2ecf20Sopenharmony_ci smb->max_charge_current); 4208c2ecf20Sopenharmony_ci if (ret < 0) 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4248c2ecf20Sopenharmony_ci CFG_CHARGE_CURRENT_FCC_MASK, 4258c2ecf20Sopenharmony_ci ret << CFG_CHARGE_CURRENT_FCC_SHIFT); 4268c2ecf20Sopenharmony_ci if (ret < 0) 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (smb->pre_charge_current) { 4318c2ecf20Sopenharmony_ci ret = current_to_hw(pcc_tbl[id], ARRAY_SIZE(pcc_tbl[id]), 4328c2ecf20Sopenharmony_ci smb->pre_charge_current); 4338c2ecf20Sopenharmony_ci if (ret < 0) 4348c2ecf20Sopenharmony_ci return ret; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4378c2ecf20Sopenharmony_ci CFG_CHARGE_CURRENT_PCC_MASK, 4388c2ecf20Sopenharmony_ci ret << CFG_CHARGE_CURRENT_PCC_SHIFT); 4398c2ecf20Sopenharmony_ci if (ret < 0) 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (smb->termination_current) { 4448c2ecf20Sopenharmony_ci ret = current_to_hw(tc_tbl[id], ARRAY_SIZE(tc_tbl[id]), 4458c2ecf20Sopenharmony_ci smb->termination_current); 4468c2ecf20Sopenharmony_ci if (ret < 0) 4478c2ecf20Sopenharmony_ci return ret; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4508c2ecf20Sopenharmony_ci CFG_CHARGE_CURRENT_TC_MASK, ret); 4518c2ecf20Sopenharmony_ci if (ret < 0) 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int smb347_set_current_limits(struct smb347_charger *smb) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci unsigned int id = smb->id; 4618c2ecf20Sopenharmony_ci int ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (smb->mains_current_limit) { 4648c2ecf20Sopenharmony_ci ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), 4658c2ecf20Sopenharmony_ci smb->mains_current_limit); 4668c2ecf20Sopenharmony_ci if (ret < 0) 4678c2ecf20Sopenharmony_ci return ret; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, 4708c2ecf20Sopenharmony_ci CFG_CURRENT_LIMIT_DC_MASK, 4718c2ecf20Sopenharmony_ci ret << CFG_CURRENT_LIMIT_DC_SHIFT); 4728c2ecf20Sopenharmony_ci if (ret < 0) 4738c2ecf20Sopenharmony_ci return ret; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (smb->usb_hc_current_limit) { 4778c2ecf20Sopenharmony_ci ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), 4788c2ecf20Sopenharmony_ci smb->usb_hc_current_limit); 4798c2ecf20Sopenharmony_ci if (ret < 0) 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, 4838c2ecf20Sopenharmony_ci CFG_CURRENT_LIMIT_USB_MASK, ret); 4848c2ecf20Sopenharmony_ci if (ret < 0) 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int smb347_set_voltage_limits(struct smb347_charger *smb) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci int ret; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (smb->pre_to_fast_voltage) { 4968c2ecf20Sopenharmony_ci ret = smb->pre_to_fast_voltage; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* uV */ 4998c2ecf20Sopenharmony_ci ret = clamp_val(ret, 2400000, 3000000) - 2400000; 5008c2ecf20Sopenharmony_ci ret /= 200000; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, 5038c2ecf20Sopenharmony_ci CFG_FLOAT_VOLTAGE_THRESHOLD_MASK, 5048c2ecf20Sopenharmony_ci ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT); 5058c2ecf20Sopenharmony_ci if (ret < 0) 5068c2ecf20Sopenharmony_ci return ret; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (smb->max_charge_voltage) { 5108c2ecf20Sopenharmony_ci ret = smb->max_charge_voltage; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* uV */ 5138c2ecf20Sopenharmony_ci ret = clamp_val(ret, 3500000, 4500000) - 3500000; 5148c2ecf20Sopenharmony_ci ret /= 20000; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, 5178c2ecf20Sopenharmony_ci CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret); 5188c2ecf20Sopenharmony_ci if (ret < 0) 5198c2ecf20Sopenharmony_ci return ret; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int smb347_set_temp_limits(struct smb347_charger *smb) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci unsigned int id = smb->id; 5288c2ecf20Sopenharmony_ci bool enable_therm_monitor = false; 5298c2ecf20Sopenharmony_ci int ret = 0; 5308c2ecf20Sopenharmony_ci int val; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (smb->chip_temp_threshold) { 5338c2ecf20Sopenharmony_ci val = smb->chip_temp_threshold; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* degree C */ 5368c2ecf20Sopenharmony_ci val = clamp_val(val, 100, 130) - 100; 5378c2ecf20Sopenharmony_ci val /= 10; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_OTG, 5408c2ecf20Sopenharmony_ci CFG_OTG_TEMP_THRESHOLD_MASK, 5418c2ecf20Sopenharmony_ci val << CFG_OTG_TEMP_THRESHOLD_SHIFT); 5428c2ecf20Sopenharmony_ci if (ret < 0) 5438c2ecf20Sopenharmony_ci return ret; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (smb->soft_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 5478c2ecf20Sopenharmony_ci val = smb->soft_cold_temp_limit; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 15); 5508c2ecf20Sopenharmony_ci val /= 5; 5518c2ecf20Sopenharmony_ci /* this goes from higher to lower so invert the value */ 5528c2ecf20Sopenharmony_ci val = ~val & 0x3; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5558c2ecf20Sopenharmony_ci CFG_TEMP_LIMIT_SOFT_COLD_MASK, 5568c2ecf20Sopenharmony_ci val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT); 5578c2ecf20Sopenharmony_ci if (ret < 0) 5588c2ecf20Sopenharmony_ci return ret; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci enable_therm_monitor = true; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (smb->soft_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 5648c2ecf20Sopenharmony_ci val = smb->soft_hot_temp_limit; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci val = clamp_val(val, 40, 55) - 40; 5678c2ecf20Sopenharmony_ci val /= 5; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5708c2ecf20Sopenharmony_ci CFG_TEMP_LIMIT_SOFT_HOT_MASK, 5718c2ecf20Sopenharmony_ci val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT); 5728c2ecf20Sopenharmony_ci if (ret < 0) 5738c2ecf20Sopenharmony_ci return ret; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci enable_therm_monitor = true; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 5798c2ecf20Sopenharmony_ci val = smb->hard_cold_temp_limit; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci val = clamp_val(val, -5, 10) + 5; 5828c2ecf20Sopenharmony_ci val /= 5; 5838c2ecf20Sopenharmony_ci /* this goes from higher to lower so invert the value */ 5848c2ecf20Sopenharmony_ci val = ~val & 0x3; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5878c2ecf20Sopenharmony_ci CFG_TEMP_LIMIT_HARD_COLD_MASK, 5888c2ecf20Sopenharmony_ci val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT); 5898c2ecf20Sopenharmony_ci if (ret < 0) 5908c2ecf20Sopenharmony_ci return ret; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci enable_therm_monitor = true; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 5968c2ecf20Sopenharmony_ci val = smb->hard_hot_temp_limit; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci val = clamp_val(val, 50, 65) - 50; 5998c2ecf20Sopenharmony_ci val /= 5; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 6028c2ecf20Sopenharmony_ci CFG_TEMP_LIMIT_HARD_HOT_MASK, 6038c2ecf20Sopenharmony_ci val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT); 6048c2ecf20Sopenharmony_ci if (ret < 0) 6058c2ecf20Sopenharmony_ci return ret; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci enable_therm_monitor = true; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* 6118c2ecf20Sopenharmony_ci * If any of the temperature limits are set, we also enable the 6128c2ecf20Sopenharmony_ci * thermistor monitoring. 6138c2ecf20Sopenharmony_ci * 6148c2ecf20Sopenharmony_ci * When soft limits are hit, the device will start to compensate 6158c2ecf20Sopenharmony_ci * current and/or voltage depending on the configuration. 6168c2ecf20Sopenharmony_ci * 6178c2ecf20Sopenharmony_ci * When hard limit is hit, the device will suspend charging 6188c2ecf20Sopenharmony_ci * depending on the configuration. 6198c2ecf20Sopenharmony_ci */ 6208c2ecf20Sopenharmony_ci if (enable_therm_monitor) { 6218c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_THERM, 6228c2ecf20Sopenharmony_ci CFG_THERM_MONITOR_DISABLED, 0); 6238c2ecf20Sopenharmony_ci if (ret < 0) 6248c2ecf20Sopenharmony_ci return ret; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (smb->suspend_on_hard_temp_limit) { 6288c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_SYSOK, 6298c2ecf20Sopenharmony_ci CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0); 6308c2ecf20Sopenharmony_ci if (ret < 0) 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (smb->soft_temp_limit_compensation != 6358c2ecf20Sopenharmony_ci SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT) { 6368c2ecf20Sopenharmony_ci val = smb->soft_temp_limit_compensation & 0x3; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_THERM, 6398c2ecf20Sopenharmony_ci CFG_THERM_SOFT_HOT_COMPENSATION_MASK, 6408c2ecf20Sopenharmony_ci val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT); 6418c2ecf20Sopenharmony_ci if (ret < 0) 6428c2ecf20Sopenharmony_ci return ret; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_THERM, 6458c2ecf20Sopenharmony_ci CFG_THERM_SOFT_COLD_COMPENSATION_MASK, 6468c2ecf20Sopenharmony_ci val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT); 6478c2ecf20Sopenharmony_ci if (ret < 0) 6488c2ecf20Sopenharmony_ci return ret; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (smb->charge_current_compensation) { 6528c2ecf20Sopenharmony_ci val = current_to_hw(ccc_tbl[id], ARRAY_SIZE(ccc_tbl[id]), 6538c2ecf20Sopenharmony_ci smb->charge_current_compensation); 6548c2ecf20Sopenharmony_ci if (val < 0) 6558c2ecf20Sopenharmony_ci return val; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_OTG, 6588c2ecf20Sopenharmony_ci CFG_OTG_CC_COMPENSATION_MASK, 6598c2ecf20Sopenharmony_ci (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT); 6608c2ecf20Sopenharmony_ci if (ret < 0) 6618c2ecf20Sopenharmony_ci return ret; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return ret; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/* 6688c2ecf20Sopenharmony_ci * smb347_set_writable - enables/disables writing to non-volatile registers 6698c2ecf20Sopenharmony_ci * @smb: pointer to smb347 charger instance 6708c2ecf20Sopenharmony_ci * 6718c2ecf20Sopenharmony_ci * You can enable/disable writing to the non-volatile configuration 6728c2ecf20Sopenharmony_ci * registers by calling this function. 6738c2ecf20Sopenharmony_ci * 6748c2ecf20Sopenharmony_ci * Returns %0 on success and negative errno in case of failure. 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_cistatic int smb347_set_writable(struct smb347_charger *smb, bool writable) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE, 6798c2ecf20Sopenharmony_ci writable ? CMD_A_ALLOW_WRITE : 0); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int smb347_hw_init(struct smb347_charger *smb) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci unsigned int val; 6858c2ecf20Sopenharmony_ci int ret; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ret = smb347_set_writable(smb, true); 6888c2ecf20Sopenharmony_ci if (ret < 0) 6898c2ecf20Sopenharmony_ci return ret; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* 6928c2ecf20Sopenharmony_ci * Program the platform specific configuration values to the device 6938c2ecf20Sopenharmony_ci * first. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci ret = smb347_set_charge_current(smb); 6968c2ecf20Sopenharmony_ci if (ret < 0) 6978c2ecf20Sopenharmony_ci goto fail; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci ret = smb347_set_current_limits(smb); 7008c2ecf20Sopenharmony_ci if (ret < 0) 7018c2ecf20Sopenharmony_ci goto fail; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci ret = smb347_set_voltage_limits(smb); 7048c2ecf20Sopenharmony_ci if (ret < 0) 7058c2ecf20Sopenharmony_ci goto fail; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci ret = smb347_set_temp_limits(smb); 7088c2ecf20Sopenharmony_ci if (ret < 0) 7098c2ecf20Sopenharmony_ci goto fail; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* If USB charging is disabled we put the USB in suspend mode */ 7128c2ecf20Sopenharmony_ci if (!smb->use_usb) { 7138c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CMD_A, 7148c2ecf20Sopenharmony_ci CMD_A_SUSPEND_ENABLED, 7158c2ecf20Sopenharmony_ci CMD_A_SUSPEND_ENABLED); 7168c2ecf20Sopenharmony_ci if (ret < 0) 7178c2ecf20Sopenharmony_ci goto fail; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* 7218c2ecf20Sopenharmony_ci * If configured by platform data, we enable hardware Auto-OTG 7228c2ecf20Sopenharmony_ci * support for driving VBUS. Otherwise we disable it. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK, 7258c2ecf20Sopenharmony_ci smb->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0); 7268c2ecf20Sopenharmony_ci if (ret < 0) 7278c2ecf20Sopenharmony_ci goto fail; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* Activate pin control, making it writable. */ 7308c2ecf20Sopenharmony_ci switch (smb->enable_control) { 7318c2ecf20Sopenharmony_ci case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW: 7328c2ecf20Sopenharmony_ci case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH: 7338c2ecf20Sopenharmony_ci ret = regmap_set_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL); 7348c2ecf20Sopenharmony_ci if (ret < 0) 7358c2ecf20Sopenharmony_ci goto fail; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * Make the charging functionality controllable by a write to the 7408c2ecf20Sopenharmony_ci * command register unless pin control is specified in the platform 7418c2ecf20Sopenharmony_ci * data. 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci switch (smb->enable_control) { 7448c2ecf20Sopenharmony_ci case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW: 7458c2ecf20Sopenharmony_ci val = CFG_PIN_EN_CTRL_ACTIVE_LOW; 7468c2ecf20Sopenharmony_ci break; 7478c2ecf20Sopenharmony_ci case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH: 7488c2ecf20Sopenharmony_ci val = CFG_PIN_EN_CTRL_ACTIVE_HIGH; 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci default: 7518c2ecf20Sopenharmony_ci val = 0; 7528c2ecf20Sopenharmony_ci break; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK, 7568c2ecf20Sopenharmony_ci val); 7578c2ecf20Sopenharmony_ci if (ret < 0) 7588c2ecf20Sopenharmony_ci goto fail; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* Disable Automatic Power Source Detection (APSD) interrupt. */ 7618c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0); 7628c2ecf20Sopenharmony_ci if (ret < 0) 7638c2ecf20Sopenharmony_ci goto fail; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci ret = smb347_update_ps_status(smb); 7668c2ecf20Sopenharmony_ci if (ret < 0) 7678c2ecf20Sopenharmony_ci goto fail; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci ret = smb347_start_stop_charging(smb); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cifail: 7728c2ecf20Sopenharmony_ci smb347_set_writable(smb, false); 7738c2ecf20Sopenharmony_ci return ret; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic irqreturn_t smb347_interrupt(int irq, void *data) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct smb347_charger *smb = data; 7798c2ecf20Sopenharmony_ci unsigned int stat_c, irqstat_c, irqstat_d, irqstat_e; 7808c2ecf20Sopenharmony_ci bool handled = false; 7818c2ecf20Sopenharmony_ci int ret; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* SMB347 it needs at least 20ms for setting IRQSTAT_E_*IN_UV_IRQ */ 7848c2ecf20Sopenharmony_ci usleep_range(25000, 35000); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, STAT_C, &stat_c); 7878c2ecf20Sopenharmony_ci if (ret < 0) { 7888c2ecf20Sopenharmony_ci dev_warn(smb->dev, "reading STAT_C failed\n"); 7898c2ecf20Sopenharmony_ci return IRQ_NONE; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c); 7938c2ecf20Sopenharmony_ci if (ret < 0) { 7948c2ecf20Sopenharmony_ci dev_warn(smb->dev, "reading IRQSTAT_C failed\n"); 7958c2ecf20Sopenharmony_ci return IRQ_NONE; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, IRQSTAT_D, &irqstat_d); 7998c2ecf20Sopenharmony_ci if (ret < 0) { 8008c2ecf20Sopenharmony_ci dev_warn(smb->dev, "reading IRQSTAT_D failed\n"); 8018c2ecf20Sopenharmony_ci return IRQ_NONE; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e); 8058c2ecf20Sopenharmony_ci if (ret < 0) { 8068c2ecf20Sopenharmony_ci dev_warn(smb->dev, "reading IRQSTAT_E failed\n"); 8078c2ecf20Sopenharmony_ci return IRQ_NONE; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* 8118c2ecf20Sopenharmony_ci * If we get charger error we report the error back to user. 8128c2ecf20Sopenharmony_ci * If the error is recovered charging will resume again. 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_ci if (stat_c & STAT_C_CHARGER_ERROR) { 8158c2ecf20Sopenharmony_ci dev_err(smb->dev, "charging stopped due to charger error\n"); 8168c2ecf20Sopenharmony_ci if (smb->use_mains) 8178c2ecf20Sopenharmony_ci power_supply_changed(smb->mains); 8188c2ecf20Sopenharmony_ci if (smb->use_usb) 8198c2ecf20Sopenharmony_ci power_supply_changed(smb->usb); 8208c2ecf20Sopenharmony_ci handled = true; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* 8248c2ecf20Sopenharmony_ci * If we reached the termination current the battery is charged and 8258c2ecf20Sopenharmony_ci * we can update the status now. Charging is automatically 8268c2ecf20Sopenharmony_ci * disabled by the hardware. 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_ci if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { 8298c2ecf20Sopenharmony_ci if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) { 8308c2ecf20Sopenharmony_ci if (smb->use_mains) 8318c2ecf20Sopenharmony_ci power_supply_changed(smb->mains); 8328c2ecf20Sopenharmony_ci if (smb->use_usb) 8338c2ecf20Sopenharmony_ci power_supply_changed(smb->usb); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci dev_dbg(smb->dev, "going to HW maintenance mode\n"); 8368c2ecf20Sopenharmony_ci handled = true; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* 8408c2ecf20Sopenharmony_ci * If we got a charger timeout INT that means the charge 8418c2ecf20Sopenharmony_ci * full is not detected with in charge timeout value. 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_ci if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ) { 8448c2ecf20Sopenharmony_ci dev_dbg(smb->dev, "total Charge Timeout INT received\n"); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT) 8478c2ecf20Sopenharmony_ci dev_warn(smb->dev, "charging stopped due to timeout\n"); 8488c2ecf20Sopenharmony_ci if (smb->use_mains) 8498c2ecf20Sopenharmony_ci power_supply_changed(smb->mains); 8508c2ecf20Sopenharmony_ci if (smb->use_usb) 8518c2ecf20Sopenharmony_ci power_supply_changed(smb->usb); 8528c2ecf20Sopenharmony_ci handled = true; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* 8568c2ecf20Sopenharmony_ci * If we got an under voltage interrupt it means that AC/USB input 8578c2ecf20Sopenharmony_ci * was connected or disconnected. 8588c2ecf20Sopenharmony_ci */ 8598c2ecf20Sopenharmony_ci if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { 8608c2ecf20Sopenharmony_ci if (smb347_update_ps_status(smb) > 0) { 8618c2ecf20Sopenharmony_ci smb347_start_stop_charging(smb); 8628c2ecf20Sopenharmony_ci if (smb->use_mains) 8638c2ecf20Sopenharmony_ci power_supply_changed(smb->mains); 8648c2ecf20Sopenharmony_ci if (smb->use_usb) 8658c2ecf20Sopenharmony_ci power_supply_changed(smb->usb); 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci handled = true; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return handled ? IRQ_HANDLED : IRQ_NONE; 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int smb347_irq_set(struct smb347_charger *smb, bool enable) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci int ret; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (smb->irq_unsupported) 8788c2ecf20Sopenharmony_ci return 0; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci ret = smb347_set_writable(smb, true); 8818c2ecf20Sopenharmony_ci if (ret < 0) 8828c2ecf20Sopenharmony_ci return ret; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* 8858c2ecf20Sopenharmony_ci * Enable/disable interrupts for: 8868c2ecf20Sopenharmony_ci * - under voltage 8878c2ecf20Sopenharmony_ci * - termination current reached 8888c2ecf20Sopenharmony_ci * - charger timeout 8898c2ecf20Sopenharmony_ci * - charger error 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff, 8928c2ecf20Sopenharmony_ci enable ? CFG_FAULT_IRQ_DCIN_UV : 0); 8938c2ecf20Sopenharmony_ci if (ret < 0) 8948c2ecf20Sopenharmony_ci goto fail; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff, 8978c2ecf20Sopenharmony_ci enable ? (CFG_STATUS_IRQ_TERMINATION_OR_TAPER | 8988c2ecf20Sopenharmony_ci CFG_STATUS_IRQ_CHARGE_TIMEOUT) : 0); 8998c2ecf20Sopenharmony_ci if (ret < 0) 9008c2ecf20Sopenharmony_ci goto fail; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR, 9038c2ecf20Sopenharmony_ci enable ? CFG_PIN_EN_CHARGER_ERROR : 0); 9048c2ecf20Sopenharmony_cifail: 9058c2ecf20Sopenharmony_ci smb347_set_writable(smb, false); 9068c2ecf20Sopenharmony_ci return ret; 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic inline int smb347_irq_enable(struct smb347_charger *smb) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci return smb347_irq_set(smb, true); 9128c2ecf20Sopenharmony_ci} 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cistatic inline int smb347_irq_disable(struct smb347_charger *smb) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci return smb347_irq_set(smb, false); 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic int smb347_irq_init(struct smb347_charger *smb, 9208c2ecf20Sopenharmony_ci struct i2c_client *client) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci int ret; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(smb->dev, client->irq, NULL, 9258c2ecf20Sopenharmony_ci smb347_interrupt, IRQF_ONESHOT, 9268c2ecf20Sopenharmony_ci client->name, smb); 9278c2ecf20Sopenharmony_ci if (ret < 0) 9288c2ecf20Sopenharmony_ci return ret; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci ret = smb347_set_writable(smb, true); 9318c2ecf20Sopenharmony_ci if (ret < 0) 9328c2ecf20Sopenharmony_ci return ret; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* 9358c2ecf20Sopenharmony_ci * Configure the STAT output to be suitable for interrupts: disable 9368c2ecf20Sopenharmony_ci * all other output (except interrupts) and make it active low. 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_ci ret = regmap_update_bits(smb->regmap, CFG_STAT, 9398c2ecf20Sopenharmony_ci CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED, 9408c2ecf20Sopenharmony_ci CFG_STAT_DISABLED); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci smb347_set_writable(smb, false); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci return ret; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci/* 9488c2ecf20Sopenharmony_ci * Returns the constant charge current programmed 9498c2ecf20Sopenharmony_ci * into the charger in uA. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_cistatic int get_const_charge_current(struct smb347_charger *smb) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci unsigned int id = smb->id; 9548c2ecf20Sopenharmony_ci int ret, intval; 9558c2ecf20Sopenharmony_ci unsigned int v; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (!smb347_is_ps_online(smb)) 9588c2ecf20Sopenharmony_ci return -ENODATA; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, STAT_B, &v); 9618c2ecf20Sopenharmony_ci if (ret < 0) 9628c2ecf20Sopenharmony_ci return ret; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* 9658c2ecf20Sopenharmony_ci * The current value is composition of FCC and PCC values 9668c2ecf20Sopenharmony_ci * and we can detect which table to use from bit 5. 9678c2ecf20Sopenharmony_ci */ 9688c2ecf20Sopenharmony_ci if (v & 0x20) { 9698c2ecf20Sopenharmony_ci intval = hw_to_current(fcc_tbl[id], 9708c2ecf20Sopenharmony_ci ARRAY_SIZE(fcc_tbl[id]), v & 7); 9718c2ecf20Sopenharmony_ci } else { 9728c2ecf20Sopenharmony_ci v >>= 3; 9738c2ecf20Sopenharmony_ci intval = hw_to_current(pcc_tbl[id], 9748c2ecf20Sopenharmony_ci ARRAY_SIZE(pcc_tbl[id]), v & 7); 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci return intval; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci/* 9818c2ecf20Sopenharmony_ci * Returns the constant charge voltage programmed 9828c2ecf20Sopenharmony_ci * into the charger in uV. 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_cistatic int get_const_charge_voltage(struct smb347_charger *smb) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci int ret, intval; 9878c2ecf20Sopenharmony_ci unsigned int v; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (!smb347_is_ps_online(smb)) 9908c2ecf20Sopenharmony_ci return -ENODATA; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, STAT_A, &v); 9938c2ecf20Sopenharmony_ci if (ret < 0) 9948c2ecf20Sopenharmony_ci return ret; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci v &= STAT_A_FLOAT_VOLTAGE_MASK; 9978c2ecf20Sopenharmony_ci if (v > 0x3d) 9988c2ecf20Sopenharmony_ci v = 0x3d; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci intval = 3500000 + v * 20000; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return intval; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic int smb347_get_charging_status(struct smb347_charger *smb, 10068c2ecf20Sopenharmony_ci struct power_supply *psy) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci int ret, status; 10098c2ecf20Sopenharmony_ci unsigned int val; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { 10128c2ecf20Sopenharmony_ci if (!smb->usb_online) 10138c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_DISCHARGING; 10148c2ecf20Sopenharmony_ci } else { 10158c2ecf20Sopenharmony_ci if (!smb->mains_online) 10168c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_DISCHARGING; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci ret = regmap_read(smb->regmap, STAT_C, &val); 10208c2ecf20Sopenharmony_ci if (ret < 0) 10218c2ecf20Sopenharmony_ci return ret; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci if ((val & STAT_C_CHARGER_ERROR) || 10248c2ecf20Sopenharmony_ci (val & STAT_C_HOLDOFF_STAT)) { 10258c2ecf20Sopenharmony_ci /* 10268c2ecf20Sopenharmony_ci * set to NOT CHARGING upon charger error 10278c2ecf20Sopenharmony_ci * or charging has stopped. 10288c2ecf20Sopenharmony_ci */ 10298c2ecf20Sopenharmony_ci status = POWER_SUPPLY_STATUS_NOT_CHARGING; 10308c2ecf20Sopenharmony_ci } else { 10318c2ecf20Sopenharmony_ci if ((val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT) { 10328c2ecf20Sopenharmony_ci /* 10338c2ecf20Sopenharmony_ci * set to charging if battery is in pre-charge, 10348c2ecf20Sopenharmony_ci * fast charge or taper charging mode. 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci status = POWER_SUPPLY_STATUS_CHARGING; 10378c2ecf20Sopenharmony_ci } else if (val & STAT_C_CHG_TERM) { 10388c2ecf20Sopenharmony_ci /* 10398c2ecf20Sopenharmony_ci * set the status to FULL if battery is not in pre 10408c2ecf20Sopenharmony_ci * charge, fast charge or taper charging mode AND 10418c2ecf20Sopenharmony_ci * charging is terminated at least once. 10428c2ecf20Sopenharmony_ci */ 10438c2ecf20Sopenharmony_ci status = POWER_SUPPLY_STATUS_FULL; 10448c2ecf20Sopenharmony_ci } else { 10458c2ecf20Sopenharmony_ci /* 10468c2ecf20Sopenharmony_ci * in this case no charger error or termination 10478c2ecf20Sopenharmony_ci * occured but charging is not in progress!!! 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci status = POWER_SUPPLY_STATUS_NOT_CHARGING; 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return status; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic int smb347_get_property_locked(struct power_supply *psy, 10578c2ecf20Sopenharmony_ci enum power_supply_property prop, 10588c2ecf20Sopenharmony_ci union power_supply_propval *val) 10598c2ecf20Sopenharmony_ci{ 10608c2ecf20Sopenharmony_ci struct smb347_charger *smb = power_supply_get_drvdata(psy); 10618c2ecf20Sopenharmony_ci int ret; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci switch (prop) { 10648c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 10658c2ecf20Sopenharmony_ci ret = smb347_get_charging_status(smb, psy); 10668c2ecf20Sopenharmony_ci if (ret < 0) 10678c2ecf20Sopenharmony_ci return ret; 10688c2ecf20Sopenharmony_ci val->intval = ret; 10698c2ecf20Sopenharmony_ci break; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 10728c2ecf20Sopenharmony_ci if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { 10738c2ecf20Sopenharmony_ci if (!smb->usb_online) 10748c2ecf20Sopenharmony_ci return -ENODATA; 10758c2ecf20Sopenharmony_ci } else { 10768c2ecf20Sopenharmony_ci if (!smb->mains_online) 10778c2ecf20Sopenharmony_ci return -ENODATA; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* 10818c2ecf20Sopenharmony_ci * We handle trickle and pre-charging the same, and taper 10828c2ecf20Sopenharmony_ci * and none the same. 10838c2ecf20Sopenharmony_ci */ 10848c2ecf20Sopenharmony_ci switch (smb347_charging_status(smb)) { 10858c2ecf20Sopenharmony_ci case 1: 10868c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci case 2: 10898c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 10908c2ecf20Sopenharmony_ci break; 10918c2ecf20Sopenharmony_ci default: 10928c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 10938c2ecf20Sopenharmony_ci break; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci break; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 10988c2ecf20Sopenharmony_ci if (psy->desc->type == POWER_SUPPLY_TYPE_USB) 10998c2ecf20Sopenharmony_ci val->intval = smb->usb_online; 11008c2ecf20Sopenharmony_ci else 11018c2ecf20Sopenharmony_ci val->intval = smb->mains_online; 11028c2ecf20Sopenharmony_ci break; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 11058c2ecf20Sopenharmony_ci ret = get_const_charge_voltage(smb); 11068c2ecf20Sopenharmony_ci if (ret < 0) 11078c2ecf20Sopenharmony_ci return ret; 11088c2ecf20Sopenharmony_ci val->intval = ret; 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 11128c2ecf20Sopenharmony_ci ret = get_const_charge_current(smb); 11138c2ecf20Sopenharmony_ci if (ret < 0) 11148c2ecf20Sopenharmony_ci return ret; 11158c2ecf20Sopenharmony_ci val->intval = ret; 11168c2ecf20Sopenharmony_ci break; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci default: 11198c2ecf20Sopenharmony_ci return -EINVAL; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci return 0; 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic int smb347_get_property(struct power_supply *psy, 11268c2ecf20Sopenharmony_ci enum power_supply_property prop, 11278c2ecf20Sopenharmony_ci union power_supply_propval *val) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci struct smb347_charger *smb = power_supply_get_drvdata(psy); 11308c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(smb->dev); 11318c2ecf20Sopenharmony_ci int ret; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci disable_irq(client->irq); 11348c2ecf20Sopenharmony_ci ret = smb347_get_property_locked(psy, prop, val); 11358c2ecf20Sopenharmony_ci enable_irq(client->irq); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci return ret; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic enum power_supply_property smb347_properties[] = { 11418c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 11428c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 11438c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 11448c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 11458c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 11468c2ecf20Sopenharmony_ci}; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_cistatic bool smb347_volatile_reg(struct device *dev, unsigned int reg) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci switch (reg) { 11518c2ecf20Sopenharmony_ci case IRQSTAT_A: 11528c2ecf20Sopenharmony_ci case IRQSTAT_C: 11538c2ecf20Sopenharmony_ci case IRQSTAT_D: 11548c2ecf20Sopenharmony_ci case IRQSTAT_E: 11558c2ecf20Sopenharmony_ci case IRQSTAT_F: 11568c2ecf20Sopenharmony_ci case STAT_A: 11578c2ecf20Sopenharmony_ci case STAT_B: 11588c2ecf20Sopenharmony_ci case STAT_C: 11598c2ecf20Sopenharmony_ci case STAT_E: 11608c2ecf20Sopenharmony_ci return true; 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return false; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic bool smb347_readable_reg(struct device *dev, unsigned int reg) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci switch (reg) { 11698c2ecf20Sopenharmony_ci case CFG_CHARGE_CURRENT: 11708c2ecf20Sopenharmony_ci case CFG_CURRENT_LIMIT: 11718c2ecf20Sopenharmony_ci case CFG_FLOAT_VOLTAGE: 11728c2ecf20Sopenharmony_ci case CFG_STAT: 11738c2ecf20Sopenharmony_ci case CFG_PIN: 11748c2ecf20Sopenharmony_ci case CFG_THERM: 11758c2ecf20Sopenharmony_ci case CFG_SYSOK: 11768c2ecf20Sopenharmony_ci case CFG_OTHER: 11778c2ecf20Sopenharmony_ci case CFG_OTG: 11788c2ecf20Sopenharmony_ci case CFG_TEMP_LIMIT: 11798c2ecf20Sopenharmony_ci case CFG_FAULT_IRQ: 11808c2ecf20Sopenharmony_ci case CFG_STATUS_IRQ: 11818c2ecf20Sopenharmony_ci case CFG_ADDRESS: 11828c2ecf20Sopenharmony_ci case CMD_A: 11838c2ecf20Sopenharmony_ci case CMD_B: 11848c2ecf20Sopenharmony_ci case CMD_C: 11858c2ecf20Sopenharmony_ci return true; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci return smb347_volatile_reg(dev, reg); 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_cistatic void smb347_dt_parse_dev_info(struct smb347_charger *smb) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci struct device *dev = smb->dev; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci smb->soft_temp_limit_compensation = 11968c2ecf20Sopenharmony_ci SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT; 11978c2ecf20Sopenharmony_ci /* 11988c2ecf20Sopenharmony_ci * These properties come from the battery info, still we need to 11998c2ecf20Sopenharmony_ci * pre-initialize the values. See smb347_get_battery_info() below. 12008c2ecf20Sopenharmony_ci */ 12018c2ecf20Sopenharmony_ci smb->soft_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 12028c2ecf20Sopenharmony_ci smb->hard_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 12038c2ecf20Sopenharmony_ci smb->soft_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 12048c2ecf20Sopenharmony_ci smb->hard_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* Charging constraints */ 12078c2ecf20Sopenharmony_ci device_property_read_u32(dev, "summit,fast-voltage-threshold-microvolt", 12088c2ecf20Sopenharmony_ci &smb->pre_to_fast_voltage); 12098c2ecf20Sopenharmony_ci device_property_read_u32(dev, "summit,mains-current-limit-microamp", 12108c2ecf20Sopenharmony_ci &smb->mains_current_limit); 12118c2ecf20Sopenharmony_ci device_property_read_u32(dev, "summit,usb-current-limit-microamp", 12128c2ecf20Sopenharmony_ci &smb->usb_hc_current_limit); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* For thermometer monitoring */ 12158c2ecf20Sopenharmony_ci device_property_read_u32(dev, "summit,chip-temperature-threshold-celsius", 12168c2ecf20Sopenharmony_ci &smb->chip_temp_threshold); 12178c2ecf20Sopenharmony_ci device_property_read_u32(dev, "summit,soft-compensation-method", 12188c2ecf20Sopenharmony_ci &smb->soft_temp_limit_compensation); 12198c2ecf20Sopenharmony_ci device_property_read_u32(dev, "summit,charge-current-compensation-microamp", 12208c2ecf20Sopenharmony_ci &smb->charge_current_compensation); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* Supported charging mode */ 12238c2ecf20Sopenharmony_ci smb->use_mains = device_property_read_bool(dev, "summit,enable-mains-charging"); 12248c2ecf20Sopenharmony_ci smb->use_usb = device_property_read_bool(dev, "summit,enable-usb-charging"); 12258c2ecf20Sopenharmony_ci smb->use_usb_otg = device_property_read_bool(dev, "summit,enable-otg-charging"); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* Select charging control */ 12288c2ecf20Sopenharmony_ci device_property_read_u32(dev, "summit,enable-charge-control", 12298c2ecf20Sopenharmony_ci &smb->enable_control); 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_cistatic int smb347_get_battery_info(struct smb347_charger *smb) 12338c2ecf20Sopenharmony_ci{ 12348c2ecf20Sopenharmony_ci struct power_supply_battery_info info = {}; 12358c2ecf20Sopenharmony_ci struct power_supply *supply; 12368c2ecf20Sopenharmony_ci int err; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (smb->mains) 12398c2ecf20Sopenharmony_ci supply = smb->mains; 12408c2ecf20Sopenharmony_ci else 12418c2ecf20Sopenharmony_ci supply = smb->usb; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci err = power_supply_get_battery_info(supply, &info); 12448c2ecf20Sopenharmony_ci if (err == -ENXIO || err == -ENODEV) 12458c2ecf20Sopenharmony_ci return 0; 12468c2ecf20Sopenharmony_ci if (err) 12478c2ecf20Sopenharmony_ci return err; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (info.constant_charge_current_max_ua != -EINVAL) 12508c2ecf20Sopenharmony_ci smb->max_charge_current = info.constant_charge_current_max_ua; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (info.constant_charge_voltage_max_uv != -EINVAL) 12538c2ecf20Sopenharmony_ci smb->max_charge_voltage = info.constant_charge_voltage_max_uv; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci if (info.precharge_current_ua != -EINVAL) 12568c2ecf20Sopenharmony_ci smb->pre_charge_current = info.precharge_current_ua; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (info.charge_term_current_ua != -EINVAL) 12598c2ecf20Sopenharmony_ci smb->termination_current = info.charge_term_current_ua; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (info.temp_alert_min != INT_MIN) 12628c2ecf20Sopenharmony_ci smb->soft_cold_temp_limit = info.temp_alert_min; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (info.temp_alert_max != INT_MAX) 12658c2ecf20Sopenharmony_ci smb->soft_hot_temp_limit = info.temp_alert_max; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci if (info.temp_min != INT_MIN) 12688c2ecf20Sopenharmony_ci smb->hard_cold_temp_limit = info.temp_min; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (info.temp_max != INT_MAX) 12718c2ecf20Sopenharmony_ci smb->hard_hot_temp_limit = info.temp_max; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci /* Suspend when battery temperature is outside hard limits */ 12748c2ecf20Sopenharmony_ci if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT || 12758c2ecf20Sopenharmony_ci smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) 12768c2ecf20Sopenharmony_ci smb->suspend_on_hard_temp_limit = true; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci return 0; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic const struct regmap_config smb347_regmap = { 12828c2ecf20Sopenharmony_ci .reg_bits = 8, 12838c2ecf20Sopenharmony_ci .val_bits = 8, 12848c2ecf20Sopenharmony_ci .max_register = SMB347_MAX_REGISTER, 12858c2ecf20Sopenharmony_ci .volatile_reg = smb347_volatile_reg, 12868c2ecf20Sopenharmony_ci .readable_reg = smb347_readable_reg, 12878c2ecf20Sopenharmony_ci}; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cistatic const struct power_supply_desc smb347_mains_desc = { 12908c2ecf20Sopenharmony_ci .name = "smb347-mains", 12918c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 12928c2ecf20Sopenharmony_ci .get_property = smb347_get_property, 12938c2ecf20Sopenharmony_ci .properties = smb347_properties, 12948c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(smb347_properties), 12958c2ecf20Sopenharmony_ci}; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic const struct power_supply_desc smb347_usb_desc = { 12988c2ecf20Sopenharmony_ci .name = "smb347-usb", 12998c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 13008c2ecf20Sopenharmony_ci .get_property = smb347_get_property, 13018c2ecf20Sopenharmony_ci .properties = smb347_properties, 13028c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(smb347_properties), 13038c2ecf20Sopenharmony_ci}; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_cistatic int smb347_probe(struct i2c_client *client, 13068c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci struct power_supply_config mains_usb_cfg = {}; 13098c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 13108c2ecf20Sopenharmony_ci struct smb347_charger *smb; 13118c2ecf20Sopenharmony_ci int ret; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL); 13148c2ecf20Sopenharmony_ci if (!smb) 13158c2ecf20Sopenharmony_ci return -ENOMEM; 13168c2ecf20Sopenharmony_ci smb->dev = &client->dev; 13178c2ecf20Sopenharmony_ci smb->id = id->driver_data; 13188c2ecf20Sopenharmony_ci i2c_set_clientdata(client, smb); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci smb347_dt_parse_dev_info(smb); 13218c2ecf20Sopenharmony_ci if (!smb->use_mains && !smb->use_usb) 13228c2ecf20Sopenharmony_ci return -EINVAL; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap); 13258c2ecf20Sopenharmony_ci if (IS_ERR(smb->regmap)) 13268c2ecf20Sopenharmony_ci return PTR_ERR(smb->regmap); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci mains_usb_cfg.drv_data = smb; 13298c2ecf20Sopenharmony_ci mains_usb_cfg.of_node = dev->of_node; 13308c2ecf20Sopenharmony_ci if (smb->use_mains) { 13318c2ecf20Sopenharmony_ci smb->mains = devm_power_supply_register(dev, &smb347_mains_desc, 13328c2ecf20Sopenharmony_ci &mains_usb_cfg); 13338c2ecf20Sopenharmony_ci if (IS_ERR(smb->mains)) 13348c2ecf20Sopenharmony_ci return PTR_ERR(smb->mains); 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (smb->use_usb) { 13388c2ecf20Sopenharmony_ci smb->usb = devm_power_supply_register(dev, &smb347_usb_desc, 13398c2ecf20Sopenharmony_ci &mains_usb_cfg); 13408c2ecf20Sopenharmony_ci if (IS_ERR(smb->usb)) 13418c2ecf20Sopenharmony_ci return PTR_ERR(smb->usb); 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci ret = smb347_get_battery_info(smb); 13458c2ecf20Sopenharmony_ci if (ret) 13468c2ecf20Sopenharmony_ci return ret; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci ret = smb347_hw_init(smb); 13498c2ecf20Sopenharmony_ci if (ret < 0) 13508c2ecf20Sopenharmony_ci return ret; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* 13538c2ecf20Sopenharmony_ci * Interrupt pin is optional. If it is connected, we setup the 13548c2ecf20Sopenharmony_ci * interrupt support here. 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_ci if (client->irq) { 13578c2ecf20Sopenharmony_ci ret = smb347_irq_init(smb, client); 13588c2ecf20Sopenharmony_ci if (ret < 0) { 13598c2ecf20Sopenharmony_ci dev_warn(dev, "failed to initialize IRQ: %d\n", ret); 13608c2ecf20Sopenharmony_ci dev_warn(dev, "disabling IRQ support\n"); 13618c2ecf20Sopenharmony_ci smb->irq_unsupported = true; 13628c2ecf20Sopenharmony_ci } else { 13638c2ecf20Sopenharmony_ci smb347_irq_enable(smb); 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci return 0; 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic int smb347_remove(struct i2c_client *client) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci struct smb347_charger *smb = i2c_get_clientdata(client); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci smb347_irq_disable(smb); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci return 0; 13778c2ecf20Sopenharmony_ci} 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_cistatic const struct i2c_device_id smb347_id[] = { 13808c2ecf20Sopenharmony_ci { "smb345", SMB345 }, 13818c2ecf20Sopenharmony_ci { "smb347", SMB347 }, 13828c2ecf20Sopenharmony_ci { "smb358", SMB358 }, 13838c2ecf20Sopenharmony_ci { }, 13848c2ecf20Sopenharmony_ci}; 13858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, smb347_id); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic const struct of_device_id smb3xx_of_match[] = { 13888c2ecf20Sopenharmony_ci { .compatible = "summit,smb345" }, 13898c2ecf20Sopenharmony_ci { .compatible = "summit,smb347" }, 13908c2ecf20Sopenharmony_ci { .compatible = "summit,smb358" }, 13918c2ecf20Sopenharmony_ci { }, 13928c2ecf20Sopenharmony_ci}; 13938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, smb3xx_of_match); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_cistatic struct i2c_driver smb347_driver = { 13968c2ecf20Sopenharmony_ci .driver = { 13978c2ecf20Sopenharmony_ci .name = "smb347", 13988c2ecf20Sopenharmony_ci .of_match_table = smb3xx_of_match, 13998c2ecf20Sopenharmony_ci }, 14008c2ecf20Sopenharmony_ci .probe = smb347_probe, 14018c2ecf20Sopenharmony_ci .remove = smb347_remove, 14028c2ecf20Sopenharmony_ci .id_table = smb347_id, 14038c2ecf20Sopenharmony_ci}; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cimodule_i2c_driver(smb347_driver); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>"); 14088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); 14098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMB347 battery charger driver"); 14108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1411