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