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