2214 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2214 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Chrager driver for Sc8551
 | |
|  *
 | |
|  * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
 | |
|  *
 | |
|  * Author: Xu Shengfei <xsf@rock-chips.com>
 | |
|  */
 | |
| #include <linux/gpio.h>
 | |
| #include <linux/i2c.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/power_supply.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/kthread.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/of.h>
 | |
| #include <linux/of_device.h>
 | |
| #include <linux/of_gpio.h>
 | |
| #include <linux/err.h>
 | |
| #include <linux/debugfs.h>
 | |
| #include <linux/bitops.h>
 | |
| 
 | |
| static int dbg_enable;
 | |
| 
 | |
| module_param_named(dbg_level, dbg_enable, int, 0644);
 | |
| 
 | |
| #define SC_DBG(args...) \
 | |
| 	do { \
 | |
| 		if (dbg_enable) { \
 | |
| 			pr_info(args); \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| 
 | |
| /* Register 00h */
 | |
| #define SC8551_REG_00				0x00
 | |
| #define SC8551_BAT_OVP_DIS_MASK			0x80
 | |
| #define SC8551_BAT_OVP_DIS_SHIFT		7
 | |
| #define SC8551_BAT_OVP_ENABLE			0
 | |
| #define SC8551_BAT_OVP_DISABLE			1
 | |
| 
 | |
| #define SC8551_BAT_OVP_MASK			0x3F
 | |
| #define SC8551_BAT_OVP_SHIFT			0
 | |
| #define SC8551_BAT_OVP_BASE			3500
 | |
| #define SC8551_BAT_OVP_LSB			25
 | |
| 
 | |
| /* Register 02h */
 | |
| #define SC8551_REG_02				0x02
 | |
| #define SC8551_BAT_OCP_DIS_MASK			0x80
 | |
| #define SC8551_BAT_OCP_DIS_SHIFT		7
 | |
| #define SC8551_BAT_OCP_ENABLE			0
 | |
| #define SC8551_BAT_OCP_DISABLE			1
 | |
| 
 | |
| #define SC8551_BAT_OCP_MASK			0x7F
 | |
| #define SC8551_BAT_OCP_SHIFT			0
 | |
| #define SC8551_BAT_OCP_BASE			2000
 | |
| #define SC8551_BAT_OCP_LSB			100
 | |
| 
 | |
| /* Register 05h */
 | |
| #define SC8551_REG_05				0x05
 | |
| #define SC8551_AC_OVP_STAT_MASK			0x80
 | |
| #define SC8551_AC_OVP_STAT_SHIFT		7
 | |
| 
 | |
| #define SC8551_AC_OVP_FLAG_MASK			0x40
 | |
| #define SC8551_AC_OVP_FLAG_SHIFT		6
 | |
| 
 | |
| #define SC8551_AC_OVP_MASK_MASK			0x20
 | |
| #define SC8551_AC_OVP_MASK_SHIFT		5
 | |
| 
 | |
| #define SC8551_VDROP_THRESHOLD_SET_MASK		0x10
 | |
| #define SC8551_VDROP_THRESHOLD_SET_SHIFT	4
 | |
| #define SC8551_VDROP_THRESHOLD_300MV		0
 | |
| #define SC8551_VDROP_THRESHOLD_400MV		1
 | |
| 
 | |
| #define SC8551_VDROP_DEGLITCH_SET_MASK		0x08
 | |
| #define SC8551_VDROP_DEGLITCH_SET_SHIFT		3
 | |
| #define SC8551_VDROP_DEGLITCH_8US		0
 | |
| #define SC8551_VDROP_DEGLITCH_5MS		1
 | |
| 
 | |
| #define SC8551_AC_OVP_MASK			0x07
 | |
| #define SC8551_AC_OVP_SHIFT			0
 | |
| #define SC8551_AC_OVP_BASE			11
 | |
| #define SC8551_AC_OVP_LSB			1
 | |
| #define SC8551_AC_OVP_6P5V			65
 | |
| 
 | |
| /* Register 06h */
 | |
| #define SC8551_REG_06				0x06
 | |
| #define SC8551_VBUS_PD_EN_MASK			0x80
 | |
| #define SC8551_VBUS_PD_EN_SHIFT			7
 | |
| #define SC8551_VBUS_PD_ENABLE			1
 | |
| #define SC8551_VBUS_PD_DISABLE			0
 | |
| 
 | |
| #define SC8551_BUS_OVP_MASK			0x7F
 | |
| #define SC8551_BUS_OVP_SHIFT			0
 | |
| #define SC8551_BUS_OVP_BASE			6000
 | |
| #define SC8551_BUS_OVP_LSB			50
 | |
| 
 | |
| /* Register 08h */
 | |
| #define SC8551_REG_08				0x08
 | |
| #define SC8551_BUS_OCP_DIS_MASK			0x80
 | |
| #define SC8551_BUS_OCP_DIS_SHIFT		7
 | |
| #define SC8551_BUS_OCP_ENABLE			0
 | |
| #define SC8551_BUS_OCP_DISABLE			1
 | |
| 
 | |
| #define SC8551_IBUS_UCP_RISE_FLAG_MASK		0x40
 | |
| #define SC8551_IBUS_UCP_RISE_FLAG_SHIFT		6
 | |
| 
 | |
| #define SC8551_IBUS_UCP_RISE_MASK_MASK		0x20
 | |
| #define SC8551_IBUS_UCP_RISE_MASK_SHIFT		5
 | |
| #define SC8551_IBUS_UCP_RISE_MASK_ENABLE	1
 | |
| #define SC8551_IBUS_UCP_RISE_MASK_DISABLE	0
 | |
| 
 | |
| #define SC8551_IBUS_UCP_FALL_FLAG_MASK		0x10
 | |
| #define SC8551_IBUS_UCP_FALL_FLAG_SHIFT		4
 | |
| 
 | |
| #define SC8551_BUS_OCP_MASK			0x0F
 | |
| #define SC8551_BUS_OCP_SHIFT			0
 | |
| #define SC8551_BUS_OCP_BASE			1000
 | |
| #define SC8551_BUS_OCP_LSB			250
 | |
| 
 | |
| /* Register 0Ah */
 | |
| #define SC8551_REG_0A				0x0A
 | |
| #define SC8551_TSHUT_FLAG_MASK			0x80
 | |
| #define SC8551_TSHUT_FLAG_SHIFT			7
 | |
| 
 | |
| #define SC8551_TSHUT_STAT_MASK			0x40
 | |
| #define SC8551_TSHUT_STAT_SHIFT			6
 | |
| 
 | |
| #define SC8551_VBUS_ERRORLO_STAT_MASK		0x20
 | |
| #define SC8551_VBUS_ERRORLO_STAT_SHIFT		5
 | |
| 
 | |
| #define SC8551_VBUS_ERRORHI_STAT_MASK		0x10
 | |
| #define SC8551_VBUS_ERRORHI_STAT_SHIFT		4
 | |
| 
 | |
| #define SC8551_SS_TIMEOUT_FLAG_MASK		0x08
 | |
| #define SC8551_SS_TIMEOUT_FLAG_SHIFT		3
 | |
| 
 | |
| #define SC8551_CONV_SWITCHING_STAT_MASK		0x04
 | |
| #define SC8551_CONV_SWITCHING_STAT_SHIFT	2
 | |
| 
 | |
| #define SC8551_CONV_OCP_FLAG_MASK		0x02
 | |
| #define SC8551_CONV_OCP_FLAG_SHIFT		1
 | |
| 
 | |
| #define SC8551_PIN_DIAG_FALL_FLAG_MASK		0x01
 | |
| #define SC8551_PIN_DIAG_FALL_FLAG_SHIFT		0
 | |
| 
 | |
| /* Register 0Bh */
 | |
| #define SC8551_REG_0B				0x0B
 | |
| #define SC8551_REG_RST_MASK			0x80
 | |
| #define SC8551_REG_RST_SHIFT			7
 | |
| #define SC8551_REG_RST_ENABLE			1
 | |
| #define SC8551_REG_RST_DISABLE			0
 | |
| 
 | |
| #define SC8551_FSW_SET_MASK			0x70
 | |
| #define SC8551_FSW_SET_SHIFT			4
 | |
| #define SC8551_FSW_SET_300KHZ			0
 | |
| #define SC8551_FSW_SET_350KHZ			1
 | |
| #define SC8551_FSW_SET_400KHZ			2
 | |
| #define SC8551_FSW_SET_450KHZ			3
 | |
| #define SC8551_FSW_SET_500KHZ			4
 | |
| #define SC8551_FSW_SET_550KHZ			5
 | |
| #define SC8551_FSW_SET_600KHZ			6
 | |
| #define SC8551_FSW_SET_750KHZ			7
 | |
| 
 | |
| #define SC8551_WD_TIMEOUT_FLAG_MASK		0x08
 | |
| #define SC8551_WD_TIMEOUT_SHIFT			3
 | |
| 
 | |
| #define SC8551_WATCHDOG_DIS_MASK		0x04
 | |
| #define SC8551_WATCHDOG_DIS_SHIFT		2
 | |
| #define SC8551_WATCHDOG_ENABLE			0
 | |
| #define SC8551_WATCHDOG_DISABLE			1
 | |
| 
 | |
| #define SC8551_WATCHDOG_MASK			0x03
 | |
| #define SC8551_WATCHDOG_SHIFT			0
 | |
| #define SC8551_WATCHDOG_0P5S			0
 | |
| #define SC8551_WATCHDOG_1S			1
 | |
| #define SC8551_WATCHDOG_5S			2
 | |
| #define SC8551_WATCHDOG_30S			3
 | |
| 
 | |
| /* Register 0Ch */
 | |
| #define SC8551_REG_0C				0x0C
 | |
| #define SC8551_CHG_EN_MASK			0x80
 | |
| #define SC8551_CHG_EN_SHIFT			7
 | |
| #define SC8551_CHG_ENABLE			1
 | |
| #define SC8551_CHG_DISABLE			0
 | |
| 
 | |
| #define SC8551_MS_MASK				0x60
 | |
| #define SC8551_MS_SHIFT				5
 | |
| #define SC8551_MS_STANDALONE			0
 | |
| #define SC8551_MS_SLAVE				1
 | |
| #define SC8551_MS_MASTER			2
 | |
| #define SC8551_ROLE_STDALONE			0
 | |
| #define SC8551_ROLE_SLAVE			1
 | |
| #define SC8551_ROLE_MASTER			2
 | |
| 
 | |
| #define SC8551_FREQ_SHIFT_MASK			0x18
 | |
| #define SC8551_FREQ_SHIFT_SHIFT			3
 | |
| #define SC8551_FREQ_SHIFT_NORMINAL		0
 | |
| #define SC8551_FREQ_SHIFT_POSITIVE10		1
 | |
| #define SC8551_FREQ_SHIFT_NEGATIVE10		2
 | |
| #define SC8551_FREQ_SHIFT_SPREAD_SPECTRUM	3
 | |
| 
 | |
| #define SC8551_TSBUS_DIS_MASK			0x04
 | |
| #define SC8551_TSBUS_DIS_SHIFT			2
 | |
| #define SC8551_TSBUS_ENABLE			0
 | |
| #define SC8551_TSBUS_DISABLE			1
 | |
| 
 | |
| #define SC8551_TSBAT_DIS_MASK			0x02
 | |
| #define SC8551_TSBAT_DIS_SHIFT			1
 | |
| #define SC8551_TSBAT_ENABLE			0
 | |
| #define SC8551_TSBAT_DISABLE			1
 | |
| 
 | |
| /* Register 0Dh */
 | |
| #define SC8551_REG_0D				0x0D
 | |
| #define SC8551_BAT_OVP_ALM_STAT_MASK		0x80
 | |
| #define SC8551_BAT_OVP_ALM_STAT_SHIFT		7
 | |
| 
 | |
| #define SC8551_BAT_OCP_ALM_STAT_MASK		0x40
 | |
| #define SC8551_BAT_OCP_ALM_STAT_SHIFT		6
 | |
| 
 | |
| #define SC8551_BUS_OVP_ALM_STAT_MASK		0x20
 | |
| #define SC8551_BUS_OVP_ALM_STAT_SHIFT		5
 | |
| 
 | |
| #define SC8551_BUS_OCP_ALM_STAT_MASK		0x10
 | |
| #define SC8551_BUS_OCP_ALM_STAT_SHIFT		4
 | |
| 
 | |
| #define SC8551_BAT_UCP_ALM_STAT_MASK		0x08
 | |
| #define SC8551_BAT_UCP_ALM_STAT_SHIFT		3
 | |
| 
 | |
| #define SC8551_ADAPTER_INSERT_STAT_MASK		0x04
 | |
| #define SC8551_ADAPTER_INSERT_STAT_SHIFT	2
 | |
| 
 | |
| #define SC8551_VBAT_INSERT_STAT_MASK		0x02
 | |
| #define SC8551_VBAT_INSERT_STAT_SHIFT		1
 | |
| 
 | |
| #define SC8551_ADC_DONE_STAT_MASK		0x01
 | |
| #define SC8551_ADC_DONE_STAT_SHIFT		0
 | |
| #define SC8551_ADC_DONE_STAT_COMPLETE		1
 | |
| #define SC8551_ADC_DONE_STAT_NOTCOMPLETE	0
 | |
| 
 | |
| /* Register 0Eh */
 | |
| #define SC8551_REG_0E				0x0E
 | |
| #define SC8551_BAT_OVP_ALM_FLAG_MASK		0x80
 | |
| #define SC8551_BAT_OVP_ALM_FLAG_SHIFT		7
 | |
| 
 | |
| #define SC8551_BAT_OCP_ALM_FLAG_MASK		0x40
 | |
| #define SC8551_BAT_OCP_ALM_FLAG_SHIFT		6
 | |
| 
 | |
| #define SC8551_BUS_OVP_ALM_FLAG_MASK		0x20
 | |
| #define SC8551_BUS_OVP_ALM_FLAG_SHIFT		5
 | |
| 
 | |
| #define SC8551_BUS_OCP_ALM_FLAG_MASK		0x10
 | |
| #define SC8551_BUS_OCP_ALM_FLAG_SHIFT		4
 | |
| 
 | |
| #define SC8551_BAT_UCP_ALM_FLAG_MASK		0x08
 | |
| #define SC8551_BAT_UCP_ALM_FLAG_SHIFT		3
 | |
| 
 | |
| #define SC8551_ADAPTER_INSERT_FLAG_MASK		0x04
 | |
| #define SC8551_ADAPTER_INSERT_FLAG_SHIFT	2
 | |
| 
 | |
| #define SC8551_VBAT_INSERT_FLAG_MASK		0x02
 | |
| #define SC8551_VBAT_INSERT_FLAG_SHIFT		1
 | |
| 
 | |
| #define SC8551_ADC_DONE_FLAG_MASK		0x01
 | |
| #define SC8551_ADC_DONE_FLAG_SHIFT		0
 | |
| #define SC8551_ADC_DONE_FLAG_COMPLETE		1
 | |
| #define SC8551_ADC_DONE_FLAG_NOTCOMPLETE	0
 | |
| 
 | |
| /* Register 0Fh */
 | |
| #define SC8551_REG_0F				0x0F
 | |
| #define SC8551_BAT_OVP_ALM_MASK_MASK		0x80
 | |
| #define SC8551_BAT_OVP_ALM_MASK_SHIFT		7
 | |
| #define SC8551_BAT_OVP_ALM_MASK_ENABLE		1
 | |
| #define SC8551_BAT_OVP_ALM_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_BAT_OCP_ALM_MASK_MASK		0x40
 | |
| #define SC8551_BAT_OCP_ALM_MASK_SHIFT		6
 | |
| #define SC8551_BAT_OCP_ALM_MASK_ENABLE		1
 | |
| #define SC8551_BAT_OCP_ALM_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_BUS_OVP_ALM_MASK_MASK		0x20
 | |
| #define SC8551_BUS_OVP_ALM_MASK_SHIFT		5
 | |
| #define SC8551_BUS_OVP_ALM_MASK_ENABLE		1
 | |
| #define SC8551_BUS_OVP_ALM_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_BUS_OCP_ALM_MASK_MASK		0x10
 | |
| #define SC8551_BUS_OCP_ALM_MASK_SHIFT		4
 | |
| #define SC8551_BUS_OCP_ALM_MASK_ENABLE		1
 | |
| #define SC8551_BUS_OCP_ALM_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_BAT_UCP_ALM_MASK_MASK		0x08
 | |
| #define SC8551_BAT_UCP_ALM_MASK_SHIFT		3
 | |
| #define SC8551_BAT_UCP_ALM_MASK_ENABLE		1
 | |
| #define SC8551_BAT_UCP_ALM_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_ADAPTER_INSERT_MASK_MASK		0x04
 | |
| #define SC8551_ADAPTER_INSERT_MASK_SHIFT	2
 | |
| #define SC8551_ADAPTER_INSERT_MASK_ENABLE	1
 | |
| #define SC8551_ADAPTER_INSERT_MASK_DISABLE	0
 | |
| 
 | |
| #define SC8551_VBAT_INSERT_MASK_MASK		0x02
 | |
| #define SC8551_VBAT_INSERT_MASK_SHIFT		1
 | |
| #define SC8551_VBAT_INSERT_MASK_ENABLE		1
 | |
| #define SC8551_VBAT_INSERT_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_ADC_DONE_MASK_MASK		0x01
 | |
| #define SC8551_ADC_DONE_MASK_SHIFT		0
 | |
| #define SC8551_ADC_DONE_MASK_ENABLE		1
 | |
| #define SC8551_ADC_DONE_MASK_DISABLE		0
 | |
| 
 | |
| /* Register 10h */
 | |
| #define SC8551_REG_10				0x10
 | |
| #define SC8551_BAT_OVP_FLT_STAT_MASK		0x80
 | |
| #define SC8551_BAT_OVP_FLT_STAT_SHIFT		7
 | |
| 
 | |
| #define SC8551_BAT_OCP_FLT_STAT_MASK		0x40
 | |
| #define SC8551_BAT_OCP_FLT_STAT_SHIFT		6
 | |
| 
 | |
| #define SC8551_BUS_OVP_FLT_STAT_MASK		0x20
 | |
| #define SC8551_BUS_OVP_FLT_STAT_SHIFT		5
 | |
| 
 | |
| #define SC8551_BUS_OCP_FLT_STAT_MASK		0x10
 | |
| #define SC8551_BUS_OCP_FLT_STAT_SHIFT		4
 | |
| 
 | |
| #define SC8551_TSBUS_TSBAT_ALM_STAT_MASK	0x08
 | |
| #define SC8551_TSBUS_TSBAT_ALM_STAT_SHIFT	3
 | |
| 
 | |
| #define SC8551_TSBAT_FLT_STAT_MASK		0x04
 | |
| #define SC8551_TSBAT_FLT_STAT_SHIFT		2
 | |
| 
 | |
| #define SC8551_TSBUS_FLT_STAT_MASK		0x02
 | |
| #define SC8551_TSBUS_FLT_STAT_SHIFT		1
 | |
| 
 | |
| #define SC8551_TDIE_ALM_STAT_MASK		0x01
 | |
| #define SC8551_TDIE_ALM_STAT_SHIFT		0
 | |
| 
 | |
| /* Register 11h */
 | |
| #define SC8551_REG_11				0x11
 | |
| #define SC8551_BAT_OVP_FLT_FLAG_MASK		0x80
 | |
| #define SC8551_BAT_OVP_FLT_FLAG_SHIFT		7
 | |
| 
 | |
| #define SC8551_BAT_OCP_FLT_FLAG_MASK		0x40
 | |
| #define SC8551_BAT_OCP_FLT_FLAG_SHIFT		6
 | |
| 
 | |
| #define SC8551_BUS_OVP_FLT_FLAG_MASK		0x20
 | |
| #define SC8551_BUS_OVP_FLT_FLAG_SHIFT		5
 | |
| 
 | |
| #define SC8551_BUS_OCP_FLT_FLAG_MASK		0x10
 | |
| #define SC8551_BUS_OCP_FLT_FLAG_SHIFT		4
 | |
| 
 | |
| #define SC8551_TSBUS_TSBAT_ALM_FLAG_MASK	0x08
 | |
| #define SC8551_TSBUS_TSBAT_ALM_FLAG_SHIFT	3
 | |
| 
 | |
| #define SC8551_TSBAT_FLT_FLAG_MASK		0x04
 | |
| #define SC8551_TSBAT_FLT_FLAG_SHIFT		2
 | |
| 
 | |
| #define SC8551_TSBUS_FLT_FLAG_MASK		0x02
 | |
| #define SC8551_TSBUS_FLT_FLAG_SHIFT		1
 | |
| 
 | |
| #define SC8551_TDIE_ALM_FLAG_MASK		0x01
 | |
| #define SC8551_TDIE_ALM_FLAG_SHIFT		0
 | |
| 
 | |
| /* Register 12h */
 | |
| #define SC8551_REG_12				0x12
 | |
| #define SC8551_BAT_OVP_FLT_MASK_MASK		0x80
 | |
| #define SC8551_BAT_OVP_FLT_MASK_SHIFT		7
 | |
| #define SC8551_BAT_OVP_FLT_MASK_ENABLE		1
 | |
| #define SC8551_BAT_OVP_FLT_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_BAT_OCP_FLT_MASK_MASK		0x40
 | |
| #define SC8551_BAT_OCP_FLT_MASK_SHIFT		6
 | |
| #define SC8551_BAT_OCP_FLT_MASK_ENABLE		1
 | |
| #define SC8551_BAT_OCP_FLT_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_BUS_OVP_FLT_MASK_MASK		0x20
 | |
| #define SC8551_BUS_OVP_FLT_MASK_SHIFT		5
 | |
| #define SC8551_BUS_OVP_FLT_MASK_ENABLE		1
 | |
| #define SC8551_BUS_OVP_FLT_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_BUS_OCP_FLT_MASK_MASK		0x10
 | |
| #define SC8551_BUS_OCP_FLT_MASK_SHIFT		4
 | |
| #define SC8551_BUS_OCP_FLT_MASK_ENABLE		1
 | |
| #define SC8551_BUS_OCP_FLT_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_TSBUS_TSBAT_ALM_MASK_MASK	0x08
 | |
| #define SC8551_TSBUS_TSBAT_ALM_MASK_SHIFT	3
 | |
| #define SC8551_TSBUS_TSBAT_ALM_MASK_ENABLE	1
 | |
| #define SC8551_TSBUS_TSBAT_ALM_MASK_DISABLE	0
 | |
| 
 | |
| #define SC8551_TSBAT_FLT_MASK_MASK		0x04
 | |
| #define SC8551_TSBAT_FLT_MASK_SHIFT		2
 | |
| #define SC8551_TSBAT_FLT_MASK_ENABLE		1
 | |
| #define SC8551_TSBAT_FLT_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_TSBUS_FLT_MASK_MASK		0x02
 | |
| #define SC8551_TSBUS_FLT_MASK_SHIFT		1
 | |
| #define SC8551_TSBUS_FLT_MASK_ENABLE		1
 | |
| #define SC8551_TSBUS_FLT_MASK_DISABLE		0
 | |
| 
 | |
| #define SC8551_TDIE_ALM_MASK_MASK		0x01
 | |
| #define SC8551_TDIE_ALM_MASK_SHIFT		0
 | |
| #define SC8551_TDIE_ALM_MASK_ENABLE		1
 | |
| #define SC8551_TDIE_ALM_MASK_DISABLE		0
 | |
| 
 | |
| /* Register 13h */
 | |
| #define SC8551_REG_13				0x13
 | |
| #define SC8551_DEV_ID_MASK			0xFF
 | |
| #define SC8551_DEV_ID_SHIFT			0
 | |
| 
 | |
| /* Register 14h */
 | |
| #define SC8551_REG_14				0x14
 | |
| #define SC8551_ADC_EN_MASK			0x80
 | |
| #define SC8551_ADC_EN_SHIFT			7
 | |
| #define SC8551_ADC_ENABLE			1
 | |
| #define SC8551_ADC_DISABLE			0
 | |
| 
 | |
| #define SC8551_ADC_RATE_MASK			0x40
 | |
| #define SC8551_ADC_RATE_SHIFT			6
 | |
| #define SC8551_ADC_RATE_CONTINOUS		0
 | |
| #define SC8551_ADC_RATE_ONESHOT			1
 | |
| 
 | |
| #define SC8551_IBUS_ADC_DIS_MASK		0x01
 | |
| #define SC8551_IBUS_ADC_DIS_SHIFT		0
 | |
| #define SC8551_IBUS_ADC_ENABLE			0
 | |
| #define SC8551_IBUS_ADC_DISABLE			1
 | |
| 
 | |
| /* Register 15h */
 | |
| #define SC8551_REG_15				0x15
 | |
| #define SC8551_VBUS_ADC_DIS_MASK		0x80
 | |
| #define SC8551_VBUS_ADC_DIS_SHIFT		7
 | |
| #define SC8551_VBUS_ADC_ENABLE			0
 | |
| #define SC8551_VBUS_ADC_DISABLE			1
 | |
| 
 | |
| #define SC8551_VAC_ADC_DIS_MASK			0x40
 | |
| #define SC8551_VAC_ADC_DIS_SHIFT		6
 | |
| #define SC8551_VAC_ADC_ENABLE			0
 | |
| #define SC8551_VAC_ADC_DISABLE			1
 | |
| 
 | |
| #define SC8551_VOUT_ADC_DIS_MASK		0x20
 | |
| #define SC8551_VOUT_ADC_DIS_SHIFT		5
 | |
| #define SC8551_VOUT_ADC_ENABLE			0
 | |
| #define SC8551_VOUT_ADC_DISABLE			1
 | |
| 
 | |
| #define SC8551_VBAT_ADC_DIS_MASK		0x10
 | |
| #define SC8551_VBAT_ADC_DIS_SHIFT		4
 | |
| #define SC8551_VBAT_ADC_ENABLE			0
 | |
| #define SC8551_VBAT_ADC_DISABLE			1
 | |
| 
 | |
| #define SC8551_IBAT_ADC_DIS_MASK		0x08
 | |
| #define SC8551_IBAT_ADC_DIS_SHIFT		3
 | |
| #define SC8551_IBAT_ADC_ENABLE			0
 | |
| #define SC8551_IBAT_ADC_DISABLE			1
 | |
| 
 | |
| #define SC8551_TSBUS_ADC_DIS_MASK		0x04
 | |
| #define SC8551_TSBUS_ADC_DIS_SHIFT		2
 | |
| #define SC8551_TSBUS_ADC_ENABLE			0
 | |
| #define SC8551_TSBUS_ADC_DISABLE		1
 | |
| 
 | |
| #define SC8551_TSBAT_ADC_DIS_MASK		0x02
 | |
| #define SC8551_TSBAT_ADC_DIS_SHIFT		1
 | |
| #define SC8551_TSBAT_ADC_ENABLE			0
 | |
| #define SC8551_TSBAT_ADC_DISABLE		1
 | |
| 
 | |
| #define SC8551_TDIE_ADC_DIS_MASK		0x01
 | |
| #define SC8551_TDIE_ADC_DIS_SHIFT		0
 | |
| #define SC8551_TDIE_ADC_ENABLE			0
 | |
| #define SC8551_TDIE_ADC_DISABLE			1
 | |
| 
 | |
| /* Register 16h */
 | |
| #define SC8551_REG_16				0x16
 | |
| #define SC8551_IBUS_POL_H_MASK			0x0F
 | |
| 
 | |
| /* Register 17h */
 | |
| #define SC8551_REG_17				0x17
 | |
| #define SC8551_IBUS_POL_L_MASK			0xFF
 | |
| 
 | |
| /* Register 18h */
 | |
| #define SC8551_REG_18				0x18
 | |
| #define SC8551_VBUS_POL_H_MASK			0x0F
 | |
| 
 | |
| /* Register 19h */
 | |
| #define SC8551_REG_19				0x19
 | |
| #define SC8551_VBUS_POL_L_MASK			0xFF
 | |
| 
 | |
| /* Register 1Ah */
 | |
| #define SC8551_REG_1A				0x1A
 | |
| #define SC8551_VAC_POL_H_MASK			0x0F
 | |
| 
 | |
| /* Register 1Bh */
 | |
| #define SC8551_REG_1B				0x1B
 | |
| #define SC8551_VAC_POL_L_MASK			0xFF
 | |
| 
 | |
| /* Register 1Ch */
 | |
| #define SC8551_REG_1C				0x1C
 | |
| #define SC8551_VOUT_POL_H_MASK			0x0F
 | |
| 
 | |
| /* Register 1Dh */
 | |
| #define SC8551_REG_1D				0x1D
 | |
| #define SC8551_VOUT_POL_L_MASK			0x0F
 | |
| 
 | |
| /* Register 1Eh */
 | |
| #define SC8551_REG_1E				0x1E
 | |
| #define SC8551_VBAT_POL_H_MASK			0x0F
 | |
| 
 | |
| /* Register 1Fh */
 | |
| #define SC8551_REG_1F				0x1F
 | |
| #define SC8551_VBAT_POL_L_MASK			0xFF
 | |
| 
 | |
| /* Register 20h */
 | |
| #define SC8551_REG_20				0x20
 | |
| #define SC8551_IBAT_POL_H_MASK			0x0F
 | |
| 
 | |
| /* Register 21h */
 | |
| #define SC8551_REG_21				0x21
 | |
| #define SC8551_IBAT_POL_L_MASK			0xFF
 | |
| 
 | |
| /* Register 26h */
 | |
| #define SC8551_REG_26				0x26
 | |
| #define SC8551_TDIE_POL_H_MASK			0x01
 | |
| 
 | |
| /* Register 2Bh */
 | |
| #define SC8551_REG_2B				0x2B
 | |
| #define SC8551_SS_TIMEOUT_SET_MASK		0xE0
 | |
| #define SC8551_SS_TIMEOUT_SET_SHIFT		5
 | |
| #define SC8551_SS_TIMEOUT_DISABLE		0
 | |
| #define SC8551_SS_TIMEOUT_12P5MS		1
 | |
| #define SC8551_SS_TIMEOUT_25MS			2
 | |
| #define SC8551_SS_TIMEOUT_50MS			3
 | |
| #define SC8551_SS_TIMEOUT_100MS			4
 | |
| #define SC8551_SS_TIMEOUT_400MS			5
 | |
| #define SC8551_SS_TIMEOUT_1500MS		6
 | |
| #define SC8551_SS_TIMEOUT_100000MS		7
 | |
| 
 | |
| #define SC8551_EN_REGULATION_MASK		0x10
 | |
| #define SC8551_EN_REGULATION_SHIFT		4
 | |
| #define SC8551_EN_REGULATION_ENABLE		1
 | |
| #define SC8551_EN_REGULATION_DISABLE		0
 | |
| 
 | |
| #define SC8551_VOUT_OVP_DIS_MASK		0x08
 | |
| #define SC8551_VOUT_OVP_DIS_SHIFT		3
 | |
| #define SC8551_VOUT_OVP_ENABLE			1
 | |
| #define SC8551_VOUT_OVP_DISABLE			0
 | |
| 
 | |
| #define SC8551_IBUS_UCP_RISE_TH_MASK		0x04
 | |
| #define SC8551_IBUS_UCP_RISE_TH_SHIFT		2
 | |
| #define SC8551_IBUS_UCP_RISE_150MA		0
 | |
| #define SC8551_IBUS_UCP_RISE_250MA		1
 | |
| 
 | |
| #define SC8551_SET_IBAT_SNS_RES_MASK		0x02
 | |
| #define SC8551_SET_IBAT_SNS_RES_SHIFT		1
 | |
| #define SC8551_SET_IBAT_SNS_RES_2MHM		0
 | |
| #define SC8551_SET_IBAT_SNS_RES_5MHM		1
 | |
| 
 | |
| #define SC8551_VAC_PD_EN_MASK			0x01
 | |
| #define SC8551_VAC_PD_EN_SHIFT			0
 | |
| #define SC8551_VAC_PD_ENABLE			1
 | |
| #define SC8551_VAC_PD_DISABLE			0
 | |
| 
 | |
| /* Register 2Ch */
 | |
| #define SC8551_REG_2C				0x2C
 | |
| #define SC8551_IBAT_REG_MASK			0xC0
 | |
| #define SC8551_IBAT_REG_SHIFT			6
 | |
| #define SC8551_IBAT_REG_200MA			0
 | |
| #define SC8551_IBAT_REG_300MA			1
 | |
| #define SC8551_IBAT_REG_400MA			2
 | |
| #define SC8551_IBAT_REG_500MA			3
 | |
| #define SC8551_VBAT_REG_MASK			0x30
 | |
| #define SC8551_VBAT_REG_SHIFT			4
 | |
| #define SC8551_VBAT_REG_50MV			0
 | |
| #define SC8551_VBAT_REG_100MV			1
 | |
| #define SC8551_VBAT_REG_150MV			2
 | |
| #define SC8551_VBAT_REG_200MV			3
 | |
| 
 | |
| #define SC8551_VBAT_REG_ACTIVE_STAT_MASK	0x08
 | |
| #define SC8551_IBAT_REG_ACTIVE_STAT_MASK	0x04
 | |
| #define SC8551_VDROP_OVP_ACTIVE_STAT_MASK	0x02
 | |
| #define SC8551_VOUT_OVP_ACTIVE_STAT_MASK	0x01
 | |
| 
 | |
| #define SC8551_REG_2D				0x2D
 | |
| #define SC8551_VBAT_REG_ACTIVE_FLAG_MASK	0x80
 | |
| #define SC8551_IBAT_REG_ACTIVE_FLAG_MASK	0x40
 | |
| #define SC8551_VDROP_OVP_FLAG_MASK		0x20
 | |
| #define SC8551_VOUT_OVP_FLAG_MASK		0x10
 | |
| #define SC8551_VBAT_REG_ACTIVE_MASK_MASK	0x08
 | |
| #define SC8551_IBAT_REG_ACTIVE_MASK_MASK	0x04
 | |
| #define SC8551_VDROP_OVP_MASK_MASK		0x02
 | |
| #define SC8551_VOUT_OVP_MASK_MASK		0x01
 | |
| 
 | |
| #define SC8551_REG_2E				0x2E
 | |
| #define SC8551_IBUS_LOW_DG_MASK			0x08
 | |
| #define SC8551_IBUS_LOW_DG_SHIFT		3
 | |
| #define SC8551_IBUS_LOW_DG_10US			0
 | |
| #define SC8551_IBUS_LOW_DG_5MS			1
 | |
| 
 | |
| #define SC8551_REG_2F				0x2F
 | |
| #define SC8551_PMID2OUT_UVP_FLAG_MASK		0x08
 | |
| #define SC8551_PMID2OUT_OVP_FLAG_MASK		0x04
 | |
| #define SC8551_PMID2OUT_UVP_STAT_MASK		0x02
 | |
| #define SC8551_PMID2OUT_OVP_STAT_MASK		0x01
 | |
| 
 | |
| #define SC8551_REG_30				0x30
 | |
| #define SC8551_IBUS_REG_EN_MASK			0x80
 | |
| #define SC8551_IBUS_REG_EN_SHIFT		7
 | |
| #define SC8551_IBUS_REG_ENABLE			1
 | |
| #define SC8551_IBUS_REG_DISABLE			0
 | |
| #define SC8551_IBUS_REG_ACTIVE_STAT_MASK	0x40
 | |
| #define SC8551_IBUS_REG_ACTIVE_FLAG_MASK	0x20
 | |
| #define SC8551_IBUS_REG_ACTIVE_MASK_MASK	0x10
 | |
| #define SC8551_IBUS_REG_ACTIVE_MASK_SHIFT	4
 | |
| #define SC8551_IBUS_REG_ACTIVE_NOT_MASK		0
 | |
| #define SC8551_IBUS_REG_ACTIVE_MASK		1
 | |
| #define SC8551_IBUS_REG_MASK			0x0F
 | |
| #define SC8551_IBUS_REG_SHIFT			0
 | |
| #define SC8551_IBUS_REG_BASE			1000
 | |
| #define SC8551_IBUS_REG_LSB			250
 | |
| 
 | |
| #define SC8551_REG_31				0x31
 | |
| #define SC8551_CHARGE_MODE_MASK			0x01
 | |
| #define SC8551_CHARGE_MODE_SHIFT		0
 | |
| #define SC8551_CHARGE_MODE_2_1			0
 | |
| #define SC8551_CHARGE_MODE_1_1			1
 | |
| 
 | |
| #define SC8551_REG_35				0x35
 | |
| #define SC8551_VBUS_IN_RANGE_MASK		0x01
 | |
| #define SC8551_VBUS_IN_RANGE_SHIFT		6
 | |
| #define SC8551_VBUS_IN_RANGE_ENABLE		0
 | |
| #define SC8551_VBUS_IN_RANGE_DISABLE		1
 | |
| 
 | |
| 
 | |
| #define SC8551_REG_36				0x36
 | |
| #define SC8551_OVPGATE_MASK			0x01
 | |
| #define SC8551_OVPGATE_SHIFT			3
 | |
| #define SC8551_OVPGATE_ENABLE			0
 | |
| #define SC8551_OVPGATE_DISABLE			1
 | |
| 
 | |
| #define VBUS_INSERT				BIT(2)
 | |
| #define VBAT_INSERT				BIT(1)
 | |
| #define ADC_DONE				BIT(0)
 | |
| 
 | |
| #define BAT_OVP_FAULT				BIT(7)
 | |
| #define BAT_OCP_FAULT				BIT(6)
 | |
| #define BUS_OVP_FAULT				BIT(5)
 | |
| #define BUS_OCP_FAULT				BIT(4)
 | |
| 
 | |
| /*below used for comm with other module*/
 | |
| #define BAT_OVP_FAULT_SHIFT			0
 | |
| #define BAT_OCP_FAULT_SHIFT			1
 | |
| #define BUS_OVP_FAULT_SHIFT			2
 | |
| #define BUS_OCP_FAULT_SHIFT			3
 | |
| #define BAT_THERM_FAULT_SHIFT			4
 | |
| #define BUS_THERM_FAULT_SHIFT			5
 | |
| #define DIE_THERM_FAULT_SHIFT			6
 | |
| 
 | |
| #define BAT_OVP_FAULT_MASK			(1 << BAT_OVP_FAULT_SHIFT)
 | |
| #define BAT_OCP_FAULT_MASK			(1 << BAT_OCP_FAULT_SHIFT)
 | |
| #define BUS_OVP_FAULT_MASK			(1 << BUS_OVP_FAULT_SHIFT)
 | |
| #define BUS_OCP_FAULT_MASK			(1 << BUS_OCP_FAULT_SHIFT)
 | |
| #define BAT_THERM_FAULT_MASK			(1 << BAT_THERM_FAULT_SHIFT)
 | |
| #define BUS_THERM_FAULT_MASK			(1 << BUS_THERM_FAULT_SHIFT)
 | |
| #define DIE_THERM_FAULT_MASK			(1 << DIE_THERM_FAULT_SHIFT)
 | |
| 
 | |
| #define BAT_OVP_ALARM_SHIFT			0
 | |
| #define BAT_OCP_ALARM_SHIFT			1
 | |
| #define BUS_OVP_ALARM_SHIFT			2
 | |
| #define BUS_OCP_ALARM_SHIFT			3
 | |
| #define BAT_THERM_ALARM_SHIFT			4
 | |
| #define BUS_THERM_ALARM_SHIFT			5
 | |
| #define DIE_THERM_ALARM_SHIFT			6
 | |
| #define BAT_UCP_ALARM_SHIFT			7
 | |
| #define BAT_OVP_ALARM_MASK			(1 << BAT_OVP_ALARM_SHIFT)
 | |
| #define BAT_OCP_ALARM_MASK			(1 << BAT_OCP_ALARM_SHIFT)
 | |
| #define BUS_OVP_ALARM_MASK			(1 << BUS_OVP_ALARM_SHIFT)
 | |
| #define BUS_OCP_ALARM_MASK			(1 << BUS_OCP_ALARM_SHIFT)
 | |
| #define BAT_THERM_ALARM_MASK			(1 << BAT_THERM_ALARM_SHIFT)
 | |
| #define BUS_THERM_ALARM_MASK			(1 << BUS_THERM_ALARM_SHIFT)
 | |
| #define DIE_THERM_ALARM_MASK			(1 << DIE_THERM_ALARM_SHIFT)
 | |
| #define BAT_UCP_ALARM_MASK			(1 << BAT_UCP_ALARM_SHIFT)
 | |
| 
 | |
| #define VBAT_REG_STATUS_SHIFT			0
 | |
| #define IBAT_REG_STATUS_SHIFT			1
 | |
| 
 | |
| #define VBAT_REG_STATUS_MASK			(1 << VBAT_REG_STATUS_SHIFT)
 | |
| #define IBAT_REG_STATUS_MASK			(1 << VBAT_REG_STATUS_SHIFT)
 | |
| 
 | |
| #define SC8551_DEBUG_BUF_LEN			30
 | |
| 
 | |
| enum {
 | |
| 	ADC_IBUS,
 | |
| 	ADC_VBUS,
 | |
| 	ADC_VAC,
 | |
| 	ADC_VOUT,
 | |
| 	ADC_VBAT,
 | |
| 	ADC_IBAT,
 | |
| 	ADC_TBUS,
 | |
| 	ADC_TBAT,
 | |
| 	ADC_TDIE,
 | |
| 	ADC_MAX_NUM,
 | |
| };
 | |
| 
 | |
| struct sc8551_cfg {
 | |
| 	bool bat_ovp_disable;
 | |
| 	bool bat_ocp_disable;
 | |
| 
 | |
| 	int bat_ovp_th;
 | |
| 	int bat_ovp_alm_th;
 | |
| 	int bat_ocp_th;
 | |
| 
 | |
| 	bool bus_ocp_disable;
 | |
| 
 | |
| 	int bus_ovp_th;
 | |
| 	int bus_ocp_th;
 | |
| 
 | |
| 	int ac_ovp_th;
 | |
| 
 | |
| 	bool bat_therm_disable;
 | |
| 	bool bus_therm_disable;
 | |
| 	bool die_therm_disable;
 | |
| 
 | |
| 	int sense_r_mohm;
 | |
| };
 | |
| 
 | |
| struct sc8551 {
 | |
| 	struct device *dev;
 | |
| 	struct i2c_client *client;
 | |
| 
 | |
| 	int part_no;
 | |
| 	int revision;
 | |
| 
 | |
| 	int mode;
 | |
| 
 | |
| 	struct mutex data_lock;
 | |
| 	struct mutex i2c_rw_lock;
 | |
| 
 | |
| 	int irq;
 | |
| 
 | |
| 	bool batt_present;
 | |
| 	bool vbus_present;
 | |
| 
 | |
| 	bool usb_present;
 | |
| 	bool charge_enabled;
 | |
| 
 | |
| 	int vbus_error;
 | |
| 
 | |
| 	/* ADC reading */
 | |
| 	int vbat_volt;
 | |
| 	int vbus_volt;
 | |
| 	int vout_volt;
 | |
| 	int vac_volt;
 | |
| 
 | |
| 	int ibat_curr;
 | |
| 	int ibus_curr;
 | |
| 
 | |
| 	int die_temp;
 | |
| 
 | |
| 	/* alarm/fault status */
 | |
| 	bool bat_ovp_fault;
 | |
| 	bool bat_ocp_fault;
 | |
| 	bool bus_ovp_fault;
 | |
| 	bool bus_ocp_fault;
 | |
| 
 | |
| 	bool vbat_reg;
 | |
| 	bool ibat_reg;
 | |
| 
 | |
| 	int prev_alarm;
 | |
| 	int prev_fault;
 | |
| 
 | |
| 	struct sc8551_cfg *cfg;
 | |
| 
 | |
| 	struct power_supply_desc psy_desc;
 | |
| 	struct power_supply_config psy_cfg;
 | |
| 	struct power_supply *fc2_psy;
 | |
| };
 | |
| 
 | |
| static int __sc8551_read_byte(struct sc8551 *sc, u8 reg, u8 *data)
 | |
| {
 | |
| 	s32 ret;
 | |
| 
 | |
| 	ret = i2c_smbus_read_byte_data(sc->client, reg);
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(sc->dev, "i2c read fail: can't read from reg 0x%02X\n", reg);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	*data = (u8) ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int __sc8551_write_byte(struct sc8551 *sc, int reg, u8 val)
 | |
| {
 | |
| 	s32 ret;
 | |
| 
 | |
| 	ret = i2c_smbus_write_byte_data(sc->client, reg, val);
 | |
| 	if (ret < 0) {
 | |
| 		dev_err(sc->dev, "i2c write fail: can't write 0x%02X to reg 0x%02X: %d\n",
 | |
| 			val, reg, ret);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sc8551_read_byte(struct sc8551 *sc, u8 reg, u8 *data)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&sc->i2c_rw_lock);
 | |
| 	ret = __sc8551_read_byte(sc, reg, data);
 | |
| 	mutex_unlock(&sc->i2c_rw_lock);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_write_byte(struct sc8551 *sc, u8 reg, u8 data)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&sc->i2c_rw_lock);
 | |
| 	ret = __sc8551_write_byte(sc, reg, data);
 | |
| 	mutex_unlock(&sc->i2c_rw_lock);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_update_bits(struct sc8551 *sc,
 | |
| 			      u8 reg,
 | |
| 			      u8 mask,
 | |
| 			      u8 data)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 tmp;
 | |
| 
 | |
| 	mutex_lock(&sc->i2c_rw_lock);
 | |
| 	ret = __sc8551_read_byte(sc, reg, &tmp);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "Failed: reg=%02X, ret=%d\n", reg, ret);
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	tmp &= ~mask;
 | |
| 	tmp |= data & mask;
 | |
| 
 | |
| 	ret = __sc8551_write_byte(sc, reg, tmp);
 | |
| 	if (ret)
 | |
| 		dev_err(sc->dev, "Failed: reg=%02X, ret=%d\n", reg, ret);
 | |
| 
 | |
| out:
 | |
| 	mutex_unlock(&sc->i2c_rw_lock);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_charge(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_CHG_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_CHG_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_CHG_EN_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_0C,
 | |
| 				 SC8551_CHG_EN_MASK,
 | |
| 				 val);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_check_charge_enabled(struct sc8551 *sc, bool *enabled)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_0C, &val);
 | |
| 
 | |
| 	if (!ret)
 | |
| 		*enabled = !!(val & SC8551_CHG_EN_MASK);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_wdt(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_WATCHDOG_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_WATCHDOG_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_WATCHDOG_DIS_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_0B,
 | |
| 				 SC8551_WATCHDOG_DIS_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_wdt(struct sc8551 *sc, int ms)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (ms == 500)
 | |
| 		val = SC8551_WATCHDOG_0P5S;
 | |
| 	else if (ms == 1000)
 | |
| 		val = SC8551_WATCHDOG_1S;
 | |
| 	else if (ms == 5000)
 | |
| 		val = SC8551_WATCHDOG_5S;
 | |
| 	else if (ms == 30000)
 | |
| 		val = SC8551_WATCHDOG_30S;
 | |
| 	else
 | |
| 		val = SC8551_WATCHDOG_30S;
 | |
| 
 | |
| 	val <<= SC8551_WATCHDOG_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_0B,
 | |
| 				 SC8551_WATCHDOG_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_batovp(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_BAT_OVP_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_BAT_OVP_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_BAT_OVP_DIS_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_00,
 | |
| 				 SC8551_BAT_OVP_DIS_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_batovp_th(struct sc8551 *sc, int threshold)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (threshold < SC8551_BAT_OVP_BASE)
 | |
| 		threshold = SC8551_BAT_OVP_BASE;
 | |
| 
 | |
| 	val = (threshold - SC8551_BAT_OVP_BASE) / SC8551_BAT_OVP_LSB;
 | |
| 
 | |
| 	val <<= SC8551_BAT_OVP_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_00,
 | |
| 				 SC8551_BAT_OVP_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_batocp(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_BAT_OCP_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_BAT_OCP_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_BAT_OCP_DIS_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_02,
 | |
| 				 SC8551_BAT_OCP_DIS_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_batocp_th(struct sc8551 *sc, int threshold)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (threshold < SC8551_BAT_OCP_BASE)
 | |
| 		threshold = SC8551_BAT_OCP_BASE;
 | |
| 
 | |
| 	val = (threshold - SC8551_BAT_OCP_BASE) / SC8551_BAT_OCP_LSB;
 | |
| 
 | |
| 	val <<= SC8551_BAT_OCP_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_02,
 | |
| 				 SC8551_BAT_OCP_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_busovp_th(struct sc8551 *sc, int threshold)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (threshold < SC8551_BUS_OVP_BASE)
 | |
| 		threshold = SC8551_BUS_OVP_BASE;
 | |
| 
 | |
| 	val = (threshold - SC8551_BUS_OVP_BASE) / SC8551_BUS_OVP_LSB;
 | |
| 
 | |
| 	val <<= SC8551_BUS_OVP_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_06,
 | |
| 				 SC8551_BUS_OVP_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_busocp(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_BUS_OCP_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_BUS_OCP_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_BUS_OCP_DIS_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_08,
 | |
| 				 SC8551_BUS_OCP_DIS_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_busocp_th(struct sc8551 *sc, int threshold)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (threshold < SC8551_BUS_OCP_BASE)
 | |
| 		threshold = SC8551_BUS_OCP_BASE;
 | |
| 
 | |
| 	val = (threshold - SC8551_BUS_OCP_BASE) / SC8551_BUS_OCP_LSB;
 | |
| 
 | |
| 	val <<= SC8551_BUS_OCP_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_08,
 | |
| 				 SC8551_BUS_OCP_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_acovp_th(struct sc8551 *sc, int threshold)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (threshold < SC8551_AC_OVP_BASE)
 | |
| 		threshold = SC8551_AC_OVP_BASE;
 | |
| 
 | |
| 	if (threshold == SC8551_AC_OVP_6P5V)
 | |
| 		val = 0x07;
 | |
| 	else
 | |
| 		val = (threshold - SC8551_AC_OVP_BASE) / SC8551_AC_OVP_LSB;
 | |
| 
 | |
| 	val <<= SC8551_AC_OVP_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc, SC8551_REG_05,
 | |
| 				 SC8551_AC_OVP_MASK, val);
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| static int sc8551_set_vdrop_th(struct sc8551 *sc, int threshold)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (threshold == 300)
 | |
| 		val = SC8551_VDROP_THRESHOLD_300MV;
 | |
| 	else
 | |
| 		val = SC8551_VDROP_THRESHOLD_400MV;
 | |
| 
 | |
| 	val <<= SC8551_VDROP_THRESHOLD_SET_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc, SC8551_REG_05,
 | |
| 				 SC8551_VDROP_THRESHOLD_SET_MASK,
 | |
| 				 val);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_vdrop_deglitch(struct sc8551 *sc, int us)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (us == 8)
 | |
| 		val = SC8551_VDROP_DEGLITCH_8US;
 | |
| 	else
 | |
| 		val = SC8551_VDROP_DEGLITCH_5MS;
 | |
| 
 | |
| 	val <<= SC8551_VDROP_DEGLITCH_SET_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_05,
 | |
| 				 SC8551_VDROP_DEGLITCH_SET_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_bat_therm(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_TSBAT_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_TSBAT_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_TSBAT_DIS_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc, SC8551_REG_0C,
 | |
| 				 SC8551_TSBAT_DIS_MASK, val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_bus_therm(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_TSBUS_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_TSBUS_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_TSBUS_DIS_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc, SC8551_REG_0C,
 | |
| 				 SC8551_TSBUS_DIS_MASK, val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_adc(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_ADC_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_ADC_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_ADC_EN_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc, SC8551_REG_14,
 | |
| 				 SC8551_ADC_EN_MASK, val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_adc_scanrate(struct sc8551 *sc, bool oneshot)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (oneshot)
 | |
| 		val = SC8551_ADC_RATE_ONESHOT;
 | |
| 	else
 | |
| 		val = SC8551_ADC_RATE_CONTINOUS;
 | |
| 
 | |
| 	val <<= SC8551_ADC_RATE_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc, SC8551_REG_14,
 | |
| 				 SC8551_ADC_RATE_MASK, val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_get_adc_data(struct sc8551 *sc, int channel,  int *result)
 | |
| {
 | |
| 	u8 val_l = 0, val_h = 0;
 | |
| 	u16 val = 0;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (channel >= ADC_MAX_NUM)
 | |
| 		return 0;
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_16 + (channel << 1), &val_h);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_16 + (channel << 1) + 1, &val_l);
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	val = (val_h << 8) | val_l;
 | |
| 
 | |
| 	if (channel == ADC_IBUS)
 | |
| 		val = val * 15625 / 10000;
 | |
| 	else if (channel == ADC_VBUS)
 | |
| 		val = val * 375 / 100;
 | |
| 	else if (channel == ADC_VAC)
 | |
| 		val = val * 5;
 | |
| 	else if (channel == ADC_VOUT)
 | |
| 		val = val * 125 / 100;
 | |
| 	else if (channel == ADC_VBAT)
 | |
| 		val = val * 125 / 100;
 | |
| 	else if (channel == ADC_IBAT)
 | |
| 		val = val * 3125 / 1000;
 | |
| 	else if (channel == ADC_TDIE)
 | |
| 		val = val * 5 / 10;
 | |
| 
 | |
| 	*result = val;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_adc_scan(struct sc8551 *sc, int channel, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 reg;
 | |
| 	u8 mask;
 | |
| 	u8 shift;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (channel > ADC_MAX_NUM)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (channel == ADC_IBUS) {
 | |
| 		reg = SC8551_REG_14;
 | |
| 		shift = SC8551_IBUS_ADC_DIS_SHIFT;
 | |
| 		mask = SC8551_IBUS_ADC_DIS_MASK;
 | |
| 	} else {
 | |
| 		reg = SC8551_REG_15;
 | |
| 		shift = 8 - channel;
 | |
| 		mask = 1 << shift;
 | |
| 	}
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = 0 << shift;
 | |
| 	else
 | |
| 		val = 1 << shift;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc, reg, mask, val);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_alarm_int_mask(struct sc8551 *sc, u8 mask)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_0F, &val);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	val |= mask;
 | |
| 
 | |
| 	ret = sc8551_write_byte(sc, SC8551_REG_0F, val);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_sense_resistor(struct sc8551 *sc, int r_mohm)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (r_mohm == 2)
 | |
| 		val = SC8551_SET_IBAT_SNS_RES_2MHM;
 | |
| 	else if (r_mohm == 5)
 | |
| 		val = SC8551_SET_IBAT_SNS_RES_5MHM;
 | |
| 	else
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	val <<= SC8551_SET_IBAT_SNS_RES_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_2B,
 | |
| 				 SC8551_SET_IBAT_SNS_RES_MASK,
 | |
| 				 val);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_enable_regulation(struct sc8551 *sc, bool enable)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (enable)
 | |
| 		val = SC8551_EN_REGULATION_ENABLE;
 | |
| 	else
 | |
| 		val = SC8551_EN_REGULATION_DISABLE;
 | |
| 
 | |
| 	val <<= SC8551_EN_REGULATION_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_2B,
 | |
| 				 SC8551_EN_REGULATION_MASK,
 | |
| 				 val);
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| static int sc8551_set_ss_timeout(struct sc8551 *sc, int timeout)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	switch (timeout) {
 | |
| 	case 0:
 | |
| 		val = SC8551_SS_TIMEOUT_DISABLE;
 | |
| 		break;
 | |
| 	case 12:
 | |
| 		val = SC8551_SS_TIMEOUT_12P5MS;
 | |
| 		break;
 | |
| 	case 25:
 | |
| 		val = SC8551_SS_TIMEOUT_25MS;
 | |
| 		break;
 | |
| 	case 50:
 | |
| 		val = SC8551_SS_TIMEOUT_50MS;
 | |
| 		break;
 | |
| 	case 100:
 | |
| 		val = SC8551_SS_TIMEOUT_100MS;
 | |
| 		break;
 | |
| 	case 400:
 | |
| 		val = SC8551_SS_TIMEOUT_400MS;
 | |
| 		break;
 | |
| 	case 1500:
 | |
| 		val = SC8551_SS_TIMEOUT_1500MS;
 | |
| 		break;
 | |
| 	case 100000:
 | |
| 		val = SC8551_SS_TIMEOUT_100000MS;
 | |
| 		break;
 | |
| 	default:
 | |
| 		val = SC8551_SS_TIMEOUT_DISABLE;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	val <<= SC8551_SS_TIMEOUT_SET_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_2B,
 | |
| 				 SC8551_SS_TIMEOUT_SET_MASK,
 | |
| 				 val);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_ibat_reg_th(struct sc8551 *sc, int th_ma)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (th_ma == 200)
 | |
| 		val = SC8551_IBAT_REG_200MA;
 | |
| 	else if (th_ma == 300)
 | |
| 		val = SC8551_IBAT_REG_300MA;
 | |
| 	else if (th_ma == 400)
 | |
| 		val = SC8551_IBAT_REG_400MA;
 | |
| 	else if (th_ma == 500)
 | |
| 		val = SC8551_IBAT_REG_500MA;
 | |
| 	else
 | |
| 		val = SC8551_IBAT_REG_500MA;
 | |
| 
 | |
| 	val <<= SC8551_IBAT_REG_SHIFT;
 | |
| 	ret = sc8551_update_bits(sc,
 | |
| 				 SC8551_REG_2C,
 | |
| 				 SC8551_IBAT_REG_MASK,
 | |
| 				 val);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_vbat_reg_th(struct sc8551 *sc, int th_mv)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	if (th_mv == 50)
 | |
| 		val = SC8551_VBAT_REG_50MV;
 | |
| 	else if (th_mv == 100)
 | |
| 		val = SC8551_VBAT_REG_100MV;
 | |
| 	else if (th_mv == 150)
 | |
| 		val = SC8551_VBAT_REG_150MV;
 | |
| 	else
 | |
| 		val = SC8551_VBAT_REG_200MV;
 | |
| 
 | |
| 	val <<= SC8551_VBAT_REG_SHIFT;
 | |
| 
 | |
| 	ret = sc8551_update_bits(sc, SC8551_REG_2C,
 | |
| 				SC8551_VBAT_REG_MASK,
 | |
| 				val);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_get_work_mode(struct sc8551 *sc, int *mode)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 val;
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_0C, &val);
 | |
| 
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "Failed to read operation mode register\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	val = (val & SC8551_MS_MASK) >> SC8551_MS_SHIFT;
 | |
| 	if (val == SC8551_MS_MASTER)
 | |
| 		*mode = SC8551_ROLE_MASTER;
 | |
| 	else if (val == SC8551_MS_SLAVE)
 | |
| 		*mode = SC8551_ROLE_SLAVE;
 | |
| 	else
 | |
| 		*mode = SC8551_ROLE_STDALONE;
 | |
| 
 | |
| 	pr_debug("work mode:%s\n", *mode == SC8551_ROLE_STDALONE ? "Standalone" :
 | |
| 		(*mode == SC8551_ROLE_SLAVE ? "Slave" : "Master"));
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_check_vbus_error_status(struct sc8551 *sc)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 data;
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_0A, &data);
 | |
| 	if (!ret)
 | |
| 		sc->vbus_error = data;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void sc8551_check_alarm_status(struct sc8551 *sc)
 | |
| {
 | |
| 	u8 flag = 0;
 | |
| 	u8 stat = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&sc->data_lock);
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_08, &flag);
 | |
| 	if (!ret && (flag & SC8551_IBUS_UCP_FALL_FLAG_MASK))
 | |
| 		pr_debug("UCP_FLAG =0x%02X\n",
 | |
| 			 !!(flag & SC8551_IBUS_UCP_FALL_FLAG_MASK));
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_2D, &flag);
 | |
| 	if (!ret && (flag & SC8551_VDROP_OVP_FLAG_MASK))
 | |
| 		pr_debug("VDROP_OVP_FLAG =0x%02X\n",
 | |
| 			 !!(flag & SC8551_VDROP_OVP_FLAG_MASK));
 | |
| 
 | |
| 	/*read to clear alarm flag*/
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_0E, &flag);
 | |
| 	if (!ret && flag)
 | |
| 		pr_debug("INT_FLAG =0x%02X\n", flag);
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_0D, &stat);
 | |
| 	if (!ret && stat != sc->prev_alarm) {
 | |
| 		pr_debug("INT_STAT = 0X%02x\n", stat);
 | |
| 		sc->prev_alarm = stat;
 | |
| 		sc->batt_present = !!(stat & VBAT_INSERT);
 | |
| 		sc->vbus_present = !!(stat & VBUS_INSERT);
 | |
| 	}
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_08, &stat);
 | |
| 	if (!ret && (stat & 0x50))
 | |
| 		dev_err(sc->dev, "Reg[05]BUS_UCPOVP = 0x%02X\n", stat);
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_0A, &stat);
 | |
| 	if (!ret && (stat & 0x02))
 | |
| 		dev_err(sc->dev, "Reg[0A]CONV_OCP = 0x%02X\n", stat);
 | |
| 
 | |
| 	mutex_unlock(&sc->data_lock);
 | |
| }
 | |
| 
 | |
| static void sc8551_check_fault_status(struct sc8551 *sc)
 | |
| {
 | |
| 	u8 flag = 0;
 | |
| 	u8 stat = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	mutex_lock(&sc->data_lock);
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_10, &stat);
 | |
| 	if (!ret && stat)
 | |
| 		dev_err(sc->dev, "FAULT_STAT = 0x%02X\n", stat);
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_11, &flag);
 | |
| 	if (!ret && flag)
 | |
| 		dev_err(sc->dev, "FAULT_FLAG = 0x%02X\n", flag);
 | |
| 
 | |
| 	if (!ret && flag != sc->prev_fault) {
 | |
| 		sc->prev_fault = flag;
 | |
| 		sc->bat_ovp_fault = !!(flag & BAT_OVP_FAULT);
 | |
| 		sc->bat_ocp_fault = !!(flag & BAT_OCP_FAULT);
 | |
| 		sc->bus_ovp_fault = !!(flag & BUS_OVP_FAULT);
 | |
| 		sc->bus_ocp_fault = !!(flag & BUS_OCP_FAULT);
 | |
| 	}
 | |
| 
 | |
| 	mutex_unlock(&sc->data_lock);
 | |
| }
 | |
| 
 | |
| static int sc8551_detect_device(struct sc8551 *sc)
 | |
| {
 | |
| 	int ret;
 | |
| 	u8 data;
 | |
| 
 | |
| 	ret = sc8551_read_byte(sc, SC8551_REG_13, &data);
 | |
| 	if (ret == 0) {
 | |
| 		sc->part_no = (data & SC8551_DEV_ID_MASK);
 | |
| 		sc->part_no >>= SC8551_DEV_ID_SHIFT;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_parse_dt(struct sc8551 *sc, struct device *dev)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct device_node *np = dev->of_node;
 | |
| 
 | |
| 	sc->cfg = devm_kzalloc(dev,
 | |
| 			       sizeof(struct sc8551_cfg),
 | |
| 			       GFP_KERNEL);
 | |
| 
 | |
| 	if (!sc->cfg)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	sc->cfg->bat_ovp_disable = of_property_read_bool(np,
 | |
| 			"sc,sc8551,bat-ovp-disable");
 | |
| 	sc->cfg->bat_ocp_disable = of_property_read_bool(np,
 | |
| 			"sc,sc8551,bat-ocp-disable");
 | |
| 	sc->cfg->bus_ocp_disable = of_property_read_bool(np,
 | |
| 			"sc,sc8551,bus-ocp-disable");
 | |
| 	sc->cfg->bat_therm_disable = of_property_read_bool(np,
 | |
| 			"sc,sc8551,bat-therm-disable");
 | |
| 	sc->cfg->bus_therm_disable = of_property_read_bool(np,
 | |
| 			"sc,sc8551,bus-therm-disable");
 | |
| 
 | |
| 	ret = of_property_read_u32(np, "sc,sc8551,bat-ovp-threshold",
 | |
| 				   &sc->cfg->bat_ovp_th);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "failed to read bat-ovp-threshold\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 	ret = of_property_read_u32(np, "sc,sc8551,bat-ocp-threshold",
 | |
| 				   &sc->cfg->bat_ocp_th);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "failed to read bat-ocp-threshold\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 	ret = of_property_read_u32(np, "sc,sc8551,bus-ovp-threshold",
 | |
| 				   &sc->cfg->bus_ovp_th);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "failed to read bus-ovp-threshold\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 	ret = of_property_read_u32(np, "sc,sc8551,bus-ocp-threshold",
 | |
| 				   &sc->cfg->bus_ocp_th);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "failed to read bus-ocp-threshold\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	ret = of_property_read_u32(np, "sc,sc8551,ac-ovp-threshold",
 | |
| 				   &sc->cfg->ac_ovp_th);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "failed to read ac-ovp-threshold\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	ret = of_property_read_u32(np, "sc,sc8551,sense-resistor-mohm",
 | |
| 				   &sc->cfg->sense_r_mohm);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "failed to read sense-resistor-mohm\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sc8551_init_protection(struct sc8551 *sc)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = sc8551_enable_batovp(sc, !sc->cfg->bat_ovp_disable);
 | |
| 	pr_debug("%s bat ovp %s\n",
 | |
| 		sc->cfg->bat_ovp_disable ? "disable" : "enable",
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_enable_batocp(sc, !sc->cfg->bat_ocp_disable);
 | |
| 	pr_debug("%s bat ocp %s\n",
 | |
| 		sc->cfg->bat_ocp_disable ? "disable" : "enable",
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_enable_busocp(sc, !sc->cfg->bus_ocp_disable);
 | |
| 	pr_debug("%s bus ocp %s\n",
 | |
| 		sc->cfg->bus_ocp_disable ? "disable" : "enable",
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_enable_bat_therm(sc, !sc->cfg->bat_therm_disable);
 | |
| 	pr_debug("%s bat therm %s\n",
 | |
| 		sc->cfg->bat_therm_disable ? "disable" : "enable",
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_enable_bus_therm(sc, !sc->cfg->bus_therm_disable);
 | |
| 	pr_debug("%s bus therm %s\n",
 | |
| 		sc->cfg->bus_therm_disable ? "disable" : "enable",
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_set_batovp_th(sc, sc->cfg->bat_ovp_th);
 | |
| 	pr_debug("set bat ovp th %d %s\n", sc->cfg->bat_ovp_th,
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_set_batocp_th(sc, sc->cfg->bat_ocp_th);
 | |
| 	pr_debug("set bat ocp threshold %d %s\n", sc->cfg->bat_ocp_th,
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_set_busovp_th(sc, sc->cfg->bus_ovp_th);
 | |
| 	pr_debug("set bus ovp threshold %d %s\n", sc->cfg->bus_ovp_th,
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_set_busocp_th(sc, sc->cfg->bus_ocp_th);
 | |
| 	pr_debug("set bus ocp threshold %d %s\n", sc->cfg->bus_ocp_th,
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	ret = sc8551_set_acovp_th(sc, sc->cfg->ac_ovp_th);
 | |
| 	pr_debug("set ac ovp threshold %d %s\n", sc->cfg->ac_ovp_th,
 | |
| 		!ret ? "successfully" : "failed");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sc8551_init_adc(struct sc8551 *sc)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = sc8551_set_adc_scanrate(sc, false);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_IBUS, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_VBUS, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_VOUT, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_VBAT, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_IBAT, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_TBUS, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_TBAT, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_TDIE, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_adc_scan(sc, ADC_VAC, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = sc8551_enable_adc(sc, true);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sc8551_init_int_src(struct sc8551 *sc)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	/*TODO:be careful ts bus and ts bat alarm bit mask is in
 | |
| 	 *	fault mask register.
 | |
| 	 */
 | |
| 	ret = sc8551_set_alarm_int_mask(sc, ADC_DONE);
 | |
| 	if (ret)
 | |
| 		dev_err(sc->dev, "failed to set alarm mask:%d\n", ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_init_regulation(struct sc8551 *sc)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = sc8551_set_ibat_reg_th(sc, 300);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_vbat_reg_th(sc, 100);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = sc8551_set_vdrop_deglitch(sc, 5000);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_vdrop_th(sc, 400);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = sc8551_enable_regulation(sc, false);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = sc8551_write_byte(sc, SC8551_REG_2E, 0x08);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sc8551_init_device(struct sc8551 *sc)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = sc8551_enable_wdt(sc, false);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_wdt(sc, 30000);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_ss_timeout(sc, 100000);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_set_sense_resistor(sc, sc->cfg->sense_r_mohm);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_init_protection(sc);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_init_adc(sc);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_init_int_src(sc);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	ret = sc8551_init_regulation(sc);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sc8551_set_present(struct sc8551 *sc, bool present)
 | |
| {
 | |
| 	sc->usb_present = present;
 | |
| 
 | |
| 	if (present)
 | |
| 		sc8551_init_device(sc);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static ssize_t registers_show(struct device *dev,
 | |
| 			      struct device_attribute *attr,
 | |
| 			      char *buf)
 | |
| {
 | |
| 	struct sc8551 *sc = dev_get_drvdata(dev);
 | |
| 	u8 tmpbuf[SC8551_DEBUG_BUF_LEN];
 | |
| 	int idx = 0;
 | |
| 	int result;
 | |
| 	u8 addr, val;
 | |
| 	int len, ret;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_VBAT, &result);
 | |
| 	if (!ret)
 | |
| 		sc->vbat_volt = result;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_VAC, &result);
 | |
| 	if (!ret)
 | |
| 		sc->vac_volt = result;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_VBUS, &result);
 | |
| 	if (!ret)
 | |
| 		sc->vbus_volt = result;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_VOUT, &result);
 | |
| 	if (!ret)
 | |
| 		sc->vout_volt = result;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_IBUS, &result);
 | |
| 	if (!ret)
 | |
| 		sc->ibus_curr = result;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_TDIE, &result);
 | |
| 	if (!ret)
 | |
| 		sc->die_temp = result;
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_IBAT, &result);
 | |
| 	if (!ret)
 | |
| 		sc->ibat_curr = result;
 | |
| 
 | |
| 	dev_err(sc->dev, "vbus_vol %d vbat_vol(vout) %d vout %d, vac: %d\n",
 | |
| 		sc->vbus_volt, sc->vbat_volt, sc->vout_volt, sc->vac_volt);
 | |
| 	dev_err(sc->dev, "ibus_curr %d ibat_curr %d\n", sc->ibus_curr, sc->ibat_curr);
 | |
| 	dev_err(sc->dev, "die_temp %d\n", sc->die_temp);
 | |
| 
 | |
| 	for (addr = SC8551_REG_00; addr <= SC8551_REG_36; addr++) {
 | |
| 		ret = sc8551_read_byte(sc, addr, &val);
 | |
| 		if (ret == 0) {
 | |
| 			len = snprintf(tmpbuf, SC8551_DEBUG_BUF_LEN,
 | |
| 				       "Reg[%.2X] = 0x%.2x\n",
 | |
| 				       addr,
 | |
| 				       val);
 | |
| 			memcpy(&buf[idx], tmpbuf, len);
 | |
| 			idx += len;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return idx;
 | |
| }
 | |
| 
 | |
| static ssize_t registers_store(struct device *dev,
 | |
| 			       struct device_attribute *attr,
 | |
| 			       const char *buf,
 | |
| 			       size_t count)
 | |
| {
 | |
| 	struct sc8551 *sc = dev_get_drvdata(dev);
 | |
| 	unsigned int reg;
 | |
| 	unsigned int val;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = sscanf(buf, "%x %x", ®, &val);
 | |
| 	if ((ret == 2) && (reg >= SC8551_REG_00) && (reg <= SC8551_REG_36))
 | |
| 		sc8551_write_byte(sc,
 | |
| 				  (unsigned char)reg,
 | |
| 				  (unsigned char)val);
 | |
| 
 | |
| 	return count;
 | |
| }
 | |
| static DEVICE_ATTR_RW(registers);
 | |
| 
 | |
| static void sc8551_create_device_node(struct device *dev)
 | |
| {
 | |
| 	device_create_file(dev, &dev_attr_registers);
 | |
| }
 | |
| 
 | |
| static enum power_supply_property sc8551_charger_props[] = {
 | |
| 	POWER_SUPPLY_PROP_PRESENT,
 | |
| 	POWER_SUPPLY_PROP_CP_CHARGING_ENABLED,
 | |
| 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
 | |
| 	POWER_SUPPLY_PROP_CURRENT_NOW,
 | |
| 	POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
 | |
| 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
 | |
| 	POWER_SUPPLY_PROP_CP_DIE_TEMPERATURE,
 | |
| 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
 | |
| 	POWER_SUPPLY_PROP_CP_BAT_OVP_FAULT,
 | |
| 	POWER_SUPPLY_PROP_CP_BAT_OCP_FAULT,
 | |
| 	POWER_SUPPLY_PROP_CP_BUS_OVP_FAULT,
 | |
| 	POWER_SUPPLY_PROP_CP_BUS_OCP_FAULT,
 | |
| 	POWER_SUPPLY_PROP_CP_VBUS_HERROR_STATUS,
 | |
| 	POWER_SUPPLY_PROP_CP_VBUS_LERROR_STATUS,
 | |
| };
 | |
| 
 | |
| static int sc8551_charger_get_property(struct power_supply *psy,
 | |
| 				       enum power_supply_property psp,
 | |
| 				       union power_supply_propval *val)
 | |
| {
 | |
| 	struct sc8551 *sc = power_supply_get_drvdata(psy);
 | |
| 	int result;
 | |
| 	int ret;
 | |
| 
 | |
| 	sc8551_check_alarm_status(sc);
 | |
| 	sc8551_check_fault_status(sc);
 | |
| 	sc8551_check_vbus_error_status(sc);
 | |
| 	switch (psp) {
 | |
| 	case POWER_SUPPLY_PROP_CP_CHARGING_ENABLED:
 | |
| 		sc8551_check_charge_enabled(sc, &sc->charge_enabled);
 | |
| 		val->intval = sc->charge_enabled;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_PRESENT:
 | |
| 		val->intval = sc->usb_present;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 | |
| 		ret = sc8551_get_adc_data(sc, ADC_VBAT, &result);
 | |
| 		if (!ret)
 | |
| 			sc->vbat_volt = result;
 | |
| 		val->intval = sc->vbat_volt * 1000;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CURRENT_NOW:
 | |
| 		ret = sc8551_get_adc_data(sc, ADC_IBAT, &result);
 | |
| 		if (!ret)
 | |
| 			sc->ibat_curr = result;
 | |
| 		val->intval = sc->ibat_curr * 1000;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CP_VBUS: /* BUS_VOLTAGE */
 | |
| 		ret = sc8551_get_adc_data(sc, ADC_VBUS, &result);
 | |
| 		if (!ret)
 | |
| 			sc->vbus_volt = result;
 | |
| 		val->intval = sc->vbus_volt * 1000;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CP_IBUS: /* BUS_CURRENT */
 | |
| 		ret = sc8551_get_adc_data(sc, ADC_IBUS, &result);
 | |
| 		if (!ret)
 | |
| 			sc->ibus_curr = result;
 | |
| 		val->intval = sc->ibus_curr * 1000;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: /* BUS_VOLTAGE */
 | |
| 		val->intval =  12000 * 1000; /* 20V */
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: /* BUS_CURRENT */
 | |
| 		val->intval =  4500 * 1000; /* 4.75A */
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CP_DIE_TEMPERATURE:/* DIE_TEMPERATURE */
 | |
| 		ret = sc8551_get_adc_data(sc, ADC_TDIE, &result);
 | |
| 		if (!ret)
 | |
| 			sc->die_temp = result;
 | |
| 		val->intval = sc->die_temp;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
 | |
| 		val->intval = 4300 * 1000;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
 | |
| 		val->intval = 8000 * 1000;
 | |
| 		break;
 | |
| 
 | |
| 	case POWER_SUPPLY_PROP_CP_BAT_OVP_FAULT:
 | |
| 		val->intval = sc->bat_ovp_fault;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CP_BAT_OCP_FAULT:
 | |
| 		val->intval = sc->bat_ocp_fault;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CP_BUS_OVP_FAULT:
 | |
| 		val->intval = sc->bus_ovp_fault;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CP_BUS_OCP_FAULT:
 | |
| 		val->intval = sc->bus_ocp_fault;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CP_VBUS_HERROR_STATUS:
 | |
| 		val->intval = (sc->vbus_error >> 0x04) & 0x01;
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_CP_VBUS_LERROR_STATUS:
 | |
| 		val->intval = (sc->vbus_error >> 0x05) & 0x01;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sc8551_charger_set_property(struct power_supply *psy,
 | |
| 				       enum power_supply_property prop,
 | |
| 				       const union power_supply_propval *val)
 | |
| {
 | |
| 	struct sc8551 *sc = power_supply_get_drvdata(psy);
 | |
| 
 | |
| 	switch (prop) {
 | |
| 	case POWER_SUPPLY_PROP_CP_CHARGING_ENABLED:
 | |
| 		sc8551_enable_charge(sc, val->intval);
 | |
| 		if (val->intval)
 | |
| 			sc8551_enable_wdt(sc, true);
 | |
| 		else
 | |
| 			sc8551_enable_wdt(sc, false);
 | |
| 		sc8551_check_charge_enabled(sc, &sc->charge_enabled);
 | |
| 		break;
 | |
| 	case POWER_SUPPLY_PROP_PRESENT:
 | |
| 		sc8551_set_present(sc, !!val->intval);
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sc8551_charger_is_writeable(struct power_supply *psy,
 | |
| 				       enum power_supply_property prop)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	switch (prop) {
 | |
| 	case POWER_SUPPLY_PROP_ONLINE:
 | |
| 		ret = 1;
 | |
| 		break;
 | |
| 	default:
 | |
| 		ret = 0;
 | |
| 		break;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int sc8551_psy_register(struct sc8551 *sc)
 | |
| {
 | |
| 	sc->psy_cfg.drv_data = sc;
 | |
| 	sc->psy_cfg.of_node = sc->dev->of_node;
 | |
| 
 | |
| 	sc->psy_desc.name = "sc8551-standalone";
 | |
| 
 | |
| 	sc->psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
 | |
| 	sc->psy_desc.properties = sc8551_charger_props;
 | |
| 	sc->psy_desc.num_properties = ARRAY_SIZE(sc8551_charger_props);
 | |
| 	sc->psy_desc.get_property = sc8551_charger_get_property;
 | |
| 	sc->psy_desc.set_property = sc8551_charger_set_property;
 | |
| 	sc->psy_desc.property_is_writeable = sc8551_charger_is_writeable;
 | |
| 
 | |
| 	sc->fc2_psy = devm_power_supply_register(sc->dev,
 | |
| 						 &sc->psy_desc,
 | |
| 						 &sc->psy_cfg);
 | |
| 	if (IS_ERR(sc->fc2_psy)) {
 | |
| 		dev_err(sc->dev, "failed to register fc2_psy\n");
 | |
| 		return PTR_ERR(sc->fc2_psy);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * interrupt does nothing, just info event change, other module could get info
 | |
|  * through power supply interface
 | |
|  */
 | |
| static irqreturn_t sc8551_charger_interrupt(int irq, void *dev_id)
 | |
| {
 | |
| 	struct sc8551 *sc = dev_id;
 | |
| 	int ret, value;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_VOUT, &value);
 | |
| 	if (!ret)
 | |
| 		sc->vbat_volt = value;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_IBAT, &value);
 | |
| 	if (!ret)
 | |
| 		sc->ibat_curr = value;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_VBUS, &value);
 | |
| 	if (!ret)
 | |
| 		sc->vbus_volt = value;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_IBUS, &value);
 | |
| 	if (!ret)
 | |
| 		sc->ibus_curr = value;
 | |
| 
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_TDIE, &value);
 | |
| 	if (!ret)
 | |
| 		sc->die_temp = value;
 | |
| 	ret = sc8551_get_adc_data(sc, ADC_IBAT, &value);
 | |
| 	if (!ret)
 | |
| 		sc->ibat_curr = value;
 | |
| 
 | |
| 	sc8551_check_alarm_status(sc);
 | |
| 	sc8551_check_fault_status(sc);
 | |
| 	sc8551_check_vbus_error_status(sc);
 | |
| 	power_supply_changed(sc->fc2_psy);
 | |
| 
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| 
 | |
| static int sc8551_init_irq(struct sc8551 *sc)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	sc->irq = sc->client->irq;
 | |
| 	if (sc->irq <= 0) {
 | |
| 		dev_err(sc->dev, "irq mapping fail\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	ret = devm_request_threaded_irq(sc->dev,
 | |
| 					sc->irq,
 | |
| 					NULL,
 | |
| 					sc8551_charger_interrupt,
 | |
| 					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 | |
| 					"sc8551 standalone irq",
 | |
| 					sc);
 | |
| 	if (ret < 0)
 | |
| 		dev_err(sc->dev, "request irq for irq=%d failed, ret =%d\n",
 | |
| 			sc->irq, ret);
 | |
| 
 | |
| 	enable_irq_wake(sc->irq);
 | |
| 	device_init_wakeup(sc->dev, 1);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void determine_initial_status(struct sc8551 *sc)
 | |
| {
 | |
| 	if (sc->client->irq)
 | |
| 		sc8551_charger_interrupt(sc->client->irq, sc);
 | |
| }
 | |
| 
 | |
| static const struct of_device_id sc8551_charger_match[] = {
 | |
| 	{ .compatible = "sc,sc8551-standalone", },
 | |
| 	{},
 | |
| };
 | |
| 
 | |
| static int sc8551_charger_probe(struct i2c_client *client,
 | |
| 				const struct i2c_device_id *id)
 | |
| {
 | |
| 	struct sc8551 *sc;
 | |
| 	const struct of_device_id *match;
 | |
| 	struct device_node *node = client->dev.of_node;
 | |
| 	int ret;
 | |
| 
 | |
| 	sc = devm_kzalloc(&client->dev, sizeof(struct sc8551), GFP_KERNEL);
 | |
| 	if (!sc)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	sc->dev = &client->dev;
 | |
| 
 | |
| 	sc->client = client;
 | |
| 
 | |
| 	mutex_init(&sc->i2c_rw_lock);
 | |
| 	mutex_init(&sc->data_lock);
 | |
| 
 | |
| 	ret = sc8551_detect_device(sc);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "No sc8551 device found!\n");
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	i2c_set_clientdata(client, sc);
 | |
| 
 | |
| 	match = of_match_node(sc8551_charger_match, node);
 | |
| 	if (match == NULL) {
 | |
| 		dev_err(sc->dev, "device tree match not found!\n");
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 	sc8551_get_work_mode(sc, &sc->mode);
 | |
| 	if (sc->mode !=  SC8551_ROLE_STDALONE) {
 | |
| 		dev_err(sc->dev, "device operation mode mismatch with dts configuration\n");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	ret = sc8551_parse_dt(sc, &client->dev);
 | |
| 	if (ret)
 | |
| 		return -EIO;
 | |
| 
 | |
| 	ret = sc8551_init_device(sc);
 | |
| 	if (ret) {
 | |
| 		dev_err(sc->dev, "Failed to init device\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	ret = sc8551_psy_register(sc);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = sc8551_init_irq(sc);
 | |
| 	if (ret)
 | |
| 		goto err_1;
 | |
| 
 | |
| 	determine_initial_status(sc);
 | |
| 	sc8551_create_device_node(&(client->dev));
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_1:
 | |
| 	power_supply_unregister(sc->fc2_psy);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void sc8551_charger_remove(struct i2c_client *client)
 | |
| {
 | |
| 	struct sc8551 *sc = i2c_get_clientdata(client);
 | |
| 
 | |
| 
 | |
| 	sc8551_enable_adc(sc, false);
 | |
| 
 | |
| 	power_supply_unregister(sc->fc2_psy);
 | |
| 
 | |
| 	mutex_destroy(&sc->data_lock);
 | |
| 	mutex_destroy(&sc->i2c_rw_lock);
 | |
| }
 | |
| 
 | |
| static void sc8551_charger_shutdown(struct i2c_client *client)
 | |
| {
 | |
| 	struct sc8551 *sc = i2c_get_clientdata(client);
 | |
| 
 | |
| 	sc8551_enable_adc(sc, false);
 | |
| }
 | |
| 
 | |
| static const struct i2c_device_id sc8551_charger_id[] = {
 | |
| 	{"sc8551-standalone", SC8551_ROLE_STDALONE},
 | |
| 	{},
 | |
| };
 | |
| 
 | |
| static struct i2c_driver sc8551_charger_driver = {
 | |
| 	.driver		= {
 | |
| 		.name	= "sc8551-charger",
 | |
| 		.owner	= THIS_MODULE,
 | |
| 		.of_match_table = sc8551_charger_match,
 | |
| 	},
 | |
| 	.id_table	= sc8551_charger_id,
 | |
| 
 | |
| 	.probe		= sc8551_charger_probe,
 | |
| 	.remove		= sc8551_charger_remove,
 | |
| 	.shutdown	= sc8551_charger_shutdown,
 | |
| };
 | |
| 
 | |
| module_i2c_driver(sc8551_charger_driver);
 | |
| 
 | |
| MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
 | |
| MODULE_DESCRIPTION("SC SC8551 Charge Pump Driver");
 | |
| MODULE_LICENSE("GPL");
 | |
| 
 |