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");
|
|
|