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/gpio.h> 148c0984e5SSebastian Reichel #include <linux/kernel.h> 158c0984e5SSebastian Reichel #include <linux/module.h> 168c0984e5SSebastian Reichel #include <linux/init.h> 178c0984e5SSebastian Reichel #include <linux/interrupt.h> 188c0984e5SSebastian Reichel #include <linux/i2c.h> 198c0984e5SSebastian Reichel #include <linux/power_supply.h> 20f385e2fcSSebastian Reichel #include <linux/property.h> 218c0984e5SSebastian Reichel #include <linux/regmap.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) 598c0984e5SSebastian Reichel #define CFG_THERM 0x07 608c0984e5SSebastian Reichel #define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03 618c0984e5SSebastian Reichel #define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0 628c0984e5SSebastian Reichel #define CFG_THERM_SOFT_COLD_COMPENSATION_MASK 0x0c 638c0984e5SSebastian Reichel #define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2 648c0984e5SSebastian Reichel #define CFG_THERM_MONITOR_DISABLED BIT(4) 658c0984e5SSebastian Reichel #define CFG_SYSOK 0x08 668c0984e5SSebastian Reichel #define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2) 678c0984e5SSebastian Reichel #define CFG_OTHER 0x09 688c0984e5SSebastian Reichel #define CFG_OTHER_RID_MASK 0xc0 698c0984e5SSebastian Reichel #define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0 708c0984e5SSebastian Reichel #define CFG_OTG 0x0a 718c0984e5SSebastian Reichel #define CFG_OTG_TEMP_THRESHOLD_MASK 0x30 728c0984e5SSebastian Reichel #define CFG_OTG_TEMP_THRESHOLD_SHIFT 4 738c0984e5SSebastian Reichel #define CFG_OTG_CC_COMPENSATION_MASK 0xc0 748c0984e5SSebastian Reichel #define CFG_OTG_CC_COMPENSATION_SHIFT 6 758c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT 0x0b 768c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_HOT_MASK 0x03 778c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT 0 788c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_COLD_MASK 0x0c 798c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT 2 808c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_HOT_MASK 0x30 818c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_HOT_SHIFT 4 828c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_COLD_MASK 0xc0 838c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_COLD_SHIFT 6 848c0984e5SSebastian Reichel #define CFG_FAULT_IRQ 0x0c 858c0984e5SSebastian Reichel #define CFG_FAULT_IRQ_DCIN_UV BIT(2) 868c0984e5SSebastian Reichel #define CFG_STATUS_IRQ 0x0d 878c0984e5SSebastian Reichel #define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4) 888c0984e5SSebastian Reichel #define CFG_STATUS_IRQ_CHARGE_TIMEOUT BIT(7) 898c0984e5SSebastian Reichel #define CFG_ADDRESS 0x0e 908c0984e5SSebastian Reichel 918c0984e5SSebastian Reichel /* Command registers */ 928c0984e5SSebastian Reichel #define CMD_A 0x30 938c0984e5SSebastian Reichel #define CMD_A_CHG_ENABLED BIT(1) 948c0984e5SSebastian Reichel #define CMD_A_SUSPEND_ENABLED BIT(2) 958c0984e5SSebastian Reichel #define CMD_A_ALLOW_WRITE BIT(7) 968c0984e5SSebastian Reichel #define CMD_B 0x31 978c0984e5SSebastian Reichel #define CMD_C 0x33 988c0984e5SSebastian Reichel 998c0984e5SSebastian Reichel /* Interrupt Status registers */ 1008c0984e5SSebastian Reichel #define IRQSTAT_A 0x35 1018c0984e5SSebastian Reichel #define IRQSTAT_C 0x37 1028c0984e5SSebastian Reichel #define IRQSTAT_C_TERMINATION_STAT BIT(0) 1038c0984e5SSebastian Reichel #define IRQSTAT_C_TERMINATION_IRQ BIT(1) 1048c0984e5SSebastian Reichel #define IRQSTAT_C_TAPER_IRQ BIT(3) 1058c0984e5SSebastian Reichel #define IRQSTAT_D 0x38 1068c0984e5SSebastian Reichel #define IRQSTAT_D_CHARGE_TIMEOUT_STAT BIT(2) 1078c0984e5SSebastian Reichel #define IRQSTAT_D_CHARGE_TIMEOUT_IRQ BIT(3) 1088c0984e5SSebastian Reichel #define IRQSTAT_E 0x39 1098c0984e5SSebastian Reichel #define IRQSTAT_E_USBIN_UV_STAT BIT(0) 1108c0984e5SSebastian Reichel #define IRQSTAT_E_USBIN_UV_IRQ BIT(1) 1118c0984e5SSebastian Reichel #define IRQSTAT_E_DCIN_UV_STAT BIT(4) 1128c0984e5SSebastian Reichel #define IRQSTAT_E_DCIN_UV_IRQ BIT(5) 1138c0984e5SSebastian Reichel #define IRQSTAT_F 0x3a 1148c0984e5SSebastian Reichel 1158c0984e5SSebastian Reichel /* Status registers */ 1168c0984e5SSebastian Reichel #define STAT_A 0x3b 1178c0984e5SSebastian Reichel #define STAT_A_FLOAT_VOLTAGE_MASK 0x3f 1188c0984e5SSebastian Reichel #define STAT_B 0x3c 1198c0984e5SSebastian Reichel #define STAT_C 0x3d 1208c0984e5SSebastian Reichel #define STAT_C_CHG_ENABLED BIT(0) 1218c0984e5SSebastian Reichel #define STAT_C_HOLDOFF_STAT BIT(3) 1228c0984e5SSebastian Reichel #define STAT_C_CHG_MASK 0x06 1238c0984e5SSebastian Reichel #define STAT_C_CHG_SHIFT 1 1248c0984e5SSebastian Reichel #define STAT_C_CHG_TERM BIT(5) 1258c0984e5SSebastian Reichel #define STAT_C_CHARGER_ERROR BIT(6) 1268c0984e5SSebastian Reichel #define STAT_E 0x3f 1278c0984e5SSebastian Reichel 1288c0984e5SSebastian Reichel #define SMB347_MAX_REGISTER 0x3f 1298c0984e5SSebastian Reichel 1308c0984e5SSebastian Reichel /** 1318c0984e5SSebastian Reichel * struct smb347_charger - smb347 charger instance 1328c0984e5SSebastian Reichel * @dev: pointer to device 1338c0984e5SSebastian Reichel * @regmap: pointer to driver regmap 1348c0984e5SSebastian Reichel * @mains: power_supply instance for AC/DC power 1358c0984e5SSebastian Reichel * @usb: power_supply instance for USB power 136de76fd29SDavid Heidelberg * @id: SMB charger ID 1378c0984e5SSebastian Reichel * @mains_online: is AC/DC input connected 1388c0984e5SSebastian Reichel * @usb_online: is USB input connected 1398c0984e5SSebastian Reichel * @charging_enabled: is charging enabled 140b6f3e21bSSebastian Reichel * @max_charge_current: maximum current (in uA) the battery can be charged 141b6f3e21bSSebastian Reichel * @max_charge_voltage: maximum voltage (in uV) the battery can be charged 142b6f3e21bSSebastian Reichel * @pre_charge_current: current (in uA) to use in pre-charging phase 143b6f3e21bSSebastian Reichel * @termination_current: current (in uA) used to determine when the 144b6f3e21bSSebastian Reichel * charging cycle terminates 145b6f3e21bSSebastian Reichel * @pre_to_fast_voltage: voltage (in uV) treshold used for transitioning to 146b6f3e21bSSebastian Reichel * pre-charge to fast charge mode 147b6f3e21bSSebastian Reichel * @mains_current_limit: maximum input current drawn from AC/DC input (in uA) 148b6f3e21bSSebastian Reichel * @usb_hc_current_limit: maximum input high current (in uA) drawn from USB 149b6f3e21bSSebastian Reichel * input 150b6f3e21bSSebastian Reichel * @chip_temp_threshold: die temperature where device starts limiting charge 151b6f3e21bSSebastian Reichel * current [%100 - %130] (in degree C) 152b6f3e21bSSebastian Reichel * @soft_cold_temp_limit: soft cold temperature limit [%0 - %15] (in degree C), 153b6f3e21bSSebastian Reichel * granularity is 5 deg C. 154b6f3e21bSSebastian Reichel * @soft_hot_temp_limit: soft hot temperature limit [%40 - %55] (in degree C), 155b6f3e21bSSebastian Reichel * granularity is 5 deg C. 156b6f3e21bSSebastian Reichel * @hard_cold_temp_limit: hard cold temperature limit [%-5 - %10] (in degree C), 157b6f3e21bSSebastian Reichel * granularity is 5 deg C. 158b6f3e21bSSebastian Reichel * @hard_hot_temp_limit: hard hot temperature limit [%50 - %65] (in degree C), 159b6f3e21bSSebastian Reichel * granularity is 5 deg C. 160b6f3e21bSSebastian Reichel * @suspend_on_hard_temp_limit: suspend charging when hard limit is hit 161b6f3e21bSSebastian Reichel * @soft_temp_limit_compensation: compensation method when soft temperature 162b6f3e21bSSebastian Reichel * limit is hit 163b6f3e21bSSebastian Reichel * @charge_current_compensation: current (in uA) for charging compensation 164b6f3e21bSSebastian Reichel * current when temperature hits soft limits 165b6f3e21bSSebastian Reichel * @use_mains: AC/DC input can be used 166b6f3e21bSSebastian Reichel * @use_usb: USB input can be used 167b6f3e21bSSebastian Reichel * @use_usb_otg: USB OTG output can be used (not implemented yet) 168b6f3e21bSSebastian Reichel * @enable_control: how charging enable/disable is controlled 169b6f3e21bSSebastian Reichel * (driver/pin controls) 170b6f3e21bSSebastian Reichel * 171b6f3e21bSSebastian Reichel * @use_main, @use_usb, and @use_usb_otg are means to enable/disable 172b6f3e21bSSebastian Reichel * hardware support for these. This is useful when we want to have for 173b6f3e21bSSebastian Reichel * example OTG charging controlled via OTG transceiver driver and not by 174b6f3e21bSSebastian Reichel * the SMB347 hardware. 175b6f3e21bSSebastian Reichel * 176b6f3e21bSSebastian Reichel * Hard and soft temperature limit values are given as described in the 177b6f3e21bSSebastian Reichel * device data sheet and assuming NTC beta value is %3750. Even if this is 178b6f3e21bSSebastian Reichel * not the case, these values should be used. They can be mapped to the 179b6f3e21bSSebastian Reichel * corresponding NTC beta values with the help of table %2 in the data 180b6f3e21bSSebastian Reichel * sheet. So for example if NTC beta is %3375 and we want to program hard 181b6f3e21bSSebastian Reichel * hot limit to be %53 deg C, @hard_hot_temp_limit should be set to %50. 182b6f3e21bSSebastian Reichel * 183b6f3e21bSSebastian Reichel * If zero value is given in any of the current and voltage values, the 184b6f3e21bSSebastian Reichel * factory programmed default will be used. For soft/hard temperature 185b6f3e21bSSebastian Reichel * values, pass in %SMB3XX_TEMP_USE_DEFAULT instead. 1868c0984e5SSebastian Reichel */ 1878c0984e5SSebastian Reichel struct smb347_charger { 1888c0984e5SSebastian Reichel struct device *dev; 1898c0984e5SSebastian Reichel struct regmap *regmap; 1908c0984e5SSebastian Reichel struct power_supply *mains; 1918c0984e5SSebastian Reichel struct power_supply *usb; 192de76fd29SDavid Heidelberg unsigned int id; 1938c0984e5SSebastian Reichel bool mains_online; 1948c0984e5SSebastian Reichel bool usb_online; 1958c0984e5SSebastian Reichel bool charging_enabled; 196b6f3e21bSSebastian Reichel 197b6f3e21bSSebastian Reichel unsigned int max_charge_current; 198b6f3e21bSSebastian Reichel unsigned int max_charge_voltage; 199b6f3e21bSSebastian Reichel unsigned int pre_charge_current; 200b6f3e21bSSebastian Reichel unsigned int termination_current; 201b6f3e21bSSebastian Reichel unsigned int pre_to_fast_voltage; 202b6f3e21bSSebastian Reichel unsigned int mains_current_limit; 203b6f3e21bSSebastian Reichel unsigned int usb_hc_current_limit; 204b6f3e21bSSebastian Reichel unsigned int chip_temp_threshold; 205b6f3e21bSSebastian Reichel int soft_cold_temp_limit; 206b6f3e21bSSebastian Reichel int soft_hot_temp_limit; 207b6f3e21bSSebastian Reichel int hard_cold_temp_limit; 208b6f3e21bSSebastian Reichel int hard_hot_temp_limit; 209b6f3e21bSSebastian Reichel bool suspend_on_hard_temp_limit; 210b6f3e21bSSebastian Reichel unsigned int soft_temp_limit_compensation; 211b6f3e21bSSebastian Reichel unsigned int charge_current_compensation; 212b6f3e21bSSebastian Reichel bool use_mains; 213b6f3e21bSSebastian Reichel bool use_usb; 214b6f3e21bSSebastian Reichel bool use_usb_otg; 215b6f3e21bSSebastian Reichel unsigned int enable_control; 2168c0984e5SSebastian Reichel }; 2178c0984e5SSebastian Reichel 218de76fd29SDavid Heidelberg enum smb_charger_chipid { 219de76fd29SDavid Heidelberg SMB345, 220de76fd29SDavid Heidelberg SMB347, 221de76fd29SDavid Heidelberg SMB358, 222de76fd29SDavid Heidelberg NUM_CHIP_TYPES, 2238c0984e5SSebastian Reichel }; 2248c0984e5SSebastian Reichel 225de76fd29SDavid Heidelberg /* Fast charge current in uA */ 226de76fd29SDavid Heidelberg static const unsigned int fcc_tbl[NUM_CHIP_TYPES][8] = { 227de76fd29SDavid Heidelberg [SMB345] = { 200000, 450000, 600000, 900000, 228de76fd29SDavid Heidelberg 1300000, 1500000, 1800000, 2000000 }, 229de76fd29SDavid Heidelberg [SMB347] = { 700000, 900000, 1200000, 1500000, 230de76fd29SDavid Heidelberg 1800000, 2000000, 2200000, 2500000 }, 231de76fd29SDavid Heidelberg [SMB358] = { 200000, 450000, 600000, 900000, 232de76fd29SDavid Heidelberg 1300000, 1500000, 1800000, 2000000 }, 233de76fd29SDavid Heidelberg }; 2348c0984e5SSebastian Reichel /* Pre-charge current in uA */ 235de76fd29SDavid Heidelberg static const unsigned int pcc_tbl[NUM_CHIP_TYPES][4] = { 236de76fd29SDavid Heidelberg [SMB345] = { 150000, 250000, 350000, 450000 }, 237de76fd29SDavid Heidelberg [SMB347] = { 100000, 150000, 200000, 250000 }, 238de76fd29SDavid Heidelberg [SMB358] = { 150000, 250000, 350000, 450000 }, 2398c0984e5SSebastian Reichel }; 2408c0984e5SSebastian Reichel 2418c0984e5SSebastian Reichel /* Termination current in uA */ 242de76fd29SDavid Heidelberg static const unsigned int tc_tbl[NUM_CHIP_TYPES][8] = { 243de76fd29SDavid Heidelberg [SMB345] = { 30000, 40000, 60000, 80000, 244de76fd29SDavid Heidelberg 100000, 125000, 150000, 200000 }, 245de76fd29SDavid Heidelberg [SMB347] = { 37500, 50000, 100000, 150000, 246de76fd29SDavid Heidelberg 200000, 250000, 500000, 600000 }, 247de76fd29SDavid Heidelberg [SMB358] = { 30000, 40000, 60000, 80000, 248de76fd29SDavid Heidelberg 100000, 125000, 150000, 200000 }, 2498c0984e5SSebastian Reichel }; 2508c0984e5SSebastian Reichel 2518c0984e5SSebastian Reichel /* Input current limit in uA */ 252de76fd29SDavid Heidelberg static const unsigned int icl_tbl[NUM_CHIP_TYPES][10] = { 253de76fd29SDavid Heidelberg [SMB345] = { 300000, 500000, 700000, 1000000, 1500000, 254de76fd29SDavid Heidelberg 1800000, 2000000, 2000000, 2000000, 2000000 }, 255de76fd29SDavid Heidelberg [SMB347] = { 300000, 500000, 700000, 900000, 1200000, 256de76fd29SDavid Heidelberg 1500000, 1800000, 2000000, 2200000, 2500000 }, 257de76fd29SDavid Heidelberg [SMB358] = { 300000, 500000, 700000, 1000000, 1500000, 258de76fd29SDavid Heidelberg 1800000, 2000000, 2000000, 2000000, 2000000 }, 2598c0984e5SSebastian Reichel }; 2608c0984e5SSebastian Reichel 2618c0984e5SSebastian Reichel /* Charge current compensation in uA */ 262de76fd29SDavid Heidelberg static const unsigned int ccc_tbl[NUM_CHIP_TYPES][4] = { 263de76fd29SDavid Heidelberg [SMB345] = { 200000, 450000, 600000, 900000 }, 264de76fd29SDavid Heidelberg [SMB347] = { 250000, 700000, 900000, 1200000 }, 265de76fd29SDavid Heidelberg [SMB358] = { 200000, 450000, 600000, 900000 }, 2668c0984e5SSebastian Reichel }; 2678c0984e5SSebastian Reichel 2688c0984e5SSebastian Reichel /* Convert register value to current using lookup table */ 2698c0984e5SSebastian Reichel static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val) 2708c0984e5SSebastian Reichel { 2718c0984e5SSebastian Reichel if (val >= size) 2728c0984e5SSebastian Reichel return -EINVAL; 2738c0984e5SSebastian Reichel return tbl[val]; 2748c0984e5SSebastian Reichel } 2758c0984e5SSebastian Reichel 2768c0984e5SSebastian Reichel /* Convert current to register value using lookup table */ 2778c0984e5SSebastian Reichel static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val) 2788c0984e5SSebastian Reichel { 2798c0984e5SSebastian Reichel size_t i; 2808c0984e5SSebastian Reichel 2818c0984e5SSebastian Reichel for (i = 0; i < size; i++) 2828c0984e5SSebastian Reichel if (val < tbl[i]) 2838c0984e5SSebastian Reichel break; 2848c0984e5SSebastian Reichel return i > 0 ? i - 1 : -EINVAL; 2858c0984e5SSebastian Reichel } 2868c0984e5SSebastian Reichel 2878c0984e5SSebastian Reichel /** 2888c0984e5SSebastian Reichel * smb347_update_ps_status - refreshes the power source status 2898c0984e5SSebastian Reichel * @smb: pointer to smb347 charger instance 2908c0984e5SSebastian Reichel * 2918c0984e5SSebastian Reichel * Function checks whether any power source is connected to the charger and 2928c0984e5SSebastian Reichel * updates internal state accordingly. If there is a change to previous state 2938c0984e5SSebastian Reichel * function returns %1, otherwise %0 and negative errno in case of errror. 2948c0984e5SSebastian Reichel */ 2958c0984e5SSebastian Reichel static int smb347_update_ps_status(struct smb347_charger *smb) 2968c0984e5SSebastian Reichel { 2978c0984e5SSebastian Reichel bool usb = false; 2988c0984e5SSebastian Reichel bool dc = false; 2998c0984e5SSebastian Reichel unsigned int val; 3008c0984e5SSebastian Reichel int ret; 3018c0984e5SSebastian Reichel 3028c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, IRQSTAT_E, &val); 3038c0984e5SSebastian Reichel if (ret < 0) 3048c0984e5SSebastian Reichel return ret; 3058c0984e5SSebastian Reichel 3068c0984e5SSebastian Reichel /* 3078c0984e5SSebastian Reichel * Dc and usb are set depending on whether they are enabled in 3088c0984e5SSebastian Reichel * platform data _and_ whether corresponding undervoltage is set. 3098c0984e5SSebastian Reichel */ 310b6f3e21bSSebastian Reichel if (smb->use_mains) 3118c0984e5SSebastian Reichel dc = !(val & IRQSTAT_E_DCIN_UV_STAT); 312b6f3e21bSSebastian Reichel if (smb->use_usb) 3138c0984e5SSebastian Reichel usb = !(val & IRQSTAT_E_USBIN_UV_STAT); 3148c0984e5SSebastian Reichel 3158c0984e5SSebastian Reichel ret = smb->mains_online != dc || smb->usb_online != usb; 3168c0984e5SSebastian Reichel smb->mains_online = dc; 3178c0984e5SSebastian Reichel smb->usb_online = usb; 3188c0984e5SSebastian Reichel 3198c0984e5SSebastian Reichel return ret; 3208c0984e5SSebastian Reichel } 3218c0984e5SSebastian Reichel 3228c0984e5SSebastian Reichel /* 3238c0984e5SSebastian Reichel * smb347_is_ps_online - returns whether input power source is connected 3248c0984e5SSebastian Reichel * @smb: pointer to smb347 charger instance 3258c0984e5SSebastian Reichel * 3268c0984e5SSebastian Reichel * Returns %true if input power source is connected. Note that this is 3278c0984e5SSebastian Reichel * dependent on what platform has configured for usable power sources. For 3288c0984e5SSebastian Reichel * example if USB is disabled, this will return %false even if the USB cable 3298c0984e5SSebastian Reichel * is connected. 3308c0984e5SSebastian Reichel */ 3318c0984e5SSebastian Reichel static bool smb347_is_ps_online(struct smb347_charger *smb) 3328c0984e5SSebastian Reichel { 33399298de5SDmitry Osipenko return smb->usb_online || smb->mains_online; 3348c0984e5SSebastian Reichel } 3358c0984e5SSebastian Reichel 3368c0984e5SSebastian Reichel /** 3378c0984e5SSebastian Reichel * smb347_charging_status - returns status of charging 3388c0984e5SSebastian Reichel * @smb: pointer to smb347 charger instance 3398c0984e5SSebastian Reichel * 3408c0984e5SSebastian Reichel * Function returns charging status. %0 means no charging is in progress, 3418c0984e5SSebastian Reichel * %1 means pre-charging, %2 fast-charging and %3 taper-charging. 3428c0984e5SSebastian Reichel */ 3438c0984e5SSebastian Reichel static int smb347_charging_status(struct smb347_charger *smb) 3448c0984e5SSebastian Reichel { 3458c0984e5SSebastian Reichel unsigned int val; 3468c0984e5SSebastian Reichel int ret; 3478c0984e5SSebastian Reichel 3488c0984e5SSebastian Reichel if (!smb347_is_ps_online(smb)) 3498c0984e5SSebastian Reichel return 0; 3508c0984e5SSebastian Reichel 3518c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_C, &val); 3528c0984e5SSebastian Reichel if (ret < 0) 3538c0984e5SSebastian Reichel return 0; 3548c0984e5SSebastian Reichel 3558c0984e5SSebastian Reichel return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT; 3568c0984e5SSebastian Reichel } 3578c0984e5SSebastian Reichel 3588c0984e5SSebastian Reichel static int smb347_charging_set(struct smb347_charger *smb, bool enable) 3598c0984e5SSebastian Reichel { 3608c0984e5SSebastian Reichel int ret = 0; 3618c0984e5SSebastian Reichel 362b6f3e21bSSebastian Reichel if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) { 3638c0984e5SSebastian Reichel dev_dbg(smb->dev, "charging enable/disable in SW disabled\n"); 3648c0984e5SSebastian Reichel return 0; 3658c0984e5SSebastian Reichel } 3668c0984e5SSebastian Reichel 3678c0984e5SSebastian Reichel if (smb->charging_enabled != enable) { 3688c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, 3698c0984e5SSebastian Reichel enable ? CMD_A_CHG_ENABLED : 0); 3708c0984e5SSebastian Reichel if (!ret) 3718c0984e5SSebastian Reichel smb->charging_enabled = enable; 3728c0984e5SSebastian Reichel } 37399298de5SDmitry Osipenko 3748c0984e5SSebastian Reichel return ret; 3758c0984e5SSebastian Reichel } 3768c0984e5SSebastian Reichel 3778c0984e5SSebastian Reichel static inline int smb347_charging_enable(struct smb347_charger *smb) 3788c0984e5SSebastian Reichel { 3798c0984e5SSebastian Reichel return smb347_charging_set(smb, true); 3808c0984e5SSebastian Reichel } 3818c0984e5SSebastian Reichel 3828c0984e5SSebastian Reichel static inline int smb347_charging_disable(struct smb347_charger *smb) 3838c0984e5SSebastian Reichel { 3848c0984e5SSebastian Reichel return smb347_charging_set(smb, false); 3858c0984e5SSebastian Reichel } 3868c0984e5SSebastian Reichel 3878c0984e5SSebastian Reichel static int smb347_start_stop_charging(struct smb347_charger *smb) 3888c0984e5SSebastian Reichel { 3898c0984e5SSebastian Reichel int ret; 3908c0984e5SSebastian Reichel 3918c0984e5SSebastian Reichel /* 3928c0984e5SSebastian Reichel * Depending on whether valid power source is connected or not, we 3938c0984e5SSebastian Reichel * disable or enable the charging. We do it manually because it 3948c0984e5SSebastian Reichel * depends on how the platform has configured the valid inputs. 3958c0984e5SSebastian Reichel */ 3968c0984e5SSebastian Reichel if (smb347_is_ps_online(smb)) { 3978c0984e5SSebastian Reichel ret = smb347_charging_enable(smb); 3988c0984e5SSebastian Reichel if (ret < 0) 3998c0984e5SSebastian Reichel dev_err(smb->dev, "failed to enable charging\n"); 4008c0984e5SSebastian Reichel } else { 4018c0984e5SSebastian Reichel ret = smb347_charging_disable(smb); 4028c0984e5SSebastian Reichel if (ret < 0) 4038c0984e5SSebastian Reichel dev_err(smb->dev, "failed to disable charging\n"); 4048c0984e5SSebastian Reichel } 4058c0984e5SSebastian Reichel 4068c0984e5SSebastian Reichel return ret; 4078c0984e5SSebastian Reichel } 4088c0984e5SSebastian Reichel 4098c0984e5SSebastian Reichel static int smb347_set_charge_current(struct smb347_charger *smb) 4108c0984e5SSebastian Reichel { 411de76fd29SDavid Heidelberg unsigned int id = smb->id; 4128c0984e5SSebastian Reichel int ret; 4138c0984e5SSebastian Reichel 414b6f3e21bSSebastian Reichel if (smb->max_charge_current) { 415de76fd29SDavid Heidelberg ret = current_to_hw(fcc_tbl[id], ARRAY_SIZE(fcc_tbl[id]), 416b6f3e21bSSebastian Reichel smb->max_charge_current); 4178c0984e5SSebastian Reichel if (ret < 0) 4188c0984e5SSebastian Reichel return ret; 4198c0984e5SSebastian Reichel 4208c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4218c0984e5SSebastian Reichel CFG_CHARGE_CURRENT_FCC_MASK, 4228c0984e5SSebastian Reichel ret << CFG_CHARGE_CURRENT_FCC_SHIFT); 4238c0984e5SSebastian Reichel if (ret < 0) 4248c0984e5SSebastian Reichel return ret; 4258c0984e5SSebastian Reichel } 4268c0984e5SSebastian Reichel 427b6f3e21bSSebastian Reichel if (smb->pre_charge_current) { 428de76fd29SDavid Heidelberg ret = current_to_hw(pcc_tbl[id], ARRAY_SIZE(pcc_tbl[id]), 429b6f3e21bSSebastian Reichel smb->pre_charge_current); 4308c0984e5SSebastian Reichel if (ret < 0) 4318c0984e5SSebastian Reichel return ret; 4328c0984e5SSebastian Reichel 4338c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4348c0984e5SSebastian Reichel CFG_CHARGE_CURRENT_PCC_MASK, 4358c0984e5SSebastian Reichel ret << CFG_CHARGE_CURRENT_PCC_SHIFT); 4368c0984e5SSebastian Reichel if (ret < 0) 4378c0984e5SSebastian Reichel return ret; 4388c0984e5SSebastian Reichel } 4398c0984e5SSebastian Reichel 440b6f3e21bSSebastian Reichel if (smb->termination_current) { 441de76fd29SDavid Heidelberg ret = current_to_hw(tc_tbl[id], ARRAY_SIZE(tc_tbl[id]), 442b6f3e21bSSebastian Reichel smb->termination_current); 4438c0984e5SSebastian Reichel if (ret < 0) 4448c0984e5SSebastian Reichel return ret; 4458c0984e5SSebastian Reichel 4468c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT, 4478c0984e5SSebastian Reichel CFG_CHARGE_CURRENT_TC_MASK, ret); 4488c0984e5SSebastian Reichel if (ret < 0) 4498c0984e5SSebastian Reichel return ret; 4508c0984e5SSebastian Reichel } 4518c0984e5SSebastian Reichel 4528c0984e5SSebastian Reichel return 0; 4538c0984e5SSebastian Reichel } 4548c0984e5SSebastian Reichel 4558c0984e5SSebastian Reichel static int smb347_set_current_limits(struct smb347_charger *smb) 4568c0984e5SSebastian Reichel { 457de76fd29SDavid Heidelberg unsigned int id = smb->id; 4588c0984e5SSebastian Reichel int ret; 4598c0984e5SSebastian Reichel 460b6f3e21bSSebastian Reichel if (smb->mains_current_limit) { 461de76fd29SDavid Heidelberg ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), 462b6f3e21bSSebastian Reichel smb->mains_current_limit); 4638c0984e5SSebastian Reichel if (ret < 0) 4648c0984e5SSebastian Reichel return ret; 4658c0984e5SSebastian Reichel 4668c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, 4678c0984e5SSebastian Reichel CFG_CURRENT_LIMIT_DC_MASK, 4688c0984e5SSebastian Reichel ret << CFG_CURRENT_LIMIT_DC_SHIFT); 4698c0984e5SSebastian Reichel if (ret < 0) 4708c0984e5SSebastian Reichel return ret; 4718c0984e5SSebastian Reichel } 4728c0984e5SSebastian Reichel 473b6f3e21bSSebastian Reichel if (smb->usb_hc_current_limit) { 474de76fd29SDavid Heidelberg ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), 475b6f3e21bSSebastian Reichel smb->usb_hc_current_limit); 4768c0984e5SSebastian Reichel if (ret < 0) 4778c0984e5SSebastian Reichel return ret; 4788c0984e5SSebastian Reichel 4798c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT, 4808c0984e5SSebastian Reichel CFG_CURRENT_LIMIT_USB_MASK, ret); 4818c0984e5SSebastian Reichel if (ret < 0) 4828c0984e5SSebastian Reichel return ret; 4838c0984e5SSebastian Reichel } 4848c0984e5SSebastian Reichel 4858c0984e5SSebastian Reichel return 0; 4868c0984e5SSebastian Reichel } 4878c0984e5SSebastian Reichel 4888c0984e5SSebastian Reichel static int smb347_set_voltage_limits(struct smb347_charger *smb) 4898c0984e5SSebastian Reichel { 4908c0984e5SSebastian Reichel int ret; 4918c0984e5SSebastian Reichel 492b6f3e21bSSebastian Reichel if (smb->pre_to_fast_voltage) { 493b6f3e21bSSebastian Reichel ret = smb->pre_to_fast_voltage; 4948c0984e5SSebastian Reichel 4958c0984e5SSebastian Reichel /* uV */ 4968c0984e5SSebastian Reichel ret = clamp_val(ret, 2400000, 3000000) - 2400000; 4978c0984e5SSebastian Reichel ret /= 200000; 4988c0984e5SSebastian Reichel 4998c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, 5008c0984e5SSebastian Reichel CFG_FLOAT_VOLTAGE_THRESHOLD_MASK, 5018c0984e5SSebastian Reichel ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT); 5028c0984e5SSebastian Reichel if (ret < 0) 5038c0984e5SSebastian Reichel return ret; 5048c0984e5SSebastian Reichel } 5058c0984e5SSebastian Reichel 506b6f3e21bSSebastian Reichel if (smb->max_charge_voltage) { 507b6f3e21bSSebastian Reichel ret = smb->max_charge_voltage; 5088c0984e5SSebastian Reichel 5098c0984e5SSebastian Reichel /* uV */ 5108c0984e5SSebastian Reichel ret = clamp_val(ret, 3500000, 4500000) - 3500000; 5118c0984e5SSebastian Reichel ret /= 20000; 5128c0984e5SSebastian Reichel 5138c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE, 5148c0984e5SSebastian Reichel CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret); 5158c0984e5SSebastian Reichel if (ret < 0) 5168c0984e5SSebastian Reichel return ret; 5178c0984e5SSebastian Reichel } 5188c0984e5SSebastian Reichel 5198c0984e5SSebastian Reichel return 0; 5208c0984e5SSebastian Reichel } 5218c0984e5SSebastian Reichel 5228c0984e5SSebastian Reichel static int smb347_set_temp_limits(struct smb347_charger *smb) 5238c0984e5SSebastian Reichel { 524de76fd29SDavid Heidelberg unsigned int id = smb->id; 5258c0984e5SSebastian Reichel bool enable_therm_monitor = false; 5268c0984e5SSebastian Reichel int ret = 0; 5278c0984e5SSebastian Reichel int val; 5288c0984e5SSebastian Reichel 529b6f3e21bSSebastian Reichel if (smb->chip_temp_threshold) { 530b6f3e21bSSebastian Reichel val = smb->chip_temp_threshold; 5318c0984e5SSebastian Reichel 5328c0984e5SSebastian Reichel /* degree C */ 5338c0984e5SSebastian Reichel val = clamp_val(val, 100, 130) - 100; 5348c0984e5SSebastian Reichel val /= 10; 5358c0984e5SSebastian Reichel 5368c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_OTG, 5378c0984e5SSebastian Reichel CFG_OTG_TEMP_THRESHOLD_MASK, 5388c0984e5SSebastian Reichel val << CFG_OTG_TEMP_THRESHOLD_SHIFT); 5398c0984e5SSebastian Reichel if (ret < 0) 5408c0984e5SSebastian Reichel return ret; 5418c0984e5SSebastian Reichel } 5428c0984e5SSebastian Reichel 543b6f3e21bSSebastian Reichel if (smb->soft_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 544b6f3e21bSSebastian Reichel val = smb->soft_cold_temp_limit; 5458c0984e5SSebastian Reichel 5468c0984e5SSebastian Reichel val = clamp_val(val, 0, 15); 5478c0984e5SSebastian Reichel val /= 5; 5488c0984e5SSebastian Reichel /* this goes from higher to lower so invert the value */ 5498c0984e5SSebastian Reichel val = ~val & 0x3; 5508c0984e5SSebastian Reichel 5518c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5528c0984e5SSebastian Reichel CFG_TEMP_LIMIT_SOFT_COLD_MASK, 5538c0984e5SSebastian Reichel val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT); 5548c0984e5SSebastian Reichel if (ret < 0) 5558c0984e5SSebastian Reichel return ret; 5568c0984e5SSebastian Reichel 5578c0984e5SSebastian Reichel enable_therm_monitor = true; 5588c0984e5SSebastian Reichel } 5598c0984e5SSebastian Reichel 560b6f3e21bSSebastian Reichel if (smb->soft_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 561b6f3e21bSSebastian Reichel val = smb->soft_hot_temp_limit; 5628c0984e5SSebastian Reichel 5638c0984e5SSebastian Reichel val = clamp_val(val, 40, 55) - 40; 5648c0984e5SSebastian Reichel val /= 5; 5658c0984e5SSebastian Reichel 5668c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5678c0984e5SSebastian Reichel CFG_TEMP_LIMIT_SOFT_HOT_MASK, 5688c0984e5SSebastian Reichel val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT); 5698c0984e5SSebastian Reichel if (ret < 0) 5708c0984e5SSebastian Reichel return ret; 5718c0984e5SSebastian Reichel 5728c0984e5SSebastian Reichel enable_therm_monitor = true; 5738c0984e5SSebastian Reichel } 5748c0984e5SSebastian Reichel 575b6f3e21bSSebastian Reichel if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 576b6f3e21bSSebastian Reichel val = smb->hard_cold_temp_limit; 5778c0984e5SSebastian Reichel 5788c0984e5SSebastian Reichel val = clamp_val(val, -5, 10) + 5; 5798c0984e5SSebastian Reichel val /= 5; 5808c0984e5SSebastian Reichel /* this goes from higher to lower so invert the value */ 5818c0984e5SSebastian Reichel val = ~val & 0x3; 5828c0984e5SSebastian Reichel 5838c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5848c0984e5SSebastian Reichel CFG_TEMP_LIMIT_HARD_COLD_MASK, 5858c0984e5SSebastian Reichel val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT); 5868c0984e5SSebastian Reichel if (ret < 0) 5878c0984e5SSebastian Reichel return ret; 5888c0984e5SSebastian Reichel 5898c0984e5SSebastian Reichel enable_therm_monitor = true; 5908c0984e5SSebastian Reichel } 5918c0984e5SSebastian Reichel 592b6f3e21bSSebastian Reichel if (smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { 593b6f3e21bSSebastian Reichel val = smb->hard_hot_temp_limit; 5948c0984e5SSebastian Reichel 5958c0984e5SSebastian Reichel val = clamp_val(val, 50, 65) - 50; 5968c0984e5SSebastian Reichel val /= 5; 5978c0984e5SSebastian Reichel 5988c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT, 5998c0984e5SSebastian Reichel CFG_TEMP_LIMIT_HARD_HOT_MASK, 6008c0984e5SSebastian Reichel val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT); 6018c0984e5SSebastian Reichel if (ret < 0) 6028c0984e5SSebastian Reichel return ret; 6038c0984e5SSebastian Reichel 6048c0984e5SSebastian Reichel enable_therm_monitor = true; 6058c0984e5SSebastian Reichel } 6068c0984e5SSebastian Reichel 6078c0984e5SSebastian Reichel /* 6088c0984e5SSebastian Reichel * If any of the temperature limits are set, we also enable the 6098c0984e5SSebastian Reichel * thermistor monitoring. 6108c0984e5SSebastian Reichel * 6118c0984e5SSebastian Reichel * When soft limits are hit, the device will start to compensate 6128c0984e5SSebastian Reichel * current and/or voltage depending on the configuration. 6138c0984e5SSebastian Reichel * 6148c0984e5SSebastian Reichel * When hard limit is hit, the device will suspend charging 6158c0984e5SSebastian Reichel * depending on the configuration. 6168c0984e5SSebastian Reichel */ 6178c0984e5SSebastian Reichel if (enable_therm_monitor) { 6188c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_THERM, 6198c0984e5SSebastian Reichel CFG_THERM_MONITOR_DISABLED, 0); 6208c0984e5SSebastian Reichel if (ret < 0) 6218c0984e5SSebastian Reichel return ret; 6228c0984e5SSebastian Reichel } 6238c0984e5SSebastian Reichel 624b6f3e21bSSebastian Reichel if (smb->suspend_on_hard_temp_limit) { 6258c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_SYSOK, 6268c0984e5SSebastian Reichel CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0); 6278c0984e5SSebastian Reichel if (ret < 0) 6288c0984e5SSebastian Reichel return ret; 6298c0984e5SSebastian Reichel } 6308c0984e5SSebastian Reichel 631b6f3e21bSSebastian Reichel if (smb->soft_temp_limit_compensation != 632b6f3e21bSSebastian Reichel SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT) { 633b6f3e21bSSebastian Reichel val = smb->soft_temp_limit_compensation & 0x3; 6348c0984e5SSebastian Reichel 6358c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_THERM, 6368c0984e5SSebastian Reichel CFG_THERM_SOFT_HOT_COMPENSATION_MASK, 6378c0984e5SSebastian Reichel val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT); 6388c0984e5SSebastian Reichel if (ret < 0) 6398c0984e5SSebastian Reichel return ret; 6408c0984e5SSebastian Reichel 6418c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_THERM, 6428c0984e5SSebastian Reichel CFG_THERM_SOFT_COLD_COMPENSATION_MASK, 6438c0984e5SSebastian Reichel val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT); 6448c0984e5SSebastian Reichel if (ret < 0) 6458c0984e5SSebastian Reichel return ret; 6468c0984e5SSebastian Reichel } 6478c0984e5SSebastian Reichel 648b6f3e21bSSebastian Reichel if (smb->charge_current_compensation) { 649de76fd29SDavid Heidelberg val = current_to_hw(ccc_tbl[id], ARRAY_SIZE(ccc_tbl[id]), 650b6f3e21bSSebastian Reichel smb->charge_current_compensation); 6518c0984e5SSebastian Reichel if (val < 0) 6528c0984e5SSebastian Reichel return val; 6538c0984e5SSebastian Reichel 6548c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_OTG, 6558c0984e5SSebastian Reichel CFG_OTG_CC_COMPENSATION_MASK, 6568c0984e5SSebastian Reichel (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT); 6578c0984e5SSebastian Reichel if (ret < 0) 6588c0984e5SSebastian Reichel return ret; 6598c0984e5SSebastian Reichel } 6608c0984e5SSebastian Reichel 6618c0984e5SSebastian Reichel return ret; 6628c0984e5SSebastian Reichel } 6638c0984e5SSebastian Reichel 6648c0984e5SSebastian Reichel /* 6658c0984e5SSebastian Reichel * smb347_set_writable - enables/disables writing to non-volatile registers 6668c0984e5SSebastian Reichel * @smb: pointer to smb347 charger instance 6678c0984e5SSebastian Reichel * 6688c0984e5SSebastian Reichel * You can enable/disable writing to the non-volatile configuration 6698c0984e5SSebastian Reichel * registers by calling this function. 6708c0984e5SSebastian Reichel * 6718c0984e5SSebastian Reichel * Returns %0 on success and negative errno in case of failure. 6728c0984e5SSebastian Reichel */ 6738c0984e5SSebastian Reichel static int smb347_set_writable(struct smb347_charger *smb, bool writable) 6748c0984e5SSebastian Reichel { 6758c0984e5SSebastian Reichel return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE, 6768c0984e5SSebastian Reichel writable ? CMD_A_ALLOW_WRITE : 0); 6778c0984e5SSebastian Reichel } 6788c0984e5SSebastian Reichel 6798c0984e5SSebastian Reichel static int smb347_hw_init(struct smb347_charger *smb) 6808c0984e5SSebastian Reichel { 6818c0984e5SSebastian Reichel unsigned int val; 6828c0984e5SSebastian Reichel int ret; 6838c0984e5SSebastian Reichel 6848c0984e5SSebastian Reichel ret = smb347_set_writable(smb, true); 6858c0984e5SSebastian Reichel if (ret < 0) 6868c0984e5SSebastian Reichel return ret; 6878c0984e5SSebastian Reichel 6888c0984e5SSebastian Reichel /* 6898c0984e5SSebastian Reichel * Program the platform specific configuration values to the device 6908c0984e5SSebastian Reichel * first. 6918c0984e5SSebastian Reichel */ 6928c0984e5SSebastian Reichel ret = smb347_set_charge_current(smb); 6938c0984e5SSebastian Reichel if (ret < 0) 6948c0984e5SSebastian Reichel goto fail; 6958c0984e5SSebastian Reichel 6968c0984e5SSebastian Reichel ret = smb347_set_current_limits(smb); 6978c0984e5SSebastian Reichel if (ret < 0) 6988c0984e5SSebastian Reichel goto fail; 6998c0984e5SSebastian Reichel 7008c0984e5SSebastian Reichel ret = smb347_set_voltage_limits(smb); 7018c0984e5SSebastian Reichel if (ret < 0) 7028c0984e5SSebastian Reichel goto fail; 7038c0984e5SSebastian Reichel 7048c0984e5SSebastian Reichel ret = smb347_set_temp_limits(smb); 7058c0984e5SSebastian Reichel if (ret < 0) 7068c0984e5SSebastian Reichel goto fail; 7078c0984e5SSebastian Reichel 7088c0984e5SSebastian Reichel /* If USB charging is disabled we put the USB in suspend mode */ 709b6f3e21bSSebastian Reichel if (!smb->use_usb) { 7108c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CMD_A, 7118c0984e5SSebastian Reichel CMD_A_SUSPEND_ENABLED, 7128c0984e5SSebastian Reichel CMD_A_SUSPEND_ENABLED); 7138c0984e5SSebastian Reichel if (ret < 0) 7148c0984e5SSebastian Reichel goto fail; 7158c0984e5SSebastian Reichel } 7168c0984e5SSebastian Reichel 7178c0984e5SSebastian Reichel /* 7188c0984e5SSebastian Reichel * If configured by platform data, we enable hardware Auto-OTG 7198c0984e5SSebastian Reichel * support for driving VBUS. Otherwise we disable it. 7208c0984e5SSebastian Reichel */ 7218c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK, 722b6f3e21bSSebastian Reichel smb->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0); 7238c0984e5SSebastian Reichel if (ret < 0) 7248c0984e5SSebastian Reichel goto fail; 7258c0984e5SSebastian Reichel 7268c0984e5SSebastian Reichel /* 7278c0984e5SSebastian Reichel * Make the charging functionality controllable by a write to the 7288c0984e5SSebastian Reichel * command register unless pin control is specified in the platform 7298c0984e5SSebastian Reichel * data. 7308c0984e5SSebastian Reichel */ 731b6f3e21bSSebastian Reichel switch (smb->enable_control) { 732b6f3e21bSSebastian Reichel case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW: 7338c0984e5SSebastian Reichel val = CFG_PIN_EN_CTRL_ACTIVE_LOW; 7348c0984e5SSebastian Reichel break; 735b6f3e21bSSebastian Reichel case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH: 7368c0984e5SSebastian Reichel val = CFG_PIN_EN_CTRL_ACTIVE_HIGH; 7378c0984e5SSebastian Reichel break; 7388c0984e5SSebastian Reichel default: 7398c0984e5SSebastian Reichel val = 0; 7408c0984e5SSebastian Reichel break; 7418c0984e5SSebastian Reichel } 7428c0984e5SSebastian Reichel 7438c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK, 7448c0984e5SSebastian Reichel val); 7458c0984e5SSebastian Reichel if (ret < 0) 7468c0984e5SSebastian Reichel goto fail; 7478c0984e5SSebastian Reichel 7488c0984e5SSebastian Reichel /* Disable Automatic Power Source Detection (APSD) interrupt. */ 7498c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0); 7508c0984e5SSebastian Reichel if (ret < 0) 7518c0984e5SSebastian Reichel goto fail; 7528c0984e5SSebastian Reichel 7538c0984e5SSebastian Reichel ret = smb347_update_ps_status(smb); 7548c0984e5SSebastian Reichel if (ret < 0) 7558c0984e5SSebastian Reichel goto fail; 7568c0984e5SSebastian Reichel 7578c0984e5SSebastian Reichel ret = smb347_start_stop_charging(smb); 7588c0984e5SSebastian Reichel 7598c0984e5SSebastian Reichel fail: 7608c0984e5SSebastian Reichel smb347_set_writable(smb, false); 7618c0984e5SSebastian Reichel return ret; 7628c0984e5SSebastian Reichel } 7638c0984e5SSebastian Reichel 7648c0984e5SSebastian Reichel static irqreturn_t smb347_interrupt(int irq, void *data) 7658c0984e5SSebastian Reichel { 7668c0984e5SSebastian Reichel struct smb347_charger *smb = data; 7678c0984e5SSebastian Reichel unsigned int stat_c, irqstat_c, irqstat_d, irqstat_e; 7688c0984e5SSebastian Reichel bool handled = false; 7698c0984e5SSebastian Reichel int ret; 7708c0984e5SSebastian Reichel 771fa7cc725SDavid Heidelberg /* SMB347 it needs at least 20ms for setting IRQSTAT_E_*IN_UV_IRQ */ 772fa7cc725SDavid Heidelberg usleep_range(25000, 35000); 773fa7cc725SDavid Heidelberg 7748c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_C, &stat_c); 7758c0984e5SSebastian Reichel if (ret < 0) { 7768c0984e5SSebastian Reichel dev_warn(smb->dev, "reading STAT_C failed\n"); 7778c0984e5SSebastian Reichel return IRQ_NONE; 7788c0984e5SSebastian Reichel } 7798c0984e5SSebastian Reichel 7808c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c); 7818c0984e5SSebastian Reichel if (ret < 0) { 7828c0984e5SSebastian Reichel dev_warn(smb->dev, "reading IRQSTAT_C failed\n"); 7838c0984e5SSebastian Reichel return IRQ_NONE; 7848c0984e5SSebastian Reichel } 7858c0984e5SSebastian Reichel 7868c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, IRQSTAT_D, &irqstat_d); 7878c0984e5SSebastian Reichel if (ret < 0) { 7888c0984e5SSebastian Reichel dev_warn(smb->dev, "reading IRQSTAT_D failed\n"); 7898c0984e5SSebastian Reichel return IRQ_NONE; 7908c0984e5SSebastian Reichel } 7918c0984e5SSebastian Reichel 7928c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e); 7938c0984e5SSebastian Reichel if (ret < 0) { 7948c0984e5SSebastian Reichel dev_warn(smb->dev, "reading IRQSTAT_E failed\n"); 7958c0984e5SSebastian Reichel return IRQ_NONE; 7968c0984e5SSebastian Reichel } 7978c0984e5SSebastian Reichel 7988c0984e5SSebastian Reichel /* 7998c0984e5SSebastian Reichel * If we get charger error we report the error back to user. 8008c0984e5SSebastian Reichel * If the error is recovered charging will resume again. 8018c0984e5SSebastian Reichel */ 8028c0984e5SSebastian Reichel if (stat_c & STAT_C_CHARGER_ERROR) { 8038c0984e5SSebastian Reichel dev_err(smb->dev, "charging stopped due to charger error\n"); 804b6f3e21bSSebastian Reichel if (smb->use_mains) 805db14d3b4SDavid Heidelberg power_supply_changed(smb->mains); 806b6f3e21bSSebastian Reichel if (smb->use_usb) 807db14d3b4SDavid Heidelberg power_supply_changed(smb->usb); 8088c0984e5SSebastian Reichel handled = true; 8098c0984e5SSebastian Reichel } 8108c0984e5SSebastian Reichel 8118c0984e5SSebastian Reichel /* 8128c0984e5SSebastian Reichel * If we reached the termination current the battery is charged and 8138c0984e5SSebastian Reichel * we can update the status now. Charging is automatically 8148c0984e5SSebastian Reichel * disabled by the hardware. 8158c0984e5SSebastian Reichel */ 8168c0984e5SSebastian Reichel if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { 817db14d3b4SDavid Heidelberg if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) { 818b6f3e21bSSebastian Reichel if (smb->use_mains) 819db14d3b4SDavid Heidelberg power_supply_changed(smb->mains); 820b6f3e21bSSebastian Reichel if (smb->use_usb) 821db14d3b4SDavid Heidelberg power_supply_changed(smb->usb); 822db14d3b4SDavid Heidelberg } 8238c0984e5SSebastian Reichel dev_dbg(smb->dev, "going to HW maintenance mode\n"); 8248c0984e5SSebastian Reichel handled = true; 8258c0984e5SSebastian Reichel } 8268c0984e5SSebastian Reichel 8278c0984e5SSebastian Reichel /* 8288c0984e5SSebastian Reichel * If we got a charger timeout INT that means the charge 8298c0984e5SSebastian Reichel * full is not detected with in charge timeout value. 8308c0984e5SSebastian Reichel */ 8318c0984e5SSebastian Reichel if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ) { 8328c0984e5SSebastian Reichel dev_dbg(smb->dev, "total Charge Timeout INT received\n"); 8338c0984e5SSebastian Reichel 8348c0984e5SSebastian Reichel if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT) 8358c0984e5SSebastian Reichel dev_warn(smb->dev, "charging stopped due to timeout\n"); 836b6f3e21bSSebastian Reichel if (smb->use_mains) 837db14d3b4SDavid Heidelberg power_supply_changed(smb->mains); 838b6f3e21bSSebastian Reichel if (smb->use_usb) 839db14d3b4SDavid Heidelberg power_supply_changed(smb->usb); 8408c0984e5SSebastian Reichel handled = true; 8418c0984e5SSebastian Reichel } 8428c0984e5SSebastian Reichel 8438c0984e5SSebastian Reichel /* 8448c0984e5SSebastian Reichel * If we got an under voltage interrupt it means that AC/USB input 8458c0984e5SSebastian Reichel * was connected or disconnected. 8468c0984e5SSebastian Reichel */ 8478c0984e5SSebastian Reichel if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { 8488c0984e5SSebastian Reichel if (smb347_update_ps_status(smb) > 0) { 8498c0984e5SSebastian Reichel smb347_start_stop_charging(smb); 850b6f3e21bSSebastian Reichel if (smb->use_mains) 8518c0984e5SSebastian Reichel power_supply_changed(smb->mains); 852b6f3e21bSSebastian Reichel if (smb->use_usb) 8538c0984e5SSebastian Reichel power_supply_changed(smb->usb); 8548c0984e5SSebastian Reichel } 8558c0984e5SSebastian Reichel handled = true; 8568c0984e5SSebastian Reichel } 8578c0984e5SSebastian Reichel 8588c0984e5SSebastian Reichel return handled ? IRQ_HANDLED : IRQ_NONE; 8598c0984e5SSebastian Reichel } 8608c0984e5SSebastian Reichel 8618c0984e5SSebastian Reichel static int smb347_irq_set(struct smb347_charger *smb, bool enable) 8628c0984e5SSebastian Reichel { 8638c0984e5SSebastian Reichel int ret; 8648c0984e5SSebastian Reichel 8658c0984e5SSebastian Reichel ret = smb347_set_writable(smb, true); 8668c0984e5SSebastian Reichel if (ret < 0) 8678c0984e5SSebastian Reichel return ret; 8688c0984e5SSebastian Reichel 8698c0984e5SSebastian Reichel /* 8708c0984e5SSebastian Reichel * Enable/disable interrupts for: 8718c0984e5SSebastian Reichel * - under voltage 8728c0984e5SSebastian Reichel * - termination current reached 8738c0984e5SSebastian Reichel * - charger timeout 8748c0984e5SSebastian Reichel * - charger error 8758c0984e5SSebastian Reichel */ 8768c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff, 8778c0984e5SSebastian Reichel enable ? CFG_FAULT_IRQ_DCIN_UV : 0); 8788c0984e5SSebastian Reichel if (ret < 0) 8798c0984e5SSebastian Reichel goto fail; 8808c0984e5SSebastian Reichel 8818c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff, 8828c0984e5SSebastian Reichel enable ? (CFG_STATUS_IRQ_TERMINATION_OR_TAPER | 8838c0984e5SSebastian Reichel CFG_STATUS_IRQ_CHARGE_TIMEOUT) : 0); 8848c0984e5SSebastian Reichel if (ret < 0) 8858c0984e5SSebastian Reichel goto fail; 8868c0984e5SSebastian Reichel 8878c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR, 8888c0984e5SSebastian Reichel enable ? CFG_PIN_EN_CHARGER_ERROR : 0); 8898c0984e5SSebastian Reichel fail: 8908c0984e5SSebastian Reichel smb347_set_writable(smb, false); 8918c0984e5SSebastian Reichel return ret; 8928c0984e5SSebastian Reichel } 8938c0984e5SSebastian Reichel 8948c0984e5SSebastian Reichel static inline int smb347_irq_enable(struct smb347_charger *smb) 8958c0984e5SSebastian Reichel { 8968c0984e5SSebastian Reichel return smb347_irq_set(smb, true); 8978c0984e5SSebastian Reichel } 8988c0984e5SSebastian Reichel 8998c0984e5SSebastian Reichel static inline int smb347_irq_disable(struct smb347_charger *smb) 9008c0984e5SSebastian Reichel { 9018c0984e5SSebastian Reichel return smb347_irq_set(smb, false); 9028c0984e5SSebastian Reichel } 9038c0984e5SSebastian Reichel 9048c0984e5SSebastian Reichel static int smb347_irq_init(struct smb347_charger *smb, 9058c0984e5SSebastian Reichel struct i2c_client *client) 9068c0984e5SSebastian Reichel { 9072d52f710SDavid Heidelberg int ret; 9088c0984e5SSebastian Reichel 9092d52f710SDavid Heidelberg ret = devm_request_threaded_irq(smb->dev, client->irq, NULL, 910b6f3e21bSSebastian Reichel smb347_interrupt, IRQF_ONESHOT, 9118c0984e5SSebastian Reichel client->name, smb); 9128c0984e5SSebastian Reichel if (ret < 0) 9132d52f710SDavid Heidelberg return ret; 9148c0984e5SSebastian Reichel 9158c0984e5SSebastian Reichel ret = smb347_set_writable(smb, true); 9168c0984e5SSebastian Reichel if (ret < 0) 9172d52f710SDavid Heidelberg return ret; 9188c0984e5SSebastian Reichel 9198c0984e5SSebastian Reichel /* 9208c0984e5SSebastian Reichel * Configure the STAT output to be suitable for interrupts: disable 9218c0984e5SSebastian Reichel * all other output (except interrupts) and make it active low. 9228c0984e5SSebastian Reichel */ 9238c0984e5SSebastian Reichel ret = regmap_update_bits(smb->regmap, CFG_STAT, 9248c0984e5SSebastian Reichel CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED, 9258c0984e5SSebastian Reichel CFG_STAT_DISABLED); 9268c0984e5SSebastian Reichel if (ret < 0) 9278c0984e5SSebastian Reichel client->irq = 0; 9282d52f710SDavid Heidelberg 9292d52f710SDavid Heidelberg smb347_set_writable(smb, false); 9302d52f710SDavid Heidelberg 9318c0984e5SSebastian Reichel return ret; 9328c0984e5SSebastian Reichel } 9338c0984e5SSebastian Reichel 9348c0984e5SSebastian Reichel /* 9358c0984e5SSebastian Reichel * Returns the constant charge current programmed 9368c0984e5SSebastian Reichel * into the charger in uA. 9378c0984e5SSebastian Reichel */ 9388c0984e5SSebastian Reichel static int get_const_charge_current(struct smb347_charger *smb) 9398c0984e5SSebastian Reichel { 940de76fd29SDavid Heidelberg unsigned int id = smb->id; 9418c0984e5SSebastian Reichel int ret, intval; 9428c0984e5SSebastian Reichel unsigned int v; 9438c0984e5SSebastian Reichel 9448c0984e5SSebastian Reichel if (!smb347_is_ps_online(smb)) 9458c0984e5SSebastian Reichel return -ENODATA; 9468c0984e5SSebastian Reichel 9478c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_B, &v); 9488c0984e5SSebastian Reichel if (ret < 0) 9498c0984e5SSebastian Reichel return ret; 9508c0984e5SSebastian Reichel 9518c0984e5SSebastian Reichel /* 9528c0984e5SSebastian Reichel * The current value is composition of FCC and PCC values 9538c0984e5SSebastian Reichel * and we can detect which table to use from bit 5. 9548c0984e5SSebastian Reichel */ 9558c0984e5SSebastian Reichel if (v & 0x20) { 956de76fd29SDavid Heidelberg intval = hw_to_current(fcc_tbl[id], 957de76fd29SDavid Heidelberg ARRAY_SIZE(fcc_tbl[id]), v & 7); 9588c0984e5SSebastian Reichel } else { 9598c0984e5SSebastian Reichel v >>= 3; 960de76fd29SDavid Heidelberg intval = hw_to_current(pcc_tbl[id], 961de76fd29SDavid Heidelberg ARRAY_SIZE(pcc_tbl[id]), v & 7); 9628c0984e5SSebastian Reichel } 9638c0984e5SSebastian Reichel 9648c0984e5SSebastian Reichel return intval; 9658c0984e5SSebastian Reichel } 9668c0984e5SSebastian Reichel 9678c0984e5SSebastian Reichel /* 9688c0984e5SSebastian Reichel * Returns the constant charge voltage programmed 9698c0984e5SSebastian Reichel * into the charger in uV. 9708c0984e5SSebastian Reichel */ 9718c0984e5SSebastian Reichel static int get_const_charge_voltage(struct smb347_charger *smb) 9728c0984e5SSebastian Reichel { 9738c0984e5SSebastian Reichel int ret, intval; 9748c0984e5SSebastian Reichel unsigned int v; 9758c0984e5SSebastian Reichel 9768c0984e5SSebastian Reichel if (!smb347_is_ps_online(smb)) 9778c0984e5SSebastian Reichel return -ENODATA; 9788c0984e5SSebastian Reichel 9798c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_A, &v); 9808c0984e5SSebastian Reichel if (ret < 0) 9818c0984e5SSebastian Reichel return ret; 9828c0984e5SSebastian Reichel 9838c0984e5SSebastian Reichel v &= STAT_A_FLOAT_VOLTAGE_MASK; 9848c0984e5SSebastian Reichel if (v > 0x3d) 9858c0984e5SSebastian Reichel v = 0x3d; 9868c0984e5SSebastian Reichel 9878c0984e5SSebastian Reichel intval = 3500000 + v * 20000; 9888c0984e5SSebastian Reichel 9898c0984e5SSebastian Reichel return intval; 9908c0984e5SSebastian Reichel } 9918c0984e5SSebastian Reichel 992db14d3b4SDavid Heidelberg static int smb347_get_charging_status(struct smb347_charger *smb, 993db14d3b4SDavid Heidelberg struct power_supply *psy) 9948c0984e5SSebastian Reichel { 9958c0984e5SSebastian Reichel int ret, status; 9968c0984e5SSebastian Reichel unsigned int val; 9978c0984e5SSebastian Reichel 998db14d3b4SDavid Heidelberg if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { 999db14d3b4SDavid Heidelberg if (!smb->usb_online) 10008c0984e5SSebastian Reichel return POWER_SUPPLY_STATUS_DISCHARGING; 1001db14d3b4SDavid Heidelberg } else { 1002db14d3b4SDavid Heidelberg if (!smb->mains_online) 1003db14d3b4SDavid Heidelberg return POWER_SUPPLY_STATUS_DISCHARGING; 1004db14d3b4SDavid Heidelberg } 10058c0984e5SSebastian Reichel 10068c0984e5SSebastian Reichel ret = regmap_read(smb->regmap, STAT_C, &val); 10078c0984e5SSebastian Reichel if (ret < 0) 10088c0984e5SSebastian Reichel return ret; 10098c0984e5SSebastian Reichel 10108c0984e5SSebastian Reichel if ((val & STAT_C_CHARGER_ERROR) || 10118c0984e5SSebastian Reichel (val & STAT_C_HOLDOFF_STAT)) { 10128c0984e5SSebastian Reichel /* 10138c0984e5SSebastian Reichel * set to NOT CHARGING upon charger error 10148c0984e5SSebastian Reichel * or charging has stopped. 10158c0984e5SSebastian Reichel */ 10168c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_NOT_CHARGING; 10178c0984e5SSebastian Reichel } else { 10188c0984e5SSebastian Reichel if ((val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT) { 10198c0984e5SSebastian Reichel /* 10208c0984e5SSebastian Reichel * set to charging if battery is in pre-charge, 10218c0984e5SSebastian Reichel * fast charge or taper charging mode. 10228c0984e5SSebastian Reichel */ 10238c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_CHARGING; 10248c0984e5SSebastian Reichel } else if (val & STAT_C_CHG_TERM) { 10258c0984e5SSebastian Reichel /* 10268c0984e5SSebastian Reichel * set the status to FULL if battery is not in pre 10278c0984e5SSebastian Reichel * charge, fast charge or taper charging mode AND 10288c0984e5SSebastian Reichel * charging is terminated at least once. 10298c0984e5SSebastian Reichel */ 10308c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_FULL; 10318c0984e5SSebastian Reichel } else { 10328c0984e5SSebastian Reichel /* 10338c0984e5SSebastian Reichel * in this case no charger error or termination 10348c0984e5SSebastian Reichel * occured but charging is not in progress!!! 10358c0984e5SSebastian Reichel */ 10368c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_NOT_CHARGING; 10378c0984e5SSebastian Reichel } 10388c0984e5SSebastian Reichel } 10398c0984e5SSebastian Reichel 10408c0984e5SSebastian Reichel return status; 10418c0984e5SSebastian Reichel } 10428c0984e5SSebastian Reichel 104399298de5SDmitry Osipenko static int smb347_get_property_locked(struct power_supply *psy, 10448c0984e5SSebastian Reichel enum power_supply_property prop, 10458c0984e5SSebastian Reichel union power_supply_propval *val) 10468c0984e5SSebastian Reichel { 10478c0984e5SSebastian Reichel struct smb347_charger *smb = power_supply_get_drvdata(psy); 10488c0984e5SSebastian Reichel int ret; 10498c0984e5SSebastian Reichel 10508c0984e5SSebastian Reichel switch (prop) { 10518c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 1052db14d3b4SDavid Heidelberg ret = smb347_get_charging_status(smb, psy); 10538c0984e5SSebastian Reichel if (ret < 0) 10548c0984e5SSebastian Reichel return ret; 10558c0984e5SSebastian Reichel val->intval = ret; 10568c0984e5SSebastian Reichel break; 10578c0984e5SSebastian Reichel 10588c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_TYPE: 1059db14d3b4SDavid Heidelberg if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { 1060db14d3b4SDavid Heidelberg if (!smb->usb_online) 10618c0984e5SSebastian Reichel return -ENODATA; 1062db14d3b4SDavid Heidelberg } else { 1063db14d3b4SDavid Heidelberg if (!smb->mains_online) 1064db14d3b4SDavid Heidelberg return -ENODATA; 1065db14d3b4SDavid Heidelberg } 10668c0984e5SSebastian Reichel 10678c0984e5SSebastian Reichel /* 10688c0984e5SSebastian Reichel * We handle trickle and pre-charging the same, and taper 10698c0984e5SSebastian Reichel * and none the same. 10708c0984e5SSebastian Reichel */ 10718c0984e5SSebastian Reichel switch (smb347_charging_status(smb)) { 10728c0984e5SSebastian Reichel case 1: 10738c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 10748c0984e5SSebastian Reichel break; 10758c0984e5SSebastian Reichel case 2: 10768c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 10778c0984e5SSebastian Reichel break; 10788c0984e5SSebastian Reichel default: 10798c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 10808c0984e5SSebastian Reichel break; 10818c0984e5SSebastian Reichel } 10828c0984e5SSebastian Reichel break; 10838c0984e5SSebastian Reichel 1084db14d3b4SDavid Heidelberg case POWER_SUPPLY_PROP_ONLINE: 1085db14d3b4SDavid Heidelberg if (psy->desc->type == POWER_SUPPLY_TYPE_USB) 1086db14d3b4SDavid Heidelberg val->intval = smb->usb_online; 1087db14d3b4SDavid Heidelberg else 1088db14d3b4SDavid Heidelberg val->intval = smb->mains_online; 10898c0984e5SSebastian Reichel break; 10908c0984e5SSebastian Reichel 1091db14d3b4SDavid Heidelberg case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 1092db14d3b4SDavid Heidelberg ret = get_const_charge_voltage(smb); 1093db14d3b4SDavid Heidelberg if (ret < 0) 1094db14d3b4SDavid Heidelberg return ret; 1095db14d3b4SDavid Heidelberg val->intval = ret; 10968c0984e5SSebastian Reichel break; 10978c0984e5SSebastian Reichel 1098db14d3b4SDavid Heidelberg case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 1099db14d3b4SDavid Heidelberg ret = get_const_charge_current(smb); 1100db14d3b4SDavid Heidelberg if (ret < 0) 1101db14d3b4SDavid Heidelberg return ret; 1102db14d3b4SDavid Heidelberg val->intval = ret; 11038c0984e5SSebastian Reichel break; 11048c0984e5SSebastian Reichel 11058c0984e5SSebastian Reichel default: 11068c0984e5SSebastian Reichel return -EINVAL; 11078c0984e5SSebastian Reichel } 11088c0984e5SSebastian Reichel 11098c0984e5SSebastian Reichel return 0; 11108c0984e5SSebastian Reichel } 11118c0984e5SSebastian Reichel 111299298de5SDmitry Osipenko static int smb347_get_property(struct power_supply *psy, 111399298de5SDmitry Osipenko enum power_supply_property prop, 111499298de5SDmitry Osipenko union power_supply_propval *val) 111599298de5SDmitry Osipenko { 111699298de5SDmitry Osipenko struct smb347_charger *smb = power_supply_get_drvdata(psy); 111799298de5SDmitry Osipenko struct i2c_client *client = to_i2c_client(smb->dev); 111899298de5SDmitry Osipenko int ret; 111999298de5SDmitry Osipenko 112099298de5SDmitry Osipenko disable_irq(client->irq); 112199298de5SDmitry Osipenko ret = smb347_get_property_locked(psy, prop, val); 112299298de5SDmitry Osipenko enable_irq(client->irq); 112399298de5SDmitry Osipenko 112499298de5SDmitry Osipenko return ret; 112599298de5SDmitry Osipenko } 112699298de5SDmitry Osipenko 1127db14d3b4SDavid Heidelberg static enum power_supply_property smb347_properties[] = { 11288c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 11298c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_TYPE, 1130db14d3b4SDavid Heidelberg POWER_SUPPLY_PROP_ONLINE, 1131db14d3b4SDavid Heidelberg POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 1132db14d3b4SDavid Heidelberg POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 11338c0984e5SSebastian Reichel }; 11348c0984e5SSebastian Reichel 11358c0984e5SSebastian Reichel static bool smb347_volatile_reg(struct device *dev, unsigned int reg) 11368c0984e5SSebastian Reichel { 11378c0984e5SSebastian Reichel switch (reg) { 11388c0984e5SSebastian Reichel case IRQSTAT_A: 11398c0984e5SSebastian Reichel case IRQSTAT_C: 1140c32ea07aSDmitry Osipenko case IRQSTAT_D: 11418c0984e5SSebastian Reichel case IRQSTAT_E: 11428c0984e5SSebastian Reichel case IRQSTAT_F: 11438c0984e5SSebastian Reichel case STAT_A: 11448c0984e5SSebastian Reichel case STAT_B: 11458c0984e5SSebastian Reichel case STAT_C: 11468c0984e5SSebastian Reichel case STAT_E: 11478c0984e5SSebastian Reichel return true; 11488c0984e5SSebastian Reichel } 11498c0984e5SSebastian Reichel 11508c0984e5SSebastian Reichel return false; 11518c0984e5SSebastian Reichel } 11528c0984e5SSebastian Reichel 11538c0984e5SSebastian Reichel static bool smb347_readable_reg(struct device *dev, unsigned int reg) 11548c0984e5SSebastian Reichel { 11558c0984e5SSebastian Reichel switch (reg) { 11568c0984e5SSebastian Reichel case CFG_CHARGE_CURRENT: 11578c0984e5SSebastian Reichel case CFG_CURRENT_LIMIT: 11588c0984e5SSebastian Reichel case CFG_FLOAT_VOLTAGE: 11598c0984e5SSebastian Reichel case CFG_STAT: 11608c0984e5SSebastian Reichel case CFG_PIN: 11618c0984e5SSebastian Reichel case CFG_THERM: 11628c0984e5SSebastian Reichel case CFG_SYSOK: 11638c0984e5SSebastian Reichel case CFG_OTHER: 11648c0984e5SSebastian Reichel case CFG_OTG: 11658c0984e5SSebastian Reichel case CFG_TEMP_LIMIT: 11668c0984e5SSebastian Reichel case CFG_FAULT_IRQ: 11678c0984e5SSebastian Reichel case CFG_STATUS_IRQ: 11688c0984e5SSebastian Reichel case CFG_ADDRESS: 11698c0984e5SSebastian Reichel case CMD_A: 11708c0984e5SSebastian Reichel case CMD_B: 11718c0984e5SSebastian Reichel case CMD_C: 11728c0984e5SSebastian Reichel return true; 11738c0984e5SSebastian Reichel } 11748c0984e5SSebastian Reichel 11758c0984e5SSebastian Reichel return smb347_volatile_reg(dev, reg); 11768c0984e5SSebastian Reichel } 11778c0984e5SSebastian Reichel 1178b6f3e21bSSebastian Reichel static void smb347_dt_parse_dev_info(struct smb347_charger *smb) 1179364bec75SDavid Heidelberg { 1180f385e2fcSSebastian Reichel struct device *dev = smb->dev; 1181b6f3e21bSSebastian Reichel 1182b6f3e21bSSebastian Reichel smb->soft_temp_limit_compensation = 1183b6f3e21bSSebastian Reichel SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT; 1184364bec75SDavid Heidelberg /* 1185364bec75SDavid Heidelberg * These properties come from the battery info, still we need to 1186364bec75SDavid Heidelberg * pre-initialize the values. See smb347_get_battery_info() below. 1187364bec75SDavid Heidelberg */ 1188b6f3e21bSSebastian Reichel smb->soft_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 1189b6f3e21bSSebastian Reichel smb->hard_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 1190b6f3e21bSSebastian Reichel smb->soft_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 1191b6f3e21bSSebastian Reichel smb->hard_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; 1192364bec75SDavid Heidelberg 1193364bec75SDavid Heidelberg /* Charging constraints */ 1194f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,fast-voltage-threshold-microvolt", 1195b6f3e21bSSebastian Reichel &smb->pre_to_fast_voltage); 1196f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,mains-current-limit-microamp", 1197b6f3e21bSSebastian Reichel &smb->mains_current_limit); 1198f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,usb-current-limit-microamp", 1199b6f3e21bSSebastian Reichel &smb->usb_hc_current_limit); 1200364bec75SDavid Heidelberg 1201364bec75SDavid Heidelberg /* For thermometer monitoring */ 1202f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,chip-temperature-threshold-celsius", 1203b6f3e21bSSebastian Reichel &smb->chip_temp_threshold); 1204f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,soft-compensation-method", 1205b6f3e21bSSebastian Reichel &smb->soft_temp_limit_compensation); 1206f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,charge-current-compensation-microamp", 1207b6f3e21bSSebastian Reichel &smb->charge_current_compensation); 1208364bec75SDavid Heidelberg 1209364bec75SDavid Heidelberg /* Supported charging mode */ 1210f385e2fcSSebastian Reichel smb->use_mains = device_property_read_bool(dev, "summit,enable-mains-charging"); 1211f385e2fcSSebastian Reichel smb->use_usb = device_property_read_bool(dev, "summit,enable-usb-charging"); 1212f385e2fcSSebastian Reichel smb->use_usb_otg = device_property_read_bool(dev, "summit,enable-otg-charging"); 1213364bec75SDavid Heidelberg 1214364bec75SDavid Heidelberg /* Select charging control */ 1215f385e2fcSSebastian Reichel device_property_read_u32(dev, "summit,enable-charge-control", 1216b6f3e21bSSebastian Reichel &smb->enable_control); 1217364bec75SDavid Heidelberg } 1218364bec75SDavid Heidelberg 1219364bec75SDavid Heidelberg static int smb347_get_battery_info(struct smb347_charger *smb) 1220364bec75SDavid Heidelberg { 1221364bec75SDavid Heidelberg struct power_supply_battery_info info = {}; 1222364bec75SDavid Heidelberg struct power_supply *supply; 1223364bec75SDavid Heidelberg int err; 1224364bec75SDavid Heidelberg 1225364bec75SDavid Heidelberg if (smb->mains) 1226364bec75SDavid Heidelberg supply = smb->mains; 1227364bec75SDavid Heidelberg else 1228364bec75SDavid Heidelberg supply = smb->usb; 1229364bec75SDavid Heidelberg 1230364bec75SDavid Heidelberg err = power_supply_get_battery_info(supply, &info); 1231364bec75SDavid Heidelberg if (err == -ENXIO || err == -ENODEV) 1232364bec75SDavid Heidelberg return 0; 1233364bec75SDavid Heidelberg if (err) 1234364bec75SDavid Heidelberg return err; 1235364bec75SDavid Heidelberg 1236364bec75SDavid Heidelberg if (info.constant_charge_current_max_ua != -EINVAL) 1237b6f3e21bSSebastian Reichel smb->max_charge_current = info.constant_charge_current_max_ua; 1238364bec75SDavid Heidelberg 1239364bec75SDavid Heidelberg if (info.constant_charge_voltage_max_uv != -EINVAL) 1240b6f3e21bSSebastian Reichel smb->max_charge_voltage = info.constant_charge_voltage_max_uv; 1241364bec75SDavid Heidelberg 1242364bec75SDavid Heidelberg if (info.precharge_current_ua != -EINVAL) 1243b6f3e21bSSebastian Reichel smb->pre_charge_current = info.precharge_current_ua; 1244364bec75SDavid Heidelberg 1245364bec75SDavid Heidelberg if (info.charge_term_current_ua != -EINVAL) 1246b6f3e21bSSebastian Reichel smb->termination_current = info.charge_term_current_ua; 1247364bec75SDavid Heidelberg 1248364bec75SDavid Heidelberg if (info.temp_alert_min != INT_MIN) 1249b6f3e21bSSebastian Reichel smb->soft_cold_temp_limit = info.temp_alert_min; 1250364bec75SDavid Heidelberg 1251364bec75SDavid Heidelberg if (info.temp_alert_max != INT_MAX) 1252b6f3e21bSSebastian Reichel smb->soft_hot_temp_limit = info.temp_alert_max; 1253364bec75SDavid Heidelberg 1254364bec75SDavid Heidelberg if (info.temp_min != INT_MIN) 1255b6f3e21bSSebastian Reichel smb->hard_cold_temp_limit = info.temp_min; 1256364bec75SDavid Heidelberg 1257364bec75SDavid Heidelberg if (info.temp_max != INT_MAX) 1258b6f3e21bSSebastian Reichel smb->hard_hot_temp_limit = info.temp_max; 1259364bec75SDavid Heidelberg 1260364bec75SDavid Heidelberg /* Suspend when battery temperature is outside hard limits */ 1261b6f3e21bSSebastian Reichel if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT || 1262b6f3e21bSSebastian Reichel smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) 1263b6f3e21bSSebastian Reichel smb->suspend_on_hard_temp_limit = true; 1264364bec75SDavid Heidelberg 1265364bec75SDavid Heidelberg return 0; 1266364bec75SDavid Heidelberg } 1267364bec75SDavid Heidelberg 12688c0984e5SSebastian Reichel static const struct regmap_config smb347_regmap = { 12698c0984e5SSebastian Reichel .reg_bits = 8, 12708c0984e5SSebastian Reichel .val_bits = 8, 12718c0984e5SSebastian Reichel .max_register = SMB347_MAX_REGISTER, 12728c0984e5SSebastian Reichel .volatile_reg = smb347_volatile_reg, 12738c0984e5SSebastian Reichel .readable_reg = smb347_readable_reg, 12748c0984e5SSebastian Reichel }; 12758c0984e5SSebastian Reichel 12768c0984e5SSebastian Reichel static const struct power_supply_desc smb347_mains_desc = { 12778c0984e5SSebastian Reichel .name = "smb347-mains", 12788c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_MAINS, 1279db14d3b4SDavid Heidelberg .get_property = smb347_get_property, 1280db14d3b4SDavid Heidelberg .properties = smb347_properties, 1281db14d3b4SDavid Heidelberg .num_properties = ARRAY_SIZE(smb347_properties), 12828c0984e5SSebastian Reichel }; 12838c0984e5SSebastian Reichel 12848c0984e5SSebastian Reichel static const struct power_supply_desc smb347_usb_desc = { 12858c0984e5SSebastian Reichel .name = "smb347-usb", 12868c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_USB, 1287db14d3b4SDavid Heidelberg .get_property = smb347_get_property, 1288db14d3b4SDavid Heidelberg .properties = smb347_properties, 1289db14d3b4SDavid Heidelberg .num_properties = ARRAY_SIZE(smb347_properties), 12908c0984e5SSebastian Reichel }; 12918c0984e5SSebastian Reichel 12928c0984e5SSebastian Reichel static int smb347_probe(struct i2c_client *client, 12938c0984e5SSebastian Reichel const struct i2c_device_id *id) 12948c0984e5SSebastian Reichel { 1295db14d3b4SDavid Heidelberg struct power_supply_config mains_usb_cfg = {}; 12968c0984e5SSebastian Reichel struct device *dev = &client->dev; 12978c0984e5SSebastian Reichel struct smb347_charger *smb; 12988c0984e5SSebastian Reichel int ret; 12998c0984e5SSebastian Reichel 13008c0984e5SSebastian Reichel smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL); 13018c0984e5SSebastian Reichel if (!smb) 13028c0984e5SSebastian Reichel return -ENOMEM; 13038c0984e5SSebastian Reichel smb->dev = &client->dev; 1304de76fd29SDavid Heidelberg smb->id = id->driver_data; 1305b6f3e21bSSebastian Reichel i2c_set_clientdata(client, smb); 1306b6f3e21bSSebastian Reichel 1307b6f3e21bSSebastian Reichel smb347_dt_parse_dev_info(smb); 1308b6f3e21bSSebastian Reichel if (!smb->use_mains && !smb->use_usb) 1309b6f3e21bSSebastian Reichel return -EINVAL; 13108c0984e5SSebastian Reichel 13118c0984e5SSebastian Reichel smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap); 13128c0984e5SSebastian Reichel if (IS_ERR(smb->regmap)) 13138c0984e5SSebastian Reichel return PTR_ERR(smb->regmap); 13148c0984e5SSebastian Reichel 13158c0984e5SSebastian Reichel mains_usb_cfg.drv_data = smb; 1316364bec75SDavid Heidelberg mains_usb_cfg.of_node = dev->of_node; 1317b6f3e21bSSebastian Reichel if (smb->use_mains) { 13182d52f710SDavid Heidelberg smb->mains = devm_power_supply_register(dev, &smb347_mains_desc, 13198c0984e5SSebastian Reichel &mains_usb_cfg); 13208c0984e5SSebastian Reichel if (IS_ERR(smb->mains)) 13218c0984e5SSebastian Reichel return PTR_ERR(smb->mains); 13228c0984e5SSebastian Reichel } 13238c0984e5SSebastian Reichel 1324b6f3e21bSSebastian Reichel if (smb->use_usb) { 13252d52f710SDavid Heidelberg smb->usb = devm_power_supply_register(dev, &smb347_usb_desc, 13268c0984e5SSebastian Reichel &mains_usb_cfg); 13272d52f710SDavid Heidelberg if (IS_ERR(smb->usb)) 13288c0984e5SSebastian Reichel return PTR_ERR(smb->usb); 13298c0984e5SSebastian Reichel } 13308c0984e5SSebastian Reichel 1331364bec75SDavid Heidelberg ret = smb347_get_battery_info(smb); 1332364bec75SDavid Heidelberg if (ret) 1333364bec75SDavid Heidelberg return ret; 1334364bec75SDavid Heidelberg 1335364bec75SDavid Heidelberg ret = smb347_hw_init(smb); 1336364bec75SDavid Heidelberg if (ret < 0) 1337364bec75SDavid Heidelberg return ret; 1338364bec75SDavid Heidelberg 13398c0984e5SSebastian Reichel /* 13408c0984e5SSebastian Reichel * Interrupt pin is optional. If it is connected, we setup the 13418c0984e5SSebastian Reichel * interrupt support here. 13428c0984e5SSebastian Reichel */ 1343b6f3e21bSSebastian Reichel if (client->irq) { 13448c0984e5SSebastian Reichel ret = smb347_irq_init(smb, client); 13458c0984e5SSebastian Reichel if (ret < 0) { 13468c0984e5SSebastian Reichel dev_warn(dev, "failed to initialize IRQ: %d\n", ret); 13478c0984e5SSebastian Reichel dev_warn(dev, "disabling IRQ support\n"); 13488c0984e5SSebastian Reichel } else { 13498c0984e5SSebastian Reichel smb347_irq_enable(smb); 13508c0984e5SSebastian Reichel } 13518c0984e5SSebastian Reichel } 13528c0984e5SSebastian Reichel 13538c0984e5SSebastian Reichel return 0; 13548c0984e5SSebastian Reichel } 13558c0984e5SSebastian Reichel 13568c0984e5SSebastian Reichel static int smb347_remove(struct i2c_client *client) 13578c0984e5SSebastian Reichel { 13588c0984e5SSebastian Reichel struct smb347_charger *smb = i2c_get_clientdata(client); 13598c0984e5SSebastian Reichel 13602d52f710SDavid Heidelberg if (client->irq) 13618c0984e5SSebastian Reichel smb347_irq_disable(smb); 13628c0984e5SSebastian Reichel return 0; 13638c0984e5SSebastian Reichel } 13648c0984e5SSebastian Reichel 13658c0984e5SSebastian Reichel static const struct i2c_device_id smb347_id[] = { 1366de76fd29SDavid Heidelberg { "smb345", SMB345 }, 1367de76fd29SDavid Heidelberg { "smb347", SMB347 }, 1368de76fd29SDavid Heidelberg { "smb358", SMB358 }, 1369de76fd29SDavid Heidelberg { }, 13708c0984e5SSebastian Reichel }; 13718c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, smb347_id); 13728c0984e5SSebastian Reichel 1373364bec75SDavid Heidelberg static const struct of_device_id smb3xx_of_match[] = { 1374de76fd29SDavid Heidelberg { .compatible = "summit,smb345" }, 1375364bec75SDavid Heidelberg { .compatible = "summit,smb347" }, 1376de76fd29SDavid Heidelberg { .compatible = "summit,smb358" }, 1377364bec75SDavid Heidelberg { }, 1378364bec75SDavid Heidelberg }; 1379364bec75SDavid Heidelberg MODULE_DEVICE_TABLE(of, smb3xx_of_match); 1380364bec75SDavid Heidelberg 13818c0984e5SSebastian Reichel static struct i2c_driver smb347_driver = { 13828c0984e5SSebastian Reichel .driver = { 13838c0984e5SSebastian Reichel .name = "smb347", 1384364bec75SDavid Heidelberg .of_match_table = smb3xx_of_match, 13858c0984e5SSebastian Reichel }, 13868c0984e5SSebastian Reichel .probe = smb347_probe, 13878c0984e5SSebastian Reichel .remove = smb347_remove, 13888c0984e5SSebastian Reichel .id_table = smb347_id, 13898c0984e5SSebastian Reichel }; 13908c0984e5SSebastian Reichel 13918c0984e5SSebastian Reichel module_i2c_driver(smb347_driver); 13928c0984e5SSebastian Reichel 13938c0984e5SSebastian Reichel MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>"); 13948c0984e5SSebastian Reichel MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); 13958c0984e5SSebastian Reichel MODULE_DESCRIPTION("SMB347 battery charger driver"); 13968c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 1397