1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * Summit Microelectronics SMB347 Battery Charger Driver
48c0984e5SSebastian Reichel  *
58c0984e5SSebastian Reichel  * Copyright (C) 2011, Intel Corporation
68c0984e5SSebastian Reichel  *
78c0984e5SSebastian Reichel  * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com>
88c0984e5SSebastian Reichel  *          Mika Westerberg <mika.westerberg@linux.intel.com>
98c0984e5SSebastian Reichel  */
108c0984e5SSebastian Reichel 
11fa7cc725SDavid Heidelberg #include <linux/delay.h>
128c0984e5SSebastian Reichel #include <linux/err.h>
138c0984e5SSebastian Reichel #include <linux/kernel.h>
148c0984e5SSebastian Reichel #include <linux/module.h>
158c0984e5SSebastian Reichel #include <linux/init.h>
168c0984e5SSebastian Reichel #include <linux/interrupt.h>
178c0984e5SSebastian Reichel #include <linux/i2c.h>
188c0984e5SSebastian Reichel #include <linux/power_supply.h>
19f385e2fcSSebastian Reichel #include <linux/property.h>
208c0984e5SSebastian Reichel #include <linux/regmap.h>
21565efae9SDmitry Osipenko #include <linux/regulator/driver.h>
228c0984e5SSebastian Reichel 
23b6f3e21bSSebastian Reichel #include <dt-bindings/power/summit,smb347-charger.h>
24b6f3e21bSSebastian Reichel 
25b6f3e21bSSebastian Reichel /* Use the default compensation method */
26b6f3e21bSSebastian Reichel #define SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT -1
27b6f3e21bSSebastian Reichel 
28b6f3e21bSSebastian Reichel /* Use default factory programmed value for hard/soft temperature limit */
29b6f3e21bSSebastian Reichel #define SMB3XX_TEMP_USE_DEFAULT		-273
30b6f3e21bSSebastian Reichel 
318c0984e5SSebastian Reichel /*
328c0984e5SSebastian Reichel  * Configuration registers. These are mirrored to volatile RAM and can be
338c0984e5SSebastian Reichel  * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be
348c0984e5SSebastian Reichel  * reloaded from non-volatile registers after POR.
358c0984e5SSebastian Reichel  */
368c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT			0x00
378c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_FCC_MASK		0xe0
388c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_FCC_SHIFT		5
398c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_PCC_MASK		0x18
408c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_PCC_SHIFT		3
418c0984e5SSebastian Reichel #define CFG_CHARGE_CURRENT_TC_MASK		0x07
428c0984e5SSebastian Reichel #define CFG_CURRENT_LIMIT			0x01
438c0984e5SSebastian Reichel #define CFG_CURRENT_LIMIT_DC_MASK		0xf0
448c0984e5SSebastian Reichel #define CFG_CURRENT_LIMIT_DC_SHIFT		4
458c0984e5SSebastian Reichel #define CFG_CURRENT_LIMIT_USB_MASK		0x0f
468c0984e5SSebastian Reichel #define CFG_FLOAT_VOLTAGE			0x03
478c0984e5SSebastian Reichel #define CFG_FLOAT_VOLTAGE_FLOAT_MASK		0x3f
488c0984e5SSebastian Reichel #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK	0xc0
498c0984e5SSebastian Reichel #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT	6
508c0984e5SSebastian Reichel #define CFG_STAT				0x05
518c0984e5SSebastian Reichel #define CFG_STAT_DISABLED			BIT(5)
528c0984e5SSebastian Reichel #define CFG_STAT_ACTIVE_HIGH			BIT(7)
538c0984e5SSebastian Reichel #define CFG_PIN					0x06
548c0984e5SSebastian Reichel #define CFG_PIN_EN_CTRL_MASK			0x60
558c0984e5SSebastian Reichel #define CFG_PIN_EN_CTRL_ACTIVE_HIGH		0x40
568c0984e5SSebastian Reichel #define CFG_PIN_EN_CTRL_ACTIVE_LOW		0x60
578c0984e5SSebastian Reichel #define CFG_PIN_EN_APSD_IRQ			BIT(1)
588c0984e5SSebastian Reichel #define CFG_PIN_EN_CHARGER_ERROR		BIT(2)
59efe21754SDmitry Osipenko #define CFG_PIN_EN_CTRL				BIT(4)
608c0984e5SSebastian Reichel #define CFG_THERM				0x07
618c0984e5SSebastian Reichel #define CFG_THERM_SOFT_HOT_COMPENSATION_MASK	0x03
628c0984e5SSebastian Reichel #define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT	0
638c0984e5SSebastian Reichel #define CFG_THERM_SOFT_COLD_COMPENSATION_MASK	0x0c
648c0984e5SSebastian Reichel #define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT	2
658c0984e5SSebastian Reichel #define CFG_THERM_MONITOR_DISABLED		BIT(4)
668c0984e5SSebastian Reichel #define CFG_SYSOK				0x08
67565efae9SDmitry Osipenko #define CFG_SYSOK_INOK_ACTIVE_HIGH		BIT(0)
688c0984e5SSebastian Reichel #define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED	BIT(2)
698c0984e5SSebastian Reichel #define CFG_OTHER				0x09
708c0984e5SSebastian Reichel #define CFG_OTHER_RID_MASK			0xc0
718c0984e5SSebastian Reichel #define CFG_OTHER_RID_ENABLED_AUTO_OTG		0xc0
728c0984e5SSebastian Reichel #define CFG_OTG					0x0a
738c0984e5SSebastian Reichel #define CFG_OTG_TEMP_THRESHOLD_MASK		0x30
74565efae9SDmitry Osipenko #define CFG_OTG_CURRENT_LIMIT_250mA		BIT(2)
75565efae9SDmitry Osipenko #define CFG_OTG_CURRENT_LIMIT_750mA		BIT(3)
768c0984e5SSebastian Reichel #define CFG_OTG_TEMP_THRESHOLD_SHIFT		4
778c0984e5SSebastian Reichel #define CFG_OTG_CC_COMPENSATION_MASK		0xc0
788c0984e5SSebastian Reichel #define CFG_OTG_CC_COMPENSATION_SHIFT		6
798c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT				0x0b
808c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_HOT_MASK		0x03
818c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT		0
828c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_COLD_MASK		0x0c
838c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT		2
848c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_HOT_MASK		0x30
858c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_HOT_SHIFT		4
868c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_COLD_MASK		0xc0
878c0984e5SSebastian Reichel #define CFG_TEMP_LIMIT_HARD_COLD_SHIFT		6
888c0984e5SSebastian Reichel #define CFG_FAULT_IRQ				0x0c
898c0984e5SSebastian Reichel #define CFG_FAULT_IRQ_DCIN_UV			BIT(2)
908c0984e5SSebastian Reichel #define CFG_STATUS_IRQ				0x0d
918c0984e5SSebastian Reichel #define CFG_STATUS_IRQ_TERMINATION_OR_TAPER	BIT(4)
928c0984e5SSebastian Reichel #define CFG_STATUS_IRQ_CHARGE_TIMEOUT		BIT(7)
938c0984e5SSebastian Reichel #define CFG_ADDRESS				0x0e
948c0984e5SSebastian Reichel 
958c0984e5SSebastian Reichel /* Command registers */
968c0984e5SSebastian Reichel #define CMD_A					0x30
978c0984e5SSebastian Reichel #define CMD_A_CHG_ENABLED			BIT(1)
988c0984e5SSebastian Reichel #define CMD_A_SUSPEND_ENABLED			BIT(2)
99565efae9SDmitry Osipenko #define CMD_A_OTG_ENABLED			BIT(4)
1008c0984e5SSebastian Reichel #define CMD_A_ALLOW_WRITE			BIT(7)
1018c0984e5SSebastian Reichel #define CMD_B					0x31
1028c0984e5SSebastian Reichel #define CMD_C					0x33
1038c0984e5SSebastian Reichel 
1048c0984e5SSebastian Reichel /* Interrupt Status registers */
1058c0984e5SSebastian Reichel #define IRQSTAT_A				0x35
1068c0984e5SSebastian Reichel #define IRQSTAT_C				0x37
1078c0984e5SSebastian Reichel #define IRQSTAT_C_TERMINATION_STAT		BIT(0)
1088c0984e5SSebastian Reichel #define IRQSTAT_C_TERMINATION_IRQ		BIT(1)
1098c0984e5SSebastian Reichel #define IRQSTAT_C_TAPER_IRQ			BIT(3)
1108c0984e5SSebastian Reichel #define IRQSTAT_D				0x38
1118c0984e5SSebastian Reichel #define IRQSTAT_D_CHARGE_TIMEOUT_STAT		BIT(2)
1128c0984e5SSebastian Reichel #define IRQSTAT_D_CHARGE_TIMEOUT_IRQ		BIT(3)
1138c0984e5SSebastian Reichel #define IRQSTAT_E				0x39
1148c0984e5SSebastian Reichel #define IRQSTAT_E_USBIN_UV_STAT			BIT(0)
1158c0984e5SSebastian Reichel #define IRQSTAT_E_USBIN_UV_IRQ			BIT(1)
1168c0984e5SSebastian Reichel #define IRQSTAT_E_DCIN_UV_STAT			BIT(4)
1178c0984e5SSebastian Reichel #define IRQSTAT_E_DCIN_UV_IRQ			BIT(5)
1188c0984e5SSebastian Reichel #define IRQSTAT_F				0x3a
1198c0984e5SSebastian Reichel 
1208c0984e5SSebastian Reichel /* Status registers */
1218c0984e5SSebastian Reichel #define STAT_A					0x3b
1228c0984e5SSebastian Reichel #define STAT_A_FLOAT_VOLTAGE_MASK		0x3f
1238c0984e5SSebastian Reichel #define STAT_B					0x3c
1248c0984e5SSebastian Reichel #define STAT_C					0x3d
1258c0984e5SSebastian Reichel #define STAT_C_CHG_ENABLED			BIT(0)
1268c0984e5SSebastian Reichel #define STAT_C_HOLDOFF_STAT			BIT(3)
1278c0984e5SSebastian Reichel #define STAT_C_CHG_MASK				0x06
1288c0984e5SSebastian Reichel #define STAT_C_CHG_SHIFT			1
1298c0984e5SSebastian Reichel #define STAT_C_CHG_TERM				BIT(5)
1308c0984e5SSebastian Reichel #define STAT_C_CHARGER_ERROR			BIT(6)
1318c0984e5SSebastian Reichel #define STAT_E					0x3f
1328c0984e5SSebastian Reichel 
1338c0984e5SSebastian Reichel #define SMB347_MAX_REGISTER			0x3f
1348c0984e5SSebastian Reichel 
1358c0984e5SSebastian Reichel /**
1368c0984e5SSebastian Reichel  * struct smb347_charger - smb347 charger instance
1378c0984e5SSebastian Reichel  * @dev: pointer to device
1388c0984e5SSebastian Reichel  * @regmap: pointer to driver regmap
1398c0984e5SSebastian Reichel  * @mains: power_supply instance for AC/DC power
1408c0984e5SSebastian Reichel  * @usb: power_supply instance for USB power
141565efae9SDmitry Osipenko  * @usb_rdev: USB VBUS regulator device
142de76fd29SDavid Heidelberg  * @id: SMB charger ID
1438c0984e5SSebastian Reichel  * @mains_online: is AC/DC input connected
1448c0984e5SSebastian Reichel  * @usb_online: is USB input connected
14569963126SDmitry Osipenko  * @irq_unsupported: is interrupt unsupported by SMB hardware
146565efae9SDmitry Osipenko  * @usb_vbus_enabled: is USB VBUS powered by SMB charger
147b6f3e21bSSebastian Reichel  * @max_charge_current: maximum current (in uA) the battery can be charged
148b6f3e21bSSebastian Reichel  * @max_charge_voltage: maximum voltage (in uV) the battery can be charged
149b6f3e21bSSebastian Reichel  * @pre_charge_current: current (in uA) to use in pre-charging phase
150b6f3e21bSSebastian Reichel  * @termination_current: current (in uA) used to determine when the
151b6f3e21bSSebastian Reichel  *			 charging cycle terminates
152b6f3e21bSSebastian Reichel  * @pre_to_fast_voltage: voltage (in uV) treshold used for transitioning to
153b6f3e21bSSebastian Reichel  *			 pre-charge to fast charge mode
154b6f3e21bSSebastian Reichel  * @mains_current_limit: maximum input current drawn from AC/DC input (in uA)
155b6f3e21bSSebastian Reichel  * @usb_hc_current_limit: maximum input high current (in uA) drawn from USB
156b6f3e21bSSebastian Reichel  *			  input
157b6f3e21bSSebastian Reichel  * @chip_temp_threshold: die temperature where device starts limiting charge
158b6f3e21bSSebastian Reichel  *			 current [%100 - %130] (in degree C)
159b6f3e21bSSebastian Reichel  * @soft_cold_temp_limit: soft cold temperature limit [%0 - %15] (in degree C),
160b6f3e21bSSebastian Reichel  *			  granularity is 5 deg C.
161b6f3e21bSSebastian Reichel  * @soft_hot_temp_limit: soft hot temperature limit [%40 - %55] (in degree  C),
162b6f3e21bSSebastian Reichel  *			 granularity is 5 deg C.
163b6f3e21bSSebastian Reichel  * @hard_cold_temp_limit: hard cold temperature limit [%-5 - %10] (in degree C),
164b6f3e21bSSebastian Reichel  *			  granularity is 5 deg C.
165b6f3e21bSSebastian Reichel  * @hard_hot_temp_limit: hard hot temperature limit [%50 - %65] (in degree C),
166b6f3e21bSSebastian Reichel  *			 granularity is 5 deg C.
167b6f3e21bSSebastian Reichel  * @suspend_on_hard_temp_limit: suspend charging when hard limit is hit
168b6f3e21bSSebastian Reichel  * @soft_temp_limit_compensation: compensation method when soft temperature
169b6f3e21bSSebastian Reichel  *				  limit is hit
170b6f3e21bSSebastian Reichel  * @charge_current_compensation: current (in uA) for charging compensation
171b6f3e21bSSebastian Reichel  *				 current when temperature hits soft limits
172b6f3e21bSSebastian Reichel  * @use_mains: AC/DC input can be used
173b6f3e21bSSebastian Reichel  * @use_usb: USB input can be used
174b6f3e21bSSebastian Reichel  * @use_usb_otg: USB OTG output can be used (not implemented yet)
175b6f3e21bSSebastian Reichel  * @enable_control: how charging enable/disable is controlled
176b6f3e21bSSebastian Reichel  *		    (driver/pin controls)
177565efae9SDmitry Osipenko  * @inok_polarity: polarity of INOK signal which denotes presence of external
178565efae9SDmitry Osipenko  *		   power supply
179b6f3e21bSSebastian Reichel  *
180b6f3e21bSSebastian Reichel  * @use_main, @use_usb, and @use_usb_otg are means to enable/disable
181b6f3e21bSSebastian Reichel  * hardware support for these. This is useful when we want to have for
182b6f3e21bSSebastian Reichel  * example OTG charging controlled via OTG transceiver driver and not by
183b6f3e21bSSebastian Reichel  * the SMB347 hardware.
184b6f3e21bSSebastian Reichel  *
185b6f3e21bSSebastian Reichel  * Hard and soft temperature limit values are given as described in the
186b6f3e21bSSebastian Reichel  * device data sheet and assuming NTC beta value is %3750. Even if this is
187b6f3e21bSSebastian Reichel  * not the case, these values should be used. They can be mapped to the
188b6f3e21bSSebastian Reichel  * corresponding NTC beta values with the help of table %2 in the data
189b6f3e21bSSebastian Reichel  * sheet. So for example if NTC beta is %3375 and we want to program hard
190b6f3e21bSSebastian Reichel  * hot limit to be %53 deg C, @hard_hot_temp_limit should be set to %50.
191b6f3e21bSSebastian Reichel  *
192b6f3e21bSSebastian Reichel  * If zero value is given in any of the current and voltage values, the
193b6f3e21bSSebastian Reichel  * factory programmed default will be used. For soft/hard temperature
194b6f3e21bSSebastian Reichel  * values, pass in %SMB3XX_TEMP_USE_DEFAULT instead.
1958c0984e5SSebastian Reichel  */
1968c0984e5SSebastian Reichel struct smb347_charger {
1978c0984e5SSebastian Reichel 	struct device		*dev;
1988c0984e5SSebastian Reichel 	struct regmap		*regmap;
1998c0984e5SSebastian Reichel 	struct power_supply	*mains;
2008c0984e5SSebastian Reichel 	struct power_supply	*usb;
201565efae9SDmitry Osipenko 	struct regulator_dev	*usb_rdev;
202de76fd29SDavid Heidelberg 	unsigned int		id;
2038c0984e5SSebastian Reichel 	bool			mains_online;
2048c0984e5SSebastian Reichel 	bool			usb_online;
20569963126SDmitry Osipenko 	bool			irq_unsupported;
206565efae9SDmitry Osipenko 	bool			usb_vbus_enabled;
207b6f3e21bSSebastian Reichel 
208b6f3e21bSSebastian Reichel 	unsigned int		max_charge_current;
209b6f3e21bSSebastian Reichel 	unsigned int		max_charge_voltage;
210b6f3e21bSSebastian Reichel 	unsigned int		pre_charge_current;
211b6f3e21bSSebastian Reichel 	unsigned int		termination_current;
212b6f3e21bSSebastian Reichel 	unsigned int		pre_to_fast_voltage;
213b6f3e21bSSebastian Reichel 	unsigned int		mains_current_limit;
214b6f3e21bSSebastian Reichel 	unsigned int		usb_hc_current_limit;
215b6f3e21bSSebastian Reichel 	unsigned int		chip_temp_threshold;
216b6f3e21bSSebastian Reichel 	int			soft_cold_temp_limit;
217b6f3e21bSSebastian Reichel 	int			soft_hot_temp_limit;
218b6f3e21bSSebastian Reichel 	int			hard_cold_temp_limit;
219b6f3e21bSSebastian Reichel 	int			hard_hot_temp_limit;
220b6f3e21bSSebastian Reichel 	bool			suspend_on_hard_temp_limit;
221b6f3e21bSSebastian Reichel 	unsigned int		soft_temp_limit_compensation;
222b6f3e21bSSebastian Reichel 	unsigned int		charge_current_compensation;
223b6f3e21bSSebastian Reichel 	bool			use_mains;
224b6f3e21bSSebastian Reichel 	bool			use_usb;
225b6f3e21bSSebastian Reichel 	bool			use_usb_otg;
226b6f3e21bSSebastian Reichel 	unsigned int		enable_control;
227565efae9SDmitry Osipenko 	unsigned int		inok_polarity;
2288c0984e5SSebastian Reichel };
2298c0984e5SSebastian Reichel 
230de76fd29SDavid Heidelberg enum smb_charger_chipid {
231de76fd29SDavid Heidelberg 	SMB345,
232de76fd29SDavid Heidelberg 	SMB347,
233de76fd29SDavid Heidelberg 	SMB358,
234de76fd29SDavid Heidelberg 	NUM_CHIP_TYPES,
2358c0984e5SSebastian Reichel };
2368c0984e5SSebastian Reichel 
237de76fd29SDavid Heidelberg /* Fast charge current in uA */
238de76fd29SDavid Heidelberg static const unsigned int fcc_tbl[NUM_CHIP_TYPES][8] = {
239de76fd29SDavid Heidelberg 	[SMB345] = {  200000,  450000,  600000,  900000,
240de76fd29SDavid Heidelberg 		     1300000, 1500000, 1800000, 2000000 },
241de76fd29SDavid Heidelberg 	[SMB347] = {  700000,  900000, 1200000, 1500000,
242de76fd29SDavid Heidelberg 		     1800000, 2000000, 2200000, 2500000 },
243de76fd29SDavid Heidelberg 	[SMB358] = {  200000,  450000,  600000,  900000,
244de76fd29SDavid Heidelberg 		     1300000, 1500000, 1800000, 2000000 },
245de76fd29SDavid Heidelberg };
2468c0984e5SSebastian Reichel /* Pre-charge current in uA */
247de76fd29SDavid Heidelberg static const unsigned int pcc_tbl[NUM_CHIP_TYPES][4] = {
248de76fd29SDavid Heidelberg 	[SMB345] = { 150000, 250000, 350000, 450000 },
249de76fd29SDavid Heidelberg 	[SMB347] = { 100000, 150000, 200000, 250000 },
250de76fd29SDavid Heidelberg 	[SMB358] = { 150000, 250000, 350000, 450000 },
2518c0984e5SSebastian Reichel };
2528c0984e5SSebastian Reichel 
2538c0984e5SSebastian Reichel /* Termination current in uA */
254de76fd29SDavid Heidelberg static const unsigned int tc_tbl[NUM_CHIP_TYPES][8] = {
255de76fd29SDavid Heidelberg 	[SMB345] = {  30000,  40000,  60000,  80000,
256de76fd29SDavid Heidelberg 		     100000, 125000, 150000, 200000 },
257de76fd29SDavid Heidelberg 	[SMB347] = {  37500,  50000, 100000, 150000,
258de76fd29SDavid Heidelberg 		     200000, 250000, 500000, 600000 },
259de76fd29SDavid Heidelberg 	[SMB358] = {  30000,  40000,  60000,  80000,
260de76fd29SDavid Heidelberg 		     100000, 125000, 150000, 200000 },
2618c0984e5SSebastian Reichel };
2628c0984e5SSebastian Reichel 
2638c0984e5SSebastian Reichel /* Input current limit in uA */
264de76fd29SDavid Heidelberg static const unsigned int icl_tbl[NUM_CHIP_TYPES][10] = {
265de76fd29SDavid Heidelberg 	[SMB345] = {  300000,  500000,  700000, 1000000, 1500000,
266de76fd29SDavid Heidelberg 		     1800000, 2000000, 2000000, 2000000, 2000000 },
267de76fd29SDavid Heidelberg 	[SMB347] = {  300000,  500000,  700000,  900000, 1200000,
268de76fd29SDavid Heidelberg 		     1500000, 1800000, 2000000, 2200000, 2500000 },
269de76fd29SDavid Heidelberg 	[SMB358] = {  300000,  500000,  700000, 1000000, 1500000,
270de76fd29SDavid Heidelberg 		     1800000, 2000000, 2000000, 2000000, 2000000 },
2718c0984e5SSebastian Reichel };
2728c0984e5SSebastian Reichel 
2738c0984e5SSebastian Reichel /* Charge current compensation in uA */
274de76fd29SDavid Heidelberg static const unsigned int ccc_tbl[NUM_CHIP_TYPES][4] = {
275de76fd29SDavid Heidelberg 	[SMB345] = {  200000,  450000,  600000,  900000 },
276de76fd29SDavid Heidelberg 	[SMB347] = {  250000,  700000,  900000, 1200000 },
277de76fd29SDavid Heidelberg 	[SMB358] = {  200000,  450000,  600000,  900000 },
2788c0984e5SSebastian Reichel };
2798c0984e5SSebastian Reichel 
2808c0984e5SSebastian Reichel /* Convert register value to current using lookup table */
hw_to_current(const unsigned int * tbl,size_t size,unsigned int val)2818c0984e5SSebastian Reichel static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
2828c0984e5SSebastian Reichel {
2838c0984e5SSebastian Reichel 	if (val >= size)
2848c0984e5SSebastian Reichel 		return -EINVAL;
2858c0984e5SSebastian Reichel 	return tbl[val];
2868c0984e5SSebastian Reichel }
2878c0984e5SSebastian Reichel 
2888c0984e5SSebastian Reichel /* Convert current to register value using lookup table */
current_to_hw(const unsigned int * tbl,size_t size,unsigned int val)2898c0984e5SSebastian Reichel static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
2908c0984e5SSebastian Reichel {
2918c0984e5SSebastian Reichel 	size_t i;
2928c0984e5SSebastian Reichel 
2938c0984e5SSebastian Reichel 	for (i = 0; i < size; i++)
2948c0984e5SSebastian Reichel 		if (val < tbl[i])
2958c0984e5SSebastian Reichel 			break;
2968c0984e5SSebastian Reichel 	return i > 0 ? i - 1 : -EINVAL;
2978c0984e5SSebastian Reichel }
2988c0984e5SSebastian Reichel 
2998c0984e5SSebastian Reichel /**
3008c0984e5SSebastian Reichel  * smb347_update_ps_status - refreshes the power source status
3018c0984e5SSebastian Reichel  * @smb: pointer to smb347 charger instance
3028c0984e5SSebastian Reichel  *
3038c0984e5SSebastian Reichel  * Function checks whether any power source is connected to the charger and
3048c0984e5SSebastian Reichel  * updates internal state accordingly. If there is a change to previous state
3058c0984e5SSebastian Reichel  * function returns %1, otherwise %0 and negative errno in case of errror.
3068c0984e5SSebastian Reichel  */
smb347_update_ps_status(struct smb347_charger * smb)3078c0984e5SSebastian Reichel static int smb347_update_ps_status(struct smb347_charger *smb)
3088c0984e5SSebastian Reichel {
3098c0984e5SSebastian Reichel 	bool usb = false;
3108c0984e5SSebastian Reichel 	bool dc = false;
3118c0984e5SSebastian Reichel 	unsigned int val;
3128c0984e5SSebastian Reichel 	int ret;
3138c0984e5SSebastian Reichel 
3148c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, IRQSTAT_E, &val);
3158c0984e5SSebastian Reichel 	if (ret < 0)
3168c0984e5SSebastian Reichel 		return ret;
3178c0984e5SSebastian Reichel 
3188c0984e5SSebastian Reichel 	/*
3198c0984e5SSebastian Reichel 	 * Dc and usb are set depending on whether they are enabled in
3208c0984e5SSebastian Reichel 	 * platform data _and_ whether corresponding undervoltage is set.
3218c0984e5SSebastian Reichel 	 */
322b6f3e21bSSebastian Reichel 	if (smb->use_mains)
3238c0984e5SSebastian Reichel 		dc = !(val & IRQSTAT_E_DCIN_UV_STAT);
324b6f3e21bSSebastian Reichel 	if (smb->use_usb)
3258c0984e5SSebastian Reichel 		usb = !(val & IRQSTAT_E_USBIN_UV_STAT);
3268c0984e5SSebastian Reichel 
3278c0984e5SSebastian Reichel 	ret = smb->mains_online != dc || smb->usb_online != usb;
3288c0984e5SSebastian Reichel 	smb->mains_online = dc;
3298c0984e5SSebastian Reichel 	smb->usb_online = usb;
3308c0984e5SSebastian Reichel 
3318c0984e5SSebastian Reichel 	return ret;
3328c0984e5SSebastian Reichel }
3338c0984e5SSebastian Reichel 
3348c0984e5SSebastian Reichel /*
3358c0984e5SSebastian Reichel  * smb347_is_ps_online - returns whether input power source is connected
3368c0984e5SSebastian Reichel  * @smb: pointer to smb347 charger instance
3378c0984e5SSebastian Reichel  *
3388c0984e5SSebastian Reichel  * Returns %true if input power source is connected. Note that this is
3398c0984e5SSebastian Reichel  * dependent on what platform has configured for usable power sources. For
3408c0984e5SSebastian Reichel  * example if USB is disabled, this will return %false even if the USB cable
3418c0984e5SSebastian Reichel  * is connected.
3428c0984e5SSebastian Reichel  */
smb347_is_ps_online(struct smb347_charger * smb)3438c0984e5SSebastian Reichel static bool smb347_is_ps_online(struct smb347_charger *smb)
3448c0984e5SSebastian Reichel {
34599298de5SDmitry Osipenko 	return smb->usb_online || smb->mains_online;
3468c0984e5SSebastian Reichel }
3478c0984e5SSebastian Reichel 
3488c0984e5SSebastian Reichel /**
3498c0984e5SSebastian Reichel  * smb347_charging_status - returns status of charging
3508c0984e5SSebastian Reichel  * @smb: pointer to smb347 charger instance
3518c0984e5SSebastian Reichel  *
3528c0984e5SSebastian Reichel  * Function returns charging status. %0 means no charging is in progress,
3538c0984e5SSebastian Reichel  * %1 means pre-charging, %2 fast-charging and %3 taper-charging.
3548c0984e5SSebastian Reichel  */
smb347_charging_status(struct smb347_charger * smb)3558c0984e5SSebastian Reichel static int smb347_charging_status(struct smb347_charger *smb)
3568c0984e5SSebastian Reichel {
3578c0984e5SSebastian Reichel 	unsigned int val;
3588c0984e5SSebastian Reichel 	int ret;
3598c0984e5SSebastian Reichel 
3608c0984e5SSebastian Reichel 	if (!smb347_is_ps_online(smb))
3618c0984e5SSebastian Reichel 		return 0;
3628c0984e5SSebastian Reichel 
3638c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, STAT_C, &val);
3648c0984e5SSebastian Reichel 	if (ret < 0)
3658c0984e5SSebastian Reichel 		return 0;
3668c0984e5SSebastian Reichel 
3678c0984e5SSebastian Reichel 	return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
3688c0984e5SSebastian Reichel }
3698c0984e5SSebastian Reichel 
smb347_charging_set(struct smb347_charger * smb,bool enable)3708c0984e5SSebastian Reichel static int smb347_charging_set(struct smb347_charger *smb, bool enable)
3718c0984e5SSebastian Reichel {
372b6f3e21bSSebastian Reichel 	if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) {
3738c0984e5SSebastian Reichel 		dev_dbg(smb->dev, "charging enable/disable in SW disabled\n");
3748c0984e5SSebastian Reichel 		return 0;
3758c0984e5SSebastian Reichel 	}
3768c0984e5SSebastian Reichel 
377565efae9SDmitry Osipenko 	if (enable && smb->usb_vbus_enabled) {
378565efae9SDmitry Osipenko 		dev_dbg(smb->dev, "charging not enabled because USB is in host mode\n");
379565efae9SDmitry Osipenko 		return 0;
380565efae9SDmitry Osipenko 	}
381565efae9SDmitry Osipenko 
38217e7bc53SDmitry Osipenko 	return regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
3838c0984e5SSebastian Reichel 				  enable ? CMD_A_CHG_ENABLED : 0);
3848c0984e5SSebastian Reichel }
3858c0984e5SSebastian Reichel 
smb347_charging_enable(struct smb347_charger * smb)3868c0984e5SSebastian Reichel static inline int smb347_charging_enable(struct smb347_charger *smb)
3878c0984e5SSebastian Reichel {
3888c0984e5SSebastian Reichel 	return smb347_charging_set(smb, true);
3898c0984e5SSebastian Reichel }
3908c0984e5SSebastian Reichel 
smb347_charging_disable(struct smb347_charger * smb)3918c0984e5SSebastian Reichel static inline int smb347_charging_disable(struct smb347_charger *smb)
3928c0984e5SSebastian Reichel {
3938c0984e5SSebastian Reichel 	return smb347_charging_set(smb, false);
3948c0984e5SSebastian Reichel }
3958c0984e5SSebastian Reichel 
smb347_start_stop_charging(struct smb347_charger * smb)3968c0984e5SSebastian Reichel static int smb347_start_stop_charging(struct smb347_charger *smb)
3978c0984e5SSebastian Reichel {
3988c0984e5SSebastian Reichel 	int ret;
3998c0984e5SSebastian Reichel 
4008c0984e5SSebastian Reichel 	/*
4018c0984e5SSebastian Reichel 	 * Depending on whether valid power source is connected or not, we
4028c0984e5SSebastian Reichel 	 * disable or enable the charging. We do it manually because it
4038c0984e5SSebastian Reichel 	 * depends on how the platform has configured the valid inputs.
4048c0984e5SSebastian Reichel 	 */
4058c0984e5SSebastian Reichel 	if (smb347_is_ps_online(smb)) {
4068c0984e5SSebastian Reichel 		ret = smb347_charging_enable(smb);
4078c0984e5SSebastian Reichel 		if (ret < 0)
4088c0984e5SSebastian Reichel 			dev_err(smb->dev, "failed to enable charging\n");
4098c0984e5SSebastian Reichel 	} else {
4108c0984e5SSebastian Reichel 		ret = smb347_charging_disable(smb);
4118c0984e5SSebastian Reichel 		if (ret < 0)
4128c0984e5SSebastian Reichel 			dev_err(smb->dev, "failed to disable charging\n");
4138c0984e5SSebastian Reichel 	}
4148c0984e5SSebastian Reichel 
4158c0984e5SSebastian Reichel 	return ret;
4168c0984e5SSebastian Reichel }
4178c0984e5SSebastian Reichel 
smb347_set_charge_current(struct smb347_charger * smb)4188c0984e5SSebastian Reichel static int smb347_set_charge_current(struct smb347_charger *smb)
4198c0984e5SSebastian Reichel {
420de76fd29SDavid Heidelberg 	unsigned int id = smb->id;
4218c0984e5SSebastian Reichel 	int ret;
4228c0984e5SSebastian Reichel 
423b6f3e21bSSebastian Reichel 	if (smb->max_charge_current) {
424de76fd29SDavid Heidelberg 		ret = current_to_hw(fcc_tbl[id], ARRAY_SIZE(fcc_tbl[id]),
425b6f3e21bSSebastian Reichel 				    smb->max_charge_current);
4268c0984e5SSebastian Reichel 		if (ret < 0)
4278c0984e5SSebastian Reichel 			return ret;
4288c0984e5SSebastian Reichel 
4298c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
4308c0984e5SSebastian Reichel 					 CFG_CHARGE_CURRENT_FCC_MASK,
4318c0984e5SSebastian Reichel 					 ret << CFG_CHARGE_CURRENT_FCC_SHIFT);
4328c0984e5SSebastian Reichel 		if (ret < 0)
4338c0984e5SSebastian Reichel 			return ret;
4348c0984e5SSebastian Reichel 	}
4358c0984e5SSebastian Reichel 
436b6f3e21bSSebastian Reichel 	if (smb->pre_charge_current) {
437de76fd29SDavid Heidelberg 		ret = current_to_hw(pcc_tbl[id], ARRAY_SIZE(pcc_tbl[id]),
438b6f3e21bSSebastian Reichel 				    smb->pre_charge_current);
4398c0984e5SSebastian Reichel 		if (ret < 0)
4408c0984e5SSebastian Reichel 			return ret;
4418c0984e5SSebastian Reichel 
4428c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
4438c0984e5SSebastian Reichel 					 CFG_CHARGE_CURRENT_PCC_MASK,
4448c0984e5SSebastian Reichel 					 ret << CFG_CHARGE_CURRENT_PCC_SHIFT);
4458c0984e5SSebastian Reichel 		if (ret < 0)
4468c0984e5SSebastian Reichel 			return ret;
4478c0984e5SSebastian Reichel 	}
4488c0984e5SSebastian Reichel 
449b6f3e21bSSebastian Reichel 	if (smb->termination_current) {
450de76fd29SDavid Heidelberg 		ret = current_to_hw(tc_tbl[id], ARRAY_SIZE(tc_tbl[id]),
451b6f3e21bSSebastian Reichel 				    smb->termination_current);
4528c0984e5SSebastian Reichel 		if (ret < 0)
4538c0984e5SSebastian Reichel 			return ret;
4548c0984e5SSebastian Reichel 
4558c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
4568c0984e5SSebastian Reichel 					 CFG_CHARGE_CURRENT_TC_MASK, ret);
4578c0984e5SSebastian Reichel 		if (ret < 0)
4588c0984e5SSebastian Reichel 			return ret;
4598c0984e5SSebastian Reichel 	}
4608c0984e5SSebastian Reichel 
4618c0984e5SSebastian Reichel 	return 0;
4628c0984e5SSebastian Reichel }
4638c0984e5SSebastian Reichel 
smb347_set_current_limits(struct smb347_charger * smb)4648c0984e5SSebastian Reichel static int smb347_set_current_limits(struct smb347_charger *smb)
4658c0984e5SSebastian Reichel {
466de76fd29SDavid Heidelberg 	unsigned int id = smb->id;
4678c0984e5SSebastian Reichel 	int ret;
4688c0984e5SSebastian Reichel 
469b6f3e21bSSebastian Reichel 	if (smb->mains_current_limit) {
470de76fd29SDavid Heidelberg 		ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]),
471b6f3e21bSSebastian Reichel 				    smb->mains_current_limit);
4728c0984e5SSebastian Reichel 		if (ret < 0)
4738c0984e5SSebastian Reichel 			return ret;
4748c0984e5SSebastian Reichel 
4758c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
4768c0984e5SSebastian Reichel 					 CFG_CURRENT_LIMIT_DC_MASK,
4778c0984e5SSebastian Reichel 					 ret << CFG_CURRENT_LIMIT_DC_SHIFT);
4788c0984e5SSebastian Reichel 		if (ret < 0)
4798c0984e5SSebastian Reichel 			return ret;
4808c0984e5SSebastian Reichel 	}
4818c0984e5SSebastian Reichel 
482b6f3e21bSSebastian Reichel 	if (smb->usb_hc_current_limit) {
483de76fd29SDavid Heidelberg 		ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]),
484b6f3e21bSSebastian Reichel 				    smb->usb_hc_current_limit);
4858c0984e5SSebastian Reichel 		if (ret < 0)
4868c0984e5SSebastian Reichel 			return ret;
4878c0984e5SSebastian Reichel 
4888c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
4898c0984e5SSebastian Reichel 					 CFG_CURRENT_LIMIT_USB_MASK, ret);
4908c0984e5SSebastian Reichel 		if (ret < 0)
4918c0984e5SSebastian Reichel 			return ret;
4928c0984e5SSebastian Reichel 	}
4938c0984e5SSebastian Reichel 
4948c0984e5SSebastian Reichel 	return 0;
4958c0984e5SSebastian Reichel }
4968c0984e5SSebastian Reichel 
smb347_set_voltage_limits(struct smb347_charger * smb)4978c0984e5SSebastian Reichel static int smb347_set_voltage_limits(struct smb347_charger *smb)
4988c0984e5SSebastian Reichel {
4998c0984e5SSebastian Reichel 	int ret;
5008c0984e5SSebastian Reichel 
501b6f3e21bSSebastian Reichel 	if (smb->pre_to_fast_voltage) {
502b6f3e21bSSebastian Reichel 		ret = smb->pre_to_fast_voltage;
5038c0984e5SSebastian Reichel 
5048c0984e5SSebastian Reichel 		/* uV */
5058c0984e5SSebastian Reichel 		ret = clamp_val(ret, 2400000, 3000000) - 2400000;
5068c0984e5SSebastian Reichel 		ret /= 200000;
5078c0984e5SSebastian Reichel 
5088c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
5098c0984e5SSebastian Reichel 				CFG_FLOAT_VOLTAGE_THRESHOLD_MASK,
5108c0984e5SSebastian Reichel 				ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT);
5118c0984e5SSebastian Reichel 		if (ret < 0)
5128c0984e5SSebastian Reichel 			return ret;
5138c0984e5SSebastian Reichel 	}
5148c0984e5SSebastian Reichel 
515b6f3e21bSSebastian Reichel 	if (smb->max_charge_voltage) {
516b6f3e21bSSebastian Reichel 		ret = smb->max_charge_voltage;
5178c0984e5SSebastian Reichel 
5188c0984e5SSebastian Reichel 		/* uV */
5198c0984e5SSebastian Reichel 		ret = clamp_val(ret, 3500000, 4500000) - 3500000;
5208c0984e5SSebastian Reichel 		ret /= 20000;
5218c0984e5SSebastian Reichel 
5228c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
5238c0984e5SSebastian Reichel 					 CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret);
5248c0984e5SSebastian Reichel 		if (ret < 0)
5258c0984e5SSebastian Reichel 			return ret;
5268c0984e5SSebastian Reichel 	}
5278c0984e5SSebastian Reichel 
5288c0984e5SSebastian Reichel 	return 0;
5298c0984e5SSebastian Reichel }
5308c0984e5SSebastian Reichel 
smb347_set_temp_limits(struct smb347_charger * smb)5318c0984e5SSebastian Reichel static int smb347_set_temp_limits(struct smb347_charger *smb)
5328c0984e5SSebastian Reichel {
533de76fd29SDavid Heidelberg 	unsigned int id = smb->id;
5348c0984e5SSebastian Reichel 	bool enable_therm_monitor = false;
5358c0984e5SSebastian Reichel 	int ret = 0;
5368c0984e5SSebastian Reichel 	int val;
5378c0984e5SSebastian Reichel 
538b6f3e21bSSebastian Reichel 	if (smb->chip_temp_threshold) {
539b6f3e21bSSebastian Reichel 		val = smb->chip_temp_threshold;
5408c0984e5SSebastian Reichel 
5418c0984e5SSebastian Reichel 		/* degree C */
5428c0984e5SSebastian Reichel 		val = clamp_val(val, 100, 130) - 100;
5438c0984e5SSebastian Reichel 		val /= 10;
5448c0984e5SSebastian Reichel 
5458c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_OTG,
5468c0984e5SSebastian Reichel 					 CFG_OTG_TEMP_THRESHOLD_MASK,
5478c0984e5SSebastian Reichel 					 val << CFG_OTG_TEMP_THRESHOLD_SHIFT);
5488c0984e5SSebastian Reichel 		if (ret < 0)
5498c0984e5SSebastian Reichel 			return ret;
5508c0984e5SSebastian Reichel 	}
5518c0984e5SSebastian Reichel 
552b6f3e21bSSebastian Reichel 	if (smb->soft_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) {
553b6f3e21bSSebastian Reichel 		val = smb->soft_cold_temp_limit;
5548c0984e5SSebastian Reichel 
5558c0984e5SSebastian Reichel 		val = clamp_val(val, 0, 15);
5568c0984e5SSebastian Reichel 		val /= 5;
5578c0984e5SSebastian Reichel 		/* this goes from higher to lower so invert the value */
5588c0984e5SSebastian Reichel 		val = ~val & 0x3;
5598c0984e5SSebastian Reichel 
5608c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
5618c0984e5SSebastian Reichel 					 CFG_TEMP_LIMIT_SOFT_COLD_MASK,
5628c0984e5SSebastian Reichel 					 val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT);
5638c0984e5SSebastian Reichel 		if (ret < 0)
5648c0984e5SSebastian Reichel 			return ret;
5658c0984e5SSebastian Reichel 
5668c0984e5SSebastian Reichel 		enable_therm_monitor = true;
5678c0984e5SSebastian Reichel 	}
5688c0984e5SSebastian Reichel 
569b6f3e21bSSebastian Reichel 	if (smb->soft_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) {
570b6f3e21bSSebastian Reichel 		val = smb->soft_hot_temp_limit;
5718c0984e5SSebastian Reichel 
5728c0984e5SSebastian Reichel 		val = clamp_val(val, 40, 55) - 40;
5738c0984e5SSebastian Reichel 		val /= 5;
5748c0984e5SSebastian Reichel 
5758c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
5768c0984e5SSebastian Reichel 					 CFG_TEMP_LIMIT_SOFT_HOT_MASK,
5778c0984e5SSebastian Reichel 					 val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT);
5788c0984e5SSebastian Reichel 		if (ret < 0)
5798c0984e5SSebastian Reichel 			return ret;
5808c0984e5SSebastian Reichel 
5818c0984e5SSebastian Reichel 		enable_therm_monitor = true;
5828c0984e5SSebastian Reichel 	}
5838c0984e5SSebastian Reichel 
584b6f3e21bSSebastian Reichel 	if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) {
585b6f3e21bSSebastian Reichel 		val = smb->hard_cold_temp_limit;
5868c0984e5SSebastian Reichel 
5878c0984e5SSebastian Reichel 		val = clamp_val(val, -5, 10) + 5;
5888c0984e5SSebastian Reichel 		val /= 5;
5898c0984e5SSebastian Reichel 		/* this goes from higher to lower so invert the value */
5908c0984e5SSebastian Reichel 		val = ~val & 0x3;
5918c0984e5SSebastian Reichel 
5928c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
5938c0984e5SSebastian Reichel 					 CFG_TEMP_LIMIT_HARD_COLD_MASK,
5948c0984e5SSebastian Reichel 					 val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT);
5958c0984e5SSebastian Reichel 		if (ret < 0)
5968c0984e5SSebastian Reichel 			return ret;
5978c0984e5SSebastian Reichel 
5988c0984e5SSebastian Reichel 		enable_therm_monitor = true;
5998c0984e5SSebastian Reichel 	}
6008c0984e5SSebastian Reichel 
601b6f3e21bSSebastian Reichel 	if (smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) {
602b6f3e21bSSebastian Reichel 		val = smb->hard_hot_temp_limit;
6038c0984e5SSebastian Reichel 
6048c0984e5SSebastian Reichel 		val = clamp_val(val, 50, 65) - 50;
6058c0984e5SSebastian Reichel 		val /= 5;
6068c0984e5SSebastian Reichel 
6078c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
6088c0984e5SSebastian Reichel 					 CFG_TEMP_LIMIT_HARD_HOT_MASK,
6098c0984e5SSebastian Reichel 					 val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT);
6108c0984e5SSebastian Reichel 		if (ret < 0)
6118c0984e5SSebastian Reichel 			return ret;
6128c0984e5SSebastian Reichel 
6138c0984e5SSebastian Reichel 		enable_therm_monitor = true;
6148c0984e5SSebastian Reichel 	}
6158c0984e5SSebastian Reichel 
6168c0984e5SSebastian Reichel 	/*
6178c0984e5SSebastian Reichel 	 * If any of the temperature limits are set, we also enable the
6188c0984e5SSebastian Reichel 	 * thermistor monitoring.
6198c0984e5SSebastian Reichel 	 *
6208c0984e5SSebastian Reichel 	 * When soft limits are hit, the device will start to compensate
6218c0984e5SSebastian Reichel 	 * current and/or voltage depending on the configuration.
6228c0984e5SSebastian Reichel 	 *
6238c0984e5SSebastian Reichel 	 * When hard limit is hit, the device will suspend charging
6248c0984e5SSebastian Reichel 	 * depending on the configuration.
6258c0984e5SSebastian Reichel 	 */
6268c0984e5SSebastian Reichel 	if (enable_therm_monitor) {
6278c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_THERM,
6288c0984e5SSebastian Reichel 					 CFG_THERM_MONITOR_DISABLED, 0);
6298c0984e5SSebastian Reichel 		if (ret < 0)
6308c0984e5SSebastian Reichel 			return ret;
6318c0984e5SSebastian Reichel 	}
6328c0984e5SSebastian Reichel 
633b6f3e21bSSebastian Reichel 	if (smb->suspend_on_hard_temp_limit) {
6348c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
6358c0984e5SSebastian Reichel 				 CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0);
6368c0984e5SSebastian Reichel 		if (ret < 0)
6378c0984e5SSebastian Reichel 			return ret;
6388c0984e5SSebastian Reichel 	}
6398c0984e5SSebastian Reichel 
640b6f3e21bSSebastian Reichel 	if (smb->soft_temp_limit_compensation !=
641b6f3e21bSSebastian Reichel 	    SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT) {
642b6f3e21bSSebastian Reichel 		val = smb->soft_temp_limit_compensation & 0x3;
6438c0984e5SSebastian Reichel 
6448c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_THERM,
6458c0984e5SSebastian Reichel 				 CFG_THERM_SOFT_HOT_COMPENSATION_MASK,
6468c0984e5SSebastian Reichel 				 val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT);
6478c0984e5SSebastian Reichel 		if (ret < 0)
6488c0984e5SSebastian Reichel 			return ret;
6498c0984e5SSebastian Reichel 
6508c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_THERM,
6518c0984e5SSebastian Reichel 				 CFG_THERM_SOFT_COLD_COMPENSATION_MASK,
6528c0984e5SSebastian Reichel 				 val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT);
6538c0984e5SSebastian Reichel 		if (ret < 0)
6548c0984e5SSebastian Reichel 			return ret;
6558c0984e5SSebastian Reichel 	}
6568c0984e5SSebastian Reichel 
657b6f3e21bSSebastian Reichel 	if (smb->charge_current_compensation) {
658de76fd29SDavid Heidelberg 		val = current_to_hw(ccc_tbl[id], ARRAY_SIZE(ccc_tbl[id]),
659b6f3e21bSSebastian Reichel 				    smb->charge_current_compensation);
6608c0984e5SSebastian Reichel 		if (val < 0)
6618c0984e5SSebastian Reichel 			return val;
6628c0984e5SSebastian Reichel 
6638c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CFG_OTG,
6648c0984e5SSebastian Reichel 				CFG_OTG_CC_COMPENSATION_MASK,
6658c0984e5SSebastian Reichel 				(val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT);
6668c0984e5SSebastian Reichel 		if (ret < 0)
6678c0984e5SSebastian Reichel 			return ret;
6688c0984e5SSebastian Reichel 	}
6698c0984e5SSebastian Reichel 
6708c0984e5SSebastian Reichel 	return ret;
6718c0984e5SSebastian Reichel }
6728c0984e5SSebastian Reichel 
6738c0984e5SSebastian Reichel /*
6748c0984e5SSebastian Reichel  * smb347_set_writable - enables/disables writing to non-volatile registers
6758c0984e5SSebastian Reichel  * @smb: pointer to smb347 charger instance
6768c0984e5SSebastian Reichel  *
6778c0984e5SSebastian Reichel  * You can enable/disable writing to the non-volatile configuration
6788c0984e5SSebastian Reichel  * registers by calling this function.
6798c0984e5SSebastian Reichel  *
6808c0984e5SSebastian Reichel  * Returns %0 on success and negative errno in case of failure.
6818c0984e5SSebastian Reichel  */
smb347_set_writable(struct smb347_charger * smb,bool writable,bool irq_toggle)6824ac59d85SDmitry Osipenko static int smb347_set_writable(struct smb347_charger *smb, bool writable,
6834ac59d85SDmitry Osipenko 			       bool irq_toggle)
6848c0984e5SSebastian Reichel {
6854ac59d85SDmitry Osipenko 	struct i2c_client *client = to_i2c_client(smb->dev);
6864ac59d85SDmitry Osipenko 	int ret;
6874ac59d85SDmitry Osipenko 
6884ac59d85SDmitry Osipenko 	if (writable && irq_toggle && !smb->irq_unsupported)
6894ac59d85SDmitry Osipenko 		disable_irq(client->irq);
6904ac59d85SDmitry Osipenko 
6914ac59d85SDmitry Osipenko 	ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
6928c0984e5SSebastian Reichel 				 writable ? CMD_A_ALLOW_WRITE : 0);
6934ac59d85SDmitry Osipenko 
6944ac59d85SDmitry Osipenko 	if ((!writable || ret) && irq_toggle && !smb->irq_unsupported)
6954ac59d85SDmitry Osipenko 		enable_irq(client->irq);
6964ac59d85SDmitry Osipenko 
6974ac59d85SDmitry Osipenko 	return ret;
6988c0984e5SSebastian Reichel }
6998c0984e5SSebastian Reichel 
smb347_hw_init(struct smb347_charger * smb)7008c0984e5SSebastian Reichel static int smb347_hw_init(struct smb347_charger *smb)
7018c0984e5SSebastian Reichel {
7028c0984e5SSebastian Reichel 	unsigned int val;
7038c0984e5SSebastian Reichel 	int ret;
7048c0984e5SSebastian Reichel 
7054ac59d85SDmitry Osipenko 	ret = smb347_set_writable(smb, true, false);
7068c0984e5SSebastian Reichel 	if (ret < 0)
7078c0984e5SSebastian Reichel 		return ret;
7088c0984e5SSebastian Reichel 
7098c0984e5SSebastian Reichel 	/*
7108c0984e5SSebastian Reichel 	 * Program the platform specific configuration values to the device
7118c0984e5SSebastian Reichel 	 * first.
7128c0984e5SSebastian Reichel 	 */
7138c0984e5SSebastian Reichel 	ret = smb347_set_charge_current(smb);
7148c0984e5SSebastian Reichel 	if (ret < 0)
7158c0984e5SSebastian Reichel 		goto fail;
7168c0984e5SSebastian Reichel 
7178c0984e5SSebastian Reichel 	ret = smb347_set_current_limits(smb);
7188c0984e5SSebastian Reichel 	if (ret < 0)
7198c0984e5SSebastian Reichel 		goto fail;
7208c0984e5SSebastian Reichel 
7218c0984e5SSebastian Reichel 	ret = smb347_set_voltage_limits(smb);
7228c0984e5SSebastian Reichel 	if (ret < 0)
7238c0984e5SSebastian Reichel 		goto fail;
7248c0984e5SSebastian Reichel 
7258c0984e5SSebastian Reichel 	ret = smb347_set_temp_limits(smb);
7268c0984e5SSebastian Reichel 	if (ret < 0)
7278c0984e5SSebastian Reichel 		goto fail;
7288c0984e5SSebastian Reichel 
7298c0984e5SSebastian Reichel 	/* If USB charging is disabled we put the USB in suspend mode */
730b6f3e21bSSebastian Reichel 	if (!smb->use_usb) {
7318c0984e5SSebastian Reichel 		ret = regmap_update_bits(smb->regmap, CMD_A,
7328c0984e5SSebastian Reichel 					 CMD_A_SUSPEND_ENABLED,
7338c0984e5SSebastian Reichel 					 CMD_A_SUSPEND_ENABLED);
7348c0984e5SSebastian Reichel 		if (ret < 0)
7358c0984e5SSebastian Reichel 			goto fail;
7368c0984e5SSebastian Reichel 	}
7378c0984e5SSebastian Reichel 
7388c0984e5SSebastian Reichel 	/*
7398c0984e5SSebastian Reichel 	 * If configured by platform data, we enable hardware Auto-OTG
7408c0984e5SSebastian Reichel 	 * support for driving VBUS. Otherwise we disable it.
7418c0984e5SSebastian Reichel 	 */
7428c0984e5SSebastian Reichel 	ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK,
743b6f3e21bSSebastian Reichel 		smb->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0);
7448c0984e5SSebastian Reichel 	if (ret < 0)
7458c0984e5SSebastian Reichel 		goto fail;
7468c0984e5SSebastian Reichel 
747efe21754SDmitry Osipenko 	/* Activate pin control, making it writable. */
748efe21754SDmitry Osipenko 	switch (smb->enable_control) {
749efe21754SDmitry Osipenko 	case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW:
750efe21754SDmitry Osipenko 	case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH:
751efe21754SDmitry Osipenko 		ret = regmap_set_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL);
752efe21754SDmitry Osipenko 		if (ret < 0)
753efe21754SDmitry Osipenko 			goto fail;
754efe21754SDmitry Osipenko 	}
755efe21754SDmitry Osipenko 
7568c0984e5SSebastian Reichel 	/*
7578c0984e5SSebastian Reichel 	 * Make the charging functionality controllable by a write to the
7588c0984e5SSebastian Reichel 	 * command register unless pin control is specified in the platform
7598c0984e5SSebastian Reichel 	 * data.
7608c0984e5SSebastian Reichel 	 */
761b6f3e21bSSebastian Reichel 	switch (smb->enable_control) {
762b6f3e21bSSebastian Reichel 	case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW:
7638c0984e5SSebastian Reichel 		val = CFG_PIN_EN_CTRL_ACTIVE_LOW;
7648c0984e5SSebastian Reichel 		break;
765b6f3e21bSSebastian Reichel 	case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH:
7668c0984e5SSebastian Reichel 		val = CFG_PIN_EN_CTRL_ACTIVE_HIGH;
7678c0984e5SSebastian Reichel 		break;
7688c0984e5SSebastian Reichel 	default:
7698c0984e5SSebastian Reichel 		val = 0;
7708c0984e5SSebastian Reichel 		break;
7718c0984e5SSebastian Reichel 	}
7728c0984e5SSebastian Reichel 
7738c0984e5SSebastian Reichel 	ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK,
7748c0984e5SSebastian Reichel 				 val);
7758c0984e5SSebastian Reichel 	if (ret < 0)
7768c0984e5SSebastian Reichel 		goto fail;
7778c0984e5SSebastian Reichel 
7788c0984e5SSebastian Reichel 	/* Disable Automatic Power Source Detection (APSD) interrupt. */
7798c0984e5SSebastian Reichel 	ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0);
7808c0984e5SSebastian Reichel 	if (ret < 0)
7818c0984e5SSebastian Reichel 		goto fail;
7828c0984e5SSebastian Reichel 
7838c0984e5SSebastian Reichel 	ret = smb347_update_ps_status(smb);
7848c0984e5SSebastian Reichel 	if (ret < 0)
7858c0984e5SSebastian Reichel 		goto fail;
7868c0984e5SSebastian Reichel 
7878c0984e5SSebastian Reichel 	ret = smb347_start_stop_charging(smb);
7888c0984e5SSebastian Reichel 
7898c0984e5SSebastian Reichel fail:
7904ac59d85SDmitry Osipenko 	smb347_set_writable(smb, false, false);
7918c0984e5SSebastian Reichel 	return ret;
7928c0984e5SSebastian Reichel }
7938c0984e5SSebastian Reichel 
smb347_interrupt(int irq,void * data)7948c0984e5SSebastian Reichel static irqreturn_t smb347_interrupt(int irq, void *data)
7958c0984e5SSebastian Reichel {
7968c0984e5SSebastian Reichel 	struct smb347_charger *smb = data;
7978c0984e5SSebastian Reichel 	unsigned int stat_c, irqstat_c, irqstat_d, irqstat_e;
7988c0984e5SSebastian Reichel 	bool handled = false;
7998c0984e5SSebastian Reichel 	int ret;
8008c0984e5SSebastian Reichel 
801fa7cc725SDavid Heidelberg 	/* SMB347 it needs at least 20ms for setting IRQSTAT_E_*IN_UV_IRQ */
802fa7cc725SDavid Heidelberg 	usleep_range(25000, 35000);
803fa7cc725SDavid Heidelberg 
8048c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, STAT_C, &stat_c);
8058c0984e5SSebastian Reichel 	if (ret < 0) {
8068c0984e5SSebastian Reichel 		dev_warn(smb->dev, "reading STAT_C failed\n");
8078c0984e5SSebastian Reichel 		return IRQ_NONE;
8088c0984e5SSebastian Reichel 	}
8098c0984e5SSebastian Reichel 
8108c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c);
8118c0984e5SSebastian Reichel 	if (ret < 0) {
8128c0984e5SSebastian Reichel 		dev_warn(smb->dev, "reading IRQSTAT_C failed\n");
8138c0984e5SSebastian Reichel 		return IRQ_NONE;
8148c0984e5SSebastian Reichel 	}
8158c0984e5SSebastian Reichel 
8168c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, IRQSTAT_D, &irqstat_d);
8178c0984e5SSebastian Reichel 	if (ret < 0) {
8188c0984e5SSebastian Reichel 		dev_warn(smb->dev, "reading IRQSTAT_D failed\n");
8198c0984e5SSebastian Reichel 		return IRQ_NONE;
8208c0984e5SSebastian Reichel 	}
8218c0984e5SSebastian Reichel 
8228c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e);
8238c0984e5SSebastian Reichel 	if (ret < 0) {
8248c0984e5SSebastian Reichel 		dev_warn(smb->dev, "reading IRQSTAT_E failed\n");
8258c0984e5SSebastian Reichel 		return IRQ_NONE;
8268c0984e5SSebastian Reichel 	}
8278c0984e5SSebastian Reichel 
8288c0984e5SSebastian Reichel 	/*
8298c0984e5SSebastian Reichel 	 * If we get charger error we report the error back to user.
8308c0984e5SSebastian Reichel 	 * If the error is recovered charging will resume again.
8318c0984e5SSebastian Reichel 	 */
8328c0984e5SSebastian Reichel 	if (stat_c & STAT_C_CHARGER_ERROR) {
8338c0984e5SSebastian Reichel 		dev_err(smb->dev, "charging stopped due to charger error\n");
834b6f3e21bSSebastian Reichel 		if (smb->use_mains)
835db14d3b4SDavid Heidelberg 			power_supply_changed(smb->mains);
836b6f3e21bSSebastian Reichel 		if (smb->use_usb)
837db14d3b4SDavid Heidelberg 			power_supply_changed(smb->usb);
8388c0984e5SSebastian Reichel 		handled = true;
8398c0984e5SSebastian Reichel 	}
8408c0984e5SSebastian Reichel 
8418c0984e5SSebastian Reichel 	/*
8428c0984e5SSebastian Reichel 	 * If we reached the termination current the battery is charged and
8438c0984e5SSebastian Reichel 	 * we can update the status now. Charging is automatically
8448c0984e5SSebastian Reichel 	 * disabled by the hardware.
8458c0984e5SSebastian Reichel 	 */
8468c0984e5SSebastian Reichel 	if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) {
847db14d3b4SDavid Heidelberg 		if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) {
848b6f3e21bSSebastian Reichel 			if (smb->use_mains)
849db14d3b4SDavid Heidelberg 				power_supply_changed(smb->mains);
850b6f3e21bSSebastian Reichel 			if (smb->use_usb)
851db14d3b4SDavid Heidelberg 				power_supply_changed(smb->usb);
852db14d3b4SDavid Heidelberg 		}
8538c0984e5SSebastian Reichel 		dev_dbg(smb->dev, "going to HW maintenance mode\n");
8548c0984e5SSebastian Reichel 		handled = true;
8558c0984e5SSebastian Reichel 	}
8568c0984e5SSebastian Reichel 
8578c0984e5SSebastian Reichel 	/*
8588c0984e5SSebastian Reichel 	 * If we got a charger timeout INT that means the charge
8598c0984e5SSebastian Reichel 	 * full is not detected with in charge timeout value.
8608c0984e5SSebastian Reichel 	 */
8618c0984e5SSebastian Reichel 	if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ) {
8628c0984e5SSebastian Reichel 		dev_dbg(smb->dev, "total Charge Timeout INT received\n");
8638c0984e5SSebastian Reichel 
8648c0984e5SSebastian Reichel 		if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT)
8658c0984e5SSebastian Reichel 			dev_warn(smb->dev, "charging stopped due to timeout\n");
866b6f3e21bSSebastian Reichel 		if (smb->use_mains)
867db14d3b4SDavid Heidelberg 			power_supply_changed(smb->mains);
868b6f3e21bSSebastian Reichel 		if (smb->use_usb)
869db14d3b4SDavid Heidelberg 			power_supply_changed(smb->usb);
8708c0984e5SSebastian Reichel 		handled = true;
8718c0984e5SSebastian Reichel 	}
8728c0984e5SSebastian Reichel 
8738c0984e5SSebastian Reichel 	/*
8748c0984e5SSebastian Reichel 	 * If we got an under voltage interrupt it means that AC/USB input
8758c0984e5SSebastian Reichel 	 * was connected or disconnected.
8768c0984e5SSebastian Reichel 	 */
8778c0984e5SSebastian Reichel 	if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) {
8788c0984e5SSebastian Reichel 		if (smb347_update_ps_status(smb) > 0) {
8798c0984e5SSebastian Reichel 			smb347_start_stop_charging(smb);
880b6f3e21bSSebastian Reichel 			if (smb->use_mains)
8818c0984e5SSebastian Reichel 				power_supply_changed(smb->mains);
882b6f3e21bSSebastian Reichel 			if (smb->use_usb)
8838c0984e5SSebastian Reichel 				power_supply_changed(smb->usb);
8848c0984e5SSebastian Reichel 		}
8858c0984e5SSebastian Reichel 		handled = true;
8868c0984e5SSebastian Reichel 	}
8878c0984e5SSebastian Reichel 
8888c0984e5SSebastian Reichel 	return handled ? IRQ_HANDLED : IRQ_NONE;
8898c0984e5SSebastian Reichel }
8908c0984e5SSebastian Reichel 
smb347_irq_set(struct smb347_charger * smb,bool enable)8918c0984e5SSebastian Reichel static int smb347_irq_set(struct smb347_charger *smb, bool enable)
8928c0984e5SSebastian Reichel {
8938c0984e5SSebastian Reichel 	int ret;
8948c0984e5SSebastian Reichel 
89569963126SDmitry Osipenko 	if (smb->irq_unsupported)
89669963126SDmitry Osipenko 		return 0;
89769963126SDmitry Osipenko 
8984ac59d85SDmitry Osipenko 	ret = smb347_set_writable(smb, true, true);
8998c0984e5SSebastian Reichel 	if (ret < 0)
9008c0984e5SSebastian Reichel 		return ret;
9018c0984e5SSebastian Reichel 
9028c0984e5SSebastian Reichel 	/*
9038c0984e5SSebastian Reichel 	 * Enable/disable interrupts for:
9048c0984e5SSebastian Reichel 	 *	- under voltage
9058c0984e5SSebastian Reichel 	 *	- termination current reached
9068c0984e5SSebastian Reichel 	 *	- charger timeout
9078c0984e5SSebastian Reichel 	 *	- charger error
9088c0984e5SSebastian Reichel 	 */
9098c0984e5SSebastian Reichel 	ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff,
9108c0984e5SSebastian Reichel 				 enable ? CFG_FAULT_IRQ_DCIN_UV : 0);
9118c0984e5SSebastian Reichel 	if (ret < 0)
9128c0984e5SSebastian Reichel 		goto fail;
9138c0984e5SSebastian Reichel 
9148c0984e5SSebastian Reichel 	ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff,
9158c0984e5SSebastian Reichel 			enable ? (CFG_STATUS_IRQ_TERMINATION_OR_TAPER |
9168c0984e5SSebastian Reichel 					CFG_STATUS_IRQ_CHARGE_TIMEOUT) : 0);
9178c0984e5SSebastian Reichel 	if (ret < 0)
9188c0984e5SSebastian Reichel 		goto fail;
9198c0984e5SSebastian Reichel 
9208c0984e5SSebastian Reichel 	ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR,
9218c0984e5SSebastian Reichel 				 enable ? CFG_PIN_EN_CHARGER_ERROR : 0);
9228c0984e5SSebastian Reichel fail:
9234ac59d85SDmitry Osipenko 	smb347_set_writable(smb, false, true);
9248c0984e5SSebastian Reichel 	return ret;
9258c0984e5SSebastian Reichel }
9268c0984e5SSebastian Reichel 
smb347_irq_enable(struct smb347_charger * smb)9278c0984e5SSebastian Reichel static inline int smb347_irq_enable(struct smb347_charger *smb)
9288c0984e5SSebastian Reichel {
9298c0984e5SSebastian Reichel 	return smb347_irq_set(smb, true);
9308c0984e5SSebastian Reichel }
9318c0984e5SSebastian Reichel 
smb347_irq_disable(struct smb347_charger * smb)9328c0984e5SSebastian Reichel static inline int smb347_irq_disable(struct smb347_charger *smb)
9338c0984e5SSebastian Reichel {
9348c0984e5SSebastian Reichel 	return smb347_irq_set(smb, false);
9358c0984e5SSebastian Reichel }
9368c0984e5SSebastian Reichel 
smb347_irq_init(struct smb347_charger * smb,struct i2c_client * client)9378c0984e5SSebastian Reichel static int smb347_irq_init(struct smb347_charger *smb,
9388c0984e5SSebastian Reichel 			   struct i2c_client *client)
9398c0984e5SSebastian Reichel {
9402d52f710SDavid Heidelberg 	int ret;
9418c0984e5SSebastian Reichel 
942d33b3f7eSDmitry Osipenko 	smb->irq_unsupported = true;
943d33b3f7eSDmitry Osipenko 
944d33b3f7eSDmitry Osipenko 	/*
945d33b3f7eSDmitry Osipenko 	 * Interrupt pin is optional. If it is connected, we setup the
946d33b3f7eSDmitry Osipenko 	 * interrupt support here.
947d33b3f7eSDmitry Osipenko 	 */
948d33b3f7eSDmitry Osipenko 	if (!client->irq)
949d33b3f7eSDmitry Osipenko 		return 0;
9508c0984e5SSebastian Reichel 
9514ac59d85SDmitry Osipenko 	ret = smb347_set_writable(smb, true, false);
9528c0984e5SSebastian Reichel 	if (ret < 0)
9532d52f710SDavid Heidelberg 		return ret;
9548c0984e5SSebastian Reichel 
9558c0984e5SSebastian Reichel 	/*
9568c0984e5SSebastian Reichel 	 * Configure the STAT output to be suitable for interrupts: disable
9578c0984e5SSebastian Reichel 	 * all other output (except interrupts) and make it active low.
9588c0984e5SSebastian Reichel 	 */
9598c0984e5SSebastian Reichel 	ret = regmap_update_bits(smb->regmap, CFG_STAT,
9608c0984e5SSebastian Reichel 				 CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED,
9618c0984e5SSebastian Reichel 				 CFG_STAT_DISABLED);
9622d52f710SDavid Heidelberg 
9634ac59d85SDmitry Osipenko 	smb347_set_writable(smb, false, false);
9642d52f710SDavid Heidelberg 
965d33b3f7eSDmitry Osipenko 	if (ret < 0) {
966d33b3f7eSDmitry Osipenko 		dev_warn(smb->dev, "failed to initialize IRQ: %d\n", ret);
967d33b3f7eSDmitry Osipenko 		dev_warn(smb->dev, "disabling IRQ support\n");
968d33b3f7eSDmitry Osipenko 		return 0;
969d33b3f7eSDmitry Osipenko 	}
970d33b3f7eSDmitry Osipenko 
971d33b3f7eSDmitry Osipenko 	ret = devm_request_threaded_irq(smb->dev, client->irq, NULL,
972d33b3f7eSDmitry Osipenko 					smb347_interrupt, IRQF_ONESHOT,
973d33b3f7eSDmitry Osipenko 					client->name, smb);
974d33b3f7eSDmitry Osipenko 	if (ret)
9758c0984e5SSebastian Reichel 		return ret;
976d33b3f7eSDmitry Osipenko 
977d33b3f7eSDmitry Osipenko 	smb->irq_unsupported = false;
978d33b3f7eSDmitry Osipenko 
979d33b3f7eSDmitry Osipenko 	ret = smb347_irq_enable(smb);
980d33b3f7eSDmitry Osipenko 	if (ret < 0)
981d33b3f7eSDmitry Osipenko 		return ret;
982d33b3f7eSDmitry Osipenko 
983d33b3f7eSDmitry Osipenko 	return 0;
9848c0984e5SSebastian Reichel }
9858c0984e5SSebastian Reichel 
9868c0984e5SSebastian Reichel /*
9878c0984e5SSebastian Reichel  * Returns the constant charge current programmed
9888c0984e5SSebastian Reichel  * into the charger in uA.
9898c0984e5SSebastian Reichel  */
get_const_charge_current(struct smb347_charger * smb)9908c0984e5SSebastian Reichel static int get_const_charge_current(struct smb347_charger *smb)
9918c0984e5SSebastian Reichel {
992de76fd29SDavid Heidelberg 	unsigned int id = smb->id;
9938c0984e5SSebastian Reichel 	int ret, intval;
9948c0984e5SSebastian Reichel 	unsigned int v;
9958c0984e5SSebastian Reichel 
9968c0984e5SSebastian Reichel 	if (!smb347_is_ps_online(smb))
9978c0984e5SSebastian Reichel 		return -ENODATA;
9988c0984e5SSebastian Reichel 
9998c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, STAT_B, &v);
10008c0984e5SSebastian Reichel 	if (ret < 0)
10018c0984e5SSebastian Reichel 		return ret;
10028c0984e5SSebastian Reichel 
10038c0984e5SSebastian Reichel 	/*
10048c0984e5SSebastian Reichel 	 * The current value is composition of FCC and PCC values
10058c0984e5SSebastian Reichel 	 * and we can detect which table to use from bit 5.
10068c0984e5SSebastian Reichel 	 */
10078c0984e5SSebastian Reichel 	if (v & 0x20) {
1008de76fd29SDavid Heidelberg 		intval = hw_to_current(fcc_tbl[id],
1009de76fd29SDavid Heidelberg 				       ARRAY_SIZE(fcc_tbl[id]), v & 7);
10108c0984e5SSebastian Reichel 	} else {
10118c0984e5SSebastian Reichel 		v >>= 3;
1012de76fd29SDavid Heidelberg 		intval = hw_to_current(pcc_tbl[id],
1013de76fd29SDavid Heidelberg 				       ARRAY_SIZE(pcc_tbl[id]), v & 7);
10148c0984e5SSebastian Reichel 	}
10158c0984e5SSebastian Reichel 
10168c0984e5SSebastian Reichel 	return intval;
10178c0984e5SSebastian Reichel }
10188c0984e5SSebastian Reichel 
10198c0984e5SSebastian Reichel /*
10208c0984e5SSebastian Reichel  * Returns the constant charge voltage programmed
10218c0984e5SSebastian Reichel  * into the charger in uV.
10228c0984e5SSebastian Reichel  */
get_const_charge_voltage(struct smb347_charger * smb)10238c0984e5SSebastian Reichel static int get_const_charge_voltage(struct smb347_charger *smb)
10248c0984e5SSebastian Reichel {
10258c0984e5SSebastian Reichel 	int ret, intval;
10268c0984e5SSebastian Reichel 	unsigned int v;
10278c0984e5SSebastian Reichel 
10288c0984e5SSebastian Reichel 	if (!smb347_is_ps_online(smb))
10298c0984e5SSebastian Reichel 		return -ENODATA;
10308c0984e5SSebastian Reichel 
10318c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, STAT_A, &v);
10328c0984e5SSebastian Reichel 	if (ret < 0)
10338c0984e5SSebastian Reichel 		return ret;
10348c0984e5SSebastian Reichel 
10358c0984e5SSebastian Reichel 	v &= STAT_A_FLOAT_VOLTAGE_MASK;
10368c0984e5SSebastian Reichel 	if (v > 0x3d)
10378c0984e5SSebastian Reichel 		v = 0x3d;
10388c0984e5SSebastian Reichel 
10398c0984e5SSebastian Reichel 	intval = 3500000 + v * 20000;
10408c0984e5SSebastian Reichel 
10418c0984e5SSebastian Reichel 	return intval;
10428c0984e5SSebastian Reichel }
10438c0984e5SSebastian Reichel 
smb347_get_charging_status(struct smb347_charger * smb,struct power_supply * psy)1044db14d3b4SDavid Heidelberg static int smb347_get_charging_status(struct smb347_charger *smb,
1045db14d3b4SDavid Heidelberg 				      struct power_supply *psy)
10468c0984e5SSebastian Reichel {
10478c0984e5SSebastian Reichel 	int ret, status;
10488c0984e5SSebastian Reichel 	unsigned int val;
10498c0984e5SSebastian Reichel 
1050db14d3b4SDavid Heidelberg 	if (psy->desc->type == POWER_SUPPLY_TYPE_USB) {
1051db14d3b4SDavid Heidelberg 		if (!smb->usb_online)
10528c0984e5SSebastian Reichel 			return POWER_SUPPLY_STATUS_DISCHARGING;
1053db14d3b4SDavid Heidelberg 	} else {
1054db14d3b4SDavid Heidelberg 		if (!smb->mains_online)
1055db14d3b4SDavid Heidelberg 			return POWER_SUPPLY_STATUS_DISCHARGING;
1056db14d3b4SDavid Heidelberg 	}
10578c0984e5SSebastian Reichel 
10588c0984e5SSebastian Reichel 	ret = regmap_read(smb->regmap, STAT_C, &val);
10598c0984e5SSebastian Reichel 	if (ret < 0)
10608c0984e5SSebastian Reichel 		return ret;
10618c0984e5SSebastian Reichel 
10628c0984e5SSebastian Reichel 	if ((val & STAT_C_CHARGER_ERROR) ||
10638c0984e5SSebastian Reichel 			(val & STAT_C_HOLDOFF_STAT)) {
10648c0984e5SSebastian Reichel 		/*
10658c0984e5SSebastian Reichel 		 * set to NOT CHARGING upon charger error
10668c0984e5SSebastian Reichel 		 * or charging has stopped.
10678c0984e5SSebastian Reichel 		 */
10688c0984e5SSebastian Reichel 		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
10698c0984e5SSebastian Reichel 	} else {
10708c0984e5SSebastian Reichel 		if ((val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT) {
10718c0984e5SSebastian Reichel 			/*
10728c0984e5SSebastian Reichel 			 * set to charging if battery is in pre-charge,
10738c0984e5SSebastian Reichel 			 * fast charge or taper charging mode.
10748c0984e5SSebastian Reichel 			 */
10758c0984e5SSebastian Reichel 			status = POWER_SUPPLY_STATUS_CHARGING;
10768c0984e5SSebastian Reichel 		} else if (val & STAT_C_CHG_TERM) {
10778c0984e5SSebastian Reichel 			/*
10788c0984e5SSebastian Reichel 			 * set the status to FULL if battery is not in pre
10798c0984e5SSebastian Reichel 			 * charge, fast charge or taper charging mode AND
10808c0984e5SSebastian Reichel 			 * charging is terminated at least once.
10818c0984e5SSebastian Reichel 			 */
10828c0984e5SSebastian Reichel 			status = POWER_SUPPLY_STATUS_FULL;
10838c0984e5SSebastian Reichel 		} else {
10848c0984e5SSebastian Reichel 			/*
10858c0984e5SSebastian Reichel 			 * in this case no charger error or termination
10868c0984e5SSebastian Reichel 			 * occured but charging is not in progress!!!
10878c0984e5SSebastian Reichel 			 */
10888c0984e5SSebastian Reichel 			status = POWER_SUPPLY_STATUS_NOT_CHARGING;
10898c0984e5SSebastian Reichel 		}
10908c0984e5SSebastian Reichel 	}
10918c0984e5SSebastian Reichel 
10928c0984e5SSebastian Reichel 	return status;
10938c0984e5SSebastian Reichel }
10948c0984e5SSebastian Reichel 
smb347_get_property_locked(struct power_supply * psy,enum power_supply_property prop,union power_supply_propval * val)109599298de5SDmitry Osipenko static int smb347_get_property_locked(struct power_supply *psy,
10968c0984e5SSebastian Reichel 				      enum power_supply_property prop,
10978c0984e5SSebastian Reichel 				      union power_supply_propval *val)
10988c0984e5SSebastian Reichel {
10998c0984e5SSebastian Reichel 	struct smb347_charger *smb = power_supply_get_drvdata(psy);
11008c0984e5SSebastian Reichel 	int ret;
11018c0984e5SSebastian Reichel 
11028c0984e5SSebastian Reichel 	switch (prop) {
11038c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
1104db14d3b4SDavid Heidelberg 		ret = smb347_get_charging_status(smb, psy);
11058c0984e5SSebastian Reichel 		if (ret < 0)
11068c0984e5SSebastian Reichel 			return ret;
11078c0984e5SSebastian Reichel 		val->intval = ret;
11088c0984e5SSebastian Reichel 		break;
11098c0984e5SSebastian Reichel 
11108c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
1111db14d3b4SDavid Heidelberg 		if (psy->desc->type == POWER_SUPPLY_TYPE_USB) {
1112db14d3b4SDavid Heidelberg 			if (!smb->usb_online)
11138c0984e5SSebastian Reichel 				return -ENODATA;
1114db14d3b4SDavid Heidelberg 		} else {
1115db14d3b4SDavid Heidelberg 			if (!smb->mains_online)
1116db14d3b4SDavid Heidelberg 				return -ENODATA;
1117db14d3b4SDavid Heidelberg 		}
11188c0984e5SSebastian Reichel 
11198c0984e5SSebastian Reichel 		/*
11208c0984e5SSebastian Reichel 		 * We handle trickle and pre-charging the same, and taper
11218c0984e5SSebastian Reichel 		 * and none the same.
11228c0984e5SSebastian Reichel 		 */
11238c0984e5SSebastian Reichel 		switch (smb347_charging_status(smb)) {
11248c0984e5SSebastian Reichel 		case 1:
11258c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
11268c0984e5SSebastian Reichel 			break;
11278c0984e5SSebastian Reichel 		case 2:
11288c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
11298c0984e5SSebastian Reichel 			break;
11308c0984e5SSebastian Reichel 		default:
11318c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
11328c0984e5SSebastian Reichel 			break;
11338c0984e5SSebastian Reichel 		}
11348c0984e5SSebastian Reichel 		break;
11358c0984e5SSebastian Reichel 
1136db14d3b4SDavid Heidelberg 	case POWER_SUPPLY_PROP_ONLINE:
1137db14d3b4SDavid Heidelberg 		if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
1138db14d3b4SDavid Heidelberg 			val->intval = smb->usb_online;
1139db14d3b4SDavid Heidelberg 		else
1140db14d3b4SDavid Heidelberg 			val->intval = smb->mains_online;
11418c0984e5SSebastian Reichel 		break;
11428c0984e5SSebastian Reichel 
1143db14d3b4SDavid Heidelberg 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
1144db14d3b4SDavid Heidelberg 		ret = get_const_charge_voltage(smb);
1145db14d3b4SDavid Heidelberg 		if (ret < 0)
1146db14d3b4SDavid Heidelberg 			return ret;
1147db14d3b4SDavid Heidelberg 		val->intval = ret;
11488c0984e5SSebastian Reichel 		break;
11498c0984e5SSebastian Reichel 
1150db14d3b4SDavid Heidelberg 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
1151db14d3b4SDavid Heidelberg 		ret = get_const_charge_current(smb);
1152db14d3b4SDavid Heidelberg 		if (ret < 0)
1153db14d3b4SDavid Heidelberg 			return ret;
1154db14d3b4SDavid Heidelberg 		val->intval = ret;
11558c0984e5SSebastian Reichel 		break;
11568c0984e5SSebastian Reichel 
11578c0984e5SSebastian Reichel 	default:
11588c0984e5SSebastian Reichel 		return -EINVAL;
11598c0984e5SSebastian Reichel 	}
11608c0984e5SSebastian Reichel 
11618c0984e5SSebastian Reichel 	return 0;
11628c0984e5SSebastian Reichel }
11638c0984e5SSebastian Reichel 
smb347_get_property(struct power_supply * psy,enum power_supply_property prop,union power_supply_propval * val)116499298de5SDmitry Osipenko static int smb347_get_property(struct power_supply *psy,
116599298de5SDmitry Osipenko 			       enum power_supply_property prop,
116699298de5SDmitry Osipenko 			       union power_supply_propval *val)
116799298de5SDmitry Osipenko {
116899298de5SDmitry Osipenko 	struct smb347_charger *smb = power_supply_get_drvdata(psy);
116999298de5SDmitry Osipenko 	struct i2c_client *client = to_i2c_client(smb->dev);
117099298de5SDmitry Osipenko 	int ret;
117199298de5SDmitry Osipenko 
1172d33b3f7eSDmitry Osipenko 	if (!smb->irq_unsupported)
117399298de5SDmitry Osipenko 		disable_irq(client->irq);
1174d33b3f7eSDmitry Osipenko 
117599298de5SDmitry Osipenko 	ret = smb347_get_property_locked(psy, prop, val);
1176d33b3f7eSDmitry Osipenko 
1177d33b3f7eSDmitry Osipenko 	if (!smb->irq_unsupported)
117899298de5SDmitry Osipenko 		enable_irq(client->irq);
117999298de5SDmitry Osipenko 
118099298de5SDmitry Osipenko 	return ret;
118199298de5SDmitry Osipenko }
118299298de5SDmitry Osipenko 
1183db14d3b4SDavid Heidelberg static enum power_supply_property smb347_properties[] = {
11848c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
11858c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CHARGE_TYPE,
1186db14d3b4SDavid Heidelberg 	POWER_SUPPLY_PROP_ONLINE,
1187db14d3b4SDavid Heidelberg 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
1188db14d3b4SDavid Heidelberg 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
11898c0984e5SSebastian Reichel };
11908c0984e5SSebastian Reichel 
smb347_volatile_reg(struct device * dev,unsigned int reg)11918c0984e5SSebastian Reichel static bool smb347_volatile_reg(struct device *dev, unsigned int reg)
11928c0984e5SSebastian Reichel {
11938c0984e5SSebastian Reichel 	switch (reg) {
11948c0984e5SSebastian Reichel 	case IRQSTAT_A:
11958c0984e5SSebastian Reichel 	case IRQSTAT_C:
1196c32ea07aSDmitry Osipenko 	case IRQSTAT_D:
11978c0984e5SSebastian Reichel 	case IRQSTAT_E:
11988c0984e5SSebastian Reichel 	case IRQSTAT_F:
11998c0984e5SSebastian Reichel 	case STAT_A:
12008c0984e5SSebastian Reichel 	case STAT_B:
12018c0984e5SSebastian Reichel 	case STAT_C:
12028c0984e5SSebastian Reichel 	case STAT_E:
12038c0984e5SSebastian Reichel 		return true;
12048c0984e5SSebastian Reichel 	}
12058c0984e5SSebastian Reichel 
12068c0984e5SSebastian Reichel 	return false;
12078c0984e5SSebastian Reichel }
12088c0984e5SSebastian Reichel 
smb347_readable_reg(struct device * dev,unsigned int reg)12098c0984e5SSebastian Reichel static bool smb347_readable_reg(struct device *dev, unsigned int reg)
12108c0984e5SSebastian Reichel {
12118c0984e5SSebastian Reichel 	switch (reg) {
12128c0984e5SSebastian Reichel 	case CFG_CHARGE_CURRENT:
12138c0984e5SSebastian Reichel 	case CFG_CURRENT_LIMIT:
12148c0984e5SSebastian Reichel 	case CFG_FLOAT_VOLTAGE:
12158c0984e5SSebastian Reichel 	case CFG_STAT:
12168c0984e5SSebastian Reichel 	case CFG_PIN:
12178c0984e5SSebastian Reichel 	case CFG_THERM:
12188c0984e5SSebastian Reichel 	case CFG_SYSOK:
12198c0984e5SSebastian Reichel 	case CFG_OTHER:
12208c0984e5SSebastian Reichel 	case CFG_OTG:
12218c0984e5SSebastian Reichel 	case CFG_TEMP_LIMIT:
12228c0984e5SSebastian Reichel 	case CFG_FAULT_IRQ:
12238c0984e5SSebastian Reichel 	case CFG_STATUS_IRQ:
12248c0984e5SSebastian Reichel 	case CFG_ADDRESS:
12258c0984e5SSebastian Reichel 	case CMD_A:
12268c0984e5SSebastian Reichel 	case CMD_B:
12278c0984e5SSebastian Reichel 	case CMD_C:
12288c0984e5SSebastian Reichel 		return true;
12298c0984e5SSebastian Reichel 	}
12308c0984e5SSebastian Reichel 
12318c0984e5SSebastian Reichel 	return smb347_volatile_reg(dev, reg);
12328c0984e5SSebastian Reichel }
12338c0984e5SSebastian Reichel 
smb347_dt_parse_dev_info(struct smb347_charger * smb)1234b6f3e21bSSebastian Reichel static void smb347_dt_parse_dev_info(struct smb347_charger *smb)
1235364bec75SDavid Heidelberg {
1236f385e2fcSSebastian Reichel 	struct device *dev = smb->dev;
1237b6f3e21bSSebastian Reichel 
1238b6f3e21bSSebastian Reichel 	smb->soft_temp_limit_compensation =
1239b6f3e21bSSebastian Reichel 					SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT;
1240364bec75SDavid Heidelberg 	/*
1241364bec75SDavid Heidelberg 	 * These properties come from the battery info, still we need to
1242364bec75SDavid Heidelberg 	 * pre-initialize the values. See smb347_get_battery_info() below.
1243364bec75SDavid Heidelberg 	 */
1244b6f3e21bSSebastian Reichel 	smb->soft_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT;
1245b6f3e21bSSebastian Reichel 	smb->hard_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT;
1246b6f3e21bSSebastian Reichel 	smb->soft_hot_temp_limit  = SMB3XX_TEMP_USE_DEFAULT;
1247b6f3e21bSSebastian Reichel 	smb->hard_hot_temp_limit  = SMB3XX_TEMP_USE_DEFAULT;
1248364bec75SDavid Heidelberg 
1249364bec75SDavid Heidelberg 	/* Charging constraints */
1250f385e2fcSSebastian Reichel 	device_property_read_u32(dev, "summit,fast-voltage-threshold-microvolt",
1251b6f3e21bSSebastian Reichel 				 &smb->pre_to_fast_voltage);
1252f385e2fcSSebastian Reichel 	device_property_read_u32(dev, "summit,mains-current-limit-microamp",
1253b6f3e21bSSebastian Reichel 				 &smb->mains_current_limit);
1254f385e2fcSSebastian Reichel 	device_property_read_u32(dev, "summit,usb-current-limit-microamp",
1255b6f3e21bSSebastian Reichel 				 &smb->usb_hc_current_limit);
1256364bec75SDavid Heidelberg 
1257364bec75SDavid Heidelberg 	/* For thermometer monitoring */
1258f385e2fcSSebastian Reichel 	device_property_read_u32(dev, "summit,chip-temperature-threshold-celsius",
1259b6f3e21bSSebastian Reichel 				 &smb->chip_temp_threshold);
1260f385e2fcSSebastian Reichel 	device_property_read_u32(dev, "summit,soft-compensation-method",
1261b6f3e21bSSebastian Reichel 				 &smb->soft_temp_limit_compensation);
1262f385e2fcSSebastian Reichel 	device_property_read_u32(dev, "summit,charge-current-compensation-microamp",
1263b6f3e21bSSebastian Reichel 				 &smb->charge_current_compensation);
1264364bec75SDavid Heidelberg 
1265364bec75SDavid Heidelberg 	/* Supported charging mode */
1266f385e2fcSSebastian Reichel 	smb->use_mains = device_property_read_bool(dev, "summit,enable-mains-charging");
1267f385e2fcSSebastian Reichel 	smb->use_usb = device_property_read_bool(dev, "summit,enable-usb-charging");
1268f385e2fcSSebastian Reichel 	smb->use_usb_otg = device_property_read_bool(dev, "summit,enable-otg-charging");
1269364bec75SDavid Heidelberg 
1270364bec75SDavid Heidelberg 	/* Select charging control */
1271f385e2fcSSebastian Reichel 	device_property_read_u32(dev, "summit,enable-charge-control",
1272b6f3e21bSSebastian Reichel 				 &smb->enable_control);
1273565efae9SDmitry Osipenko 
1274565efae9SDmitry Osipenko 	/*
1275565efae9SDmitry Osipenko 	 * Polarity of INOK signal indicating presence of external power
1276565efae9SDmitry Osipenko 	 * supply connected to the charger.
1277565efae9SDmitry Osipenko 	 */
1278565efae9SDmitry Osipenko 	device_property_read_u32(dev, "summit,inok-polarity",
1279565efae9SDmitry Osipenko 				 &smb->inok_polarity);
1280364bec75SDavid Heidelberg }
1281364bec75SDavid Heidelberg 
smb347_get_battery_info(struct smb347_charger * smb)1282364bec75SDavid Heidelberg static int smb347_get_battery_info(struct smb347_charger *smb)
1283364bec75SDavid Heidelberg {
128425fd3303SLinus Walleij 	struct power_supply_battery_info *info;
1285364bec75SDavid Heidelberg 	struct power_supply *supply;
1286364bec75SDavid Heidelberg 	int err;
1287364bec75SDavid Heidelberg 
1288364bec75SDavid Heidelberg 	if (smb->mains)
1289364bec75SDavid Heidelberg 		supply = smb->mains;
1290364bec75SDavid Heidelberg 	else
1291364bec75SDavid Heidelberg 		supply = smb->usb;
1292364bec75SDavid Heidelberg 
1293364bec75SDavid Heidelberg 	err = power_supply_get_battery_info(supply, &info);
1294364bec75SDavid Heidelberg 	if (err == -ENXIO || err == -ENODEV)
1295364bec75SDavid Heidelberg 		return 0;
1296364bec75SDavid Heidelberg 	if (err)
1297364bec75SDavid Heidelberg 		return err;
1298364bec75SDavid Heidelberg 
129925fd3303SLinus Walleij 	if (info->constant_charge_current_max_ua != -EINVAL)
130025fd3303SLinus Walleij 		smb->max_charge_current = info->constant_charge_current_max_ua;
1301364bec75SDavid Heidelberg 
130225fd3303SLinus Walleij 	if (info->constant_charge_voltage_max_uv != -EINVAL)
130325fd3303SLinus Walleij 		smb->max_charge_voltage = info->constant_charge_voltage_max_uv;
1304364bec75SDavid Heidelberg 
130525fd3303SLinus Walleij 	if (info->precharge_current_ua != -EINVAL)
130625fd3303SLinus Walleij 		smb->pre_charge_current = info->precharge_current_ua;
1307364bec75SDavid Heidelberg 
130825fd3303SLinus Walleij 	if (info->charge_term_current_ua != -EINVAL)
130925fd3303SLinus Walleij 		smb->termination_current = info->charge_term_current_ua;
1310364bec75SDavid Heidelberg 
131125fd3303SLinus Walleij 	if (info->temp_alert_min != INT_MIN)
131225fd3303SLinus Walleij 		smb->soft_cold_temp_limit = info->temp_alert_min;
1313364bec75SDavid Heidelberg 
131425fd3303SLinus Walleij 	if (info->temp_alert_max != INT_MAX)
131525fd3303SLinus Walleij 		smb->soft_hot_temp_limit = info->temp_alert_max;
1316364bec75SDavid Heidelberg 
131725fd3303SLinus Walleij 	if (info->temp_min != INT_MIN)
131825fd3303SLinus Walleij 		smb->hard_cold_temp_limit = info->temp_min;
1319364bec75SDavid Heidelberg 
132025fd3303SLinus Walleij 	if (info->temp_max != INT_MAX)
132125fd3303SLinus Walleij 		smb->hard_hot_temp_limit = info->temp_max;
1322364bec75SDavid Heidelberg 
1323364bec75SDavid Heidelberg 	/* Suspend when battery temperature is outside hard limits */
1324b6f3e21bSSebastian Reichel 	if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT ||
1325b6f3e21bSSebastian Reichel 	    smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT)
1326b6f3e21bSSebastian Reichel 		smb->suspend_on_hard_temp_limit = true;
1327364bec75SDavid Heidelberg 
1328364bec75SDavid Heidelberg 	return 0;
1329364bec75SDavid Heidelberg }
1330364bec75SDavid Heidelberg 
smb347_usb_vbus_get_current_limit(struct regulator_dev * rdev)1331565efae9SDmitry Osipenko static int smb347_usb_vbus_get_current_limit(struct regulator_dev *rdev)
1332565efae9SDmitry Osipenko {
1333565efae9SDmitry Osipenko 	struct smb347_charger *smb = rdev_get_drvdata(rdev);
1334565efae9SDmitry Osipenko 	unsigned int val;
1335565efae9SDmitry Osipenko 	int ret;
1336565efae9SDmitry Osipenko 
1337565efae9SDmitry Osipenko 	ret = regmap_read(smb->regmap, CFG_OTG, &val);
1338565efae9SDmitry Osipenko 	if (ret < 0)
1339565efae9SDmitry Osipenko 		return ret;
1340565efae9SDmitry Osipenko 
1341565efae9SDmitry Osipenko 	/*
1342565efae9SDmitry Osipenko 	 * It's unknown what happens if this bit is unset due to lack of
1343565efae9SDmitry Osipenko 	 * access to the datasheet, assume it's limit-enable.
1344565efae9SDmitry Osipenko 	 */
1345565efae9SDmitry Osipenko 	if (!(val & CFG_OTG_CURRENT_LIMIT_250mA))
1346565efae9SDmitry Osipenko 		return 0;
1347565efae9SDmitry Osipenko 
1348565efae9SDmitry Osipenko 	return val & CFG_OTG_CURRENT_LIMIT_750mA ? 750000 : 250000;
1349565efae9SDmitry Osipenko }
1350565efae9SDmitry Osipenko 
smb347_usb_vbus_set_new_current_limit(struct smb347_charger * smb,int max_uA)1351565efae9SDmitry Osipenko static int smb347_usb_vbus_set_new_current_limit(struct smb347_charger *smb,
1352565efae9SDmitry Osipenko 						 int max_uA)
1353565efae9SDmitry Osipenko {
1354565efae9SDmitry Osipenko 	const unsigned int mask = CFG_OTG_CURRENT_LIMIT_750mA |
1355565efae9SDmitry Osipenko 				  CFG_OTG_CURRENT_LIMIT_250mA;
1356565efae9SDmitry Osipenko 	unsigned int val = CFG_OTG_CURRENT_LIMIT_250mA;
1357565efae9SDmitry Osipenko 	int ret;
1358565efae9SDmitry Osipenko 
1359565efae9SDmitry Osipenko 	if (max_uA >= 750000)
1360565efae9SDmitry Osipenko 		val |= CFG_OTG_CURRENT_LIMIT_750mA;
1361565efae9SDmitry Osipenko 
1362565efae9SDmitry Osipenko 	ret = regmap_update_bits(smb->regmap, CFG_OTG, mask, val);
1363565efae9SDmitry Osipenko 	if (ret < 0)
1364565efae9SDmitry Osipenko 		dev_err(smb->dev, "failed to change USB current limit\n");
1365565efae9SDmitry Osipenko 
1366565efae9SDmitry Osipenko 	return ret;
1367565efae9SDmitry Osipenko }
1368565efae9SDmitry Osipenko 
smb347_usb_vbus_set_current_limit(struct regulator_dev * rdev,int min_uA,int max_uA)1369565efae9SDmitry Osipenko static int smb347_usb_vbus_set_current_limit(struct regulator_dev *rdev,
1370565efae9SDmitry Osipenko 					     int min_uA, int max_uA)
1371565efae9SDmitry Osipenko {
1372565efae9SDmitry Osipenko 	struct smb347_charger *smb = rdev_get_drvdata(rdev);
1373565efae9SDmitry Osipenko 	int ret;
1374565efae9SDmitry Osipenko 
1375565efae9SDmitry Osipenko 	ret = smb347_set_writable(smb, true, true);
1376565efae9SDmitry Osipenko 	if (ret < 0)
1377565efae9SDmitry Osipenko 		return ret;
1378565efae9SDmitry Osipenko 
1379565efae9SDmitry Osipenko 	ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA);
1380565efae9SDmitry Osipenko 	smb347_set_writable(smb, false, true);
1381565efae9SDmitry Osipenko 
1382565efae9SDmitry Osipenko 	return ret;
1383565efae9SDmitry Osipenko }
1384565efae9SDmitry Osipenko 
smb347_usb_vbus_regulator_enable(struct regulator_dev * rdev)1385565efae9SDmitry Osipenko static int smb347_usb_vbus_regulator_enable(struct regulator_dev *rdev)
1386565efae9SDmitry Osipenko {
1387565efae9SDmitry Osipenko 	struct smb347_charger *smb = rdev_get_drvdata(rdev);
1388565efae9SDmitry Osipenko 	int ret, max_uA;
1389565efae9SDmitry Osipenko 
1390565efae9SDmitry Osipenko 	ret = smb347_set_writable(smb, true, true);
1391565efae9SDmitry Osipenko 	if (ret < 0)
1392565efae9SDmitry Osipenko 		return ret;
1393565efae9SDmitry Osipenko 
1394565efae9SDmitry Osipenko 	smb347_charging_disable(smb);
1395565efae9SDmitry Osipenko 
1396565efae9SDmitry Osipenko 	if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) {
1397565efae9SDmitry Osipenko 		unsigned int sysok = 0;
1398565efae9SDmitry Osipenko 
1399565efae9SDmitry Osipenko 		if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_LOW)
1400565efae9SDmitry Osipenko 			sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
1401565efae9SDmitry Osipenko 
1402565efae9SDmitry Osipenko 		/*
1403565efae9SDmitry Osipenko 		 * VBUS won't be powered if INOK is active, so we need to
1404565efae9SDmitry Osipenko 		 * manually disable INOK on some platforms.
1405565efae9SDmitry Osipenko 		 */
1406565efae9SDmitry Osipenko 		ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
1407565efae9SDmitry Osipenko 					 CFG_SYSOK_INOK_ACTIVE_HIGH, sysok);
1408565efae9SDmitry Osipenko 		if (ret < 0) {
1409565efae9SDmitry Osipenko 			dev_err(smb->dev, "failed to disable INOK\n");
1410565efae9SDmitry Osipenko 			goto done;
1411565efae9SDmitry Osipenko 		}
1412565efae9SDmitry Osipenko 	}
1413565efae9SDmitry Osipenko 
1414565efae9SDmitry Osipenko 	ret = smb347_usb_vbus_get_current_limit(rdev);
1415565efae9SDmitry Osipenko 	if (ret < 0) {
1416565efae9SDmitry Osipenko 		dev_err(smb->dev, "failed to get USB VBUS current limit\n");
1417565efae9SDmitry Osipenko 		goto done;
1418565efae9SDmitry Osipenko 	}
1419565efae9SDmitry Osipenko 
1420565efae9SDmitry Osipenko 	max_uA = ret;
1421565efae9SDmitry Osipenko 
1422565efae9SDmitry Osipenko 	ret = smb347_usb_vbus_set_new_current_limit(smb, 250000);
1423565efae9SDmitry Osipenko 	if (ret < 0) {
1424565efae9SDmitry Osipenko 		dev_err(smb->dev, "failed to preset USB VBUS current limit\n");
1425565efae9SDmitry Osipenko 		goto done;
1426565efae9SDmitry Osipenko 	}
1427565efae9SDmitry Osipenko 
1428565efae9SDmitry Osipenko 	ret = regmap_set_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED);
1429565efae9SDmitry Osipenko 	if (ret < 0) {
1430565efae9SDmitry Osipenko 		dev_err(smb->dev, "failed to enable USB VBUS\n");
1431565efae9SDmitry Osipenko 		goto done;
1432565efae9SDmitry Osipenko 	}
1433565efae9SDmitry Osipenko 
1434565efae9SDmitry Osipenko 	smb->usb_vbus_enabled = true;
1435565efae9SDmitry Osipenko 
1436565efae9SDmitry Osipenko 	ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA);
1437565efae9SDmitry Osipenko 	if (ret < 0) {
1438565efae9SDmitry Osipenko 		dev_err(smb->dev, "failed to restore USB VBUS current limit\n");
1439565efae9SDmitry Osipenko 		goto done;
1440565efae9SDmitry Osipenko 	}
1441565efae9SDmitry Osipenko done:
1442565efae9SDmitry Osipenko 	smb347_set_writable(smb, false, true);
1443565efae9SDmitry Osipenko 
1444565efae9SDmitry Osipenko 	return ret;
1445565efae9SDmitry Osipenko }
1446565efae9SDmitry Osipenko 
smb347_usb_vbus_regulator_disable(struct regulator_dev * rdev)1447565efae9SDmitry Osipenko static int smb347_usb_vbus_regulator_disable(struct regulator_dev *rdev)
1448565efae9SDmitry Osipenko {
1449565efae9SDmitry Osipenko 	struct smb347_charger *smb = rdev_get_drvdata(rdev);
1450565efae9SDmitry Osipenko 	int ret;
1451565efae9SDmitry Osipenko 
1452565efae9SDmitry Osipenko 	ret = smb347_set_writable(smb, true, true);
1453565efae9SDmitry Osipenko 	if (ret < 0)
1454565efae9SDmitry Osipenko 		return ret;
1455565efae9SDmitry Osipenko 
1456565efae9SDmitry Osipenko 	ret = regmap_clear_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED);
1457565efae9SDmitry Osipenko 	if (ret < 0) {
1458565efae9SDmitry Osipenko 		dev_err(smb->dev, "failed to disable USB VBUS\n");
1459565efae9SDmitry Osipenko 		goto done;
1460565efae9SDmitry Osipenko 	}
1461565efae9SDmitry Osipenko 
1462565efae9SDmitry Osipenko 	smb->usb_vbus_enabled = false;
1463565efae9SDmitry Osipenko 
1464565efae9SDmitry Osipenko 	if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) {
1465565efae9SDmitry Osipenko 		unsigned int sysok = 0;
1466565efae9SDmitry Osipenko 
1467565efae9SDmitry Osipenko 		if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_HIGH)
1468565efae9SDmitry Osipenko 			sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
1469565efae9SDmitry Osipenko 
1470565efae9SDmitry Osipenko 		ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
1471565efae9SDmitry Osipenko 					 CFG_SYSOK_INOK_ACTIVE_HIGH, sysok);
1472565efae9SDmitry Osipenko 		if (ret < 0) {
1473565efae9SDmitry Osipenko 			dev_err(smb->dev, "failed to enable INOK\n");
1474565efae9SDmitry Osipenko 			goto done;
1475565efae9SDmitry Osipenko 		}
1476565efae9SDmitry Osipenko 	}
1477565efae9SDmitry Osipenko 
1478565efae9SDmitry Osipenko 	smb347_start_stop_charging(smb);
1479565efae9SDmitry Osipenko done:
1480565efae9SDmitry Osipenko 	smb347_set_writable(smb, false, true);
1481565efae9SDmitry Osipenko 
1482565efae9SDmitry Osipenko 	return ret;
1483565efae9SDmitry Osipenko }
1484565efae9SDmitry Osipenko 
14858c0984e5SSebastian Reichel static const struct regmap_config smb347_regmap = {
14868c0984e5SSebastian Reichel 	.reg_bits	= 8,
14878c0984e5SSebastian Reichel 	.val_bits	= 8,
14888c0984e5SSebastian Reichel 	.max_register	= SMB347_MAX_REGISTER,
14898c0984e5SSebastian Reichel 	.volatile_reg	= smb347_volatile_reg,
14908c0984e5SSebastian Reichel 	.readable_reg	= smb347_readable_reg,
14914c678b7aSMark Brown 	.cache_type	= REGCACHE_RBTREE,
14928c0984e5SSebastian Reichel };
14938c0984e5SSebastian Reichel 
1494565efae9SDmitry Osipenko static const struct regulator_ops smb347_usb_vbus_regulator_ops = {
1495565efae9SDmitry Osipenko 	.is_enabled	= regulator_is_enabled_regmap,
1496565efae9SDmitry Osipenko 	.enable		= smb347_usb_vbus_regulator_enable,
1497565efae9SDmitry Osipenko 	.disable	= smb347_usb_vbus_regulator_disable,
1498565efae9SDmitry Osipenko 	.get_current_limit = smb347_usb_vbus_get_current_limit,
1499565efae9SDmitry Osipenko 	.set_current_limit = smb347_usb_vbus_set_current_limit,
1500565efae9SDmitry Osipenko };
1501565efae9SDmitry Osipenko 
15028c0984e5SSebastian Reichel static const struct power_supply_desc smb347_mains_desc = {
15038c0984e5SSebastian Reichel 	.name		= "smb347-mains",
15048c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_MAINS,
1505db14d3b4SDavid Heidelberg 	.get_property	= smb347_get_property,
1506db14d3b4SDavid Heidelberg 	.properties	= smb347_properties,
1507db14d3b4SDavid Heidelberg 	.num_properties	= ARRAY_SIZE(smb347_properties),
15088c0984e5SSebastian Reichel };
15098c0984e5SSebastian Reichel 
15108c0984e5SSebastian Reichel static const struct power_supply_desc smb347_usb_desc = {
15118c0984e5SSebastian Reichel 	.name		= "smb347-usb",
15128c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_USB,
1513db14d3b4SDavid Heidelberg 	.get_property	= smb347_get_property,
1514db14d3b4SDavid Heidelberg 	.properties	= smb347_properties,
1515db14d3b4SDavid Heidelberg 	.num_properties	= ARRAY_SIZE(smb347_properties),
15168c0984e5SSebastian Reichel };
15178c0984e5SSebastian Reichel 
1518565efae9SDmitry Osipenko static const struct regulator_desc smb347_usb_vbus_regulator_desc = {
1519565efae9SDmitry Osipenko 	.name		= "smb347-usb-vbus",
1520565efae9SDmitry Osipenko 	.of_match	= of_match_ptr("usb-vbus"),
1521565efae9SDmitry Osipenko 	.ops		= &smb347_usb_vbus_regulator_ops,
1522565efae9SDmitry Osipenko 	.type		= REGULATOR_VOLTAGE,
1523565efae9SDmitry Osipenko 	.owner		= THIS_MODULE,
1524565efae9SDmitry Osipenko 	.enable_reg	= CMD_A,
1525565efae9SDmitry Osipenko 	.enable_mask	= CMD_A_OTG_ENABLED,
1526565efae9SDmitry Osipenko 	.enable_val	= CMD_A_OTG_ENABLED,
1527565efae9SDmitry Osipenko 	.fixed_uV	= 5000000,
1528565efae9SDmitry Osipenko 	.n_voltages	= 1,
1529565efae9SDmitry Osipenko };
1530565efae9SDmitry Osipenko 
smb347_probe(struct i2c_client * client)15316d43a4b0SUwe Kleine-König static int smb347_probe(struct i2c_client *client)
15328c0984e5SSebastian Reichel {
15336d43a4b0SUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
1534db14d3b4SDavid Heidelberg 	struct power_supply_config mains_usb_cfg = {};
1535565efae9SDmitry Osipenko 	struct regulator_config usb_rdev_cfg = {};
15368c0984e5SSebastian Reichel 	struct device *dev = &client->dev;
15378c0984e5SSebastian Reichel 	struct smb347_charger *smb;
15388c0984e5SSebastian Reichel 	int ret;
15398c0984e5SSebastian Reichel 
15408c0984e5SSebastian Reichel 	smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL);
15418c0984e5SSebastian Reichel 	if (!smb)
15428c0984e5SSebastian Reichel 		return -ENOMEM;
15438c0984e5SSebastian Reichel 	smb->dev = &client->dev;
1544de76fd29SDavid Heidelberg 	smb->id = id->driver_data;
1545b6f3e21bSSebastian Reichel 	i2c_set_clientdata(client, smb);
1546b6f3e21bSSebastian Reichel 
1547b6f3e21bSSebastian Reichel 	smb347_dt_parse_dev_info(smb);
1548b6f3e21bSSebastian Reichel 	if (!smb->use_mains && !smb->use_usb)
1549b6f3e21bSSebastian Reichel 		return -EINVAL;
15508c0984e5SSebastian Reichel 
15518c0984e5SSebastian Reichel 	smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap);
15528c0984e5SSebastian Reichel 	if (IS_ERR(smb->regmap))
15538c0984e5SSebastian Reichel 		return PTR_ERR(smb->regmap);
15548c0984e5SSebastian Reichel 
15558c0984e5SSebastian Reichel 	mains_usb_cfg.drv_data = smb;
1556364bec75SDavid Heidelberg 	mains_usb_cfg.of_node = dev->of_node;
1557b6f3e21bSSebastian Reichel 	if (smb->use_mains) {
15582d52f710SDavid Heidelberg 		smb->mains = devm_power_supply_register(dev, &smb347_mains_desc,
15598c0984e5SSebastian Reichel 							&mains_usb_cfg);
15608c0984e5SSebastian Reichel 		if (IS_ERR(smb->mains))
15618c0984e5SSebastian Reichel 			return PTR_ERR(smb->mains);
15628c0984e5SSebastian Reichel 	}
15638c0984e5SSebastian Reichel 
1564b6f3e21bSSebastian Reichel 	if (smb->use_usb) {
15652d52f710SDavid Heidelberg 		smb->usb = devm_power_supply_register(dev, &smb347_usb_desc,
15668c0984e5SSebastian Reichel 						      &mains_usb_cfg);
15672d52f710SDavid Heidelberg 		if (IS_ERR(smb->usb))
15688c0984e5SSebastian Reichel 			return PTR_ERR(smb->usb);
15698c0984e5SSebastian Reichel 	}
15708c0984e5SSebastian Reichel 
1571364bec75SDavid Heidelberg 	ret = smb347_get_battery_info(smb);
1572364bec75SDavid Heidelberg 	if (ret)
1573364bec75SDavid Heidelberg 		return ret;
1574364bec75SDavid Heidelberg 
1575364bec75SDavid Heidelberg 	ret = smb347_hw_init(smb);
1576364bec75SDavid Heidelberg 	if (ret < 0)
1577364bec75SDavid Heidelberg 		return ret;
1578364bec75SDavid Heidelberg 
15798c0984e5SSebastian Reichel 	ret = smb347_irq_init(smb, client);
1580d33b3f7eSDmitry Osipenko 	if (ret)
1581d33b3f7eSDmitry Osipenko 		return ret;
15828c0984e5SSebastian Reichel 
1583565efae9SDmitry Osipenko 	usb_rdev_cfg.dev = dev;
1584565efae9SDmitry Osipenko 	usb_rdev_cfg.driver_data = smb;
1585565efae9SDmitry Osipenko 	usb_rdev_cfg.regmap = smb->regmap;
1586565efae9SDmitry Osipenko 
1587565efae9SDmitry Osipenko 	smb->usb_rdev = devm_regulator_register(dev,
1588565efae9SDmitry Osipenko 						&smb347_usb_vbus_regulator_desc,
1589565efae9SDmitry Osipenko 						&usb_rdev_cfg);
1590565efae9SDmitry Osipenko 	if (IS_ERR(smb->usb_rdev)) {
1591565efae9SDmitry Osipenko 		smb347_irq_disable(smb);
1592565efae9SDmitry Osipenko 		return PTR_ERR(smb->usb_rdev);
1593565efae9SDmitry Osipenko 	}
1594565efae9SDmitry Osipenko 
15958c0984e5SSebastian Reichel 	return 0;
15968c0984e5SSebastian Reichel }
15978c0984e5SSebastian Reichel 
smb347_remove(struct i2c_client * client)1598ed5c2f5fSUwe Kleine-König static void smb347_remove(struct i2c_client *client)
15998c0984e5SSebastian Reichel {
16008c0984e5SSebastian Reichel 	struct smb347_charger *smb = i2c_get_clientdata(client);
16018c0984e5SSebastian Reichel 
1602565efae9SDmitry Osipenko 	smb347_usb_vbus_regulator_disable(smb->usb_rdev);
16038c0984e5SSebastian Reichel 	smb347_irq_disable(smb);
16048c0984e5SSebastian Reichel }
16058c0984e5SSebastian Reichel 
smb347_shutdown(struct i2c_client * client)1606565efae9SDmitry Osipenko static void smb347_shutdown(struct i2c_client *client)
1607565efae9SDmitry Osipenko {
1608565efae9SDmitry Osipenko 	smb347_remove(client);
1609565efae9SDmitry Osipenko }
1610565efae9SDmitry Osipenko 
16118c0984e5SSebastian Reichel static const struct i2c_device_id smb347_id[] = {
1612de76fd29SDavid Heidelberg 	{ "smb345", SMB345 },
1613de76fd29SDavid Heidelberg 	{ "smb347", SMB347 },
1614de76fd29SDavid Heidelberg 	{ "smb358", SMB358 },
1615de76fd29SDavid Heidelberg 	{ },
16168c0984e5SSebastian Reichel };
16178c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, smb347_id);
16188c0984e5SSebastian Reichel 
1619364bec75SDavid Heidelberg static const struct of_device_id smb3xx_of_match[] = {
1620de76fd29SDavid Heidelberg 	{ .compatible = "summit,smb345" },
1621364bec75SDavid Heidelberg 	{ .compatible = "summit,smb347" },
1622de76fd29SDavid Heidelberg 	{ .compatible = "summit,smb358" },
1623364bec75SDavid Heidelberg 	{ },
1624364bec75SDavid Heidelberg };
1625364bec75SDavid Heidelberg MODULE_DEVICE_TABLE(of, smb3xx_of_match);
1626364bec75SDavid Heidelberg 
16278c0984e5SSebastian Reichel static struct i2c_driver smb347_driver = {
16288c0984e5SSebastian Reichel 	.driver = {
16298c0984e5SSebastian Reichel 		.name = "smb347",
1630364bec75SDavid Heidelberg 		.of_match_table = smb3xx_of_match,
16318c0984e5SSebastian Reichel 	},
1632*fe20b1dcSUwe Kleine-König 	.probe = smb347_probe,
16338c0984e5SSebastian Reichel 	.remove = smb347_remove,
1634565efae9SDmitry Osipenko 	.shutdown = smb347_shutdown,
16358c0984e5SSebastian Reichel 	.id_table = smb347_id,
16368c0984e5SSebastian Reichel };
16378c0984e5SSebastian Reichel module_i2c_driver(smb347_driver);
16388c0984e5SSebastian Reichel 
16398c0984e5SSebastian Reichel MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
16408c0984e5SSebastian Reichel MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
16418c0984e5SSebastian Reichel MODULE_DESCRIPTION("SMB347 battery charger driver");
16428c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
1643