1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 28c0984e5SSebastian Reichel /* 38c0984e5SSebastian Reichel * Summit Microelectronics SMB347 Battery Charger Driver 48c0984e5SSebastian Reichel * 58c0984e5SSebastian Reichel * Copyright (C) 2011, Intel Corporation 68c0984e5SSebastian Reichel * 78c0984e5SSebastian Reichel * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com> 88c0984e5SSebastian Reichel * Mika Westerberg <mika.westerberg@linux.intel.com> 98c0984e5SSebastian Reichel */ 108c0984e5SSebastian Reichel 11fa7cc725SDavid Heidelberg #include <linux/delay.h> 128c0984e5SSebastian Reichel #include <linux/err.h> 138c0984e5SSebastian Reichel #include <linux/kernel.h> 148c0984e5SSebastian Reichel #include <linux/module.h> 158c0984e5SSebastian Reichel #include <linux/init.h> 168c0984e5SSebastian Reichel #include <linux/interrupt.h> 178c0984e5SSebastian Reichel #include <linux/i2c.h> 188c0984e5SSebastian Reichel #include <linux/power_supply.h> 19f385e2fcSSebastian Reichel #include <linux/property.h> 208c0984e5SSebastian Reichel #include <linux/regmap.h> 21*565efae9SDmitry Osipenko #include <linux/regulator/driver.h> 228c0984e5SSebastian Reichel 23b6f3e21bSSebastian Reichel #include <dt-bindings/power/summit,smb347-charger.h> 24b6f3e21bSSebastian Reichel 25b6f3e21bSSebastian Reichel /* Use the default compensation method */ 26b6f3e21bSSebastian Reichel #define SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT -1 27b6f3e21bSSebastian Reichel 28b6f3e21bSSebastian Reichel /* Use default factory programmed value for hard/soft temperature limit */ 29b6f3e21bSSebastian Reichel #define SMB3XX_TEMP_USE_DEFAULT -273 30b6f3e21bSSebastian Reichel 318c0984e5SSebastian Reichel /* 328c0984e5SSebastian Reichel * Configuration registers. These are mirrored to volatile RAM and can be 338c0984e5SSebastian Reichel * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be 348c0984e5SSebastian Reichel * reloaded from non-volatile registers after POR. 358c0984e5SSebastian Reichel */ 368c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT 0x00 378c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_FCC_MASK 0xe0 388c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_FCC_SHIFT 5 398c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_PCC_MASK 0x18 408c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_PCC_SHIFT 3 418c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_TC_MASK 0x07 428c0984e5SSebastian Reichel #define CFG_CURRENT_LIMIT 0x01 438c0984e5SSebastian Reichel #define CFG_CURRENT_LIMIT_DC_MASK 0xf0 448c0984e5SSebastian Reichel #define CFG_CURRENT_LIMIT_DC_SHIFT 4 458c0984e5SSebastian Reichel #define CFG_CURRENT_LIMIT_USB_MASK 0x0f 468c0984e5SSebastian Reichel #define CFG_FLOAT_VOLTAGE 0x03 478c0984e5SSebastian Reichel #define CFG_FLOAT_VOLTAGE_FLOAT_MASK 0x3f 488c0984e5SSebastian Reichel #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0 498c0984e5SSebastian Reichel #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6 508c0984e5SSebastian Reichel #define CFG_STAT 0x05 518c0984e5SSebastian Reichel #define CFG_STAT_DISABLED BIT(5) 528c0984e5SSebastian Reichel #define CFG_STAT_ACTIVE_HIGH BIT(7) 538c0984e5SSebastian Reichel #define CFG_PIN 0x06 548c0984e5SSebastian Reichel #define CFG_PIN_EN_CTRL_MASK 0x60 558c0984e5SSebastian Reichel #define CFG_PIN_EN_CTRL_ACTIVE_HIGH 0x40 568c0984e5SSebastian Reichel #define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60 578c0984e5SSebastian Reichel #define CFG_PIN_EN_APSD_IRQ BIT(1) 588c0984e5SSebastian Reichel #define CFG_PIN_EN_CHARGER_ERROR BIT(2) 59efe21754SDmitry Osipenko #define CFG_PIN_EN_CTRL BIT(4) 608c0984e5SSebastian Reichel #define CFG_THERM 0x07 618c0984e5SSebastian Reichel #define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03 628c0984e5SSebastian Reichel #define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0 638c0984e5SSebastian Reichel #define CFG_THERM_SOFT_COLD_COMPENSATION_MASK 0x0c 648c0984e5SSebastian Reichel #define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2 658c0984e5SSebastian Reichel #define CFG_THERM_MONITOR_DISABLED BIT(4) 668c0984e5SSebastian Reichel #define CFG_SYSOK 0x08 67*565efae9SDmitry Osipenko #define CFG_SYSOK_INOK_ACTIVE_HIGH BIT(0) 688c0984e5SSebastian Reichel #define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2) 698c0984e5SSebastian Reichel #define CFG_OTHER 0x09 708c0984e5SSebastian Reichel #define CFG_OTHER_RID_MASK 0xc0 718c0984e5SSebastian Reichel #define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0 728c0984e5SSebastian Reichel #define CFG_OTG 0x0a 738c0984e5SSebastian Reichel #define CFG_OTG_TEMP_THRESHOLD_MASK 0x30 74*565efae9SDmitry Osipenko #define CFG_OTG_CURRENT_LIMIT_250mA BIT(2) 75*565efae9SDmitry Osipenko #define CFG_OTG_CURRENT_LIMIT_750mA BIT(3) 768c0984e5SSebastian Reichel #define CFG_OTG_TEMP_THRESHOLD_SHIFT 4 778c0984e5SSebastian Reichel #define CFG_OTG_CC_COMPENSATION_MASK 0xc0 788c0984e5SSebastian Reichel #define CFG_OTG_CC_COMPENSATION_SHIFT 6 798c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT 0x0b 808c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_HOT_MASK 0x03 818c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT 0 828c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_COLD_MASK 0x0c 838c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT 2 848c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_HOT_MASK 0x30 858c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_HOT_SHIFT 4 868c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_COLD_MASK 0xc0 878c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_COLD_SHIFT 6 888c0984e5SSebastian Reichel #define CFG_FAULT_IRQ 0x0c 898c0984e5SSebastian Reichel #define CFG_FAULT_IRQ_DCIN_UV BIT(2) 908c0984e5SSebastian Reichel #define CFG_STATUS_IRQ 0x0d 918c0984e5SSebastian Reichel #define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4) 928c0984e5SSebastian Reichel #define CFG_STATUS_IRQ_CHARGE_TIMEOUT BIT(7) 938c0984e5SSebastian Reichel #define CFG_ADDRESS 0x0e 948c0984e5SSebastian Reichel 958c0984e5SSebastian Reichel /* Command registers */ 968c0984e5SSebastian Reichel #define CMD_A 0x30 978c0984e5SSebastian Reichel #define CMD_A_CHG_ENABLED BIT(1) 988c0984e5SSebastian Reichel #define CMD_A_SUSPEND_ENABLED BIT(2) 99*565efae9SDmitry Osipenko #define CMD_A_OTG_ENABLED BIT(4) 1008c0984e5SSebastian Reichel #define CMD_A_ALLOW_WRITE BIT(7) 1018c0984e5SSebastian Reichel #define CMD_B 0x31 1028c0984e5SSebastian Reichel #define CMD_C 0x33 1038c0984e5SSebastian Reichel 1048c0984e5SSebastian Reichel /* Interrupt Status registers */ 1058c0984e5SSebastian Reichel #define IRQSTAT_A 0x35 1068c0984e5SSebastian Reichel #define IRQSTAT_C 0x37 1078c0984e5SSebastian Reichel #define IRQSTAT_C_TERMINATION_STAT BIT(0) 1088c0984e5SSebastian Reichel #define IRQSTAT_C_TERMINATION_IRQ BIT(1) 1098c0984e5SSebastian Reichel #define IRQSTAT_C_TAPER_IRQ BIT(3) 1108c0984e5SSebastian Reichel #define IRQSTAT_D 0x38 1118c0984e5SSebastian Reichel #define IRQSTAT_D_CHARGE_TIMEOUT_STAT BIT(2) 1128c0984e5SSebastian Reichel #define IRQSTAT_D_CHARGE_TIMEOUT_IRQ BIT(3) 1138c0984e5SSebastian Reichel #define IRQSTAT_E 0x39 1148c0984e5SSebastian Reichel #define IRQSTAT_E_USBIN_UV_STAT BIT(0) 1158c0984e5SSebastian Reichel #define IRQSTAT_E_USBIN_UV_IRQ BIT(1) 1168c0984e5SSebastian Reichel #define IRQSTAT_E_DCIN_UV_STAT BIT(4) 1178c0984e5SSebastian Reichel #define IRQSTAT_E_DCIN_UV_IRQ BIT(5) 1188c0984e5SSebastian Reichel #define IRQSTAT_F 0x3a 1198c0984e5SSebastian Reichel 1208c0984e5SSebastian Reichel /* Status registers */ 1218c0984e5SSebastian Reichel #define STAT_A 0x3b 1228c0984e5SSebastian Reichel #define STAT_A_FLOAT_VOLTAGE_MASK 0x3f 1238c0984e5SSebastian Reichel #define STAT_B 0x3c 1248c0984e5SSebastian Reichel #define STAT_C 0x3d 1258c0984e5SSebastian Reichel #define STAT_C_CHG_ENABLED BIT(0) 1268c0984e5SSebastian Reichel #define STAT_C_HOLDOFF_STAT BIT(3) 1278c0984e5SSebastian Reichel #define STAT_C_CHG_MASK 0x06 1288c0984e5SSebastian Reichel #define STAT_C_CHG_SHIFT 1 1298c0984e5SSebastian Reichel #define STAT_C_CHG_TERM BIT(5) 1308c0984e5SSebastian Reichel #define STAT_C_CHARGER_ERROR BIT(6) 1318c0984e5SSebastian Reichel #define STAT_E 0x3f 1328c0984e5SSebastian Reichel 1338c0984e5SSebastian Reichel #define SMB347_MAX_REGISTER 0x3f 1348c0984e5SSebastian Reichel 1358c0984e5SSebastian Reichel /** 1368c0984e5SSebastian Reichel * struct smb347_charger - smb347 charger instance 1378c0984e5SSebastian Reichel * @dev: pointer to device 1388c0984e5SSebastian Reichel * @regmap: pointer to driver regmap 1398c0984e5SSebastian Reichel * @mains: power_supply instance for AC/DC power 1408c0984e5SSebastian Reichel * @usb: power_supply instance for USB power 141*565efae9SDmitry Osipenko * @usb_rdev: USB VBUS regulator device 142de76fd29SDavid Heidelberg * @id: SMB charger ID 1438c0984e5SSebastian Reichel * @mains_online: is AC/DC input connected 1448c0984e5SSebastian Reichel * @usb_online: is USB input connected 14569963126SDmitry Osipenko * @irq_unsupported: is interrupt unsupported by SMB hardware 146*565efae9SDmitry Osipenko * @usb_vbus_enabled: is USB VBUS powered by SMB charger 147b6f3e21bSSebastian Reichel * @max_charge_current: maximum current (in uA) the battery can be charged 148b6f3e21bSSebastian Reichel * @max_charge_voltage: maximum voltage (in uV) the battery can be charged 149b6f3e21bSSebastian Reichel * @pre_charge_current: current (in uA) to use in pre-charging phase 150b6f3e21bSSebastian Reichel * @termination_current: current (in uA) used to determine when the 151b6f3e21bSSebastian Reichel * charging cycle terminates 152b6f3e21bSSebastian Reichel * @pre_to_fast_voltage: voltage (in uV) treshold used for transitioning to 153b6f3e21bSSebastian Reichel * pre-charge to fast charge mode 154b6f3e21bSSebastian Reichel * @mains_current_limit: maximum input current drawn from AC/DC input (in uA) 155b6f3e21bSSebastian Reichel * @usb_hc_current_limit: maximum input high current (in uA) drawn from USB 156b6f3e21bSSebastian Reichel * input 157b6f3e21bSSebastian Reichel * @chip_temp_threshold: die temperature where device starts limiting charge 158b6f3e21bSSebastian Reichel * current [%100 - %130] (in degree C) 159b6f3e21bSSebastian Reichel * @soft_cold_temp_limit: soft cold temperature limit [%0 - %15] (in degree C), 160b6f3e21bSSebastian Reichel * granularity is 5 deg C. 161b6f3e21bSSebastian Reichel * @soft_hot_temp_limit: soft hot temperature limit [%40 - %55] (in degree C), 162b6f3e21bSSebastian Reichel * granularity is 5 deg C. 163b6f3e21bSSebastian Reichel * @hard_cold_temp_limit: hard cold temperature limit [%-5 - %10] (in degree C), 164b6f3e21bSSebastian Reichel * granularity is 5 deg C. 165b6f3e21bSSebastian Reichel * @hard_hot_temp_limit: hard hot temperature limit [%50 - %65] (in degree C), 166b6f3e21bSSebastian Reichel * granularity is 5 deg C. 167b6f3e21bSSebastian Reichel * @suspend_on_hard_temp_limit: suspend charging when hard limit is hit 168b6f3e21bSSebastian Reichel * @soft_temp_limit_compensation: compensation method when soft temperature 169b6f3e21bSSebastian Reichel * limit is hit 170b6f3e21bSSebastian Reichel * @charge_current_compensation: current (in uA) for charging compensation 171b6f3e21bSSebastian Reichel * current when temperature hits soft limits 172b6f3e21bSSebastian Reichel * @use_mains: AC/DC input can be used 173b6f3e21bSSebastian Reichel * @use_usb: USB input can be used 174b6f3e21bSSebastian Reichel * @use_usb_otg: USB OTG output can be used (not implemented yet) 175b6f3e21bSSebastian Reichel * @enable_control: how charging enable/disable is controlled 176b6f3e21bSSebastian Reichel * (driver/pin controls) 177*565efae9SDmitry Osipenko * @inok_polarity: polarity of INOK signal which denotes presence of external 178*565efae9SDmitry Osipenko * power supply 179b6f3e21bSSebastian Reichel * 180b6f3e21bSSebastian Reichel * @use_main, @use_usb, and @use_usb_otg are means to enable/disable 181b6f3e21bSSebastian Reichel * hardware support for these. This is useful when we want to have for 182b6f3e21bSSebastian Reichel * example OTG charging controlled via OTG transceiver driver and not by 183b6f3e21bSSebastian Reichel * the SMB347 hardware. 184b6f3e21bSSebastian Reichel * 185b6f3e21bSSebastian Reichel * Hard and soft temperature limit values are given as described in the 186b6f3e21bSSebastian Reichel * device data sheet and assuming NTC beta value is %3750. Even if this is 187b6f3e21bSSebastian Reichel * not the case, these values should be used. They can be mapped to the 188b6f3e21bSSebastian Reichel * corresponding NTC beta values with the help of table %2 in the data 189b6f3e21bSSebastian Reichel * sheet. So for example if NTC beta is %3375 and we want to program hard 190b6f3e21bSSebastian Reichel * hot limit to be %53 deg C, @hard_hot_temp_limit should be set to %50. 191b6f3e21bSSebastian Reichel * 192b6f3e21bSSebastian Reichel * If zero value is given in any of the current and voltage values, the 193b6f3e21bSSebastian Reichel * factory programmed default will be used. For soft/hard temperature 194b6f3e21bSSebastian Reichel * values, pass in %SMB3XX_TEMP_USE_DEFAULT instead. 1958c0984e5SSebastian Reichel */ 1968c0984e5SSebastian Reichel struct smb347_charger { 1978c0984e5SSebastian Reichel struct device *dev; 1988c0984e5SSebastian Reichel struct regmap *regmap; 1998c0984e5SSebastian Reichel struct power_supply *mains; 2008c0984e5SSebastian Reichel struct power_supply *usb; 201*565efae9SDmitry Osipenko struct regulator_dev *usb_rdev; 202de76fd29SDavid Heidelberg unsigned int id; 2038c0984e5SSebastian Reichel bool mains_online; 2048c0984e5SSebastian Reichel bool usb_online; 20569963126SDmitry Osipenko bool irq_unsupported; 206*565efae9SDmitry Osipenko bool usb_vbus_enabled; 207b6f3e21bSSebastian Reichel 208b6f3e21bSSebastian Reichel unsigned int max_charge_current; 209b6f3e21bSSebastian Reichel unsigned int max_charge_voltage; 210b6f3e21bSSebastian Reichel unsigned int pre_charge_current; 211b6f3e21bSSebastian Reichel unsigned int termination_current; 212b6f3e21bSSebastian Reichel unsigned int pre_to_fast_voltage; 213b6f3e21bSSebastian Reichel unsigned int mains_current_limit; 214b6f3e21bSSebastian Reichel unsigned int usb_hc_current_limit; 215b6f3e21bSSebastian Reichel unsigned int chip_temp_threshold; 216b6f3e21bSSebastian Reichel int soft_cold_temp_limit; 217b6f3e21bSSebastian Reichel int soft_hot_temp_limit; 218b6f3e21bSSebastian Reichel int hard_cold_temp_limit; 219b6f3e21bSSebastian Reichel int hard_hot_temp_limit; 220b6f3e21bSSebastian Reichel bool suspend_on_hard_temp_limit; 221b6f3e21bSSebastian Reichel unsigned int soft_temp_limit_compensation; 222b6f3e21bSSebastian Reichel unsigned int charge_current_compensation; 223b6f3e21bSSebastian Reichel bool use_mains; 224b6f3e21bSSebastian Reichel bool use_usb; 225b6f3e21bSSebastian Reichel bool use_usb_otg; 226b6f3e21bSSebastian Reichel unsigned int enable_control; 227*565efae9SDmitry Osipenko unsigned int inok_polarity; 2288c0984e5SSebastian Reichel }; 2298c0984e5SSebastian Reichel 230de76fd29SDavid Heidelberg enum smb_charger_chipid { 231de76fd29SDavid Heidelberg SMB345, 232de76fd29SDavid Heidelberg SMB347, 233de76fd29SDavid Heidelberg SMB358, 234de76fd29SDavid Heidelberg NUM_CHIP_TYPES, 2358c0984e5SSebastian Reichel }; 2368c0984e5SSebastian Reichel 237de76fd29SDavid Heidelberg /* Fast charge current in uA */ 238de76fd29SDavid Heidelberg static const unsigned int fcc_tbl[NUM_CHIP_TYPES][8] = { 239de76fd29SDavid Heidelberg [SMB345] = { 200000, 450000, 600000, 900000, 240de76fd29SDavid Heidelberg 1300000, 1500000, 1800000, 2000000 }, 241de76fd29SDavid Heidelberg [SMB347] = { 700000, 900000, 1200000, 1500000, 242de76fd29SDavid Heidelberg 1800000, 2000000, 2200000, 2500000 }, 243de76fd29SDavid Heidelberg [SMB358] = { 200000, 450000, 600000, 900000, 244de76fd29SDavid Heidelberg 1300000, 1500000, 1800000, 2000000 }, 245de76fd29SDavid Heidelberg }; 2468c0984e5SSebastian Reichel /* Pre-charge current in uA */ 247de76fd29SDavid Heidelberg static const unsigned int pcc_tbl[NUM_CHIP_TYPES][4] = { 248de76fd29SDavid Heidelberg [SMB345] = { 150000, 250000, 350000, 450000 }, 249de76fd29SDavid Heidelberg [SMB347] = { 100000, 150000, 200000, 250000 }, 250de76fd29SDavid Heidelberg [SMB358] = { 150000, 250000, 350000, 450000 }, 2518c0984e5SSebastian Reichel }; 2528c0984e5SSebastian Reichel 2538c0984e5SSebastian Reichel /* Termination current in uA */ 254de76fd29SDavid Heidelberg static const unsigned int tc_tbl[NUM_CHIP_TYPES][8] = { 255de76fd29SDavid Heidelberg [SMB345] = { 30000, 40000, 60000, 80000, 256de76fd29SDavid Heidelberg 100000, 125000, 150000, 200000 }, 257de76fd29SDavid Heidelberg [SMB347] = { 37500, 50000, 100000, 150000, 258de76fd29SDavid Heidelberg 200000, 250000, 500000, 600000 }, 259de76fd29SDavid Heidelberg [SMB358] = { 30000, 40000, 60000, 80000, 260de76fd29SDavid Heidelberg 100000, 125000, 150000, 200000 }, 2618c0984e5SSebastian Reichel }; 2628c0984e5SSebastian Reichel 2638c0984e5SSebastian Reichel /* Input current limit in uA */ 264de76fd29SDavid Heidelberg static const unsigned int icl_tbl[NUM_CHIP_TYPES][10] = { 265de76fd29SDavid Heidelberg [SMB345] = { 300000, 500000, 700000, 1000000, 1500000, 266de76fd29SDavid Heidelberg 1800000, 2000000, 2000000, 2000000, 2000000 }, 267de76fd29SDavid Heidelberg [SMB347] = { 300000, 500000, 700000, 900000, 1200000, 268de76fd29SDavid Heidelberg 1500000, 1800000, 2000000, 2200000, 2500000 }, 269de76fd29SDavid Heidelberg [SMB358] = { 300000, 500000, 700000, 1000000, 1500000, 270de76fd29SDavid Heidelberg 1800000, 2000000, 2000000, 2000000, 2000000 }, 2718c0984e5SSebastian Reichel }; 2728c0984e5SSebastian Reichel 2738c0984e5SSebastian Reichel /* Charge current compensation in uA */ 274de76fd29SDavid Heidelberg static const unsigned int ccc_tbl[NUM_CHIP_TYPES][4] = { 275de76fd29SDavid Heidelberg [SMB345] = { 200000, 450000, 600000, 900000 }, 276de76fd29SDavid Heidelberg [SMB347] = { 250000, 700000, 900000, 1200000 }, 277de76fd29SDavid Heidelberg [SMB358] = { 200000, 450000, 600000, 900000 }, 2788c0984e5SSebastian Reichel }; 2798c0984e5SSebastian Reichel 2808c0984e5SSebastian Reichel /* Convert register value to current using lookup table */ 2818c0984e5SSebastian Reichel static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val) 2828c0984e5SSebastian Reichel { 2838c0984e5SSebastian Reichel if (val >= size) 2848c0984e5SSebastian Reichel return -EINVAL; 2858c0984e5SSebastian Reichel return tbl[val]; 2868c0984e5SSebastian Reichel } 2878c0984e5SSebastian Reichel 2888c0984e5SSebastian Reichel /* Convert current to register value using lookup table */ 2898c0984e5SSebastian Reichel static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) 2908c0984e5SSebastian Reichel { 2918c0984e5SSebastian Reichel size_t i; 2928c0984e5SSebastian Reichel 2938c0984e5SSebastian Reichel for (i = 0; i < size; i++) 2948c0984e5SSebastian Reichel if (val < tbl[i]) 2958c0984e5SSebastian Reichel break; 2968c0984e5SSebastian Reichel return i > 0 ? i - 1 : -EINVAL; 2978c0984e5SSebastian Reichel } 2988c0984e5SSebastian Reichel 2998c0984e5SSebastian Reichel /** 3008c0984e5SSebastian Reichel * smb347_update_ps_status - refreshes the power source status 3018c0984e5SSebastian Reichel * @smb: pointer to smb347 charger instance 3028c0984e5SSebastian Reichel * 3038c0984e5SSebastian Reichel * Function checks whether any power source is connected to the charger and 3048c0984e5SSebastian Reichel * updates internal state accordingly. If there is a change to previous state 3058c0984e5SSebastian Reichel * function returns %1, otherwise %0 and negative errno in case of errror. 3068c0984e5SSebastian Reichel */ 3078c0984e5SSebastian Reichel static int smb347_update_ps_status(struct smb347_charger *smb) 3088c0984e5SSebastian Reichel { 3098c0984e5SSebastian Reichel bool usb = false; 3108c0984e5SSebastian Reichel bool dc = false; 3118c0984e5SSebastian Reichel unsigned int val; 3128c0984e5SSebastian Reichel int ret; 3138c0984e5SSebastian Reichel 3148c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, IRQSTAT_E, &val); 3158c0984e5SSebastian Reichel if (ret < 0) 3168c0984e5SSebastian Reichel return ret; 3178c0984e5SSebastian Reichel 3188c0984e5SSebastian Reichel /* 3198c0984e5SSebastian Reichel * Dc and usb are set depending on whether they are enabled in 3208c0984e5SSebastian Reichel * platform data _and_ whether corresponding undervoltage is set. 3218c0984e5SSebastian Reichel */ 322b6f3e21bSSebastian Reichel if (smb->use_mains) 3238c0984e5SSebastian Reichel dc = !(val & IRQSTAT_E_DCIN_UV_STAT); 324b6f3e21bSSebastian Reichel if (smb->use_usb) 3258c0984e5SSebastian Reichel usb = !(val & IRQSTAT_E_USBIN_UV_STAT); 3268c0984e5SSebastian Reichel 3278c0984e5SSebastian Reichel ret = smb->mains_online != dc || smb->usb_online != usb; 3288c0984e5SSebastian Reichel smb->mains_online = dc; 3298c0984e5SSebastian Reichel smb->usb_online = usb; 3308c0984e5SSebastian Reichel 3318c0984e5SSebastian Reichel return ret; 3328c0984e5SSebastian Reichel } 3338c0984e5SSebastian Reichel 3348c0984e5SSebastian Reichel /* 3358c0984e5SSebastian Reichel * smb347_is_ps_online - returns whether input power source is connected 3368c0984e5SSebastian Reichel * @smb: pointer to smb347 charger instance 3378c0984e5SSebastian Reichel * 3388c0984e5SSebastian Reichel * Returns %true if input power source is connected. Note that this is 3398c0984e5SSebastian Reichel * dependent on what platform has configured for usable power sources. For 3408c0984e5SSebastian Reichel * example if USB is disabled, this will return %false even if the USB cable 3418c0984e5SSebastian Reichel * is connected. 3428c0984e5SSebastian Reichel */ 3438c0984e5SSebastian Reichel static bool smb347_is_ps_online(struct smb347_charger *smb) 3448c0984e5SSebastian Reichel { 34599298de5SDmitry Osipenko return smb->usb_online || smb->mains_online; 3468c0984e5SSebastian Reichel } 3478c0984e5SSebastian Reichel 3488c0984e5SSebastian Reichel /** 3498c0984e5SSebastian Reichel * smb347_charging_status - returns status of charging 3508c0984e5SSebastian Reichel * @smb: pointer to smb347 charger instance 3518c0984e5SSebastian Reichel * 3528c0984e5SSebastian Reichel * Function returns charging status. %0 means no charging is in progress, 3538c0984e5SSebastian Reichel * %1 means pre-charging, %2 fast-charging and %3 taper-charging. 3548c0984e5SSebastian Reichel */ 3558c0984e5SSebastian Reichel static int smb347_charging_status(struct smb347_charger *smb) 3568c0984e5SSebastian Reichel { 3578c0984e5SSebastian Reichel unsigned int val; 3588c0984e5SSebastian Reichel int ret; 3598c0984e5SSebastian Reichel 3608c0984e5SSebastian Reichel if (!smb347_is_ps_online(smb)) 3618c0984e5SSebastian Reichel return 0; 3628c0984e5SSebastian Reichel 3638c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_C, &val); 3648c0984e5SSebastian Reichel if (ret < 0) 3658c0984e5SSebastian Reichel return 0; 3668c0984e5SSebastian Reichel 3678c0984e5SSebastian Reichel return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT; 3688c0984e5SSebastian Reichel } 3698c0984e5SSebastian Reichel 3708c0984e5SSebastian Reichel static int smb347_charging_set(struct smb347_charger *smb, bool enable) 3718c0984e5SSebastian Reichel { 372b6f3e21bSSebastian Reichel if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) { 3738c0984e5SSebastian Reichel dev_dbg(smb->dev, "charging enable/disable in SW disabled\n"); 3748c0984e5SSebastian Reichel return 0; 3758c0984e5SSebastian Reichel } 3768c0984e5SSebastian Reichel 377*565efae9SDmitry Osipenko if (enable && smb->usb_vbus_enabled) { 378*565efae9SDmitry Osipenko dev_dbg(smb->dev, "charging not enabled because USB is in host mode\n"); 379*565efae9SDmitry Osipenko return 0; 380*565efae9SDmitry Osipenko } 381*565efae9SDmitry Osipenko 38217e7bc53SDmitry Osipenko return regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, 3838c0984e5SSebastian Reichel enable ? CMD_A_CHG_ENABLED : 0); 3848c0984e5SSebastian Reichel } 3858c0984e5SSebastian Reichel 3868c0984e5SSebastian Reichel static inline int smb347_charging_enable(struct smb347_charger *smb) 3878c0984e5SSebastian Reichel { 3888c0984e5SSebastian Reichel return smb347_charging_set(smb, true); 3898c0984e5SSebastian Reichel } 3908c0984e5SSebastian Reichel 3918c0984e5SSebastian Reichel static inline int smb347_charging_disable(struct smb347_charger *smb) 3928c0984e5SSebastian Reichel { 3938c0984e5SSebastian Reichel return smb347_charging_set(smb, false); 3948c0984e5SSebastian Reichel } 3958c0984e5SSebastian Reichel 3968c0984e5SSebastian Reichel static int smb347_start_stop_charging(struct smb347_charger *smb) 3978c0984e5SSebastian Reichel { 3988c0984e5SSebastian Reichel int ret; 3998c0984e5SSebastian Reichel 4008c0984e5SSebastian Reichel /* 4018c0984e5SSebastian Reichel * Depending on whether valid power source is connected or not, we 4028c0984e5SSebastian Reichel * disable or enable the charging. We do it manually because it 4038c0984e5SSebastian Reichel * depends on how the platform has configured the valid inputs. 4048c0984e5SSebastian Reichel */ 4058c0984e5SSebastian Reichel if (smb347_is_ps_online(smb)) { 4068c0984e5SSebastian Reichel ret = smb347_charging_enable(smb); 4078c0984e5SSebastian Reichel if (ret < 0) 4088c0984e5SSebastian Reichel dev_err(smb->dev, "failed to enable charging\n"); 4098c0984e5SSebastian Reichel } else { 4108c0984e5SSebastian Reichel ret = smb347_charging_disable(smb); 4118c0984e5SSebastian Reichel if (ret < 0) 4128c0984e5SSebastian Reichel dev_err(smb->dev, "failed to disable charging\n"); 4138c0984e5SSebastian Reichel } 4148c0984e5SSebastian Reichel 4158c0984e5SSebastian Reichel return ret; 4168c0984e5SSebastian Reichel } 4178c0984e5SSebastian Reichel 4188c0984e5SSebastian Reichel static int smb347_set_charge_current(struct smb347_charger *smb) 4198c0984e5SSebastian Reichel { 420de76fd29SDavid Heidelberg unsigned int id = smb->id; 4218c0984e5SSebastian Reichel int ret; 4228c0984e5SSebastian Reichel 423b6f3e21bSSebastian Reichel if (smb->max_charge_current) { 424de76fd29SDavid Heidelberg ret = current_to_hw(fcc_tbl[id], ARRAY_SIZE(fcc_tbl[id]), 425b6f3e21bSSebastian Reichel smb->max_charge_current); 4268c0984e5SSebastian Reichel if (ret < 0) 4278c0984e5SSebastian Reichel return ret; 4288c0984e5SSebastian Reichel 4298c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4308c0984e5SSebastian Reichel CFG_CHARGE_CURRENT_FCC_MASK, 4318c0984e5SSebastian Reichel ret << CFG_CHARGE_CURRENT_FCC_SHIFT); 4328c0984e5SSebastian Reichel if (ret < 0) 4338c0984e5SSebastian Reichel return ret; 4348c0984e5SSebastian Reichel } 4358c0984e5SSebastian Reichel 436b6f3e21bSSebastian Reichel if (smb->pre_charge_current) { 437de76fd29SDavid Heidelberg ret = current_to_hw(pcc_tbl[id], ARRAY_SIZE(pcc_tbl[id]), 438b6f3e21bSSebastian Reichel smb->pre_charge_current); 4398c0984e5SSebastian Reichel if (ret < 0) 4408c0984e5SSebastian Reichel return ret; 4418c0984e5SSebastian Reichel 4428c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4438c0984e5SSebastian Reichel CFG_CHARGE_CURRENT_PCC_MASK, 4448c0984e5SSebastian Reichel ret << CFG_CHARGE_CURRENT_PCC_SHIFT); 4458c0984e5SSebastian Reichel if (ret < 0) 4468c0984e5SSebastian Reichel return ret; 4478c0984e5SSebastian Reichel } 4488c0984e5SSebastian Reichel 449b6f3e21bSSebastian Reichel if (smb->termination_current) { 450de76fd29SDavid Heidelberg ret = current_to_hw(tc_tbl[id], ARRAY_SIZE(tc_tbl[id]), 451b6f3e21bSSebastian Reichel smb->termination_current); 4528c0984e5SSebastian Reichel if (ret < 0) 4538c0984e5SSebastian Reichel return ret; 4548c0984e5SSebastian Reichel 4558c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4568c0984e5SSebastian Reichel CFG_CHARGE_CURRENT_TC_MASK, ret); 4578c0984e5SSebastian Reichel if (ret < 0) 4588c0984e5SSebastian Reichel return ret; 4598c0984e5SSebastian Reichel } 4608c0984e5SSebastian Reichel 4618c0984e5SSebastian Reichel return 0; 4628c0984e5SSebastian Reichel } 4638c0984e5SSebastian Reichel 4648c0984e5SSebastian Reichel static int smb347_set_current_limits(struct smb347_charger *smb) 4658c0984e5SSebastian Reichel { 466de76fd29SDavid Heidelberg unsigned int id = smb->id; 4678c0984e5SSebastian Reichel int ret; 4688c0984e5SSebastian Reichel 469b6f3e21bSSebastian Reichel if (smb->mains_current_limit) { 470de76fd29SDavid Heidelberg ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), 471b6f3e21bSSebastian Reichel smb->mains_current_limit); 4728c0984e5SSebastian Reichel if (ret < 0) 4738c0984e5SSebastian Reichel return ret; 4748c0984e5SSebastian Reichel 4758c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, 4768c0984e5SSebastian Reichel CFG_CURRENT_LIMIT_DC_MASK, 4778c0984e5SSebastian Reichel ret << CFG_CURRENT_LIMIT_DC_SHIFT); 4788c0984e5SSebastian Reichel if (ret < 0) 4798c0984e5SSebastian Reichel return ret; 4808c0984e5SSebastian Reichel } 4818c0984e5SSebastian Reichel 482b6f3e21bSSebastian Reichel if (smb->usb_hc_current_limit) { 483de76fd29SDavid Heidelberg ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), 484b6f3e21bSSebastian Reichel smb->usb_hc_current_limit); 4858c0984e5SSebastian Reichel if (ret < 0) 4868c0984e5SSebastian Reichel return ret; 4878c0984e5SSebastian Reichel 4888c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, 4898c0984e5SSebastian Reichel CFG_CURRENT_LIMIT_USB_MASK, ret); 4908c0984e5SSebastian Reichel if (ret < 0) 4918c0984e5SSebastian Reichel return ret; 4928c0984e5SSebastian Reichel } 4938c0984e5SSebastian Reichel 4948c0984e5SSebastian Reichel return 0; 4958c0984e5SSebastian Reichel } 4968c0984e5SSebastian Reichel 4978c0984e5SSebastian Reichel static int smb347_set_voltage_limits(struct smb347_charger *smb) 4988c0984e5SSebastian Reichel { 4998c0984e5SSebastian Reichel int ret; 5008c0984e5SSebastian Reichel 501b6f3e21bSSebastian Reichel if (smb->pre_to_fast_voltage) { 502b6f3e21bSSebastian Reichel ret = smb->pre_to_fast_voltage; 5038c0984e5SSebastian Reichel 5048c0984e5SSebastian Reichel /* uV */ 5058c0984e5SSebastian Reichel ret = clamp_val(ret, 2400000, 3000000) - 2400000; 5068c0984e5SSebastian Reichel ret /= 200000; 5078c0984e5SSebastian Reichel 5088c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, 5098c0984e5SSebastian Reichel CFG_FLOAT_VOLTAGE_THRESHOLD_MASK, 5108c0984e5SSebastian Reichel ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT); 5118c0984e5SSebastian Reichel if (ret < 0) 5128c0984e5SSebastian Reichel return ret; 5138c0984e5SSebastian Reichel } 5148c0984e5SSebastian Reichel 515b6f3e21bSSebastian Reichel if (smb->max_charge_voltage) { 516b6f3e21bSSebastian Reichel ret = smb->max_charge_voltage; 5178c0984e5SSebastian Reichel 5188c0984e5SSebastian Reichel /* uV */ 5198c0984e5SSebastian Reichel ret = clamp_val(ret, 3500000, 4500000) - 3500000; 5208c0984e5SSebastian Reichel ret /= 20000; 5218c0984e5SSebastian Reichel 5228c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, 5238c0984e5SSebastian Reichel CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret); 5248c0984e5SSebastian Reichel if (ret < 0) 5258c0984e5SSebastian Reichel return ret; 5268c0984e5SSebastian Reichel } 5278c0984e5SSebastian Reichel 5288c0984e5SSebastian Reichel return 0; 5298c0984e5SSebastian Reichel } 5308c0984e5SSebastian Reichel 5318c0984e5SSebastian Reichel static int smb347_set_temp_limits(struct smb347_charger *smb) 5328c0984e5SSebastian Reichel { 533de76fd29SDavid Heidelberg unsigned int id = smb->id; 5348c0984e5SSebastian Reichel bool enable_therm_monitor = false; 5358c0984e5SSebastian Reichel int ret = 0; 5368c0984e5SSebastian Reichel int val; 5378c0984e5SSebastian Reichel 538b6f3e21bSSebastian Reichel if (smb->chip_temp_threshold) { 539b6f3e21bSSebastian Reichel val = smb->chip_temp_threshold; 5408c0984e5SSebastian Reichel 5418c0984e5SSebastian Reichel /* degree C */ 5428c0984e5SSebastian Reichel val = clamp_val(val, 100, 130) - 100; 5438c0984e5SSebastian Reichel val /= 10; 5448c0984e5SSebastian Reichel 5458c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_OTG, 5468c0984e5SSebastian Reichel CFG_OTG_TEMP_THRESHOLD_MASK, 5478c0984e5SSebastian Reichel val << CFG_OTG_TEMP_THRESHOLD_SHIFT); 5488c0984e5SSebastian Reichel if (ret < 0) 5498c0984e5SSebastian Reichel return ret; 5508c0984e5SSebastian Reichel } 5518c0984e5SSebastian Reichel 552b6f3e21bSSebastian Reichel if (smb->soft_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 553b6f3e21bSSebastian Reichel val = smb->soft_cold_temp_limit; 5548c0984e5SSebastian Reichel 5558c0984e5SSebastian Reichel val = clamp_val(val, 0, 15); 5568c0984e5SSebastian Reichel val /= 5; 5578c0984e5SSebastian Reichel /* this goes from higher to lower so invert the value */ 5588c0984e5SSebastian Reichel val = ~val & 0x3; 5598c0984e5SSebastian Reichel 5608c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5618c0984e5SSebastian Reichel CFG_TEMP_LIMIT_SOFT_COLD_MASK, 5628c0984e5SSebastian Reichel val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT); 5638c0984e5SSebastian Reichel if (ret < 0) 5648c0984e5SSebastian Reichel return ret; 5658c0984e5SSebastian Reichel 5668c0984e5SSebastian Reichel enable_therm_monitor = true; 5678c0984e5SSebastian Reichel } 5688c0984e5SSebastian Reichel 569b6f3e21bSSebastian Reichel if (smb->soft_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 570b6f3e21bSSebastian Reichel val = smb->soft_hot_temp_limit; 5718c0984e5SSebastian Reichel 5728c0984e5SSebastian Reichel val = clamp_val(val, 40, 55) - 40; 5738c0984e5SSebastian Reichel val /= 5; 5748c0984e5SSebastian Reichel 5758c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5768c0984e5SSebastian Reichel CFG_TEMP_LIMIT_SOFT_HOT_MASK, 5778c0984e5SSebastian Reichel val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT); 5788c0984e5SSebastian Reichel if (ret < 0) 5798c0984e5SSebastian Reichel return ret; 5808c0984e5SSebastian Reichel 5818c0984e5SSebastian Reichel enable_therm_monitor = true; 5828c0984e5SSebastian Reichel } 5838c0984e5SSebastian Reichel 584b6f3e21bSSebastian Reichel if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 585b6f3e21bSSebastian Reichel val = smb->hard_cold_temp_limit; 5868c0984e5SSebastian Reichel 5878c0984e5SSebastian Reichel val = clamp_val(val, -5, 10) + 5; 5888c0984e5SSebastian Reichel val /= 5; 5898c0984e5SSebastian Reichel /* this goes from higher to lower so invert the value */ 5908c0984e5SSebastian Reichel val = ~val & 0x3; 5918c0984e5SSebastian Reichel 5928c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5938c0984e5SSebastian Reichel CFG_TEMP_LIMIT_HARD_COLD_MASK, 5948c0984e5SSebastian Reichel val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT); 5958c0984e5SSebastian Reichel if (ret < 0) 5968c0984e5SSebastian Reichel return ret; 5978c0984e5SSebastian Reichel 5988c0984e5SSebastian Reichel enable_therm_monitor = true; 5998c0984e5SSebastian Reichel } 6008c0984e5SSebastian Reichel 601b6f3e21bSSebastian Reichel if (smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 602b6f3e21bSSebastian Reichel val = smb->hard_hot_temp_limit; 6038c0984e5SSebastian Reichel 6048c0984e5SSebastian Reichel val = clamp_val(val, 50, 65) - 50; 6058c0984e5SSebastian Reichel val /= 5; 6068c0984e5SSebastian Reichel 6078c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 6088c0984e5SSebastian Reichel CFG_TEMP_LIMIT_HARD_HOT_MASK, 6098c0984e5SSebastian Reichel val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT); 6108c0984e5SSebastian Reichel if (ret < 0) 6118c0984e5SSebastian Reichel return ret; 6128c0984e5SSebastian Reichel 6138c0984e5SSebastian Reichel enable_therm_monitor = true; 6148c0984e5SSebastian Reichel } 6158c0984e5SSebastian Reichel 6168c0984e5SSebastian Reichel /* 6178c0984e5SSebastian Reichel * If any of the temperature limits are set, we also enable the 6188c0984e5SSebastian Reichel * thermistor monitoring. 6198c0984e5SSebastian Reichel * 6208c0984e5SSebastian Reichel * When soft limits are hit, the device will start to compensate 6218c0984e5SSebastian Reichel * current and/or voltage depending on the configuration. 6228c0984e5SSebastian Reichel * 6238c0984e5SSebastian Reichel * When hard limit is hit, the device will suspend charging 6248c0984e5SSebastian Reichel * depending on the configuration. 6258c0984e5SSebastian Reichel */ 6268c0984e5SSebastian Reichel if (enable_therm_monitor) { 6278c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_THERM, 6288c0984e5SSebastian Reichel CFG_THERM_MONITOR_DISABLED, 0); 6298c0984e5SSebastian Reichel if (ret < 0) 6308c0984e5SSebastian Reichel return ret; 6318c0984e5SSebastian Reichel } 6328c0984e5SSebastian Reichel 633b6f3e21bSSebastian Reichel if (smb->suspend_on_hard_temp_limit) { 6348c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_SYSOK, 6358c0984e5SSebastian Reichel CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0); 6368c0984e5SSebastian Reichel if (ret < 0) 6378c0984e5SSebastian Reichel return ret; 6388c0984e5SSebastian Reichel } 6398c0984e5SSebastian Reichel 640b6f3e21bSSebastian Reichel if (smb->soft_temp_limit_compensation != 641b6f3e21bSSebastian Reichel SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT) { 642b6f3e21bSSebastian Reichel val = smb->soft_temp_limit_compensation & 0x3; 6438c0984e5SSebastian Reichel 6448c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_THERM, 6458c0984e5SSebastian Reichel CFG_THERM_SOFT_HOT_COMPENSATION_MASK, 6468c0984e5SSebastian Reichel val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT); 6478c0984e5SSebastian Reichel if (ret < 0) 6488c0984e5SSebastian Reichel return ret; 6498c0984e5SSebastian Reichel 6508c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_THERM, 6518c0984e5SSebastian Reichel CFG_THERM_SOFT_COLD_COMPENSATION_MASK, 6528c0984e5SSebastian Reichel val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT); 6538c0984e5SSebastian Reichel if (ret < 0) 6548c0984e5SSebastian Reichel return ret; 6558c0984e5SSebastian Reichel } 6568c0984e5SSebastian Reichel 657b6f3e21bSSebastian Reichel if (smb->charge_current_compensation) { 658de76fd29SDavid Heidelberg val = current_to_hw(ccc_tbl[id], ARRAY_SIZE(ccc_tbl[id]), 659b6f3e21bSSebastian Reichel smb->charge_current_compensation); 6608c0984e5SSebastian Reichel if (val < 0) 6618c0984e5SSebastian Reichel return val; 6628c0984e5SSebastian Reichel 6638c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_OTG, 6648c0984e5SSebastian Reichel CFG_OTG_CC_COMPENSATION_MASK, 6658c0984e5SSebastian Reichel (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT); 6668c0984e5SSebastian Reichel if (ret < 0) 6678c0984e5SSebastian Reichel return ret; 6688c0984e5SSebastian Reichel } 6698c0984e5SSebastian Reichel 6708c0984e5SSebastian Reichel return ret; 6718c0984e5SSebastian Reichel } 6728c0984e5SSebastian Reichel 6738c0984e5SSebastian Reichel /* 6748c0984e5SSebastian Reichel * smb347_set_writable - enables/disables writing to non-volatile registers 6758c0984e5SSebastian Reichel * @smb: pointer to smb347 charger instance 6768c0984e5SSebastian Reichel * 6778c0984e5SSebastian Reichel * You can enable/disable writing to the non-volatile configuration 6788c0984e5SSebastian Reichel * registers by calling this function. 6798c0984e5SSebastian Reichel * 6808c0984e5SSebastian Reichel * Returns %0 on success and negative errno in case of failure. 6818c0984e5SSebastian Reichel */ 6824ac59d85SDmitry Osipenko static int smb347_set_writable(struct smb347_charger *smb, bool writable, 6834ac59d85SDmitry Osipenko bool irq_toggle) 6848c0984e5SSebastian Reichel { 6854ac59d85SDmitry Osipenko struct i2c_client *client = to_i2c_client(smb->dev); 6864ac59d85SDmitry Osipenko int ret; 6874ac59d85SDmitry Osipenko 6884ac59d85SDmitry Osipenko if (writable && irq_toggle && !smb->irq_unsupported) 6894ac59d85SDmitry Osipenko disable_irq(client->irq); 6904ac59d85SDmitry Osipenko 6914ac59d85SDmitry Osipenko ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE, 6928c0984e5SSebastian Reichel writable ? CMD_A_ALLOW_WRITE : 0); 6934ac59d85SDmitry Osipenko 6944ac59d85SDmitry Osipenko if ((!writable || ret) && irq_toggle && !smb->irq_unsupported) 6954ac59d85SDmitry Osipenko enable_irq(client->irq); 6964ac59d85SDmitry Osipenko 6974ac59d85SDmitry Osipenko return ret; 6988c0984e5SSebastian Reichel } 6998c0984e5SSebastian Reichel 7008c0984e5SSebastian Reichel static int smb347_hw_init(struct smb347_charger *smb) 7018c0984e5SSebastian Reichel { 7028c0984e5SSebastian Reichel unsigned int val; 7038c0984e5SSebastian Reichel int ret; 7048c0984e5SSebastian Reichel 7054ac59d85SDmitry Osipenko ret = smb347_set_writable(smb, true, false); 7068c0984e5SSebastian Reichel if (ret < 0) 7078c0984e5SSebastian Reichel return ret; 7088c0984e5SSebastian Reichel 7098c0984e5SSebastian Reichel /* 7108c0984e5SSebastian Reichel * Program the platform specific configuration values to the device 7118c0984e5SSebastian Reichel * first. 7128c0984e5SSebastian Reichel */ 7138c0984e5SSebastian Reichel ret = smb347_set_charge_current(smb); 7148c0984e5SSebastian Reichel if (ret < 0) 7158c0984e5SSebastian Reichel goto fail; 7168c0984e5SSebastian Reichel 7178c0984e5SSebastian Reichel ret = smb347_set_current_limits(smb); 7188c0984e5SSebastian Reichel if (ret < 0) 7198c0984e5SSebastian Reichel goto fail; 7208c0984e5SSebastian Reichel 7218c0984e5SSebastian Reichel ret = smb347_set_voltage_limits(smb); 7228c0984e5SSebastian Reichel if (ret < 0) 7238c0984e5SSebastian Reichel goto fail; 7248c0984e5SSebastian Reichel 7258c0984e5SSebastian Reichel ret = smb347_set_temp_limits(smb); 7268c0984e5SSebastian Reichel if (ret < 0) 7278c0984e5SSebastian Reichel goto fail; 7288c0984e5SSebastian Reichel 7298c0984e5SSebastian Reichel /* If USB charging is disabled we put the USB in suspend mode */ 730b6f3e21bSSebastian Reichel if (!smb->use_usb) { 7318c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CMD_A, 7328c0984e5SSebastian Reichel CMD_A_SUSPEND_ENABLED, 7338c0984e5SSebastian Reichel CMD_A_SUSPEND_ENABLED); 7348c0984e5SSebastian Reichel if (ret < 0) 7358c0984e5SSebastian Reichel goto fail; 7368c0984e5SSebastian Reichel } 7378c0984e5SSebastian Reichel 7388c0984e5SSebastian Reichel /* 7398c0984e5SSebastian Reichel * If configured by platform data, we enable hardware Auto-OTG 7408c0984e5SSebastian Reichel * support for driving VBUS. Otherwise we disable it. 7418c0984e5SSebastian Reichel */ 7428c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK, 743b6f3e21bSSebastian Reichel smb->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0); 7448c0984e5SSebastian Reichel if (ret < 0) 7458c0984e5SSebastian Reichel goto fail; 7468c0984e5SSebastian Reichel 747efe21754SDmitry Osipenko /* Activate pin control, making it writable. */ 748efe21754SDmitry Osipenko switch (smb->enable_control) { 749efe21754SDmitry Osipenko case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW: 750efe21754SDmitry Osipenko case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH: 751efe21754SDmitry Osipenko ret = regmap_set_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL); 752efe21754SDmitry Osipenko if (ret < 0) 753efe21754SDmitry Osipenko goto fail; 754efe21754SDmitry Osipenko } 755efe21754SDmitry Osipenko 7568c0984e5SSebastian Reichel /* 7578c0984e5SSebastian Reichel * Make the charging functionality controllable by a write to the 7588c0984e5SSebastian Reichel * command register unless pin control is specified in the platform 7598c0984e5SSebastian Reichel * data. 7608c0984e5SSebastian Reichel */ 761b6f3e21bSSebastian Reichel switch (smb->enable_control) { 762b6f3e21bSSebastian Reichel case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW: 7638c0984e5SSebastian Reichel val = CFG_PIN_EN_CTRL_ACTIVE_LOW; 7648c0984e5SSebastian Reichel break; 765b6f3e21bSSebastian Reichel case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH: 7668c0984e5SSebastian Reichel val = CFG_PIN_EN_CTRL_ACTIVE_HIGH; 7678c0984e5SSebastian Reichel break; 7688c0984e5SSebastian Reichel default: 7698c0984e5SSebastian Reichel val = 0; 7708c0984e5SSebastian Reichel break; 7718c0984e5SSebastian Reichel } 7728c0984e5SSebastian Reichel 7738c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK, 7748c0984e5SSebastian Reichel val); 7758c0984e5SSebastian Reichel if (ret < 0) 7768c0984e5SSebastian Reichel goto fail; 7778c0984e5SSebastian Reichel 7788c0984e5SSebastian Reichel /* Disable Automatic Power Source Detection (APSD) interrupt. */ 7798c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0); 7808c0984e5SSebastian Reichel if (ret < 0) 7818c0984e5SSebastian Reichel goto fail; 7828c0984e5SSebastian Reichel 7838c0984e5SSebastian Reichel ret = smb347_update_ps_status(smb); 7848c0984e5SSebastian Reichel if (ret < 0) 7858c0984e5SSebastian Reichel goto fail; 7868c0984e5SSebastian Reichel 7878c0984e5SSebastian Reichel ret = smb347_start_stop_charging(smb); 7888c0984e5SSebastian Reichel 7898c0984e5SSebastian Reichel fail: 7904ac59d85SDmitry Osipenko smb347_set_writable(smb, false, false); 7918c0984e5SSebastian Reichel return ret; 7928c0984e5SSebastian Reichel } 7938c0984e5SSebastian Reichel 7948c0984e5SSebastian Reichel static irqreturn_t smb347_interrupt(int irq, void *data) 7958c0984e5SSebastian Reichel { 7968c0984e5SSebastian Reichel struct smb347_charger *smb = data; 7978c0984e5SSebastian Reichel unsigned int stat_c, irqstat_c, irqstat_d, irqstat_e; 7988c0984e5SSebastian Reichel bool handled = false; 7998c0984e5SSebastian Reichel int ret; 8008c0984e5SSebastian Reichel 801fa7cc725SDavid Heidelberg /* SMB347 it needs at least 20ms for setting IRQSTAT_E_*IN_UV_IRQ */ 802fa7cc725SDavid Heidelberg usleep_range(25000, 35000); 803fa7cc725SDavid Heidelberg 8048c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_C, &stat_c); 8058c0984e5SSebastian Reichel if (ret < 0) { 8068c0984e5SSebastian Reichel dev_warn(smb->dev, "reading STAT_C failed\n"); 8078c0984e5SSebastian Reichel return IRQ_NONE; 8088c0984e5SSebastian Reichel } 8098c0984e5SSebastian Reichel 8108c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c); 8118c0984e5SSebastian Reichel if (ret < 0) { 8128c0984e5SSebastian Reichel dev_warn(smb->dev, "reading IRQSTAT_C failed\n"); 8138c0984e5SSebastian Reichel return IRQ_NONE; 8148c0984e5SSebastian Reichel } 8158c0984e5SSebastian Reichel 8168c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, IRQSTAT_D, &irqstat_d); 8178c0984e5SSebastian Reichel if (ret < 0) { 8188c0984e5SSebastian Reichel dev_warn(smb->dev, "reading IRQSTAT_D failed\n"); 8198c0984e5SSebastian Reichel return IRQ_NONE; 8208c0984e5SSebastian Reichel } 8218c0984e5SSebastian Reichel 8228c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e); 8238c0984e5SSebastian Reichel if (ret < 0) { 8248c0984e5SSebastian Reichel dev_warn(smb->dev, "reading IRQSTAT_E failed\n"); 8258c0984e5SSebastian Reichel return IRQ_NONE; 8268c0984e5SSebastian Reichel } 8278c0984e5SSebastian Reichel 8288c0984e5SSebastian Reichel /* 8298c0984e5SSebastian Reichel * If we get charger error we report the error back to user. 8308c0984e5SSebastian Reichel * If the error is recovered charging will resume again. 8318c0984e5SSebastian Reichel */ 8328c0984e5SSebastian Reichel if (stat_c & STAT_C_CHARGER_ERROR) { 8338c0984e5SSebastian Reichel dev_err(smb->dev, "charging stopped due to charger error\n"); 834b6f3e21bSSebastian Reichel if (smb->use_mains) 835db14d3b4SDavid Heidelberg power_supply_changed(smb->mains); 836b6f3e21bSSebastian Reichel if (smb->use_usb) 837db14d3b4SDavid Heidelberg power_supply_changed(smb->usb); 8388c0984e5SSebastian Reichel handled = true; 8398c0984e5SSebastian Reichel } 8408c0984e5SSebastian Reichel 8418c0984e5SSebastian Reichel /* 8428c0984e5SSebastian Reichel * If we reached the termination current the battery is charged and 8438c0984e5SSebastian Reichel * we can update the status now. Charging is automatically 8448c0984e5SSebastian Reichel * disabled by the hardware. 8458c0984e5SSebastian Reichel */ 8468c0984e5SSebastian Reichel if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { 847db14d3b4SDavid Heidelberg if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) { 848b6f3e21bSSebastian Reichel if (smb->use_mains) 849db14d3b4SDavid Heidelberg power_supply_changed(smb->mains); 850b6f3e21bSSebastian Reichel if (smb->use_usb) 851db14d3b4SDavid Heidelberg power_supply_changed(smb->usb); 852db14d3b4SDavid Heidelberg } 8538c0984e5SSebastian Reichel dev_dbg(smb->dev, "going to HW maintenance mode\n"); 8548c0984e5SSebastian Reichel handled = true; 8558c0984e5SSebastian Reichel } 8568c0984e5SSebastian Reichel 8578c0984e5SSebastian Reichel /* 8588c0984e5SSebastian Reichel * If we got a charger timeout INT that means the charge 8598c0984e5SSebastian Reichel * full is not detected with in charge timeout value. 8608c0984e5SSebastian Reichel */ 8618c0984e5SSebastian Reichel if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ) { 8628c0984e5SSebastian Reichel dev_dbg(smb->dev, "total Charge Timeout INT received\n"); 8638c0984e5SSebastian Reichel 8648c0984e5SSebastian Reichel if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT) 8658c0984e5SSebastian Reichel dev_warn(smb->dev, "charging stopped due to timeout\n"); 866b6f3e21bSSebastian Reichel if (smb->use_mains) 867db14d3b4SDavid Heidelberg power_supply_changed(smb->mains); 868b6f3e21bSSebastian Reichel if (smb->use_usb) 869db14d3b4SDavid Heidelberg power_supply_changed(smb->usb); 8708c0984e5SSebastian Reichel handled = true; 8718c0984e5SSebastian Reichel } 8728c0984e5SSebastian Reichel 8738c0984e5SSebastian Reichel /* 8748c0984e5SSebastian Reichel * If we got an under voltage interrupt it means that AC/USB input 8758c0984e5SSebastian Reichel * was connected or disconnected. 8768c0984e5SSebastian Reichel */ 8778c0984e5SSebastian Reichel if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { 8788c0984e5SSebastian Reichel if (smb347_update_ps_status(smb) > 0) { 8798c0984e5SSebastian Reichel smb347_start_stop_charging(smb); 880b6f3e21bSSebastian Reichel if (smb->use_mains) 8818c0984e5SSebastian Reichel power_supply_changed(smb->mains); 882b6f3e21bSSebastian Reichel if (smb->use_usb) 8838c0984e5SSebastian Reichel power_supply_changed(smb->usb); 8848c0984e5SSebastian Reichel } 8858c0984e5SSebastian Reichel handled = true; 8868c0984e5SSebastian Reichel } 8878c0984e5SSebastian Reichel 8888c0984e5SSebastian Reichel return handled ? IRQ_HANDLED : IRQ_NONE; 8898c0984e5SSebastian Reichel } 8908c0984e5SSebastian Reichel 8918c0984e5SSebastian Reichel static int smb347_irq_set(struct smb347_charger *smb, bool enable) 8928c0984e5SSebastian Reichel { 8938c0984e5SSebastian Reichel int ret; 8948c0984e5SSebastian Reichel 89569963126SDmitry Osipenko if (smb->irq_unsupported) 89669963126SDmitry Osipenko return 0; 89769963126SDmitry Osipenko 8984ac59d85SDmitry Osipenko ret = smb347_set_writable(smb, true, true); 8998c0984e5SSebastian Reichel if (ret < 0) 9008c0984e5SSebastian Reichel return ret; 9018c0984e5SSebastian Reichel 9028c0984e5SSebastian Reichel /* 9038c0984e5SSebastian Reichel * Enable/disable interrupts for: 9048c0984e5SSebastian Reichel * - under voltage 9058c0984e5SSebastian Reichel * - termination current reached 9068c0984e5SSebastian Reichel * - charger timeout 9078c0984e5SSebastian Reichel * - charger error 9088c0984e5SSebastian Reichel */ 9098c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff, 9108c0984e5SSebastian Reichel enable ? CFG_FAULT_IRQ_DCIN_UV : 0); 9118c0984e5SSebastian Reichel if (ret < 0) 9128c0984e5SSebastian Reichel goto fail; 9138c0984e5SSebastian Reichel 9148c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff, 9158c0984e5SSebastian Reichel enable ? (CFG_STATUS_IRQ_TERMINATION_OR_TAPER | 9168c0984e5SSebastian Reichel CFG_STATUS_IRQ_CHARGE_TIMEOUT) : 0); 9178c0984e5SSebastian Reichel if (ret < 0) 9188c0984e5SSebastian Reichel goto fail; 9198c0984e5SSebastian Reichel 9208c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR, 9218c0984e5SSebastian Reichel enable ? CFG_PIN_EN_CHARGER_ERROR : 0); 9228c0984e5SSebastian Reichel fail: 9234ac59d85SDmitry Osipenko smb347_set_writable(smb, false, true); 9248c0984e5SSebastian Reichel return ret; 9258c0984e5SSebastian Reichel } 9268c0984e5SSebastian Reichel 9278c0984e5SSebastian Reichel static inline int smb347_irq_enable(struct smb347_charger *smb) 9288c0984e5SSebastian Reichel { 9298c0984e5SSebastian Reichel return smb347_irq_set(smb, true); 9308c0984e5SSebastian Reichel } 9318c0984e5SSebastian Reichel 9328c0984e5SSebastian Reichel static inline int smb347_irq_disable(struct smb347_charger *smb) 9338c0984e5SSebastian Reichel { 9348c0984e5SSebastian Reichel return smb347_irq_set(smb, false); 9358c0984e5SSebastian Reichel } 9368c0984e5SSebastian Reichel 9378c0984e5SSebastian Reichel static int smb347_irq_init(struct smb347_charger *smb, 9388c0984e5SSebastian Reichel struct i2c_client *client) 9398c0984e5SSebastian Reichel { 9402d52f710SDavid Heidelberg int ret; 9418c0984e5SSebastian Reichel 942d33b3f7eSDmitry Osipenko smb->irq_unsupported = true; 943d33b3f7eSDmitry Osipenko 944d33b3f7eSDmitry Osipenko /* 945d33b3f7eSDmitry Osipenko * Interrupt pin is optional. If it is connected, we setup the 946d33b3f7eSDmitry Osipenko * interrupt support here. 947d33b3f7eSDmitry Osipenko */ 948d33b3f7eSDmitry Osipenko if (!client->irq) 949d33b3f7eSDmitry Osipenko return 0; 9508c0984e5SSebastian Reichel 9514ac59d85SDmitry Osipenko ret = smb347_set_writable(smb, true, false); 9528c0984e5SSebastian Reichel if (ret < 0) 9532d52f710SDavid Heidelberg return ret; 9548c0984e5SSebastian Reichel 9558c0984e5SSebastian Reichel /* 9568c0984e5SSebastian Reichel * Configure the STAT output to be suitable for interrupts: disable 9578c0984e5SSebastian Reichel * all other output (except interrupts) and make it active low. 9588c0984e5SSebastian Reichel */ 9598c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_STAT, 9608c0984e5SSebastian Reichel CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED, 9618c0984e5SSebastian Reichel CFG_STAT_DISABLED); 9622d52f710SDavid Heidelberg 9634ac59d85SDmitry Osipenko smb347_set_writable(smb, false, false); 9642d52f710SDavid Heidelberg 965d33b3f7eSDmitry Osipenko if (ret < 0) { 966d33b3f7eSDmitry Osipenko dev_warn(smb->dev, "failed to initialize IRQ: %d\n", ret); 967d33b3f7eSDmitry Osipenko dev_warn(smb->dev, "disabling IRQ support\n"); 968d33b3f7eSDmitry Osipenko return 0; 969d33b3f7eSDmitry Osipenko } 970d33b3f7eSDmitry Osipenko 971d33b3f7eSDmitry Osipenko ret = devm_request_threaded_irq(smb->dev, client->irq, NULL, 972d33b3f7eSDmitry Osipenko smb347_interrupt, IRQF_ONESHOT, 973d33b3f7eSDmitry Osipenko client->name, smb); 974d33b3f7eSDmitry Osipenko if (ret) 9758c0984e5SSebastian Reichel return ret; 976d33b3f7eSDmitry Osipenko 977d33b3f7eSDmitry Osipenko smb->irq_unsupported = false; 978d33b3f7eSDmitry Osipenko 979d33b3f7eSDmitry Osipenko ret = smb347_irq_enable(smb); 980d33b3f7eSDmitry Osipenko if (ret < 0) 981d33b3f7eSDmitry Osipenko return ret; 982d33b3f7eSDmitry Osipenko 983d33b3f7eSDmitry Osipenko return 0; 9848c0984e5SSebastian Reichel } 9858c0984e5SSebastian Reichel 9868c0984e5SSebastian Reichel /* 9878c0984e5SSebastian Reichel * Returns the constant charge current programmed 9888c0984e5SSebastian Reichel * into the charger in uA. 9898c0984e5SSebastian Reichel */ 9908c0984e5SSebastian Reichel static int get_const_charge_current(struct smb347_charger *smb) 9918c0984e5SSebastian Reichel { 992de76fd29SDavid Heidelberg unsigned int id = smb->id; 9938c0984e5SSebastian Reichel int ret, intval; 9948c0984e5SSebastian Reichel unsigned int v; 9958c0984e5SSebastian Reichel 9968c0984e5SSebastian Reichel if (!smb347_is_ps_online(smb)) 9978c0984e5SSebastian Reichel return -ENODATA; 9988c0984e5SSebastian Reichel 9998c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_B, &v); 10008c0984e5SSebastian Reichel if (ret < 0) 10018c0984e5SSebastian Reichel return ret; 10028c0984e5SSebastian Reichel 10038c0984e5SSebastian Reichel /* 10048c0984e5SSebastian Reichel * The current value is composition of FCC and PCC values 10058c0984e5SSebastian Reichel * and we can detect which table to use from bit 5. 10068c0984e5SSebastian Reichel */ 10078c0984e5SSebastian Reichel if (v & 0x20) { 1008de76fd29SDavid Heidelberg intval = hw_to_current(fcc_tbl[id], 1009de76fd29SDavid Heidelberg ARRAY_SIZE(fcc_tbl[id]), v & 7); 10108c0984e5SSebastian Reichel } else { 10118c0984e5SSebastian Reichel v >>= 3; 1012de76fd29SDavid Heidelberg intval = hw_to_current(pcc_tbl[id], 1013de76fd29SDavid Heidelberg ARRAY_SIZE(pcc_tbl[id]), v & 7); 10148c0984e5SSebastian Reichel } 10158c0984e5SSebastian Reichel 10168c0984e5SSebastian Reichel return intval; 10178c0984e5SSebastian Reichel } 10188c0984e5SSebastian Reichel 10198c0984e5SSebastian Reichel /* 10208c0984e5SSebastian Reichel * Returns the constant charge voltage programmed 10218c0984e5SSebastian Reichel * into the charger in uV. 10228c0984e5SSebastian Reichel */ 10238c0984e5SSebastian Reichel static int get_const_charge_voltage(struct smb347_charger *smb) 10248c0984e5SSebastian Reichel { 10258c0984e5SSebastian Reichel int ret, intval; 10268c0984e5SSebastian Reichel unsigned int v; 10278c0984e5SSebastian Reichel 10288c0984e5SSebastian Reichel if (!smb347_is_ps_online(smb)) 10298c0984e5SSebastian Reichel return -ENODATA; 10308c0984e5SSebastian Reichel 10318c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_A, &v); 10328c0984e5SSebastian Reichel if (ret < 0) 10338c0984e5SSebastian Reichel return ret; 10348c0984e5SSebastian Reichel 10358c0984e5SSebastian Reichel v &= STAT_A_FLOAT_VOLTAGE_MASK; 10368c0984e5SSebastian Reichel if (v > 0x3d) 10378c0984e5SSebastian Reichel v = 0x3d; 10388c0984e5SSebastian Reichel 10398c0984e5SSebastian Reichel intval = 3500000 + v * 20000; 10408c0984e5SSebastian Reichel 10418c0984e5SSebastian Reichel return intval; 10428c0984e5SSebastian Reichel } 10438c0984e5SSebastian Reichel 1044db14d3b4SDavid Heidelberg static int smb347_get_charging_status(struct smb347_charger *smb, 1045db14d3b4SDavid Heidelberg struct power_supply *psy) 10468c0984e5SSebastian Reichel { 10478c0984e5SSebastian Reichel int ret, status; 10488c0984e5SSebastian Reichel unsigned int val; 10498c0984e5SSebastian Reichel 1050db14d3b4SDavid Heidelberg if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { 1051db14d3b4SDavid Heidelberg if (!smb->usb_online) 10528c0984e5SSebastian Reichel return POWER_SUPPLY_STATUS_DISCHARGING; 1053db14d3b4SDavid Heidelberg } else { 1054db14d3b4SDavid Heidelberg if (!smb->mains_online) 1055db14d3b4SDavid Heidelberg return POWER_SUPPLY_STATUS_DISCHARGING; 1056db14d3b4SDavid Heidelberg } 10578c0984e5SSebastian Reichel 10588c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_C, &val); 10598c0984e5SSebastian Reichel if (ret < 0) 10608c0984e5SSebastian Reichel return ret; 10618c0984e5SSebastian Reichel 10628c0984e5SSebastian Reichel if ((val & STAT_C_CHARGER_ERROR) || 10638c0984e5SSebastian Reichel (val & STAT_C_HOLDOFF_STAT)) { 10648c0984e5SSebastian Reichel /* 10658c0984e5SSebastian Reichel * set to NOT CHARGING upon charger error 10668c0984e5SSebastian Reichel * or charging has stopped. 10678c0984e5SSebastian Reichel */ 10688c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_NOT_CHARGING; 10698c0984e5SSebastian Reichel } else { 10708c0984e5SSebastian Reichel if ((val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT) { 10718c0984e5SSebastian Reichel /* 10728c0984e5SSebastian Reichel * set to charging if battery is in pre-charge, 10738c0984e5SSebastian Reichel * fast charge or taper charging mode. 10748c0984e5SSebastian Reichel */ 10758c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_CHARGING; 10768c0984e5SSebastian Reichel } else if (val & STAT_C_CHG_TERM) { 10778c0984e5SSebastian Reichel /* 10788c0984e5SSebastian Reichel * set the status to FULL if battery is not in pre 10798c0984e5SSebastian Reichel * charge, fast charge or taper charging mode AND 10808c0984e5SSebastian Reichel * charging is terminated at least once. 10818c0984e5SSebastian Reichel */ 10828c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_FULL; 10838c0984e5SSebastian Reichel } else { 10848c0984e5SSebastian Reichel /* 10858c0984e5SSebastian Reichel * in this case no charger error or termination 10868c0984e5SSebastian Reichel * occured but charging is not in progress!!! 10878c0984e5SSebastian Reichel */ 10888c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_NOT_CHARGING; 10898c0984e5SSebastian Reichel } 10908c0984e5SSebastian Reichel } 10918c0984e5SSebastian Reichel 10928c0984e5SSebastian Reichel return status; 10938c0984e5SSebastian Reichel } 10948c0984e5SSebastian Reichel 109599298de5SDmitry Osipenko static int smb347_get_property_locked(struct power_supply *psy, 10968c0984e5SSebastian Reichel enum power_supply_property prop, 10978c0984e5SSebastian Reichel union power_supply_propval *val) 10988c0984e5SSebastian Reichel { 10998c0984e5SSebastian Reichel struct smb347_charger *smb = power_supply_get_drvdata(psy); 11008c0984e5SSebastian Reichel int ret; 11018c0984e5SSebastian Reichel 11028c0984e5SSebastian Reichel switch (prop) { 11038c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 1104db14d3b4SDavid Heidelberg ret = smb347_get_charging_status(smb, psy); 11058c0984e5SSebastian Reichel if (ret < 0) 11068c0984e5SSebastian Reichel return ret; 11078c0984e5SSebastian Reichel val->intval = ret; 11088c0984e5SSebastian Reichel break; 11098c0984e5SSebastian Reichel 11108c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_TYPE: 1111db14d3b4SDavid Heidelberg if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { 1112db14d3b4SDavid Heidelberg if (!smb->usb_online) 11138c0984e5SSebastian Reichel return -ENODATA; 1114db14d3b4SDavid Heidelberg } else { 1115db14d3b4SDavid Heidelberg if (!smb->mains_online) 1116db14d3b4SDavid Heidelberg return -ENODATA; 1117db14d3b4SDavid Heidelberg } 11188c0984e5SSebastian Reichel 11198c0984e5SSebastian Reichel /* 11208c0984e5SSebastian Reichel * We handle trickle and pre-charging the same, and taper 11218c0984e5SSebastian Reichel * and none the same. 11228c0984e5SSebastian Reichel */ 11238c0984e5SSebastian Reichel switch (smb347_charging_status(smb)) { 11248c0984e5SSebastian Reichel case 1: 11258c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 11268c0984e5SSebastian Reichel break; 11278c0984e5SSebastian Reichel case 2: 11288c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 11298c0984e5SSebastian Reichel break; 11308c0984e5SSebastian Reichel default: 11318c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 11328c0984e5SSebastian Reichel break; 11338c0984e5SSebastian Reichel } 11348c0984e5SSebastian Reichel break; 11358c0984e5SSebastian Reichel 1136db14d3b4SDavid Heidelberg case POWER_SUPPLY_PROP_ONLINE: 1137db14d3b4SDavid Heidelberg if (psy->desc->type == POWER_SUPPLY_TYPE_USB) 1138db14d3b4SDavid Heidelberg val->intval = smb->usb_online; 1139db14d3b4SDavid Heidelberg else 1140db14d3b4SDavid Heidelberg val->intval = smb->mains_online; 11418c0984e5SSebastian Reichel break; 11428c0984e5SSebastian Reichel 1143db14d3b4SDavid Heidelberg case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 1144db14d3b4SDavid Heidelberg ret = get_const_charge_voltage(smb); 1145db14d3b4SDavid Heidelberg if (ret < 0) 1146db14d3b4SDavid Heidelberg return ret; 1147db14d3b4SDavid Heidelberg val->intval = ret; 11488c0984e5SSebastian Reichel break; 11498c0984e5SSebastian Reichel 1150db14d3b4SDavid Heidelberg case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 1151db14d3b4SDavid Heidelberg ret = get_const_charge_current(smb); 1152db14d3b4SDavid Heidelberg if (ret < 0) 1153db14d3b4SDavid Heidelberg return ret; 1154db14d3b4SDavid Heidelberg val->intval = ret; 11558c0984e5SSebastian Reichel break; 11568c0984e5SSebastian Reichel 11578c0984e5SSebastian Reichel default: 11588c0984e5SSebastian Reichel return -EINVAL; 11598c0984e5SSebastian Reichel } 11608c0984e5SSebastian Reichel 11618c0984e5SSebastian Reichel return 0; 11628c0984e5SSebastian Reichel } 11638c0984e5SSebastian Reichel 116499298de5SDmitry Osipenko static int smb347_get_property(struct power_supply *psy, 116599298de5SDmitry Osipenko enum power_supply_property prop, 116699298de5SDmitry Osipenko union power_supply_propval *val) 116799298de5SDmitry Osipenko { 116899298de5SDmitry Osipenko struct smb347_charger *smb = power_supply_get_drvdata(psy); 116999298de5SDmitry Osipenko struct i2c_client *client = to_i2c_client(smb->dev); 117099298de5SDmitry Osipenko int ret; 117199298de5SDmitry Osipenko 1172d33b3f7eSDmitry Osipenko if (!smb->irq_unsupported) 117399298de5SDmitry Osipenko disable_irq(client->irq); 1174d33b3f7eSDmitry Osipenko 117599298de5SDmitry Osipenko ret = smb347_get_property_locked(psy, prop, val); 1176d33b3f7eSDmitry Osipenko 1177d33b3f7eSDmitry Osipenko if (!smb->irq_unsupported) 117899298de5SDmitry Osipenko enable_irq(client->irq); 117999298de5SDmitry Osipenko 118099298de5SDmitry Osipenko return ret; 118199298de5SDmitry Osipenko } 118299298de5SDmitry Osipenko 1183db14d3b4SDavid Heidelberg static enum power_supply_property smb347_properties[] = { 11848c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 11858c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_TYPE, 1186db14d3b4SDavid Heidelberg POWER_SUPPLY_PROP_ONLINE, 1187db14d3b4SDavid Heidelberg POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 1188db14d3b4SDavid Heidelberg POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 11898c0984e5SSebastian Reichel }; 11908c0984e5SSebastian Reichel 11918c0984e5SSebastian Reichel static bool smb347_volatile_reg(struct device *dev, unsigned int reg) 11928c0984e5SSebastian Reichel { 11938c0984e5SSebastian Reichel switch (reg) { 11948c0984e5SSebastian Reichel case IRQSTAT_A: 11958c0984e5SSebastian Reichel case IRQSTAT_C: 1196c32ea07aSDmitry Osipenko case IRQSTAT_D: 11978c0984e5SSebastian Reichel case IRQSTAT_E: 11988c0984e5SSebastian Reichel case IRQSTAT_F: 11998c0984e5SSebastian Reichel case STAT_A: 12008c0984e5SSebastian Reichel case STAT_B: 12018c0984e5SSebastian Reichel case STAT_C: 12028c0984e5SSebastian Reichel case STAT_E: 12038c0984e5SSebastian Reichel return true; 12048c0984e5SSebastian Reichel } 12058c0984e5SSebastian Reichel 12068c0984e5SSebastian Reichel return false; 12078c0984e5SSebastian Reichel } 12088c0984e5SSebastian Reichel 12098c0984e5SSebastian Reichel static bool smb347_readable_reg(struct device *dev, unsigned int reg) 12108c0984e5SSebastian Reichel { 12118c0984e5SSebastian Reichel switch (reg) { 12128c0984e5SSebastian Reichel case CFG_CHARGE_CURRENT: 12138c0984e5SSebastian Reichel case CFG_CURRENT_LIMIT: 12148c0984e5SSebastian Reichel case CFG_FLOAT_VOLTAGE: 12158c0984e5SSebastian Reichel case CFG_STAT: 12168c0984e5SSebastian Reichel case CFG_PIN: 12178c0984e5SSebastian Reichel case CFG_THERM: 12188c0984e5SSebastian Reichel case CFG_SYSOK: 12198c0984e5SSebastian Reichel case CFG_OTHER: 12208c0984e5SSebastian Reichel case CFG_OTG: 12218c0984e5SSebastian Reichel case CFG_TEMP_LIMIT: 12228c0984e5SSebastian Reichel case CFG_FAULT_IRQ: 12238c0984e5SSebastian Reichel case CFG_STATUS_IRQ: 12248c0984e5SSebastian Reichel case CFG_ADDRESS: 12258c0984e5SSebastian Reichel case CMD_A: 12268c0984e5SSebastian Reichel case CMD_B: 12278c0984e5SSebastian Reichel case CMD_C: 12288c0984e5SSebastian Reichel return true; 12298c0984e5SSebastian Reichel } 12308c0984e5SSebastian Reichel 12318c0984e5SSebastian Reichel return smb347_volatile_reg(dev, reg); 12328c0984e5SSebastian Reichel } 12338c0984e5SSebastian Reichel 1234b6f3e21bSSebastian Reichel static void smb347_dt_parse_dev_info(struct smb347_charger *smb) 1235364bec75SDavid Heidelberg { 1236f385e2fcSSebastian Reichel struct device *dev = smb->dev; 1237b6f3e21bSSebastian Reichel 1238b6f3e21bSSebastian Reichel smb->soft_temp_limit_compensation = 1239b6f3e21bSSebastian Reichel SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT; 1240364bec75SDavid Heidelberg /* 1241364bec75SDavid Heidelberg * These properties come from the battery info, still we need to 1242364bec75SDavid Heidelberg * pre-initialize the values. See smb347_get_battery_info() below. 1243364bec75SDavid Heidelberg */ 1244b6f3e21bSSebastian Reichel smb->soft_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 1245b6f3e21bSSebastian Reichel smb->hard_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 1246b6f3e21bSSebastian Reichel smb->soft_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 1247b6f3e21bSSebastian Reichel smb->hard_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 1248364bec75SDavid Heidelberg 1249364bec75SDavid Heidelberg /* Charging constraints */ 1250f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,fast-voltage-threshold-microvolt", 1251b6f3e21bSSebastian Reichel &smb->pre_to_fast_voltage); 1252f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,mains-current-limit-microamp", 1253b6f3e21bSSebastian Reichel &smb->mains_current_limit); 1254f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,usb-current-limit-microamp", 1255b6f3e21bSSebastian Reichel &smb->usb_hc_current_limit); 1256364bec75SDavid Heidelberg 1257364bec75SDavid Heidelberg /* For thermometer monitoring */ 1258f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,chip-temperature-threshold-celsius", 1259b6f3e21bSSebastian Reichel &smb->chip_temp_threshold); 1260f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,soft-compensation-method", 1261b6f3e21bSSebastian Reichel &smb->soft_temp_limit_compensation); 1262f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,charge-current-compensation-microamp", 1263b6f3e21bSSebastian Reichel &smb->charge_current_compensation); 1264364bec75SDavid Heidelberg 1265364bec75SDavid Heidelberg /* Supported charging mode */ 1266f385e2fcSSebastian Reichel smb->use_mains = device_property_read_bool(dev, "summit,enable-mains-charging"); 1267f385e2fcSSebastian Reichel smb->use_usb = device_property_read_bool(dev, "summit,enable-usb-charging"); 1268f385e2fcSSebastian Reichel smb->use_usb_otg = device_property_read_bool(dev, "summit,enable-otg-charging"); 1269364bec75SDavid Heidelberg 1270364bec75SDavid Heidelberg /* Select charging control */ 1271f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,enable-charge-control", 1272b6f3e21bSSebastian Reichel &smb->enable_control); 1273*565efae9SDmitry Osipenko 1274*565efae9SDmitry Osipenko /* 1275*565efae9SDmitry Osipenko * Polarity of INOK signal indicating presence of external power 1276*565efae9SDmitry Osipenko * supply connected to the charger. 1277*565efae9SDmitry Osipenko */ 1278*565efae9SDmitry Osipenko device_property_read_u32(dev, "summit,inok-polarity", 1279*565efae9SDmitry Osipenko &smb->inok_polarity); 1280364bec75SDavid Heidelberg } 1281364bec75SDavid Heidelberg 1282364bec75SDavid Heidelberg static int smb347_get_battery_info(struct smb347_charger *smb) 1283364bec75SDavid Heidelberg { 1284364bec75SDavid Heidelberg struct power_supply_battery_info info = {}; 1285364bec75SDavid Heidelberg struct power_supply *supply; 1286364bec75SDavid Heidelberg int err; 1287364bec75SDavid Heidelberg 1288364bec75SDavid Heidelberg if (smb->mains) 1289364bec75SDavid Heidelberg supply = smb->mains; 1290364bec75SDavid Heidelberg else 1291364bec75SDavid Heidelberg supply = smb->usb; 1292364bec75SDavid Heidelberg 1293364bec75SDavid Heidelberg err = power_supply_get_battery_info(supply, &info); 1294364bec75SDavid Heidelberg if (err == -ENXIO || err == -ENODEV) 1295364bec75SDavid Heidelberg return 0; 1296364bec75SDavid Heidelberg if (err) 1297364bec75SDavid Heidelberg return err; 1298364bec75SDavid Heidelberg 1299364bec75SDavid Heidelberg if (info.constant_charge_current_max_ua != -EINVAL) 1300b6f3e21bSSebastian Reichel smb->max_charge_current = info.constant_charge_current_max_ua; 1301364bec75SDavid Heidelberg 1302364bec75SDavid Heidelberg if (info.constant_charge_voltage_max_uv != -EINVAL) 1303b6f3e21bSSebastian Reichel smb->max_charge_voltage = info.constant_charge_voltage_max_uv; 1304364bec75SDavid Heidelberg 1305364bec75SDavid Heidelberg if (info.precharge_current_ua != -EINVAL) 1306b6f3e21bSSebastian Reichel smb->pre_charge_current = info.precharge_current_ua; 1307364bec75SDavid Heidelberg 1308364bec75SDavid Heidelberg if (info.charge_term_current_ua != -EINVAL) 1309b6f3e21bSSebastian Reichel smb->termination_current = info.charge_term_current_ua; 1310364bec75SDavid Heidelberg 1311364bec75SDavid Heidelberg if (info.temp_alert_min != INT_MIN) 1312b6f3e21bSSebastian Reichel smb->soft_cold_temp_limit = info.temp_alert_min; 1313364bec75SDavid Heidelberg 1314364bec75SDavid Heidelberg if (info.temp_alert_max != INT_MAX) 1315b6f3e21bSSebastian Reichel smb->soft_hot_temp_limit = info.temp_alert_max; 1316364bec75SDavid Heidelberg 1317364bec75SDavid Heidelberg if (info.temp_min != INT_MIN) 1318b6f3e21bSSebastian Reichel smb->hard_cold_temp_limit = info.temp_min; 1319364bec75SDavid Heidelberg 1320364bec75SDavid Heidelberg if (info.temp_max != INT_MAX) 1321b6f3e21bSSebastian Reichel smb->hard_hot_temp_limit = info.temp_max; 1322364bec75SDavid Heidelberg 1323364bec75SDavid Heidelberg /* Suspend when battery temperature is outside hard limits */ 1324b6f3e21bSSebastian Reichel if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT || 1325b6f3e21bSSebastian Reichel smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) 1326b6f3e21bSSebastian Reichel smb->suspend_on_hard_temp_limit = true; 1327364bec75SDavid Heidelberg 1328364bec75SDavid Heidelberg return 0; 1329364bec75SDavid Heidelberg } 1330364bec75SDavid Heidelberg 1331*565efae9SDmitry Osipenko static int smb347_usb_vbus_get_current_limit(struct regulator_dev *rdev) 1332*565efae9SDmitry Osipenko { 1333*565efae9SDmitry Osipenko struct smb347_charger *smb = rdev_get_drvdata(rdev); 1334*565efae9SDmitry Osipenko unsigned int val; 1335*565efae9SDmitry Osipenko int ret; 1336*565efae9SDmitry Osipenko 1337*565efae9SDmitry Osipenko ret = regmap_read(smb->regmap, CFG_OTG, &val); 1338*565efae9SDmitry Osipenko if (ret < 0) 1339*565efae9SDmitry Osipenko return ret; 1340*565efae9SDmitry Osipenko 1341*565efae9SDmitry Osipenko /* 1342*565efae9SDmitry Osipenko * It's unknown what happens if this bit is unset due to lack of 1343*565efae9SDmitry Osipenko * access to the datasheet, assume it's limit-enable. 1344*565efae9SDmitry Osipenko */ 1345*565efae9SDmitry Osipenko if (!(val & CFG_OTG_CURRENT_LIMIT_250mA)) 1346*565efae9SDmitry Osipenko return 0; 1347*565efae9SDmitry Osipenko 1348*565efae9SDmitry Osipenko return val & CFG_OTG_CURRENT_LIMIT_750mA ? 750000 : 250000; 1349*565efae9SDmitry Osipenko } 1350*565efae9SDmitry Osipenko 1351*565efae9SDmitry Osipenko static int smb347_usb_vbus_set_new_current_limit(struct smb347_charger *smb, 1352*565efae9SDmitry Osipenko int max_uA) 1353*565efae9SDmitry Osipenko { 1354*565efae9SDmitry Osipenko const unsigned int mask = CFG_OTG_CURRENT_LIMIT_750mA | 1355*565efae9SDmitry Osipenko CFG_OTG_CURRENT_LIMIT_250mA; 1356*565efae9SDmitry Osipenko unsigned int val = CFG_OTG_CURRENT_LIMIT_250mA; 1357*565efae9SDmitry Osipenko int ret; 1358*565efae9SDmitry Osipenko 1359*565efae9SDmitry Osipenko if (max_uA >= 750000) 1360*565efae9SDmitry Osipenko val |= CFG_OTG_CURRENT_LIMIT_750mA; 1361*565efae9SDmitry Osipenko 1362*565efae9SDmitry Osipenko ret = regmap_update_bits(smb->regmap, CFG_OTG, mask, val); 1363*565efae9SDmitry Osipenko if (ret < 0) 1364*565efae9SDmitry Osipenko dev_err(smb->dev, "failed to change USB current limit\n"); 1365*565efae9SDmitry Osipenko 1366*565efae9SDmitry Osipenko return ret; 1367*565efae9SDmitry Osipenko } 1368*565efae9SDmitry Osipenko 1369*565efae9SDmitry Osipenko static int smb347_usb_vbus_set_current_limit(struct regulator_dev *rdev, 1370*565efae9SDmitry Osipenko int min_uA, int max_uA) 1371*565efae9SDmitry Osipenko { 1372*565efae9SDmitry Osipenko struct smb347_charger *smb = rdev_get_drvdata(rdev); 1373*565efae9SDmitry Osipenko int ret; 1374*565efae9SDmitry Osipenko 1375*565efae9SDmitry Osipenko ret = smb347_set_writable(smb, true, true); 1376*565efae9SDmitry Osipenko if (ret < 0) 1377*565efae9SDmitry Osipenko return ret; 1378*565efae9SDmitry Osipenko 1379*565efae9SDmitry Osipenko ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA); 1380*565efae9SDmitry Osipenko smb347_set_writable(smb, false, true); 1381*565efae9SDmitry Osipenko 1382*565efae9SDmitry Osipenko return ret; 1383*565efae9SDmitry Osipenko } 1384*565efae9SDmitry Osipenko 1385*565efae9SDmitry Osipenko static int smb347_usb_vbus_regulator_enable(struct regulator_dev *rdev) 1386*565efae9SDmitry Osipenko { 1387*565efae9SDmitry Osipenko struct smb347_charger *smb = rdev_get_drvdata(rdev); 1388*565efae9SDmitry Osipenko int ret, max_uA; 1389*565efae9SDmitry Osipenko 1390*565efae9SDmitry Osipenko ret = smb347_set_writable(smb, true, true); 1391*565efae9SDmitry Osipenko if (ret < 0) 1392*565efae9SDmitry Osipenko return ret; 1393*565efae9SDmitry Osipenko 1394*565efae9SDmitry Osipenko smb347_charging_disable(smb); 1395*565efae9SDmitry Osipenko 1396*565efae9SDmitry Osipenko if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) { 1397*565efae9SDmitry Osipenko unsigned int sysok = 0; 1398*565efae9SDmitry Osipenko 1399*565efae9SDmitry Osipenko if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_LOW) 1400*565efae9SDmitry Osipenko sysok = CFG_SYSOK_INOK_ACTIVE_HIGH; 1401*565efae9SDmitry Osipenko 1402*565efae9SDmitry Osipenko /* 1403*565efae9SDmitry Osipenko * VBUS won't be powered if INOK is active, so we need to 1404*565efae9SDmitry Osipenko * manually disable INOK on some platforms. 1405*565efae9SDmitry Osipenko */ 1406*565efae9SDmitry Osipenko ret = regmap_update_bits(smb->regmap, CFG_SYSOK, 1407*565efae9SDmitry Osipenko CFG_SYSOK_INOK_ACTIVE_HIGH, sysok); 1408*565efae9SDmitry Osipenko if (ret < 0) { 1409*565efae9SDmitry Osipenko dev_err(smb->dev, "failed to disable INOK\n"); 1410*565efae9SDmitry Osipenko goto done; 1411*565efae9SDmitry Osipenko } 1412*565efae9SDmitry Osipenko } 1413*565efae9SDmitry Osipenko 1414*565efae9SDmitry Osipenko ret = smb347_usb_vbus_get_current_limit(rdev); 1415*565efae9SDmitry Osipenko if (ret < 0) { 1416*565efae9SDmitry Osipenko dev_err(smb->dev, "failed to get USB VBUS current limit\n"); 1417*565efae9SDmitry Osipenko goto done; 1418*565efae9SDmitry Osipenko } 1419*565efae9SDmitry Osipenko 1420*565efae9SDmitry Osipenko max_uA = ret; 1421*565efae9SDmitry Osipenko 1422*565efae9SDmitry Osipenko ret = smb347_usb_vbus_set_new_current_limit(smb, 250000); 1423*565efae9SDmitry Osipenko if (ret < 0) { 1424*565efae9SDmitry Osipenko dev_err(smb->dev, "failed to preset USB VBUS current limit\n"); 1425*565efae9SDmitry Osipenko goto done; 1426*565efae9SDmitry Osipenko } 1427*565efae9SDmitry Osipenko 1428*565efae9SDmitry Osipenko ret = regmap_set_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED); 1429*565efae9SDmitry Osipenko if (ret < 0) { 1430*565efae9SDmitry Osipenko dev_err(smb->dev, "failed to enable USB VBUS\n"); 1431*565efae9SDmitry Osipenko goto done; 1432*565efae9SDmitry Osipenko } 1433*565efae9SDmitry Osipenko 1434*565efae9SDmitry Osipenko smb->usb_vbus_enabled = true; 1435*565efae9SDmitry Osipenko 1436*565efae9SDmitry Osipenko ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA); 1437*565efae9SDmitry Osipenko if (ret < 0) { 1438*565efae9SDmitry Osipenko dev_err(smb->dev, "failed to restore USB VBUS current limit\n"); 1439*565efae9SDmitry Osipenko goto done; 1440*565efae9SDmitry Osipenko } 1441*565efae9SDmitry Osipenko done: 1442*565efae9SDmitry Osipenko smb347_set_writable(smb, false, true); 1443*565efae9SDmitry Osipenko 1444*565efae9SDmitry Osipenko return ret; 1445*565efae9SDmitry Osipenko } 1446*565efae9SDmitry Osipenko 1447*565efae9SDmitry Osipenko static int smb347_usb_vbus_regulator_disable(struct regulator_dev *rdev) 1448*565efae9SDmitry Osipenko { 1449*565efae9SDmitry Osipenko struct smb347_charger *smb = rdev_get_drvdata(rdev); 1450*565efae9SDmitry Osipenko int ret; 1451*565efae9SDmitry Osipenko 1452*565efae9SDmitry Osipenko ret = smb347_set_writable(smb, true, true); 1453*565efae9SDmitry Osipenko if (ret < 0) 1454*565efae9SDmitry Osipenko return ret; 1455*565efae9SDmitry Osipenko 1456*565efae9SDmitry Osipenko ret = regmap_clear_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED); 1457*565efae9SDmitry Osipenko if (ret < 0) { 1458*565efae9SDmitry Osipenko dev_err(smb->dev, "failed to disable USB VBUS\n"); 1459*565efae9SDmitry Osipenko goto done; 1460*565efae9SDmitry Osipenko } 1461*565efae9SDmitry Osipenko 1462*565efae9SDmitry Osipenko smb->usb_vbus_enabled = false; 1463*565efae9SDmitry Osipenko 1464*565efae9SDmitry Osipenko if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) { 1465*565efae9SDmitry Osipenko unsigned int sysok = 0; 1466*565efae9SDmitry Osipenko 1467*565efae9SDmitry Osipenko if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_HIGH) 1468*565efae9SDmitry Osipenko sysok = CFG_SYSOK_INOK_ACTIVE_HIGH; 1469*565efae9SDmitry Osipenko 1470*565efae9SDmitry Osipenko ret = regmap_update_bits(smb->regmap, CFG_SYSOK, 1471*565efae9SDmitry Osipenko CFG_SYSOK_INOK_ACTIVE_HIGH, sysok); 1472*565efae9SDmitry Osipenko if (ret < 0) { 1473*565efae9SDmitry Osipenko dev_err(smb->dev, "failed to enable INOK\n"); 1474*565efae9SDmitry Osipenko goto done; 1475*565efae9SDmitry Osipenko } 1476*565efae9SDmitry Osipenko } 1477*565efae9SDmitry Osipenko 1478*565efae9SDmitry Osipenko smb347_start_stop_charging(smb); 1479*565efae9SDmitry Osipenko done: 1480*565efae9SDmitry Osipenko smb347_set_writable(smb, false, true); 1481*565efae9SDmitry Osipenko 1482*565efae9SDmitry Osipenko return ret; 1483*565efae9SDmitry Osipenko } 1484*565efae9SDmitry Osipenko 14858c0984e5SSebastian Reichel static const struct regmap_config smb347_regmap = { 14868c0984e5SSebastian Reichel .reg_bits = 8, 14878c0984e5SSebastian Reichel .val_bits = 8, 14888c0984e5SSebastian Reichel .max_register = SMB347_MAX_REGISTER, 14898c0984e5SSebastian Reichel .volatile_reg = smb347_volatile_reg, 14908c0984e5SSebastian Reichel .readable_reg = smb347_readable_reg, 149117e7bc53SDmitry Osipenko .cache_type = REGCACHE_FLAT, 149217e7bc53SDmitry Osipenko .num_reg_defaults_raw = SMB347_MAX_REGISTER, 14938c0984e5SSebastian Reichel }; 14948c0984e5SSebastian Reichel 1495*565efae9SDmitry Osipenko static const struct regulator_ops smb347_usb_vbus_regulator_ops = { 1496*565efae9SDmitry Osipenko .is_enabled = regulator_is_enabled_regmap, 1497*565efae9SDmitry Osipenko .enable = smb347_usb_vbus_regulator_enable, 1498*565efae9SDmitry Osipenko .disable = smb347_usb_vbus_regulator_disable, 1499*565efae9SDmitry Osipenko .get_current_limit = smb347_usb_vbus_get_current_limit, 1500*565efae9SDmitry Osipenko .set_current_limit = smb347_usb_vbus_set_current_limit, 1501*565efae9SDmitry Osipenko }; 1502*565efae9SDmitry Osipenko 15038c0984e5SSebastian Reichel static const struct power_supply_desc smb347_mains_desc = { 15048c0984e5SSebastian Reichel .name = "smb347-mains", 15058c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_MAINS, 1506db14d3b4SDavid Heidelberg .get_property = smb347_get_property, 1507db14d3b4SDavid Heidelberg .properties = smb347_properties, 1508db14d3b4SDavid Heidelberg .num_properties = ARRAY_SIZE(smb347_properties), 15098c0984e5SSebastian Reichel }; 15108c0984e5SSebastian Reichel 15118c0984e5SSebastian Reichel static const struct power_supply_desc smb347_usb_desc = { 15128c0984e5SSebastian Reichel .name = "smb347-usb", 15138c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_USB, 1514db14d3b4SDavid Heidelberg .get_property = smb347_get_property, 1515db14d3b4SDavid Heidelberg .properties = smb347_properties, 1516db14d3b4SDavid Heidelberg .num_properties = ARRAY_SIZE(smb347_properties), 15178c0984e5SSebastian Reichel }; 15188c0984e5SSebastian Reichel 1519*565efae9SDmitry Osipenko static const struct regulator_desc smb347_usb_vbus_regulator_desc = { 1520*565efae9SDmitry Osipenko .name = "smb347-usb-vbus", 1521*565efae9SDmitry Osipenko .of_match = of_match_ptr("usb-vbus"), 1522*565efae9SDmitry Osipenko .ops = &smb347_usb_vbus_regulator_ops, 1523*565efae9SDmitry Osipenko .type = REGULATOR_VOLTAGE, 1524*565efae9SDmitry Osipenko .owner = THIS_MODULE, 1525*565efae9SDmitry Osipenko .enable_reg = CMD_A, 1526*565efae9SDmitry Osipenko .enable_mask = CMD_A_OTG_ENABLED, 1527*565efae9SDmitry Osipenko .enable_val = CMD_A_OTG_ENABLED, 1528*565efae9SDmitry Osipenko .fixed_uV = 5000000, 1529*565efae9SDmitry Osipenko .n_voltages = 1, 1530*565efae9SDmitry Osipenko }; 1531*565efae9SDmitry Osipenko 15328c0984e5SSebastian Reichel static int smb347_probe(struct i2c_client *client, 15338c0984e5SSebastian Reichel const struct i2c_device_id *id) 15348c0984e5SSebastian Reichel { 1535db14d3b4SDavid Heidelberg struct power_supply_config mains_usb_cfg = {}; 1536*565efae9SDmitry Osipenko struct regulator_config usb_rdev_cfg = {}; 15378c0984e5SSebastian Reichel struct device *dev = &client->dev; 15388c0984e5SSebastian Reichel struct smb347_charger *smb; 15398c0984e5SSebastian Reichel int ret; 15408c0984e5SSebastian Reichel 15418c0984e5SSebastian Reichel smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL); 15428c0984e5SSebastian Reichel if (!smb) 15438c0984e5SSebastian Reichel return -ENOMEM; 15448c0984e5SSebastian Reichel smb->dev = &client->dev; 1545de76fd29SDavid Heidelberg smb->id = id->driver_data; 1546b6f3e21bSSebastian Reichel i2c_set_clientdata(client, smb); 1547b6f3e21bSSebastian Reichel 1548b6f3e21bSSebastian Reichel smb347_dt_parse_dev_info(smb); 1549b6f3e21bSSebastian Reichel if (!smb->use_mains && !smb->use_usb) 1550b6f3e21bSSebastian Reichel return -EINVAL; 15518c0984e5SSebastian Reichel 15528c0984e5SSebastian Reichel smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap); 15538c0984e5SSebastian Reichel if (IS_ERR(smb->regmap)) 15548c0984e5SSebastian Reichel return PTR_ERR(smb->regmap); 15558c0984e5SSebastian Reichel 15568c0984e5SSebastian Reichel mains_usb_cfg.drv_data = smb; 1557364bec75SDavid Heidelberg mains_usb_cfg.of_node = dev->of_node; 1558b6f3e21bSSebastian Reichel if (smb->use_mains) { 15592d52f710SDavid Heidelberg smb->mains = devm_power_supply_register(dev, &smb347_mains_desc, 15608c0984e5SSebastian Reichel &mains_usb_cfg); 15618c0984e5SSebastian Reichel if (IS_ERR(smb->mains)) 15628c0984e5SSebastian Reichel return PTR_ERR(smb->mains); 15638c0984e5SSebastian Reichel } 15648c0984e5SSebastian Reichel 1565b6f3e21bSSebastian Reichel if (smb->use_usb) { 15662d52f710SDavid Heidelberg smb->usb = devm_power_supply_register(dev, &smb347_usb_desc, 15678c0984e5SSebastian Reichel &mains_usb_cfg); 15682d52f710SDavid Heidelberg if (IS_ERR(smb->usb)) 15698c0984e5SSebastian Reichel return PTR_ERR(smb->usb); 15708c0984e5SSebastian Reichel } 15718c0984e5SSebastian Reichel 1572364bec75SDavid Heidelberg ret = smb347_get_battery_info(smb); 1573364bec75SDavid Heidelberg if (ret) 1574364bec75SDavid Heidelberg return ret; 1575364bec75SDavid Heidelberg 1576364bec75SDavid Heidelberg ret = smb347_hw_init(smb); 1577364bec75SDavid Heidelberg if (ret < 0) 1578364bec75SDavid Heidelberg return ret; 1579364bec75SDavid Heidelberg 15808c0984e5SSebastian Reichel ret = smb347_irq_init(smb, client); 1581d33b3f7eSDmitry Osipenko if (ret) 1582d33b3f7eSDmitry Osipenko return ret; 15838c0984e5SSebastian Reichel 1584*565efae9SDmitry Osipenko usb_rdev_cfg.dev = dev; 1585*565efae9SDmitry Osipenko usb_rdev_cfg.driver_data = smb; 1586*565efae9SDmitry Osipenko usb_rdev_cfg.regmap = smb->regmap; 1587*565efae9SDmitry Osipenko 1588*565efae9SDmitry Osipenko smb->usb_rdev = devm_regulator_register(dev, 1589*565efae9SDmitry Osipenko &smb347_usb_vbus_regulator_desc, 1590*565efae9SDmitry Osipenko &usb_rdev_cfg); 1591*565efae9SDmitry Osipenko if (IS_ERR(smb->usb_rdev)) { 1592*565efae9SDmitry Osipenko smb347_irq_disable(smb); 1593*565efae9SDmitry Osipenko return PTR_ERR(smb->usb_rdev); 1594*565efae9SDmitry Osipenko } 1595*565efae9SDmitry Osipenko 15968c0984e5SSebastian Reichel return 0; 15978c0984e5SSebastian Reichel } 15988c0984e5SSebastian Reichel 15998c0984e5SSebastian Reichel static int smb347_remove(struct i2c_client *client) 16008c0984e5SSebastian Reichel { 16018c0984e5SSebastian Reichel struct smb347_charger *smb = i2c_get_clientdata(client); 16028c0984e5SSebastian Reichel 1603*565efae9SDmitry Osipenko smb347_usb_vbus_regulator_disable(smb->usb_rdev); 16048c0984e5SSebastian Reichel smb347_irq_disable(smb); 160569963126SDmitry Osipenko 16068c0984e5SSebastian Reichel return 0; 16078c0984e5SSebastian Reichel } 16088c0984e5SSebastian Reichel 1609*565efae9SDmitry Osipenko static void smb347_shutdown(struct i2c_client *client) 1610*565efae9SDmitry Osipenko { 1611*565efae9SDmitry Osipenko smb347_remove(client); 1612*565efae9SDmitry Osipenko } 1613*565efae9SDmitry Osipenko 16148c0984e5SSebastian Reichel static const struct i2c_device_id smb347_id[] = { 1615de76fd29SDavid Heidelberg { "smb345", SMB345 }, 1616de76fd29SDavid Heidelberg { "smb347", SMB347 }, 1617de76fd29SDavid Heidelberg { "smb358", SMB358 }, 1618de76fd29SDavid Heidelberg { }, 16198c0984e5SSebastian Reichel }; 16208c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, smb347_id); 16218c0984e5SSebastian Reichel 1622364bec75SDavid Heidelberg static const struct of_device_id smb3xx_of_match[] = { 1623de76fd29SDavid Heidelberg { .compatible = "summit,smb345" }, 1624364bec75SDavid Heidelberg { .compatible = "summit,smb347" }, 1625de76fd29SDavid Heidelberg { .compatible = "summit,smb358" }, 1626364bec75SDavid Heidelberg { }, 1627364bec75SDavid Heidelberg }; 1628364bec75SDavid Heidelberg MODULE_DEVICE_TABLE(of, smb3xx_of_match); 1629364bec75SDavid Heidelberg 16308c0984e5SSebastian Reichel static struct i2c_driver smb347_driver = { 16318c0984e5SSebastian Reichel .driver = { 16328c0984e5SSebastian Reichel .name = "smb347", 1633364bec75SDavid Heidelberg .of_match_table = smb3xx_of_match, 16348c0984e5SSebastian Reichel }, 16358c0984e5SSebastian Reichel .probe = smb347_probe, 16368c0984e5SSebastian Reichel .remove = smb347_remove, 1637*565efae9SDmitry Osipenko .shutdown = smb347_shutdown, 16388c0984e5SSebastian Reichel .id_table = smb347_id, 16398c0984e5SSebastian Reichel }; 16408c0984e5SSebastian Reichel module_i2c_driver(smb347_driver); 16418c0984e5SSebastian Reichel 16428c0984e5SSebastian Reichel MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>"); 16438c0984e5SSebastian Reichel MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); 16448c0984e5SSebastian Reichel MODULE_DESCRIPTION("SMB347 battery charger driver"); 16458c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 1646