You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3134 lines
79 KiB
3134 lines
79 KiB
/* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/extcon.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/power_supply.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/regulator/of_regulator.h>
|
|
#include <linux/regulator/machine.h>
|
|
#include <linux/of.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
|
/* Mask/Bit helpers */
|
|
#define _SMB1351_MASK(BITS, POS) \
|
|
((unsigned char)(((1 << (BITS)) - 1) << (POS)))
|
|
#define SMB1351_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
|
|
_SMB1351_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
|
|
(RIGHT_BIT_POS))
|
|
|
|
/* Configuration registers */
|
|
#define CHG_CURRENT_CTRL_REG 0x0
|
|
#define FAST_CHG_CURRENT_MASK SMB1351_MASK(7, 4)
|
|
#define AC_INPUT_CURRENT_LIMIT_MASK SMB1351_MASK(3, 0)
|
|
|
|
#define CHG_OTH_CURRENT_CTRL_REG 0x1
|
|
#define PRECHG_CURRENT_MASK SMB1351_MASK(7, 5)
|
|
#define ITERM_MASK SMB1351_MASK(4, 2)
|
|
#define USB_2_3_MODE_SEL_BIT BIT(1)
|
|
#define USB_2_3_MODE_SEL_BY_I2C 0
|
|
#define USB_2_3_MODE_SEL_BY_PIN 0x2
|
|
#define USB_5_1_CMD_POLARITY_BIT BIT(0)
|
|
#define USB_CMD_POLARITY_500_1_100_0 0
|
|
#define USB_CMD_POLARITY_500_0_100_1 0x1
|
|
|
|
#define VARIOUS_FUNC_REG 0x2
|
|
#define SUSPEND_MODE_CTRL_BIT BIT(7)
|
|
#define SUSPEND_MODE_CTRL_BY_PIN 0
|
|
#define SUSPEND_MODE_CTRL_BY_I2C 0x80
|
|
#define BATT_TO_SYS_POWER_CTRL_BIT BIT(6)
|
|
#define MAX_SYS_VOLTAGE BIT(5)
|
|
#define AICL_EN_BIT BIT(4)
|
|
#define AICL_DET_TH_BIT BIT(3)
|
|
#define APSD_EN_BIT BIT(2)
|
|
#define BATT_OV_BIT BIT(1)
|
|
#define VCHG_FUNC_BIT BIT(0)
|
|
|
|
#define VFLOAT_REG 0x3
|
|
#define PRECHG_TO_FAST_VOLTAGE_CFG_MASK SMB1351_MASK(7, 6)
|
|
#define VFLOAT_MASK SMB1351_MASK(5, 0)
|
|
|
|
#define CHG_CTRL_REG 0x4
|
|
#define AUTO_RECHG_BIT BIT(7)
|
|
#define AUTO_RECHG_ENABLE 0
|
|
#define AUTO_RECHG_DISABLE 0x80
|
|
#define ITERM_EN_BIT BIT(6)
|
|
#define ITERM_ENABLE 0
|
|
#define ITERM_DISABLE 0x40
|
|
#define MAPPED_AC_INPUT_CURRENT_LIMIT_MASK SMB1351_MASK(5, 4)
|
|
#define AUTO_RECHG_TH_BIT BIT(3)
|
|
#define AUTO_RECHG_TH_50MV 0
|
|
#define AUTO_RECHG_TH_100MV 0x8
|
|
#define AFCV_MASK SMB1351_MASK(2, 0)
|
|
|
|
#define CHG_STAT_TIMERS_CTRL_REG 0x5
|
|
#define STAT_OUTPUT_POLARITY_BIT BIT(7)
|
|
#define STAT_OUTPUT_MODE_BIT BIT(6)
|
|
#define STAT_OUTPUT_CTRL_BIT BIT(5)
|
|
#define OTH_CHG_IL_BIT BIT(4)
|
|
#define COMPLETE_CHG_TIMEOUT_MASK SMB1351_MASK(3, 2)
|
|
#define PRECHG_TIMEOUT_MASK SMB1351_MASK(1, 0)
|
|
|
|
#define CHG_PIN_EN_CTRL_REG 0x6
|
|
#define LED_BLINK_FUNC_BIT BIT(7)
|
|
#define EN_PIN_CTRL_MASK SMB1351_MASK(6, 5)
|
|
#define EN_BY_I2C_0_DISABLE 0
|
|
#define EN_BY_I2C_0_ENABLE 0x20
|
|
#define EN_BY_PIN_HIGH_ENABLE 0x40
|
|
#define EN_BY_PIN_LOW_ENABLE 0x60
|
|
#define USBCS_CTRL_BIT BIT(4)
|
|
#define USBCS_CTRL_BY_I2C 0
|
|
#define USBCS_CTRL_BY_PIN 0x10
|
|
#define USBCS_INPUT_STATE_BIT BIT(3)
|
|
#define CHG_ERR_BIT BIT(2)
|
|
#define APSD_DONE_BIT BIT(1)
|
|
#define USB_FAIL_BIT BIT(0)
|
|
|
|
#define THERM_A_CTRL_REG 0x7
|
|
#define MIN_SYS_VOLTAGE_MASK SMB1351_MASK(7, 6)
|
|
#define LOAD_BATT_10MA_FVC_BIT BIT(5)
|
|
#define THERM_MONITOR_BIT BIT(4)
|
|
#define THERM_MONITOR_EN 0
|
|
#define SOFT_COLD_TEMP_LIMIT_MASK SMB1351_MASK(3, 2)
|
|
#define SOFT_HOT_TEMP_LIMIT_MASK SMB1351_MASK(1, 0)
|
|
|
|
#define WDOG_SAFETY_TIMER_CTRL_REG 0x8
|
|
#define AICL_FAIL_OPTION_BIT BIT(7)
|
|
#define AICL_FAIL_TO_SUSPEND 0
|
|
#define AICL_FAIL_TO_150_MA 0x80
|
|
#define WDOG_TIMEOUT_MASK SMB1351_MASK(6, 5)
|
|
#define WDOG_IRQ_SAFETY_TIMER_MASK SMB1351_MASK(4, 3)
|
|
#define WDOG_IRQ_SAFETY_TIMER_EN_BIT BIT(2)
|
|
#define WDOG_OPTION_BIT BIT(1)
|
|
#define WDOG_TIMER_EN_BIT BIT(0)
|
|
|
|
#define OTG_USBIN_AICL_CTRL_REG 0x9
|
|
#define OTG_ID_PIN_CTRL_MASK SMB1351_MASK(7, 6)
|
|
#define OTG_PIN_POLARITY_BIT BIT(5)
|
|
#define DCIN_IC_GLITCH_FILTER_HV_ADAPTER_MASK SMB1351_MASK(4, 3)
|
|
#define DCIN_IC_GLITCH_FILTER_LV_ADAPTER_BIT BIT(2)
|
|
#define USBIN_AICL_CFG1_BIT BIT(1)
|
|
#define USBIN_AICL_CFG0_BIT BIT(0)
|
|
|
|
#define OTG_TLIM_CTRL_REG 0xA
|
|
#define SWITCH_FREQ_MASK SMB1351_MASK(7, 6)
|
|
#define THERM_LOOP_TEMP_SEL_MASK SMB1351_MASK(5, 4)
|
|
#define OTG_OC_LIMIT_MASK SMB1351_MASK(3, 2)
|
|
#define OTG_BATT_UVLO_TH_MASK SMB1351_MASK(1, 0)
|
|
|
|
#define HARD_SOFT_LIMIT_CELL_TEMP_REG 0xB
|
|
#define HARD_LIMIT_COLD_TEMP_ALARM_TRIP_MASK SMB1351_MASK(7, 6)
|
|
#define HARD_LIMIT_HOT_TEMP_ALARM_TRIP_MASK SMB1351_MASK(5, 4)
|
|
#define SOFT_LIMIT_COLD_TEMP_ALARM_TRIP_MASK SMB1351_MASK(3, 2)
|
|
#define SOFT_LIMIT_HOT_TEMP_ALARM_TRIP_MASK SMB1351_MASK(1, 0)
|
|
|
|
#define FAULT_INT_REG 0xC
|
|
#define HOT_COLD_HARD_LIMIT_BIT BIT(7)
|
|
#define HOT_COLD_SOFT_LIMIT_BIT BIT(6)
|
|
#define BATT_UVLO_IN_OTG_BIT BIT(5)
|
|
#define OTG_OC_BIT BIT(4)
|
|
#define INPUT_OVLO_BIT BIT(3)
|
|
#define INPUT_UVLO_BIT BIT(2)
|
|
#define AICL_DONE_FAIL_BIT BIT(1)
|
|
#define INTERNAL_OVER_TEMP_BIT BIT(0)
|
|
|
|
#define STATUS_INT_REG 0xD
|
|
#define CHG_OR_PRECHG_TIMEOUT_BIT BIT(7)
|
|
#define RID_CHANGE_BIT BIT(6)
|
|
#define BATT_OVP_BIT BIT(5)
|
|
#define FAST_TERM_TAPER_RECHG_INHIBIT_BIT BIT(4)
|
|
#define WDOG_TIMER_BIT BIT(3)
|
|
#define POK_BIT BIT(2)
|
|
#define BATT_MISSING_BIT BIT(1)
|
|
#define BATT_LOW_BIT BIT(0)
|
|
|
|
#define VARIOUS_FUNC_2_REG 0xE
|
|
#define CHG_HOLD_OFF_TIMER_AFTER_PLUGIN_BIT BIT(7)
|
|
#define CHG_INHIBIT_BIT BIT(6)
|
|
#define FAST_CHG_CC_IN_BATT_SOFT_LIMIT_MODE_BIT BIT(5)
|
|
#define FVCL_IN_BATT_SOFT_LIMIT_MODE_MASK SMB1351_MASK(4, 3)
|
|
#define HARD_TEMP_LIMIT_BEHAVIOR_BIT BIT(2)
|
|
#define PRECHG_TO_FASTCHG_BIT BIT(1)
|
|
#define STAT_PIN_CONFIG_BIT BIT(0)
|
|
|
|
#define FLEXCHARGER_REG 0x10
|
|
#define AFVC_IRQ_BIT BIT(7)
|
|
#define CHG_CONFIG_MASK SMB1351_MASK(6, 4)
|
|
#define LOW_BATT_VOLTAGE_DET_TH_MASK SMB1351_MASK(3, 0)
|
|
|
|
#define VARIOUS_FUNC_3_REG 0x11
|
|
#define SAFETY_TIMER_EN_MASK SMB1351_MASK(7, 6)
|
|
#define BLOCK_SUSPEND_DURING_VBATT_LOW_BIT BIT(5)
|
|
#define TIMEOUT_SEL_FOR_APSD_BIT BIT(4)
|
|
#define SDP_SUSPEND_BIT BIT(3)
|
|
#define QC_2P1_AUTO_INCREMENT_MODE_BIT BIT(2)
|
|
#define QC_2P1_AUTH_ALGO_BIT BIT(1)
|
|
#define DCD_EN_BIT BIT(0)
|
|
|
|
#define HVDCP_BATT_MISSING_CTRL_REG 0x12
|
|
#define HVDCP_ADAPTER_SEL_MASK SMB1351_MASK(7, 6)
|
|
#define HVDCP_EN_BIT BIT(5)
|
|
#define HVDCP_AUTO_INCREMENT_LIMIT_BIT BIT(4)
|
|
#define BATT_MISSING_ON_INPUT_PLUGIN_BIT BIT(3)
|
|
#define BATT_MISSING_2P6S_POLLER_BIT BIT(2)
|
|
#define BATT_MISSING_ALGO_BIT BIT(1)
|
|
#define BATT_MISSING_THERM_PIN_SOURCE_BIT BIT(0)
|
|
|
|
#define PON_OPTIONS_REG 0x13
|
|
#define SYSOK_INOK_POLARITY_BIT BIT(7)
|
|
#define SYSOK_OPTIONS_MASK SMB1351_MASK(6, 4)
|
|
#define INPUT_MISSING_POLLER_CONFIG_BIT BIT(3)
|
|
#define VBATT_LOW_DISABLED_OR_RESET_STATE_BIT BIT(2)
|
|
#define QC_2P1_AUTH_ALGO_IRQ_EN_BIT BIT(0)
|
|
|
|
#define OTG_MODE_POWER_OPTIONS_REG 0x14
|
|
#define ADAPTER_CONFIG_MASK SMB1351_MASK(7, 6)
|
|
#define MAP_HVDCP_BIT BIT(5)
|
|
#define SDP_LOW_BATT_FORCE_USB5_OVER_USB1_BIT BIT(4)
|
|
#define OTG_HICCUP_MODE_BIT BIT(2)
|
|
#define INPUT_CURRENT_LIMIT_MASK SMB1351_MASK(1, 0)
|
|
|
|
#define CHARGER_I2C_CTRL_REG 0x15
|
|
#define FULLON_MODE_EN_BIT BIT(7)
|
|
#define I2C_HS_MODE_EN_BIT BIT(6)
|
|
#define SYSON_LDO_OUTPUT_SEL_BIT BIT(5)
|
|
#define VBATT_TRACKING_VOLTAGE_DIFF_BIT BIT(4)
|
|
#define DISABLE_AFVC_WHEN_ENTER_TAPER_BIT BIT(3)
|
|
#define VCHG_IINV_BIT BIT(2)
|
|
#define AFVC_OVERRIDE_BIT BIT(1)
|
|
#define SYSOK_PIN_CONFIG_BIT BIT(0)
|
|
|
|
#define VERSION_REG 0x2E
|
|
#define VERSION_MASK BIT(1)
|
|
|
|
/* Command registers */
|
|
#define CMD_I2C_REG 0x30
|
|
#define CMD_RELOAD_BIT BIT(7)
|
|
#define CMD_BQ_CFG_ACCESS_BIT BIT(6)
|
|
|
|
#define CMD_INPUT_LIMIT_REG 0x31
|
|
#define CMD_OVERRIDE_BIT BIT(7)
|
|
#define CMD_SUSPEND_MODE_BIT BIT(6)
|
|
#define CMD_INPUT_CURRENT_MODE_BIT BIT(3)
|
|
#define CMD_INPUT_CURRENT_MODE_APSD 0
|
|
#define CMD_INPUT_CURRENT_MODE_CMD 0x08
|
|
#define CMD_USB_2_3_SEL_BIT BIT(2)
|
|
#define CMD_USB_2_MODE 0
|
|
#define CMD_USB_3_MODE 0x4
|
|
#define CMD_USB_1_5_AC_CTRL_MASK SMB1351_MASK(1, 0)
|
|
#define CMD_USB_100_MODE 0
|
|
#define CMD_USB_500_MODE 0x2
|
|
#define CMD_USB_AC_MODE 0x1
|
|
|
|
#define CMD_CHG_REG 0x32
|
|
#define CMD_DISABLE_THERM_MONITOR_BIT BIT(4)
|
|
#define CMD_TURN_OFF_STAT_PIN_BIT BIT(3)
|
|
#define CMD_PRE_TO_FAST_EN_BIT BIT(2)
|
|
#define CMD_CHG_EN_BIT BIT(1)
|
|
#define CMD_CHG_DISABLE 0
|
|
#define CMD_CHG_ENABLE 0x2
|
|
#define CMD_OTG_EN_BIT BIT(0)
|
|
|
|
#define CMD_DEAD_BATT_REG 0x33
|
|
#define CMD_STOP_DEAD_BATT_TIMER_MASK SMB1351_MASK(7, 0)
|
|
|
|
#define CMD_HVDCP_REG 0x34
|
|
#define CMD_APSD_RE_RUN_BIT BIT(7)
|
|
#define CMD_FORCE_HVDCP_2P0_BIT BIT(5)
|
|
#define CMD_HVDCP_MODE_MASK SMB1351_MASK(5, 0)
|
|
|
|
/* Status registers */
|
|
#define STATUS_0_REG 0x36
|
|
#define STATUS_AICL_BIT BIT(7)
|
|
#define STATUS_INPUT_CURRENT_LIMIT_MASK SMB1351_MASK(6, 5)
|
|
#define STATUS_DCIN_INPUT_CURRENT_LIMIT_MASK SMB1351_MASK(4, 0)
|
|
|
|
#define STATUS_1_REG 0x37
|
|
#define STATUS_INPUT_RANGE_MASK SMB1351_MASK(7, 4)
|
|
#define STATUS_INPUT_USB_BIT BIT(0)
|
|
|
|
#define STATUS_2_REG 0x38
|
|
#define STATUS_FAST_CHG_BIT BIT(7)
|
|
#define STATUS_HARD_LIMIT_BIT BIT(6)
|
|
#define STATUS_FLOAT_VOLTAGE_MASK SMB1351_MASK(5, 0)
|
|
|
|
#define STATUS_3_REG 0x39
|
|
#define STATUS_CHG_BIT BIT(7)
|
|
#define STATUS_PRECHG_CURRENT_MASK SMB1351_MASK(6, 4)
|
|
#define STATUS_FAST_CHG_CURRENT_MASK SMB1351_MASK(3, 0)
|
|
|
|
#define STATUS_4_REG 0x3A
|
|
#define STATUS_OTG_BIT BIT(7)
|
|
#define STATUS_AFVC_BIT BIT(6)
|
|
#define STATUS_DONE_BIT BIT(5)
|
|
#define STATUS_BATT_LESS_THAN_2V_BIT BIT(4)
|
|
#define STATUS_HOLD_OFF_BIT BIT(3)
|
|
#define STATUS_CHG_MASK SMB1351_MASK(2, 1)
|
|
#define STATUS_NO_CHARGING 0
|
|
#define STATUS_FAST_CHARGING 0x4
|
|
#define STATUS_PRE_CHARGING 0x2
|
|
#define STATUS_TAPER_CHARGING 0x6
|
|
#define STATUS_CHG_EN_STATUS_BIT BIT(0)
|
|
|
|
#define STATUS_5_REG 0x3B
|
|
#define STATUS_SOURCE_DETECTED_MASK SMB1351_MASK(7, 0)
|
|
#define STATUS_PORT_CDP 0x80
|
|
#define STATUS_PORT_DCP 0x40
|
|
#define STATUS_PORT_OTHER 0x20
|
|
#define STATUS_PORT_SDP 0x10
|
|
#define STATUS_PORT_ACA_A 0x8
|
|
#define STATUS_PORT_ACA_B 0x4
|
|
#define STATUS_PORT_ACA_C 0x2
|
|
#define STATUS_PORT_ACA_DOCK 0x1
|
|
|
|
#define STATUS_6_REG 0x3C
|
|
#define STATUS_DCD_TIMEOUT_BIT BIT(7)
|
|
#define STATUS_DCD_GOOD_DG_BIT BIT(6)
|
|
#define STATUS_OCD_GOOD_DG_BIT BIT(5)
|
|
#define STATUS_RID_ABD_DG_BIT BIT(4)
|
|
#define STATUS_RID_FLOAT_STATE_MACHINE_BIT BIT(3)
|
|
#define STATUS_RID_A_STATE_MACHINE_BIT BIT(2)
|
|
#define STATUS_RID_B_STATE_MACHINE_BIT BIT(1)
|
|
#define STATUS_RID_C_STATE_MACHINE_BIT BIT(0)
|
|
|
|
#define STATUS_7_REG 0x3D
|
|
#define STATUS_HVDCP_MASK SMB1351_MASK(7, 0)
|
|
|
|
#define STATUS_8_REG 0x3E
|
|
#define STATUS_USNIN_HV_INPUT_SEL_BIT BIT(5)
|
|
#define STATUS_USBIN_LV_UNDER_INPUT_SEL_BIT BIT(4)
|
|
#define STATUS_USBIN_LV_INPUT_SEL_BIT BIT(3)
|
|
|
|
/* Revision register */
|
|
#define CHG_REVISION_REG 0x3F
|
|
#define GUI_REVISION_MASK SMB1351_MASK(7, 4)
|
|
#define DEVICE_REVISION_MASK SMB1351_MASK(3, 0)
|
|
|
|
/* IRQ status registers */
|
|
#define IRQ_A_REG 0x40
|
|
#define IRQ_HOT_HARD_BIT BIT(6)
|
|
#define IRQ_COLD_HARD_BIT BIT(4)
|
|
#define IRQ_HOT_SOFT_BIT BIT(2)
|
|
#define IRQ_COLD_SOFT_BIT BIT(0)
|
|
|
|
#define IRQ_B_REG 0x41
|
|
#define IRQ_BATT_TERMINAL_REMOVED_BIT BIT(6)
|
|
#define IRQ_BATT_MISSING_BIT BIT(4)
|
|
#define IRQ_LOW_BATT_VOLTAGE_BIT BIT(2)
|
|
#define IRQ_INTERNAL_TEMP_LIMIT_BIT BIT(0)
|
|
|
|
#define IRQ_C_REG 0x42
|
|
#define IRQ_PRE_TO_FAST_VOLTAGE_BIT BIT(6)
|
|
#define IRQ_RECHG_BIT BIT(4)
|
|
#define IRQ_TAPER_BIT BIT(2)
|
|
#define IRQ_TERM_BIT BIT(0)
|
|
|
|
#define IRQ_D_REG 0x43
|
|
#define IRQ_BATT_OV_BIT BIT(6)
|
|
#define IRQ_CHG_ERROR_BIT BIT(4)
|
|
#define IRQ_CHG_TIMEOUT_BIT BIT(2)
|
|
#define IRQ_PRECHG_TIMEOUT_BIT BIT(0)
|
|
|
|
#define IRQ_E_REG 0x44
|
|
#define IRQ_USBIN_OV_BIT BIT(6)
|
|
#define IRQ_USBIN_UV_BIT BIT(4)
|
|
#define IRQ_AFVC_BIT BIT(2)
|
|
#define IRQ_POWER_OK_BIT BIT(0)
|
|
|
|
#define IRQ_F_REG 0x45
|
|
#define IRQ_OTG_OVER_CURRENT_BIT BIT(6)
|
|
#define IRQ_OTG_FAIL_BIT BIT(4)
|
|
#define IRQ_RID_BIT BIT(2)
|
|
#define IRQ_OTG_OC_RETRY_BIT BIT(0)
|
|
|
|
#define IRQ_G_REG 0x46
|
|
#define IRQ_SOURCE_DET_BIT BIT(6)
|
|
#define IRQ_AICL_DONE_BIT BIT(4)
|
|
#define IRQ_AICL_FAIL_BIT BIT(2)
|
|
#define IRQ_CHG_INHIBIT_BIT BIT(0)
|
|
|
|
#define IRQ_H_REG 0x47
|
|
#define IRQ_IC_LIMIT_STATUS_BIT BIT(5)
|
|
#define IRQ_HVDCP_2P1_STATUS_BIT BIT(4)
|
|
#define IRQ_HVDCP_AUTH_DONE_BIT BIT(2)
|
|
#define IRQ_WDOG_TIMEOUT_BIT BIT(0)
|
|
|
|
/* constants */
|
|
#define USB2_MIN_CURRENT_MA 100
|
|
#define USB2_MAX_CURRENT_MA 500
|
|
#define USB3_MIN_CURRENT_MA 150
|
|
#define USB3_MAX_CURRENT_MA 900
|
|
#define DCP_MAX_CURRENT_MA 1500
|
|
#define SMB1351_IRQ_REG_COUNT 8
|
|
#define SMB1351_CHG_PRE_MIN_MA 100
|
|
#define SMB1351_CHG_FAST_MIN_MA 1000
|
|
#define SMB1351_CHG_FAST_MAX_MA 4500
|
|
#define SMB1351_CHG_PRE_SHIFT 5
|
|
#define SMB1351_CHG_FAST_SHIFT 4
|
|
#define DEFAULT_BATT_CAPACITY 50
|
|
#define DEFAULT_BATT_TEMP 250
|
|
#define SUSPEND_CURRENT_MA 2
|
|
|
|
#define CHG_ITERM_200MA 0x0
|
|
#define CHG_ITERM_300MA 0x04
|
|
#define CHG_ITERM_400MA 0x08
|
|
#define CHG_ITERM_500MA 0x0C
|
|
#define CHG_ITERM_600MA 0x10
|
|
#define CHG_ITERM_700MA 0x14
|
|
|
|
#define OTG_ID_PIN_CTRL_SHIFT 6
|
|
|
|
enum otg_control {
|
|
RID_DISABLED_OTG_I2C = 0,
|
|
RID_DISABLED_OTG_PIN,
|
|
RID_ENABLED_OTG_I2C,
|
|
RID_ENABLED_OTG_AUTO,
|
|
};
|
|
|
|
enum reason {
|
|
USER = BIT(0),
|
|
THERMAL = BIT(1),
|
|
CURRENT = BIT(2),
|
|
SOC = BIT(3),
|
|
};
|
|
|
|
static char *pm_batt_supplied_to[] = {
|
|
"bms",
|
|
};
|
|
|
|
struct smb1351_regulator {
|
|
struct regulator_desc rdesc;
|
|
struct regulator_dev *rdev;
|
|
};
|
|
|
|
enum chip_version {
|
|
SMB_UNKNOWN = 0,
|
|
SMB1350,
|
|
SMB1351,
|
|
SMB_MAX_TYPE,
|
|
};
|
|
|
|
static const char *smb1351_version_str[SMB_MAX_TYPE] = {
|
|
[SMB_UNKNOWN] = "Unknown",
|
|
[SMB1350] = "SMB1350",
|
|
[SMB1351] = "SMB1351",
|
|
};
|
|
|
|
static const unsigned int smb1351_extcon_cable[] = {
|
|
EXTCON_USB,
|
|
EXTCON_USB_HOST,
|
|
EXTCON_NONE,
|
|
};
|
|
|
|
struct smb1351_charger {
|
|
struct i2c_client *client;
|
|
struct device *dev;
|
|
|
|
bool recharge_disabled;
|
|
int recharge_mv;
|
|
bool iterm_disabled;
|
|
int iterm_ma;
|
|
int vfloat_mv;
|
|
int chg_present;
|
|
int fake_battery_soc;
|
|
bool chg_autonomous_mode;
|
|
bool disable_apsd;
|
|
bool battery_missing;
|
|
const char *bms_psy_name;
|
|
bool resume_completed;
|
|
bool irq_waiting;
|
|
struct delayed_work chg_remove_work;
|
|
struct delayed_work hvdcp_det_work;
|
|
|
|
/* status tracking */
|
|
bool batt_full;
|
|
bool batt_hot;
|
|
bool batt_cold;
|
|
bool batt_warm;
|
|
bool batt_cool;
|
|
|
|
int battchg_disabled_status;
|
|
int usb_suspended_status;
|
|
int target_fastchg_current_max_ma;
|
|
int fastchg_current_max_ma;
|
|
int workaround_flags;
|
|
|
|
int parallel_pin_polarity_setting;
|
|
int parallel_mode;
|
|
int pl_batfet_mode;
|
|
bool parallel_charger;
|
|
bool parallel_charger_suspended;
|
|
bool bms_controlled_charging;
|
|
bool apsd_rerun;
|
|
bool usbin_ov;
|
|
bool chg_remove_work_scheduled;
|
|
bool force_hvdcp_2p0;
|
|
enum chip_version version;
|
|
|
|
/* psy */
|
|
struct power_supply_desc usb_psy_d;
|
|
struct power_supply *usb_psy;
|
|
int usb_psy_ma;
|
|
struct power_supply *bms_psy;
|
|
struct power_supply_desc batt_psy_d;
|
|
struct power_supply *batt_psy;
|
|
struct power_supply *parallel_psy;
|
|
struct power_supply_desc parallel_psy_d;
|
|
|
|
struct smb1351_regulator otg_vreg;
|
|
struct mutex irq_complete;
|
|
|
|
struct dentry *debug_root;
|
|
u32 peek_poke_address;
|
|
|
|
/* pinctrl parameters */
|
|
const char *pinctrl_state_name;
|
|
struct pinctrl *smb_pinctrl;
|
|
|
|
/* standalone */
|
|
bool charger_present;
|
|
struct extcon_dev *extcon;
|
|
struct regulator *dpdm_reg;
|
|
enum power_supply_type charger_type;
|
|
bool otg_enable;
|
|
};
|
|
|
|
struct smb_irq_info {
|
|
const char *name;
|
|
int (*smb_irq)(struct smb1351_charger *chip, u8 rt_stat);
|
|
int high;
|
|
int low;
|
|
};
|
|
|
|
struct irq_handler_info {
|
|
u8 stat_reg;
|
|
u8 val;
|
|
u8 prev_val;
|
|
struct smb_irq_info irq_info[4];
|
|
};
|
|
|
|
/* USB input charge current */
|
|
static int usb_chg_current[] = {
|
|
500, 685, 1000, 1100, 1200, 1300, 1500, 1600,
|
|
1700, 1800, 2000, 2200, 2500, 3000,
|
|
};
|
|
|
|
static int fast_chg_current[] = {
|
|
1000, 1200, 1400, 1600, 1800, 2000, 2200,
|
|
2400, 2600, 2800, 3000, 3400, 3600, 3800,
|
|
4000, 4640,
|
|
};
|
|
|
|
static int pre_chg_current[] = {
|
|
200, 300, 400, 500, 600, 700,
|
|
};
|
|
|
|
static int smb1351_read_reg(struct smb1351_charger *chip, int reg, u8 *val)
|
|
{
|
|
s32 ret;
|
|
|
|
pm_stay_awake(chip->dev);
|
|
ret = i2c_smbus_read_byte_data(chip->client, reg);
|
|
if (ret < 0) {
|
|
pr_err("i2c read fail: can't read from %02x: %d\n", reg, ret);
|
|
pm_relax(chip->dev);
|
|
return ret;
|
|
}
|
|
|
|
*val = ret;
|
|
|
|
pm_relax(chip->dev);
|
|
pr_debug("Reading 0x%02x=0x%02x\n", reg, *val);
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_write_reg(struct smb1351_charger *chip, int reg, u8 val)
|
|
{
|
|
s32 ret;
|
|
|
|
pm_stay_awake(chip->dev);
|
|
ret = i2c_smbus_write_byte_data(chip->client, reg, val);
|
|
if (ret < 0) {
|
|
pr_err("i2c write fail: can't write %02x to %02x: %d\n",
|
|
val, reg, ret);
|
|
pm_relax(chip->dev);
|
|
return ret;
|
|
}
|
|
pm_relax(chip->dev);
|
|
pr_debug("Writing 0x%02x=0x%02x\n", reg, val);
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_masked_write(struct smb1351_charger *chip, int reg,
|
|
u8 mask, u8 val)
|
|
{
|
|
s32 rc;
|
|
u8 temp;
|
|
|
|
rc = smb1351_read_reg(chip, reg, &temp);
|
|
if (rc) {
|
|
pr_err("read failed: reg=%03X, rc=%d\n", reg, rc);
|
|
return rc;
|
|
}
|
|
temp &= ~mask;
|
|
temp |= val & mask;
|
|
rc = smb1351_write_reg(chip, reg, temp);
|
|
if (rc) {
|
|
pr_err("write failed: reg=%03X, rc=%d\n", reg, rc);
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_enable_volatile_writes(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
|
|
rc = smb1351_masked_write(chip, CMD_I2C_REG, CMD_BQ_CFG_ACCESS_BIT,
|
|
CMD_BQ_CFG_ACCESS_BIT);
|
|
if (rc)
|
|
pr_err("Couldn't write CMD_BQ_CFG_ACCESS_BIT rc=%d\n", rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_get_closest_usb_setpoint(int val)
|
|
{
|
|
int i;
|
|
|
|
for (i = ARRAY_SIZE(usb_chg_current) - 1; i >= 0; i--) {
|
|
if (usb_chg_current[i] <= val)
|
|
break;
|
|
}
|
|
if (i < 0)
|
|
i = 0;
|
|
|
|
if (i >= ARRAY_SIZE(usb_chg_current) - 1)
|
|
return ARRAY_SIZE(usb_chg_current) - 1;
|
|
|
|
/* check what is closer, i or i + 1 */
|
|
if (abs(usb_chg_current[i] - val) < abs(usb_chg_current[i + 1] - val))
|
|
return i;
|
|
else
|
|
return i + 1;
|
|
}
|
|
|
|
static int smb1351_request_dpdm(struct smb1351_charger *chip, bool enable)
|
|
{
|
|
int rc = 0;
|
|
|
|
/* fetch the DPDM regulator */
|
|
if (!chip->dpdm_reg && of_get_property(chip->dev->of_node,
|
|
"dpdm-supply", NULL)) {
|
|
chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm");
|
|
if (IS_ERR(chip->dpdm_reg)) {
|
|
rc = PTR_ERR(chip->dpdm_reg);
|
|
pr_err("Couldn't get dpdm regulator rc=%d\n",
|
|
rc);
|
|
chip->dpdm_reg = NULL;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
if (enable) {
|
|
if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) {
|
|
pr_err("enabling DPDM regulator\n");
|
|
rc = regulator_enable(chip->dpdm_reg);
|
|
if (rc < 0)
|
|
pr_err("Couldn't enable dpdm regulator rc=%d\n",
|
|
rc);
|
|
}
|
|
} else {
|
|
if (chip->dpdm_reg && regulator_is_enabled(chip->dpdm_reg)) {
|
|
pr_err("disabling DPDM regulator\n");
|
|
rc = regulator_disable(chip->dpdm_reg);
|
|
if (rc < 0)
|
|
pr_err("Couldn't disable dpdm regulator rc=%d\n",
|
|
rc);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_usb_suspend(struct smb1351_charger *chip, int reason,
|
|
bool suspend)
|
|
{
|
|
int rc = 0;
|
|
int suspended;
|
|
|
|
suspended = chip->usb_suspended_status;
|
|
|
|
pr_debug("reason = %d requested_suspend = %d suspended_status = %d\n",
|
|
reason, suspend, suspended);
|
|
|
|
if (suspend == false)
|
|
suspended &= ~reason;
|
|
else
|
|
suspended |= reason;
|
|
|
|
pr_debug("new suspended_status = %d\n", suspended);
|
|
|
|
rc = smb1351_masked_write(chip, CMD_INPUT_LIMIT_REG,
|
|
CMD_SUSPEND_MODE_BIT,
|
|
suspended ? CMD_SUSPEND_MODE_BIT : 0);
|
|
if (rc)
|
|
pr_err("Couldn't suspend rc = %d\n", rc);
|
|
else
|
|
chip->usb_suspended_status = suspended;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_battchg_disable(struct smb1351_charger *chip,
|
|
int reason, int disable)
|
|
{
|
|
int rc = 0;
|
|
int disabled;
|
|
|
|
if (chip->chg_autonomous_mode) {
|
|
pr_debug("Charger in autonomous mode\n");
|
|
return 0;
|
|
}
|
|
|
|
disabled = chip->battchg_disabled_status;
|
|
|
|
pr_debug("reason = %d requested_disable = %d disabled_status = %d\n",
|
|
reason, disable, disabled);
|
|
if (disable == true)
|
|
disabled |= reason;
|
|
else
|
|
disabled &= ~reason;
|
|
|
|
pr_debug("new disabled_status = %d\n", disabled);
|
|
|
|
rc = smb1351_masked_write(chip, CMD_CHG_REG, CMD_CHG_EN_BIT,
|
|
disabled ? 0 : CMD_CHG_ENABLE);
|
|
if (rc)
|
|
pr_err("Couldn't %s charging rc=%d\n",
|
|
disable ? "disable" : "enable", rc);
|
|
else
|
|
chip->battchg_disabled_status = disabled;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_fastchg_current_set(struct smb1351_charger *chip,
|
|
unsigned int fastchg_current)
|
|
{
|
|
int i, rc;
|
|
bool is_pre_chg = false;
|
|
|
|
|
|
if ((fastchg_current < SMB1351_CHG_PRE_MIN_MA) ||
|
|
(fastchg_current > SMB1351_CHG_FAST_MAX_MA)) {
|
|
pr_err("bad pre_fastchg current mA=%d asked to set\n",
|
|
fastchg_current);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* fast chg current could not support less than 1000mA
|
|
* use pre chg to instead for the parallel charging
|
|
*/
|
|
if (fastchg_current < SMB1351_CHG_FAST_MIN_MA) {
|
|
is_pre_chg = true;
|
|
pr_debug("is_pre_chg true, current is %d\n", fastchg_current);
|
|
}
|
|
|
|
if (is_pre_chg) {
|
|
/* set prechg current */
|
|
for (i = ARRAY_SIZE(pre_chg_current) - 1; i >= 0; i--) {
|
|
if (pre_chg_current[i] <= fastchg_current)
|
|
break;
|
|
}
|
|
if (i < 0)
|
|
i = 0;
|
|
chip->fastchg_current_max_ma = pre_chg_current[i];
|
|
pr_debug("prechg setting %02x\n", i);
|
|
|
|
i = i << SMB1351_CHG_PRE_SHIFT;
|
|
|
|
rc = smb1351_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
|
|
PRECHG_CURRENT_MASK, i);
|
|
if (rc)
|
|
pr_err("Couldn't write CHG_OTH_CURRENT_CTRL_REG rc=%d\n",
|
|
rc);
|
|
|
|
return smb1351_masked_write(chip, VARIOUS_FUNC_2_REG,
|
|
PRECHG_TO_FASTCHG_BIT, PRECHG_TO_FASTCHG_BIT);
|
|
} else {
|
|
if (chip->version == SMB_UNKNOWN)
|
|
return -EINVAL;
|
|
|
|
/* SMB1350 supports FCC upto 2600 mA */
|
|
if (chip->version == SMB1350 && fastchg_current > 2600)
|
|
fastchg_current = 2600;
|
|
|
|
/* set fastchg current */
|
|
for (i = ARRAY_SIZE(fast_chg_current) - 1; i >= 0; i--) {
|
|
if (fast_chg_current[i] <= fastchg_current)
|
|
break;
|
|
}
|
|
if (i < 0)
|
|
i = 0;
|
|
chip->fastchg_current_max_ma = fast_chg_current[i];
|
|
|
|
i = i << SMB1351_CHG_FAST_SHIFT;
|
|
pr_debug("fastchg limit=%d setting %02x\n",
|
|
chip->fastchg_current_max_ma, i);
|
|
|
|
/* make sure pre chg mode is disabled */
|
|
rc = smb1351_masked_write(chip, VARIOUS_FUNC_2_REG,
|
|
PRECHG_TO_FASTCHG_BIT, 0);
|
|
if (rc)
|
|
pr_err("Couldn't write VARIOUS_FUNC_2_REG rc=%d\n", rc);
|
|
|
|
return smb1351_masked_write(chip, CHG_CURRENT_CTRL_REG,
|
|
FAST_CHG_CURRENT_MASK, i);
|
|
}
|
|
}
|
|
|
|
#define MIN_FLOAT_MV 3500
|
|
#define MAX_FLOAT_MV 4500
|
|
#define VFLOAT_STEP_MV 20
|
|
|
|
static int smb1351_float_voltage_set(struct smb1351_charger *chip,
|
|
int vfloat_mv)
|
|
{
|
|
u8 temp;
|
|
|
|
if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
|
|
pr_err("bad float voltage mv =%d asked to set\n", vfloat_mv);
|
|
return -EINVAL;
|
|
}
|
|
|
|
temp = (vfloat_mv - MIN_FLOAT_MV) / VFLOAT_STEP_MV;
|
|
|
|
return smb1351_masked_write(chip, VFLOAT_REG, VFLOAT_MASK, temp);
|
|
}
|
|
|
|
static int smb1351_iterm_set(struct smb1351_charger *chip, int iterm_ma)
|
|
{
|
|
int rc;
|
|
u8 reg;
|
|
|
|
if (iterm_ma <= 200)
|
|
reg = CHG_ITERM_200MA;
|
|
else if (iterm_ma <= 300)
|
|
reg = CHG_ITERM_300MA;
|
|
else if (iterm_ma <= 400)
|
|
reg = CHG_ITERM_400MA;
|
|
else if (iterm_ma <= 500)
|
|
reg = CHG_ITERM_500MA;
|
|
else if (iterm_ma <= 600)
|
|
reg = CHG_ITERM_600MA;
|
|
else
|
|
reg = CHG_ITERM_700MA;
|
|
|
|
rc = smb1351_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
|
|
ITERM_MASK, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set iterm rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
/* enable the iterm */
|
|
rc = smb1351_masked_write(chip, CHG_CTRL_REG,
|
|
ITERM_EN_BIT, ITERM_ENABLE);
|
|
if (rc) {
|
|
pr_err("Couldn't enable iterm rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_chg_otg_regulator_enable(struct regulator_dev *rdev)
|
|
{
|
|
int rc = 0;
|
|
struct smb1351_charger *chip = rdev_get_drvdata(rdev);
|
|
|
|
rc = smb1351_masked_write(chip, CMD_CHG_REG, CMD_OTG_EN_BIT,
|
|
CMD_OTG_EN_BIT);
|
|
if (rc)
|
|
pr_err("Couldn't enable OTG mode rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_chg_otg_regulator_disable(struct regulator_dev *rdev)
|
|
{
|
|
int rc = 0;
|
|
struct smb1351_charger *chip = rdev_get_drvdata(rdev);
|
|
|
|
rc = smb1351_masked_write(chip, CMD_CHG_REG, CMD_OTG_EN_BIT, 0);
|
|
if (rc)
|
|
pr_err("Couldn't disable OTG mode rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_chg_otg_regulator_is_enable(struct regulator_dev *rdev)
|
|
{
|
|
int rc = 0;
|
|
u8 reg = 0;
|
|
struct smb1351_charger *chip = rdev_get_drvdata(rdev);
|
|
|
|
rc = smb1351_read_reg(chip, CMD_CHG_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read OTG enable bit rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
return (reg & CMD_OTG_EN_BIT) ? 1 : 0;
|
|
}
|
|
|
|
static struct regulator_ops smb1351_chg_otg_reg_ops = {
|
|
.enable = smb1351_chg_otg_regulator_enable,
|
|
.disable = smb1351_chg_otg_regulator_disable,
|
|
.is_enabled = smb1351_chg_otg_regulator_is_enable,
|
|
};
|
|
|
|
static int smb1351_regulator_init(struct smb1351_charger *chip)
|
|
{
|
|
int rc = 0;
|
|
struct regulator_config cfg = {};
|
|
struct regulator_init_data *init_data;
|
|
|
|
chip->otg_vreg.rdesc.owner = THIS_MODULE;
|
|
chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
|
|
chip->otg_vreg.rdesc.ops = &smb1351_chg_otg_reg_ops;
|
|
chip->otg_vreg.rdesc.name =
|
|
chip->dev->of_node->name;
|
|
chip->otg_vreg.rdesc.of_match =
|
|
chip->dev->of_node->name;
|
|
|
|
init_data = of_get_regulator_init_data(chip->dev, chip->dev->of_node,
|
|
&chip->otg_vreg.rdesc);
|
|
if (!init_data) {
|
|
pr_err("regulator init data is missing\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
cfg.dev = chip->dev;
|
|
cfg.driver_data = chip;
|
|
cfg.init_data = init_data;
|
|
cfg.of_node = chip->dev->of_node;
|
|
|
|
chip->otg_vreg.rdev = regulator_register(
|
|
&chip->otg_vreg.rdesc, &cfg);
|
|
if (IS_ERR(chip->otg_vreg.rdev)) {
|
|
rc = PTR_ERR(chip->otg_vreg.rdev);
|
|
chip->otg_vreg.rdev = NULL;
|
|
if (rc != -EPROBE_DEFER)
|
|
pr_err("OTG reg failed, rc=%d\n", rc);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int smb_chip_get_version(struct smb1351_charger *chip)
|
|
{
|
|
u8 ver;
|
|
int rc = 0;
|
|
|
|
if (chip->version == SMB_UNKNOWN) {
|
|
rc = smb1351_read_reg(chip, VERSION_REG, &ver);
|
|
if (rc) {
|
|
pr_err("Couldn't read version rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/* If bit 1 is set, it is SMB1350 */
|
|
if (ver & VERSION_MASK)
|
|
chip->version = SMB1350;
|
|
else
|
|
chip->version = SMB1351;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_hw_init(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
u8 reg = 0, mask = 0;
|
|
|
|
/* configure smb_pinctrl to enable irqs */
|
|
if (chip->pinctrl_state_name) {
|
|
chip->smb_pinctrl = pinctrl_get_select(chip->dev,
|
|
chip->pinctrl_state_name);
|
|
if (IS_ERR(chip->smb_pinctrl)) {
|
|
pr_err("Could not get/set %s pinctrl state rc = %ld\n",
|
|
chip->pinctrl_state_name,
|
|
PTR_ERR(chip->smb_pinctrl));
|
|
return PTR_ERR(chip->smb_pinctrl);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the charger is pre-configured for autonomous operation,
|
|
* do not apply additional settings
|
|
*/
|
|
if (chip->chg_autonomous_mode) {
|
|
pr_debug("Charger configured for autonomous mode\n");
|
|
return 0;
|
|
}
|
|
|
|
rc = smb_chip_get_version(chip);
|
|
if (rc) {
|
|
pr_err("Couldn't get version rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = smb1351_enable_volatile_writes(chip);
|
|
if (rc) {
|
|
pr_err("Couldn't configure volatile writes rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/* enable/disable charging by suspending usb */
|
|
rc = smb1351_usb_suspend(chip, USER, chip->usb_suspended_status);
|
|
if (rc) {
|
|
pr_err("Unable to %ssuspend usb. rc=%d\n",
|
|
chip->usb_suspended_status ? "" : "un-", rc);
|
|
return rc;
|
|
}
|
|
|
|
/* enable/disable battery charging */
|
|
rc = smb1351_battchg_disable(chip, USER, chip->battchg_disabled_status);
|
|
if (rc) {
|
|
pr_err("Unable to %s battery charging. rc=%d\n",
|
|
chip->battchg_disabled_status ? "disable" : "enable",
|
|
rc);
|
|
return rc;
|
|
}
|
|
|
|
/* setup battery missing source */
|
|
reg = BATT_MISSING_THERM_PIN_SOURCE_BIT;
|
|
mask = BATT_MISSING_THERM_PIN_SOURCE_BIT;
|
|
rc = smb1351_masked_write(chip, HVDCP_BATT_MISSING_CTRL_REG,
|
|
mask, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set HVDCP_BATT_MISSING_CTRL_REG rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
/* setup defaults for CHG_PIN_EN_CTRL_REG */
|
|
reg = EN_BY_I2C_0_DISABLE | USBCS_CTRL_BY_I2C | CHG_ERR_BIT |
|
|
APSD_DONE_BIT | LED_BLINK_FUNC_BIT;
|
|
mask = EN_PIN_CTRL_MASK | USBCS_CTRL_BIT | CHG_ERR_BIT |
|
|
APSD_DONE_BIT | LED_BLINK_FUNC_BIT;
|
|
rc = smb1351_masked_write(chip, CHG_PIN_EN_CTRL_REG, mask, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set CHG_PIN_EN_CTRL_REG rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
/* setup USB 2.0/3.0 detection and USB 500/100 command polarity */
|
|
reg = USB_2_3_MODE_SEL_BY_I2C | USB_CMD_POLARITY_500_1_100_0;
|
|
mask = USB_2_3_MODE_SEL_BIT | USB_5_1_CMD_POLARITY_BIT;
|
|
rc = smb1351_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG, mask, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set CHG_OTH_CURRENT_CTRL_REG rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
/* setup USB suspend, AICL and APSD */
|
|
reg = SUSPEND_MODE_CTRL_BY_I2C | AICL_EN_BIT;
|
|
if (!chip->disable_apsd)
|
|
reg |= APSD_EN_BIT;
|
|
mask = SUSPEND_MODE_CTRL_BIT | AICL_EN_BIT | APSD_EN_BIT;
|
|
rc = smb1351_masked_write(chip, VARIOUS_FUNC_REG, mask, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set VARIOUS_FUNC_REG rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
/* Fault and Status IRQ configuration */
|
|
reg = HOT_COLD_HARD_LIMIT_BIT | HOT_COLD_SOFT_LIMIT_BIT
|
|
| INPUT_OVLO_BIT | INPUT_UVLO_BIT | AICL_DONE_FAIL_BIT;
|
|
rc = smb1351_write_reg(chip, FAULT_INT_REG, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set FAULT_INT_REG rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
reg = CHG_OR_PRECHG_TIMEOUT_BIT | BATT_OVP_BIT |
|
|
FAST_TERM_TAPER_RECHG_INHIBIT_BIT |
|
|
BATT_MISSING_BIT | BATT_LOW_BIT;
|
|
if (chip->otg_enable)
|
|
reg = reg | RID_CHANGE_BIT;
|
|
rc = smb1351_write_reg(chip, STATUS_INT_REG, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set STATUS_INT_REG rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
/* setup THERM Monitor */
|
|
rc = smb1351_masked_write(chip, THERM_A_CTRL_REG,
|
|
THERM_MONITOR_BIT, THERM_MONITOR_EN);
|
|
if (rc) {
|
|
pr_err("Couldn't set THERM_A_CTRL_REG rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
/* set the fast charge current limit */
|
|
rc = smb1351_fastchg_current_set(chip,
|
|
chip->target_fastchg_current_max_ma);
|
|
if (rc) {
|
|
pr_err("Couldn't set fastchg current rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/* set the float voltage */
|
|
if (chip->vfloat_mv != -EINVAL) {
|
|
rc = smb1351_float_voltage_set(chip, chip->vfloat_mv);
|
|
if (rc) {
|
|
pr_err("Couldn't set float voltage rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* set iterm */
|
|
if (chip->iterm_ma != -EINVAL) {
|
|
if (chip->iterm_disabled) {
|
|
pr_err("Error: Both iterm_disabled and iterm_ma set\n");
|
|
return -EINVAL;
|
|
}
|
|
rc = smb1351_iterm_set(chip, chip->iterm_ma);
|
|
if (rc) {
|
|
pr_err("Couldn't set iterm rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
} else if (chip->iterm_disabled) {
|
|
rc = smb1351_masked_write(chip, CHG_CTRL_REG,
|
|
ITERM_EN_BIT, ITERM_DISABLE);
|
|
if (rc) {
|
|
pr_err("Couldn't set iterm rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* set recharge-threshold */
|
|
if (chip->recharge_mv != -EINVAL) {
|
|
if (chip->recharge_disabled) {
|
|
pr_err("Error: Both recharge_disabled and recharge_mv set\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
reg = AUTO_RECHG_ENABLE;
|
|
if (chip->recharge_mv > 50)
|
|
reg |= AUTO_RECHG_TH_100MV;
|
|
else
|
|
reg |= AUTO_RECHG_TH_50MV;
|
|
|
|
rc = smb1351_masked_write(chip, CHG_CTRL_REG,
|
|
AUTO_RECHG_BIT |
|
|
AUTO_RECHG_TH_BIT, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set rechg-cfg rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
} else if (chip->recharge_disabled) {
|
|
rc = smb1351_masked_write(chip, CHG_CTRL_REG,
|
|
AUTO_RECHG_BIT,
|
|
AUTO_RECHG_DISABLE);
|
|
if (rc) {
|
|
pr_err("Couldn't disable auto-rechg rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
if (chip->otg_enable) {
|
|
/* Configure OTG/ID pin control: RID enabled, OTG I2C control */
|
|
rc = smb1351_masked_write(chip, OTG_USBIN_AICL_CTRL_REG,
|
|
OTG_ID_PIN_CTRL_MASK,
|
|
RID_ENABLED_OTG_I2C << OTG_ID_PIN_CTRL_SHIFT);
|
|
if (rc) {
|
|
pr_err("Couldn't configure RID enable rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* Disable watchdog */
|
|
rc = smb1351_masked_write(chip, WDOG_SAFETY_TIMER_CTRL_REG,
|
|
WDOG_TIMER_EN_BIT, 0);
|
|
if (rc) {
|
|
pr_err("Couldn't disable watchdog rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static enum power_supply_property smb1351_battery_properties[] = {
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
POWER_SUPPLY_PROP_PRESENT,
|
|
POWER_SUPPLY_PROP_CHARGING_ENABLED,
|
|
POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
|
|
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
|
POWER_SUPPLY_PROP_CAPACITY,
|
|
POWER_SUPPLY_PROP_HEALTH,
|
|
POWER_SUPPLY_PROP_TEMP,
|
|
POWER_SUPPLY_PROP_TECHNOLOGY,
|
|
POWER_SUPPLY_PROP_MODEL_NAME,
|
|
};
|
|
|
|
static int smb1351_get_prop_batt_status(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
u8 reg = 0;
|
|
|
|
if (chip->batt_full)
|
|
return POWER_SUPPLY_STATUS_FULL;
|
|
|
|
rc = smb1351_read_reg(chip, STATUS_4_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read STATUS_4 rc = %d\n", rc);
|
|
return POWER_SUPPLY_STATUS_UNKNOWN;
|
|
}
|
|
|
|
pr_debug("STATUS_4_REG(0x3A)=%x\n", reg);
|
|
|
|
if (reg & STATUS_HOLD_OFF_BIT)
|
|
return POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
|
|
if (reg & STATUS_CHG_MASK)
|
|
return POWER_SUPPLY_STATUS_CHARGING;
|
|
|
|
return POWER_SUPPLY_STATUS_DISCHARGING;
|
|
}
|
|
|
|
static int smb1351_get_prop_batt_present(struct smb1351_charger *chip)
|
|
{
|
|
return !chip->battery_missing;
|
|
}
|
|
|
|
static int smb1351_get_prop_batt_capacity(struct smb1351_charger *chip)
|
|
{
|
|
union power_supply_propval ret = {0, };
|
|
|
|
if (chip->fake_battery_soc >= 0)
|
|
return chip->fake_battery_soc;
|
|
|
|
if (chip->bms_psy) {
|
|
power_supply_get_property(chip->bms_psy,
|
|
POWER_SUPPLY_PROP_CAPACITY, &ret);
|
|
return ret.intval;
|
|
}
|
|
pr_debug("return DEFAULT_BATT_CAPACITY\n");
|
|
return DEFAULT_BATT_CAPACITY;
|
|
}
|
|
|
|
static int smb1351_get_prop_batt_temp(struct smb1351_charger *chip)
|
|
{
|
|
union power_supply_propval ret = {0, };
|
|
|
|
if (chip->bms_psy) {
|
|
power_supply_get_property(chip->bms_psy,
|
|
POWER_SUPPLY_PROP_TEMP, &ret);
|
|
return ret.intval;
|
|
}
|
|
|
|
pr_debug("return default temperature\n");
|
|
return DEFAULT_BATT_TEMP;
|
|
}
|
|
|
|
static int smb1351_get_prop_charge_type(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
u8 reg = 0;
|
|
|
|
rc = smb1351_read_reg(chip, STATUS_4_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read STATUS_4 rc = %d\n", rc);
|
|
return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
|
|
}
|
|
|
|
pr_debug("STATUS_4_REG(0x3A)=%x\n", reg);
|
|
|
|
reg &= STATUS_CHG_MASK;
|
|
|
|
if (reg == STATUS_FAST_CHARGING)
|
|
return POWER_SUPPLY_CHARGE_TYPE_FAST;
|
|
else if (reg == STATUS_TAPER_CHARGING)
|
|
return POWER_SUPPLY_CHARGE_TYPE_TAPER;
|
|
else if (reg == STATUS_PRE_CHARGING)
|
|
return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
|
else
|
|
return POWER_SUPPLY_CHARGE_TYPE_NONE;
|
|
}
|
|
|
|
static int smb1351_get_prop_batt_health(struct smb1351_charger *chip)
|
|
{
|
|
union power_supply_propval ret = {0, };
|
|
|
|
if (chip->batt_hot)
|
|
ret.intval = POWER_SUPPLY_HEALTH_OVERHEAT;
|
|
else if (chip->batt_cold)
|
|
ret.intval = POWER_SUPPLY_HEALTH_COLD;
|
|
else if (chip->batt_warm)
|
|
ret.intval = POWER_SUPPLY_HEALTH_WARM;
|
|
else if (chip->batt_cool)
|
|
ret.intval = POWER_SUPPLY_HEALTH_COOL;
|
|
else
|
|
ret.intval = POWER_SUPPLY_HEALTH_GOOD;
|
|
|
|
return ret.intval;
|
|
}
|
|
|
|
static int smb1351_set_usb_chg_current(struct smb1351_charger *chip,
|
|
int current_ma)
|
|
{
|
|
int i, rc = 0;
|
|
u8 reg = 0, mask = 0;
|
|
|
|
pr_debug("USB current_ma = %d\n", current_ma);
|
|
|
|
if (chip->chg_autonomous_mode) {
|
|
pr_debug("Charger in autonomous mode\n");
|
|
return 0;
|
|
}
|
|
|
|
/* set suspend bit when urrent_ma <= 2 */
|
|
if (current_ma <= SUSPEND_CURRENT_MA) {
|
|
smb1351_usb_suspend(chip, CURRENT, true);
|
|
pr_debug("USB suspend\n");
|
|
return 0;
|
|
}
|
|
|
|
if (current_ma > SUSPEND_CURRENT_MA &&
|
|
current_ma < USB2_MIN_CURRENT_MA)
|
|
current_ma = USB2_MIN_CURRENT_MA;
|
|
|
|
if (current_ma == USB2_MIN_CURRENT_MA) {
|
|
/* USB 2.0 - 100mA */
|
|
reg = CMD_USB_2_MODE | CMD_USB_100_MODE;
|
|
} else if (current_ma == USB3_MIN_CURRENT_MA) {
|
|
/* USB 3.0 - 150mA */
|
|
reg = CMD_USB_3_MODE | CMD_USB_100_MODE;
|
|
} else if (current_ma == USB2_MAX_CURRENT_MA) {
|
|
/* USB 2.0 - 500mA */
|
|
reg = CMD_USB_2_MODE | CMD_USB_500_MODE;
|
|
} else if (current_ma == USB3_MAX_CURRENT_MA) {
|
|
/* USB 3.0 - 900mA */
|
|
reg = CMD_USB_3_MODE | CMD_USB_500_MODE;
|
|
} else if (current_ma > USB2_MAX_CURRENT_MA) {
|
|
/* HC mode - if none of the above */
|
|
reg = CMD_USB_AC_MODE;
|
|
|
|
for (i = ARRAY_SIZE(usb_chg_current) - 1; i >= 0; i--) {
|
|
if (usb_chg_current[i] <= current_ma)
|
|
break;
|
|
}
|
|
if (i < 0)
|
|
i = 0;
|
|
rc = smb1351_masked_write(chip, CHG_CURRENT_CTRL_REG,
|
|
AC_INPUT_CURRENT_LIMIT_MASK, i);
|
|
if (rc) {
|
|
pr_err("Couldn't set input mA rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
/* control input current mode by command */
|
|
reg |= CMD_INPUT_CURRENT_MODE_CMD;
|
|
mask = CMD_INPUT_CURRENT_MODE_BIT | CMD_USB_2_3_SEL_BIT |
|
|
CMD_USB_1_5_AC_CTRL_MASK;
|
|
rc = smb1351_masked_write(chip, CMD_INPUT_LIMIT_REG, mask, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set charging mode rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/* unset the suspend bit here */
|
|
smb1351_usb_suspend(chip, CURRENT, false);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static char *smb1351_usb_supplicants[] = {
|
|
"bms",
|
|
};
|
|
|
|
static enum power_supply_property smb1351_usb_properties[] = {
|
|
POWER_SUPPLY_PROP_PRESENT,
|
|
POWER_SUPPLY_PROP_ONLINE,
|
|
POWER_SUPPLY_PROP_CURRENT_MAX,
|
|
POWER_SUPPLY_PROP_TYPE,
|
|
POWER_SUPPLY_PROP_REAL_TYPE,
|
|
POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
|
|
};
|
|
|
|
static int smb1351_usb_get_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
union power_supply_propval *val)
|
|
{
|
|
struct smb1351_charger *chip = power_supply_get_drvdata(psy);
|
|
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
|
case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
|
|
val->intval = chip->usb_psy_ma * 1000;
|
|
break;
|
|
case POWER_SUPPLY_PROP_PRESENT:
|
|
val->intval = chip->chg_present;
|
|
break;
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
val->intval = chip->chg_present && !chip->usb_suspended_status;
|
|
break;
|
|
case POWER_SUPPLY_PROP_TYPE:
|
|
val->intval = chip->charger_type;
|
|
break;
|
|
case POWER_SUPPLY_PROP_REAL_TYPE:
|
|
if (chip->charger_type == POWER_SUPPLY_TYPE_USB_HVDCP)
|
|
val->intval = POWER_SUPPLY_TYPE_USB_DCP;
|
|
else if (chip->charger_type == POWER_SUPPLY_TYPE_UNKNOWN)
|
|
val->intval = POWER_SUPPLY_TYPE_USB;
|
|
else
|
|
val->intval = chip->charger_type;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_usb_set_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
const union power_supply_propval *val)
|
|
{
|
|
struct smb1351_charger *chip = power_supply_get_drvdata(psy);
|
|
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
|
case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
|
|
chip->usb_psy_ma = val->intval / 1000;
|
|
smb1351_enable_volatile_writes(chip);
|
|
smb1351_set_usb_chg_current(chip, chip->usb_psy_ma);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
power_supply_changed(psy);
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_usb_is_writeable(struct power_supply *psy,
|
|
enum power_supply_property psp)
|
|
{
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
|
return 1;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_batt_property_is_writeable(struct power_supply *psy,
|
|
enum power_supply_property psp)
|
|
{
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
|
|
case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
|
|
case POWER_SUPPLY_PROP_CAPACITY:
|
|
return 1;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_battery_set_property(struct power_supply *psy,
|
|
enum power_supply_property prop,
|
|
const union power_supply_propval *val)
|
|
{
|
|
int rc;
|
|
struct smb1351_charger *chip = power_supply_get_drvdata(psy);
|
|
|
|
switch (prop) {
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
if (!chip->bms_controlled_charging)
|
|
return -EINVAL;
|
|
switch (val->intval) {
|
|
case POWER_SUPPLY_STATUS_FULL:
|
|
rc = smb1351_battchg_disable(chip, SOC, true);
|
|
if (rc) {
|
|
pr_err("Couldn't disable charging rc = %d\n",
|
|
rc);
|
|
} else {
|
|
chip->batt_full = true;
|
|
pr_debug("status = FULL, batt_full = %d\n",
|
|
chip->batt_full);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_STATUS_DISCHARGING:
|
|
chip->batt_full = false;
|
|
power_supply_changed(chip->batt_psy);
|
|
pr_debug("status = DISCHARGING, batt_full = %d\n",
|
|
chip->batt_full);
|
|
break;
|
|
case POWER_SUPPLY_STATUS_CHARGING:
|
|
rc = smb1351_battchg_disable(chip, SOC, false);
|
|
if (rc) {
|
|
pr_err("Couldn't enable charging rc = %d\n",
|
|
rc);
|
|
} else {
|
|
chip->batt_full = false;
|
|
pr_debug("status = CHARGING, batt_full = %d\n",
|
|
chip->batt_full);
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
|
|
smb1351_usb_suspend(chip, USER, !val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
|
|
smb1351_battchg_disable(chip, USER, !val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CAPACITY:
|
|
chip->fake_battery_soc = val->intval;
|
|
power_supply_changed(chip->batt_psy);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_battery_get_property(struct power_supply *psy,
|
|
enum power_supply_property prop,
|
|
union power_supply_propval *val)
|
|
{
|
|
struct smb1351_charger *chip = power_supply_get_drvdata(psy);
|
|
|
|
switch (prop) {
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
val->intval = smb1351_get_prop_batt_status(chip);
|
|
break;
|
|
case POWER_SUPPLY_PROP_PRESENT:
|
|
val->intval = smb1351_get_prop_batt_present(chip);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CAPACITY:
|
|
val->intval = smb1351_get_prop_batt_capacity(chip);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
|
|
val->intval = !chip->usb_suspended_status;
|
|
break;
|
|
case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
|
|
val->intval = !chip->battchg_disabled_status;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
|
val->intval = smb1351_get_prop_charge_type(chip);
|
|
break;
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
val->intval = smb1351_get_prop_batt_health(chip);
|
|
break;
|
|
case POWER_SUPPLY_PROP_TEMP:
|
|
val->intval = smb1351_get_prop_batt_temp(chip);
|
|
break;
|
|
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
|
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
|
break;
|
|
case POWER_SUPPLY_PROP_MODEL_NAME:
|
|
val->strval = "smb1351";
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static enum power_supply_property smb1351_parallel_properties[] = {
|
|
POWER_SUPPLY_PROP_CHARGING_ENABLED,
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
POWER_SUPPLY_PROP_CURRENT_MAX,
|
|
POWER_SUPPLY_PROP_VOLTAGE_MAX,
|
|
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
|
|
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
|
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
|
POWER_SUPPLY_PROP_PARALLEL_MODE,
|
|
POWER_SUPPLY_PROP_INPUT_SUSPEND,
|
|
POWER_SUPPLY_PROP_PARALLEL_BATFET_MODE,
|
|
};
|
|
|
|
static int smb1351_parallel_set_chg_suspend(struct smb1351_charger *chip,
|
|
int suspend)
|
|
{
|
|
int rc;
|
|
u8 reg, mask = 0;
|
|
|
|
if (chip->parallel_charger_suspended == suspend) {
|
|
pr_debug("Skip same state request suspended = %d suspend=%d\n",
|
|
chip->parallel_charger_suspended, !suspend);
|
|
return 0;
|
|
}
|
|
|
|
if (!suspend) {
|
|
rc = smb_chip_get_version(chip);
|
|
if (rc) {
|
|
pr_err("Couldn't get version rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = smb1351_enable_volatile_writes(chip);
|
|
if (rc) {
|
|
pr_err("Couldn't configure for volatile rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/* set the float voltage */
|
|
if (chip->vfloat_mv != -EINVAL) {
|
|
rc = smb1351_float_voltage_set(chip, chip->vfloat_mv);
|
|
if (rc) {
|
|
pr_err("Couldn't set float voltage rc = %d\n",
|
|
rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* set recharge-threshold and enable auto recharge */
|
|
if (chip->recharge_mv != -EINVAL) {
|
|
reg = AUTO_RECHG_ENABLE;
|
|
if (chip->recharge_mv > 50)
|
|
reg |= AUTO_RECHG_TH_100MV;
|
|
else
|
|
reg |= AUTO_RECHG_TH_50MV;
|
|
|
|
rc = smb1351_masked_write(chip, CHG_CTRL_REG,
|
|
AUTO_RECHG_BIT |
|
|
AUTO_RECHG_TH_BIT, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set rechg-cfg rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* control USB suspend via command bits */
|
|
rc = smb1351_masked_write(chip, VARIOUS_FUNC_REG,
|
|
APSD_EN_BIT | SUSPEND_MODE_CTRL_BIT,
|
|
SUSPEND_MODE_CTRL_BY_I2C);
|
|
if (rc) {
|
|
pr_err("Couldn't set USB suspend rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* When present is being set force USB suspend, start charging
|
|
* only when POWER_SUPPLY_PROP_CURRENT_MAX is set.
|
|
*/
|
|
rc = smb1351_usb_suspend(chip, CURRENT, true);
|
|
if (rc) {
|
|
pr_err("failed to suspend rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
chip->usb_psy_ma = SUSPEND_CURRENT_MA;
|
|
|
|
/* set chg en by pin active low */
|
|
reg = chip->parallel_pin_polarity_setting | USBCS_CTRL_BY_I2C;
|
|
rc = smb1351_masked_write(chip, CHG_PIN_EN_CTRL_REG,
|
|
EN_PIN_CTRL_MASK | USBCS_CTRL_BIT, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set en pin rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* setup USB 2.0/3.0 detection and USB 500/100
|
|
* command polarity
|
|
*/
|
|
reg = USB_2_3_MODE_SEL_BY_I2C | USB_CMD_POLARITY_500_1_100_0;
|
|
mask = USB_2_3_MODE_SEL_BIT | USB_5_1_CMD_POLARITY_BIT;
|
|
rc = smb1351_masked_write(chip,
|
|
CHG_OTH_CURRENT_CTRL_REG, mask, reg);
|
|
if (rc) {
|
|
pr_err("Couldn't set CHG_OTH_CURRENT_CTRL_REG rc=%d\n",
|
|
rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = smb1351_fastchg_current_set(chip,
|
|
chip->target_fastchg_current_max_ma);
|
|
if (rc) {
|
|
pr_err("Couldn't set fastchg current rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
chip->parallel_charger_suspended = false;
|
|
} else {
|
|
rc = smb1351_usb_suspend(chip, CURRENT, true);
|
|
if (rc)
|
|
pr_debug("failed to suspend rc=%d\n", rc);
|
|
|
|
chip->usb_psy_ma = SUSPEND_CURRENT_MA;
|
|
chip->parallel_charger_suspended = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool smb1351_is_input_current_limited(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
u8 reg;
|
|
|
|
rc = smb1351_read_reg(chip, IRQ_H_REG, ®);
|
|
if (rc) {
|
|
pr_err("Failed to read IRQ_H_REG for ICL status: %d\n", rc);
|
|
return false;
|
|
}
|
|
|
|
return !!(reg & IRQ_IC_LIMIT_STATUS_BIT);
|
|
}
|
|
|
|
static bool smb1351_is_usb_present(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
union power_supply_propval val = {0, };
|
|
|
|
if (!chip->usb_psy)
|
|
chip->usb_psy = power_supply_get_by_name("usb");
|
|
if (!chip->usb_psy) {
|
|
pr_err("USB psy not found\n");
|
|
return false;
|
|
}
|
|
|
|
rc = power_supply_get_property(chip->usb_psy,
|
|
POWER_SUPPLY_PROP_ONLINE, &val);
|
|
if (rc < 0) {
|
|
pr_err("Failed to get present property rc=%d\n", rc);
|
|
return false;
|
|
}
|
|
|
|
if (val.intval)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static int smb1351_parallel_set_property(struct power_supply *psy,
|
|
enum power_supply_property prop,
|
|
const union power_supply_propval *val)
|
|
{
|
|
int rc = 0, index;
|
|
struct smb1351_charger *chip = power_supply_get_drvdata(psy);
|
|
|
|
switch (prop) {
|
|
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
|
|
/*
|
|
*CHG EN is controlled by pin in the parallel charging.
|
|
*Use suspend if disable charging by command.
|
|
*/
|
|
if (!chip->parallel_charger_suspended)
|
|
rc = smb1351_usb_suspend(chip, USER, !val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
|
|
rc = smb1351_parallel_set_chg_suspend(chip, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
|
chip->target_fastchg_current_max_ma =
|
|
val->intval / 1000;
|
|
if (!chip->parallel_charger_suspended)
|
|
rc = smb1351_fastchg_current_set(chip,
|
|
chip->target_fastchg_current_max_ma);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
|
index = smb1351_get_closest_usb_setpoint(val->intval / 1000);
|
|
chip->usb_psy_ma = usb_chg_current[index];
|
|
if (!chip->parallel_charger_suspended)
|
|
rc = smb1351_set_usb_chg_current(chip,
|
|
chip->usb_psy_ma);
|
|
break;
|
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
|
chip->vfloat_mv = val->intval / 1000;
|
|
if (!chip->parallel_charger_suspended)
|
|
rc = smb1351_float_voltage_set(chip, val->intval);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_parallel_is_writeable(struct power_supply *psy,
|
|
enum power_supply_property prop)
|
|
{
|
|
switch (prop) {
|
|
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int smb1351_parallel_get_property(struct power_supply *psy,
|
|
enum power_supply_property prop,
|
|
union power_supply_propval *val)
|
|
{
|
|
struct smb1351_charger *chip = power_supply_get_drvdata(psy);
|
|
|
|
switch (prop) {
|
|
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
|
|
val->intval = !chip->parallel_charger_suspended;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CURRENT_MAX:
|
|
if (!chip->parallel_charger_suspended)
|
|
val->intval = chip->usb_psy_ma * 1000;
|
|
else
|
|
val->intval = 0;
|
|
break;
|
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
|
if (!chip->parallel_charger_suspended)
|
|
val->intval = chip->vfloat_mv;
|
|
else
|
|
val->intval = 0;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
|
/* Check if SMB1351 is present */
|
|
if (smb1351_is_usb_present(chip)) {
|
|
val->intval = smb1351_get_prop_charge_type(chip);
|
|
if (val->intval == POWER_SUPPLY_CHARGE_TYPE_UNKNOWN) {
|
|
pr_debug("Failed to charge type, charger may be absent\n");
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
|
if (!chip->parallel_charger_suspended)
|
|
val->intval = chip->fastchg_current_max_ma * 1000;
|
|
else
|
|
val->intval = 0;
|
|
break;
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
if (!chip->parallel_charger_suspended)
|
|
val->intval = smb1351_get_prop_batt_status(chip);
|
|
else
|
|
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
|
break;
|
|
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
|
|
if (!chip->parallel_charger_suspended)
|
|
val->intval =
|
|
smb1351_is_input_current_limited(chip) ? 1 : 0;
|
|
else
|
|
val->intval = 0;
|
|
break;
|
|
case POWER_SUPPLY_PROP_PARALLEL_MODE:
|
|
val->intval = chip->parallel_mode;
|
|
break;
|
|
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
|
|
val->intval = chip->parallel_charger_suspended;
|
|
break;
|
|
case POWER_SUPPLY_PROP_PARALLEL_BATFET_MODE:
|
|
val->intval = chip->pl_batfet_mode;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int rerun_apsd(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
|
|
pr_debug("Reruning APSD\nDisabling APSD\n");
|
|
|
|
rc = smb1351_masked_write(chip, CMD_HVDCP_REG, CMD_APSD_RE_RUN_BIT,
|
|
CMD_APSD_RE_RUN_BIT);
|
|
if (rc)
|
|
pr_err("Couldn't re-run APSD algo\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void smb1351_hvdcp_det_work(struct work_struct *work)
|
|
{
|
|
int rc;
|
|
u8 reg;
|
|
union power_supply_propval pval = {0, };
|
|
struct smb1351_charger *chip = container_of(work,
|
|
struct smb1351_charger,
|
|
hvdcp_det_work.work);
|
|
|
|
rc = smb1351_read_reg(chip, STATUS_7_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read STATUS_7_REG rc == %d\n", rc);
|
|
goto end;
|
|
}
|
|
pr_debug("STATUS_7_REG = 0x%02X\n", reg);
|
|
|
|
if (reg) {
|
|
pr_debug("HVDCP detected; notifying USB PSY\n");
|
|
pval.intval = POWER_SUPPLY_TYPE_USB_HVDCP;
|
|
power_supply_set_property(chip->usb_psy,
|
|
POWER_SUPPLY_PROP_TYPE, &pval);
|
|
}
|
|
end:
|
|
pm_relax(chip->dev);
|
|
}
|
|
|
|
#define HVDCP_NOTIFY_MS 2500
|
|
static int smb1351_apsd_complete_handler(struct smb1351_charger *chip,
|
|
u8 status)
|
|
{
|
|
int rc, usb_psy_ma = 0;
|
|
u8 reg = 0;
|
|
enum power_supply_type type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
union extcon_property_value val;
|
|
|
|
/*
|
|
* If apsd is disabled, charger detection is done by
|
|
* USB phy driver.
|
|
*/
|
|
if (chip->disable_apsd || chip->usbin_ov) {
|
|
pr_debug("APSD %s, status = %d\n",
|
|
chip->disable_apsd ? "disabled" : "enabled", !!status);
|
|
pr_debug("USBIN ov, status = %d\n", chip->usbin_ov);
|
|
return 0;
|
|
}
|
|
|
|
rc = smb1351_read_reg(chip, STATUS_5_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read STATUS_5 rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
pr_debug("STATUS_5_REG(0x3B)=%x\n", reg);
|
|
|
|
switch (reg) {
|
|
case STATUS_PORT_ACA_DOCK:
|
|
case STATUS_PORT_ACA_C:
|
|
case STATUS_PORT_ACA_B:
|
|
case STATUS_PORT_ACA_A:
|
|
type = POWER_SUPPLY_TYPE_USB_ACA;
|
|
break;
|
|
case STATUS_PORT_CDP:
|
|
type = POWER_SUPPLY_TYPE_USB_CDP;
|
|
break;
|
|
case STATUS_PORT_DCP:
|
|
type = POWER_SUPPLY_TYPE_USB_DCP;
|
|
break;
|
|
case STATUS_PORT_SDP:
|
|
type = POWER_SUPPLY_TYPE_USB;
|
|
break;
|
|
case STATUS_PORT_OTHER:
|
|
type = POWER_SUPPLY_TYPE_USB_DCP;
|
|
break;
|
|
default:
|
|
type = POWER_SUPPLY_TYPE_USB;
|
|
break;
|
|
}
|
|
|
|
if (status) {
|
|
chip->chg_present = true;
|
|
pr_debug("APSD complete. USB type detected=%d chg_present=%d\n",
|
|
type, chip->chg_present);
|
|
if (!chip->battery_missing && !chip->apsd_rerun) {
|
|
if (type == POWER_SUPPLY_TYPE_USB) {
|
|
smb1351_request_dpdm(chip, false);
|
|
smb1351_request_dpdm(chip, true);
|
|
chip->apsd_rerun = true;
|
|
rerun_apsd(chip);
|
|
return 0;
|
|
}
|
|
}
|
|
/*
|
|
* If defined force hvdcp 2p0 property,
|
|
* we force to hvdcp 2p0 in the APSD handler.
|
|
*/
|
|
if (chip->force_hvdcp_2p0) {
|
|
pr_debug("Force set to HVDCP 2.0 mode\n");
|
|
smb1351_masked_write(chip, VARIOUS_FUNC_3_REG,
|
|
QC_2P1_AUTH_ALGO_BIT, 0);
|
|
smb1351_masked_write(chip, CMD_HVDCP_REG,
|
|
CMD_FORCE_HVDCP_2P0_BIT,
|
|
CMD_FORCE_HVDCP_2P0_BIT);
|
|
type = POWER_SUPPLY_TYPE_USB_HVDCP;
|
|
} else if (type == POWER_SUPPLY_TYPE_USB_DCP) {
|
|
pr_debug("schedule hvdcp detection worker\n");
|
|
pm_stay_awake(chip->dev);
|
|
schedule_delayed_work(&chip->hvdcp_det_work,
|
|
msecs_to_jiffies(HVDCP_NOTIFY_MS));
|
|
}
|
|
|
|
chip->charger_type = type;
|
|
if (type == POWER_SUPPLY_TYPE_USB
|
|
|| type == POWER_SUPPLY_TYPE_USB_CDP) {
|
|
val.intval = true;
|
|
extcon_set_property(chip->extcon, EXTCON_USB,
|
|
EXTCON_PROP_USB_SS, val);
|
|
extcon_set_state_sync(chip->extcon, EXTCON_USB, true);
|
|
pr_debug("extcon notify: EXTCON_USB present = 1\n");
|
|
}
|
|
chip->apsd_rerun = false;
|
|
|
|
/* set the charge current as required */
|
|
if (type == POWER_SUPPLY_TYPE_USB)
|
|
usb_psy_ma = USB2_MIN_CURRENT_MA;
|
|
else /* DCP */
|
|
usb_psy_ma = DCP_MAX_CURRENT_MA;
|
|
|
|
chip->usb_psy_ma = usb_psy_ma;
|
|
smb1351_enable_volatile_writes(chip);
|
|
rc = smb1351_set_usb_chg_current(chip, chip->usb_psy_ma);
|
|
if (rc < 0)
|
|
pr_err("Failed to set USB current rc=%d\n", rc);
|
|
|
|
} else if (!chip->apsd_rerun) {
|
|
/* Handle Charger removal */
|
|
chip->chg_present = 0;
|
|
chip->charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
val.intval = false;
|
|
extcon_set_property(chip->extcon, EXTCON_USB,
|
|
EXTCON_PROP_USB_SS, val);
|
|
extcon_set_state_sync(chip->extcon, EXTCON_USB, false);
|
|
pr_debug("extcon notify: EXTCON_USB present = 0\n");
|
|
smb1351_request_dpdm(chip, false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* As source detect interrupt is not triggered on the falling edge,
|
|
* we need to schedule a work for checking source detect status after
|
|
* charger UV interrupt fired.
|
|
*/
|
|
#define FIRST_CHECK_DELAY 100
|
|
#define SECOND_CHECK_DELAY 1000
|
|
static void smb1351_chg_remove_work(struct work_struct *work)
|
|
{
|
|
int rc;
|
|
u8 reg;
|
|
struct smb1351_charger *chip = container_of(work,
|
|
struct smb1351_charger, chg_remove_work.work);
|
|
|
|
rc = smb1351_read_reg(chip, IRQ_G_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read IRQ_G_REG rc = %d\n", rc);
|
|
goto end;
|
|
}
|
|
|
|
if (!(reg & IRQ_SOURCE_DET_BIT)) {
|
|
pr_debug("chg removed\n");
|
|
smb1351_apsd_complete_handler(chip, 0);
|
|
} else if (!chip->chg_remove_work_scheduled) {
|
|
chip->chg_remove_work_scheduled = true;
|
|
goto reschedule;
|
|
} else {
|
|
pr_debug("charger is present\n");
|
|
}
|
|
end:
|
|
chip->chg_remove_work_scheduled = false;
|
|
pm_relax(chip->dev);
|
|
return;
|
|
|
|
reschedule:
|
|
pr_debug("reschedule after 1s\n");
|
|
schedule_delayed_work(&chip->chg_remove_work,
|
|
msecs_to_jiffies(SECOND_CHECK_DELAY));
|
|
}
|
|
|
|
static int smb1351_usbin_uv_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
smb1351_request_dpdm(chip, !!status);
|
|
|
|
if (status) {
|
|
cancel_delayed_work_sync(&chip->hvdcp_det_work);
|
|
pm_relax(chip->dev);
|
|
pr_debug("schedule charger remove worker\n");
|
|
schedule_delayed_work(&chip->chg_remove_work,
|
|
msecs_to_jiffies(FIRST_CHECK_DELAY));
|
|
pm_stay_awake(chip->dev);
|
|
}
|
|
|
|
pr_debug("chip->chg_present = %d\n", chip->chg_present);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_usbin_ov_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
int rc;
|
|
u8 reg = 0;
|
|
|
|
rc = smb1351_read_reg(chip, IRQ_E_REG, ®);
|
|
if (rc)
|
|
pr_err("Couldn't read IRQ_E rc = %d\n", rc);
|
|
|
|
if (status != 0) {
|
|
chip->usbin_ov = true;
|
|
chip->charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
if (chip->chg_present) {
|
|
extcon_set_state_sync(chip->extcon, EXTCON_USB, false);
|
|
pr_debug("extcon notify: EXTCON_USB present = 0\n");
|
|
}
|
|
chip->chg_present = false;
|
|
|
|
} else {
|
|
chip->usbin_ov = false;
|
|
if (reg & IRQ_USBIN_UV_BIT)
|
|
pr_debug("Charger unplugged from OV\n");
|
|
else
|
|
smb1351_apsd_complete_handler(chip, 1);
|
|
}
|
|
|
|
pr_debug("chip->chg_present = %d\n", chip->chg_present);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_fast_chg_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
pr_debug("enter\n");
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_chg_term_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
pr_debug("enter\n");
|
|
if (!chip->bms_controlled_charging)
|
|
chip->batt_full = !!status;
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_safety_timeout_handler(struct smb1351_charger *chip,
|
|
u8 status)
|
|
{
|
|
pr_debug("safety_timeout triggered\n");
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_aicl_done_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
pr_debug("aicl_done triggered\n");
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_hot_hard_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
pr_debug("status = 0x%02x\n", status);
|
|
chip->batt_hot = !!status;
|
|
return 0;
|
|
}
|
|
static int smb1351_cold_hard_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
pr_debug("status = 0x%02x\n", status);
|
|
chip->batt_cold = !!status;
|
|
return 0;
|
|
}
|
|
static int smb1351_hot_soft_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
pr_debug("status = 0x%02x\n", status);
|
|
chip->batt_warm = !!status;
|
|
return 0;
|
|
}
|
|
static int smb1351_cold_soft_handler(struct smb1351_charger *chip, u8 status)
|
|
{
|
|
pr_debug("status = 0x%02x\n", status);
|
|
chip->batt_cool = !!status;
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_battery_missing_handler(struct smb1351_charger *chip,
|
|
u8 status)
|
|
{
|
|
if (status)
|
|
chip->battery_missing = true;
|
|
else
|
|
chip->battery_missing = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_rid_handler(struct smb1351_charger *chip,
|
|
u8 status)
|
|
{
|
|
union extcon_property_value val;
|
|
bool rid_status;
|
|
u8 reg = 0;
|
|
int rc;
|
|
|
|
rc = smb1351_read_reg(chip, STATUS_6_REG, ®);
|
|
if (rc < 0)
|
|
pr_err("Couldn't read status_6_reg, rc=%d\n", rc);
|
|
pr_debug("rt_status = 0x%02x, status_6_reg=0x%x\n", status, reg);
|
|
|
|
rid_status = (!!status) || !(reg & STATUS_RID_FLOAT_STATE_MACHINE_BIT);
|
|
if (rid_status) {
|
|
val.intval = true;
|
|
extcon_set_property(chip->extcon, EXTCON_USB_HOST,
|
|
EXTCON_PROP_USB_SS, val);
|
|
}
|
|
|
|
extcon_set_state_sync(chip->extcon, EXTCON_USB_HOST, rid_status);
|
|
pr_debug("extcon notify: EXTCON_USB_HOST present = %d\n", rid_status);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct irq_handler_info handlers[] = {
|
|
[0] = {
|
|
.stat_reg = IRQ_A_REG,
|
|
.val = 0,
|
|
.prev_val = 0,
|
|
.irq_info = {
|
|
{ .name = "cold_soft",
|
|
.smb_irq = smb1351_cold_soft_handler,
|
|
},
|
|
{ .name = "hot_soft",
|
|
.smb_irq = smb1351_hot_soft_handler,
|
|
},
|
|
{ .name = "cold_hard",
|
|
.smb_irq = smb1351_cold_hard_handler,
|
|
},
|
|
{ .name = "hot_hard",
|
|
.smb_irq = smb1351_hot_hard_handler,
|
|
},
|
|
},
|
|
},
|
|
[1] = {
|
|
.stat_reg = IRQ_B_REG,
|
|
.val = 0,
|
|
.prev_val = 0,
|
|
.irq_info = {
|
|
{ .name = "internal_temp_limit",
|
|
},
|
|
{ .name = "vbatt_low",
|
|
},
|
|
{ .name = "battery_missing",
|
|
.smb_irq = smb1351_battery_missing_handler,
|
|
},
|
|
{ .name = "batt_therm_removed",
|
|
},
|
|
},
|
|
},
|
|
[2] = {
|
|
.stat_reg = IRQ_C_REG,
|
|
.val = 0,
|
|
.prev_val = 0,
|
|
.irq_info = {
|
|
{ .name = "chg_term",
|
|
.smb_irq = smb1351_chg_term_handler,
|
|
},
|
|
{ .name = "taper",
|
|
},
|
|
{ .name = "recharge",
|
|
},
|
|
{ .name = "fast_chg",
|
|
.smb_irq = smb1351_fast_chg_handler,
|
|
},
|
|
},
|
|
},
|
|
[3] = {
|
|
.stat_reg = IRQ_D_REG,
|
|
.val = 0,
|
|
.prev_val = 0,
|
|
.irq_info = {
|
|
{ .name = "prechg_timeout",
|
|
},
|
|
{ .name = "safety_timeout",
|
|
.smb_irq = smb1351_safety_timeout_handler,
|
|
},
|
|
{ .name = "chg_error",
|
|
},
|
|
{ .name = "batt_ov",
|
|
},
|
|
},
|
|
},
|
|
[4] = {
|
|
.stat_reg = IRQ_E_REG,
|
|
.val = 0,
|
|
.prev_val = 0,
|
|
.irq_info = {
|
|
{ .name = "power_ok",
|
|
},
|
|
{ .name = "afvc",
|
|
},
|
|
{ .name = "usbin_uv",
|
|
.smb_irq = smb1351_usbin_uv_handler,
|
|
},
|
|
{ .name = "usbin_ov",
|
|
.smb_irq = smb1351_usbin_ov_handler,
|
|
},
|
|
},
|
|
},
|
|
[5] = {
|
|
.stat_reg = IRQ_F_REG,
|
|
.val = 0,
|
|
.prev_val = 0,
|
|
.irq_info = {
|
|
{ .name = "otg_oc_retry",
|
|
},
|
|
{ .name = "rid",
|
|
.smb_irq = smb1351_rid_handler,
|
|
},
|
|
{ .name = "otg_fail",
|
|
},
|
|
{ .name = "otg_oc",
|
|
},
|
|
},
|
|
},
|
|
[6] = {
|
|
.stat_reg = IRQ_G_REG,
|
|
.val = 0,
|
|
.prev_val = 0,
|
|
.irq_info = {
|
|
{ .name = "chg_inhibit",
|
|
},
|
|
{ .name = "aicl_fail",
|
|
},
|
|
{ .name = "aicl_done",
|
|
.smb_irq = smb1351_aicl_done_handler,
|
|
},
|
|
{ .name = "apsd_complete",
|
|
.smb_irq = smb1351_apsd_complete_handler,
|
|
},
|
|
},
|
|
},
|
|
[7] = {
|
|
.stat_reg = IRQ_H_REG,
|
|
.val = 0,
|
|
.prev_val = 0,
|
|
.irq_info = {
|
|
{ .name = "wdog_timeout",
|
|
},
|
|
{ .name = "hvdcp_auth_done",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
#define IRQ_LATCHED_MASK 0x02
|
|
#define IRQ_STATUS_MASK 0x01
|
|
#define BITS_PER_IRQ 2
|
|
static irqreturn_t smb1351_chg_stat_handler(int irq, void *dev_id)
|
|
{
|
|
struct smb1351_charger *chip = dev_id;
|
|
int i, j;
|
|
u8 triggered;
|
|
u8 changed;
|
|
u8 rt_stat, prev_rt_stat;
|
|
int rc;
|
|
int handler_count = 0;
|
|
|
|
mutex_lock(&chip->irq_complete);
|
|
|
|
chip->irq_waiting = true;
|
|
if (!chip->resume_completed) {
|
|
pr_debug("IRQ triggered before device-resume\n");
|
|
disable_irq_nosync(irq);
|
|
mutex_unlock(&chip->irq_complete);
|
|
return IRQ_HANDLED;
|
|
}
|
|
chip->irq_waiting = false;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(handlers); i++) {
|
|
rc = smb1351_read_reg(chip, handlers[i].stat_reg,
|
|
&handlers[i].val);
|
|
if (rc) {
|
|
pr_err("Couldn't read %d rc = %d\n",
|
|
handlers[i].stat_reg, rc);
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < ARRAY_SIZE(handlers[i].irq_info); j++) {
|
|
triggered = handlers[i].val
|
|
& (IRQ_LATCHED_MASK << (j * BITS_PER_IRQ));
|
|
rt_stat = handlers[i].val
|
|
& (IRQ_STATUS_MASK << (j * BITS_PER_IRQ));
|
|
prev_rt_stat = handlers[i].prev_val
|
|
& (IRQ_STATUS_MASK << (j * BITS_PER_IRQ));
|
|
changed = prev_rt_stat ^ rt_stat;
|
|
|
|
if (triggered || changed)
|
|
rt_stat ? handlers[i].irq_info[j].high++ :
|
|
handlers[i].irq_info[j].low++;
|
|
|
|
if ((triggered || changed)
|
|
&& handlers[i].irq_info[j].smb_irq != NULL) {
|
|
handler_count++;
|
|
rc = handlers[i].irq_info[j].smb_irq(chip,
|
|
rt_stat);
|
|
if (rc)
|
|
pr_err("Couldn't handle %d irq for reg 0x%02x rc = %d\n",
|
|
j, handlers[i].stat_reg, rc);
|
|
}
|
|
}
|
|
handlers[i].prev_val = handlers[i].val;
|
|
}
|
|
|
|
pr_debug("handler count = %d\n", handler_count);
|
|
if (handler_count) {
|
|
pr_debug("batt psy changed\n");
|
|
power_supply_changed(chip->batt_psy);
|
|
}
|
|
|
|
mutex_unlock(&chip->irq_complete);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#define LAST_CNFG_REG 0x16
|
|
static int show_cnfg_regs(struct seq_file *m, void *data)
|
|
{
|
|
struct smb1351_charger *chip = m->private;
|
|
int rc;
|
|
u8 reg;
|
|
u8 addr;
|
|
|
|
for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
|
|
rc = smb1351_read_reg(chip, addr, ®);
|
|
if (!rc)
|
|
seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cnfg_debugfs_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct smb1351_charger *chip = inode->i_private;
|
|
|
|
return single_open(file, show_cnfg_regs, chip);
|
|
}
|
|
|
|
static const struct file_operations cnfg_debugfs_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = cnfg_debugfs_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
#define FIRST_CMD_REG 0x30
|
|
#define LAST_CMD_REG 0x34
|
|
static int show_cmd_regs(struct seq_file *m, void *data)
|
|
{
|
|
struct smb1351_charger *chip = m->private;
|
|
int rc;
|
|
u8 reg;
|
|
u8 addr;
|
|
|
|
for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
|
|
rc = smb1351_read_reg(chip, addr, ®);
|
|
if (!rc)
|
|
seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_debugfs_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct smb1351_charger *chip = inode->i_private;
|
|
|
|
return single_open(file, show_cmd_regs, chip);
|
|
}
|
|
|
|
static const struct file_operations cmd_debugfs_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = cmd_debugfs_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
#define FIRST_STATUS_REG 0x36
|
|
#define LAST_STATUS_REG 0x3F
|
|
static int show_status_regs(struct seq_file *m, void *data)
|
|
{
|
|
struct smb1351_charger *chip = m->private;
|
|
int rc;
|
|
u8 reg;
|
|
u8 addr;
|
|
|
|
for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
|
|
rc = smb1351_read_reg(chip, addr, ®);
|
|
if (!rc)
|
|
seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int status_debugfs_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct smb1351_charger *chip = inode->i_private;
|
|
|
|
return single_open(file, show_status_regs, chip);
|
|
}
|
|
|
|
static const struct file_operations status_debugfs_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = status_debugfs_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static int show_irq_count(struct seq_file *m, void *data)
|
|
{
|
|
int i, j, total = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(handlers); i++)
|
|
for (j = 0; j < 4; j++) {
|
|
seq_printf(m, "%s=%d\t(high=%d low=%d)\n",
|
|
handlers[i].irq_info[j].name,
|
|
handlers[i].irq_info[j].high
|
|
+ handlers[i].irq_info[j].low,
|
|
handlers[i].irq_info[j].high,
|
|
handlers[i].irq_info[j].low);
|
|
total += (handlers[i].irq_info[j].high
|
|
+ handlers[i].irq_info[j].low);
|
|
}
|
|
|
|
seq_printf(m, "\n\tTotal = %d\n", total);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int irq_count_debugfs_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct smb1351_charger *chip = inode->i_private;
|
|
|
|
return single_open(file, show_irq_count, chip);
|
|
}
|
|
|
|
static const struct file_operations irq_count_debugfs_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = irq_count_debugfs_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static int get_reg(void *data, u64 *val)
|
|
{
|
|
struct smb1351_charger *chip = data;
|
|
int rc;
|
|
u8 temp;
|
|
|
|
rc = smb1351_read_reg(chip, chip->peek_poke_address, &temp);
|
|
if (rc) {
|
|
pr_err("Couldn't read reg %x rc = %d\n",
|
|
chip->peek_poke_address, rc);
|
|
return -EAGAIN;
|
|
}
|
|
*val = temp;
|
|
return 0;
|
|
}
|
|
|
|
static int set_reg(void *data, u64 val)
|
|
{
|
|
struct smb1351_charger *chip = data;
|
|
int rc;
|
|
u8 temp;
|
|
|
|
temp = (u8) val;
|
|
rc = smb1351_write_reg(chip, chip->peek_poke_address, temp);
|
|
if (rc) {
|
|
pr_err("Couldn't write 0x%02x to 0x%02x rc= %d\n",
|
|
temp, chip->peek_poke_address, rc);
|
|
return -EAGAIN;
|
|
}
|
|
return 0;
|
|
}
|
|
DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n");
|
|
|
|
static int force_irq_set(void *data, u64 val)
|
|
{
|
|
struct smb1351_charger *chip = data;
|
|
|
|
smb1351_chg_stat_handler(chip->client->irq, data);
|
|
return 0;
|
|
}
|
|
DEFINE_SIMPLE_ATTRIBUTE(force_irq_ops, NULL, force_irq_set, "0x%02llx\n");
|
|
|
|
#ifdef DEBUG
|
|
static void dump_regs(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
u8 reg;
|
|
u8 addr;
|
|
|
|
for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
|
|
rc = smb1351_read_reg(chip, addr, ®);
|
|
if (rc)
|
|
pr_err("Couldn't read 0x%02x rc = %d\n", addr, rc);
|
|
else
|
|
pr_debug("0x%02x = 0x%02x\n", addr, reg);
|
|
}
|
|
|
|
for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
|
|
rc = smb1351_read_reg(chip, addr, ®);
|
|
if (rc)
|
|
pr_err("Couldn't read 0x%02x rc = %d\n", addr, rc);
|
|
else
|
|
pr_debug("0x%02x = 0x%02x\n", addr, reg);
|
|
}
|
|
|
|
for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
|
|
rc = smb1351_read_reg(chip, addr, ®);
|
|
if (rc)
|
|
pr_err("Couldn't read 0x%02x rc = %d\n", addr, rc);
|
|
else
|
|
pr_debug("0x%02x = 0x%02x\n", addr, reg);
|
|
}
|
|
}
|
|
#else
|
|
static void dump_regs(struct smb1351_charger *chip)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static int smb1351_parse_dt(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
struct device_node *node = chip->dev->of_node;
|
|
|
|
if (!node) {
|
|
pr_err("device tree info. missing\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
chip->usb_suspended_status = of_property_read_bool(node,
|
|
"qcom,charging-disabled");
|
|
|
|
chip->battchg_disabled_status = of_property_read_bool(node,
|
|
"qcom,batt-charging-disabled");
|
|
|
|
chip->chg_autonomous_mode = of_property_read_bool(node,
|
|
"qcom,chg-autonomous-mode");
|
|
|
|
chip->disable_apsd = of_property_read_bool(node, "qcom,disable-apsd");
|
|
|
|
chip->bms_controlled_charging = of_property_read_bool(node,
|
|
"qcom,bms-controlled-charging");
|
|
chip->force_hvdcp_2p0 = of_property_read_bool(node,
|
|
"qcom,force-hvdcp-2p0");
|
|
|
|
rc = of_property_read_string(node, "qcom,bms-psy-name",
|
|
&chip->bms_psy_name);
|
|
if (rc)
|
|
chip->bms_psy_name = NULL;
|
|
|
|
rc = of_property_read_u32(node, "qcom,fastchg-current-max-ma",
|
|
&chip->target_fastchg_current_max_ma);
|
|
if (rc)
|
|
chip->target_fastchg_current_max_ma = SMB1351_CHG_FAST_MAX_MA;
|
|
|
|
chip->iterm_disabled = of_property_read_bool(node,
|
|
"qcom,iterm-disabled");
|
|
|
|
rc = of_property_read_u32(node, "qcom,iterm-ma", &chip->iterm_ma);
|
|
if (rc)
|
|
chip->iterm_ma = -EINVAL;
|
|
|
|
rc = of_property_read_u32(node, "qcom,float-voltage-mv",
|
|
&chip->vfloat_mv);
|
|
if (rc)
|
|
chip->vfloat_mv = -EINVAL;
|
|
|
|
rc = of_property_read_u32(node, "qcom,recharge-mv",
|
|
&chip->recharge_mv);
|
|
if (rc)
|
|
chip->recharge_mv = -EINVAL;
|
|
|
|
chip->recharge_disabled = of_property_read_bool(node,
|
|
"qcom,recharge-disabled");
|
|
|
|
chip->pinctrl_state_name = of_get_property(node, "pinctrl-names", NULL);
|
|
chip->otg_enable = of_property_read_bool(node, "qcom,otg-enable");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_determine_initial_state(struct smb1351_charger *chip)
|
|
{
|
|
int rc;
|
|
u8 reg = 0;
|
|
|
|
/*
|
|
* It is okay to read the interrupt status here since
|
|
* interrupts aren't requested. Reading interrupt status
|
|
* clears the interrupt so be careful to read interrupt
|
|
* status only in interrupt handling code
|
|
*/
|
|
|
|
rc = smb1351_read_reg(chip, IRQ_B_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read IRQ_B rc = %d\n", rc);
|
|
goto fail_init_status;
|
|
}
|
|
|
|
chip->battery_missing = (reg & IRQ_BATT_MISSING_BIT) ? true : false;
|
|
|
|
rc = smb1351_read_reg(chip, IRQ_C_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read IRQ_C rc = %d\n", rc);
|
|
goto fail_init_status;
|
|
}
|
|
chip->batt_full = (reg & IRQ_TERM_BIT) ? true : false;
|
|
|
|
rc = smb1351_read_reg(chip, IRQ_A_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read irq A rc = %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
if (reg & IRQ_HOT_HARD_BIT)
|
|
chip->batt_hot = true;
|
|
if (reg & IRQ_COLD_HARD_BIT)
|
|
chip->batt_cold = true;
|
|
if (reg & IRQ_HOT_SOFT_BIT)
|
|
chip->batt_warm = true;
|
|
if (reg & IRQ_COLD_SOFT_BIT)
|
|
chip->batt_cool = true;
|
|
|
|
rc = smb1351_read_reg(chip, IRQ_E_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read IRQ_E rc = %d\n", rc);
|
|
goto fail_init_status;
|
|
}
|
|
|
|
if (reg & IRQ_USBIN_UV_BIT) {
|
|
smb1351_usbin_uv_handler(chip, 1);
|
|
} else {
|
|
smb1351_usbin_uv_handler(chip, 0);
|
|
smb1351_apsd_complete_handler(chip, 1);
|
|
}
|
|
|
|
rc = smb1351_read_reg(chip, IRQ_G_REG, ®);
|
|
if (rc) {
|
|
pr_err("Couldn't read IRQ_G rc = %d\n", rc);
|
|
goto fail_init_status;
|
|
}
|
|
|
|
if (reg & IRQ_SOURCE_DET_BIT)
|
|
smb1351_apsd_complete_handler(chip, 1);
|
|
|
|
return 0;
|
|
|
|
fail_init_status:
|
|
pr_err("Couldn't determine initial status\n");
|
|
return rc;
|
|
}
|
|
|
|
static int is_parallel_charger(struct i2c_client *client)
|
|
{
|
|
struct device_node *node = client->dev.of_node;
|
|
|
|
return of_property_read_bool(node, "qcom,parallel-charger");
|
|
}
|
|
|
|
static int create_debugfs_entries(struct smb1351_charger *chip)
|
|
{
|
|
struct dentry *ent;
|
|
|
|
chip->debug_root = debugfs_create_dir("smb1351", NULL);
|
|
if (!chip->debug_root) {
|
|
pr_err("Couldn't create debug dir\n");
|
|
} else {
|
|
ent = debugfs_create_file("config_registers", S_IFREG | 0444,
|
|
chip->debug_root, chip,
|
|
&cnfg_debugfs_ops);
|
|
if (!ent)
|
|
pr_err("Couldn't create cnfg debug file\n");
|
|
|
|
ent = debugfs_create_file("status_registers", S_IFREG | 0444,
|
|
chip->debug_root, chip,
|
|
&status_debugfs_ops);
|
|
if (!ent)
|
|
pr_err("Couldn't create status debug file\n");
|
|
|
|
ent = debugfs_create_file("cmd_registers", S_IFREG | 0444,
|
|
chip->debug_root, chip,
|
|
&cmd_debugfs_ops);
|
|
if (!ent)
|
|
pr_err("Couldn't create cmd debug file\n");
|
|
|
|
ent = debugfs_create_x32("address", S_IFREG | 0644,
|
|
chip->debug_root,
|
|
&(chip->peek_poke_address));
|
|
if (!ent)
|
|
pr_err("Couldn't create address debug file\n");
|
|
|
|
ent = debugfs_create_file("data", S_IFREG | 0644,
|
|
chip->debug_root, chip,
|
|
&poke_poke_debug_ops);
|
|
if (!ent)
|
|
pr_err("Couldn't create data debug file\n");
|
|
|
|
ent = debugfs_create_file("force_irq",
|
|
S_IFREG | 0644,
|
|
chip->debug_root, chip,
|
|
&force_irq_ops);
|
|
if (!ent)
|
|
pr_err("Couldn't create data debug file\n");
|
|
|
|
ent = debugfs_create_file("irq_count", S_IFREG | 0444,
|
|
chip->debug_root, chip,
|
|
&irq_count_debugfs_ops);
|
|
if (!ent)
|
|
pr_err("Couldn't create count debug file\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_main_charger_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int rc;
|
|
struct smb1351_charger *chip;
|
|
struct power_supply_config batt_psy_cfg = {};
|
|
struct power_supply_config usb_psy_cfg = {};
|
|
u8 reg = 0;
|
|
|
|
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
|
if (!chip)
|
|
return -ENOMEM;
|
|
|
|
chip->client = client;
|
|
chip->dev = &client->dev;
|
|
chip->fake_battery_soc = -EINVAL;
|
|
|
|
chip->extcon = devm_extcon_dev_allocate(chip->dev,
|
|
smb1351_extcon_cable);
|
|
if (IS_ERR(chip->extcon)) {
|
|
pr_err("failed to allocate extcon device\n");
|
|
rc = PTR_ERR(chip->extcon);
|
|
return rc;
|
|
}
|
|
|
|
rc = devm_extcon_dev_register(chip->dev, chip->extcon);
|
|
if (rc) {
|
|
pr_err("failed to register extcon device\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = extcon_set_property_capability(chip->extcon,
|
|
EXTCON_USB, EXTCON_PROP_USB_SS);
|
|
rc |= extcon_set_property_capability(chip->extcon,
|
|
EXTCON_USB_HOST, EXTCON_PROP_USB_SS);
|
|
if (rc < 0) {
|
|
pr_err("Failed to register extcon capability rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
chip->usb_psy_d.name = "usb";
|
|
chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
|
|
chip->usb_psy_d.get_property = smb1351_usb_get_property;
|
|
chip->usb_psy_d.set_property = smb1351_usb_set_property;
|
|
chip->usb_psy_d.properties = smb1351_usb_properties;
|
|
chip->usb_psy_d.num_properties = ARRAY_SIZE(smb1351_usb_properties);
|
|
chip->usb_psy_d.property_is_writeable = smb1351_usb_is_writeable;
|
|
|
|
usb_psy_cfg.drv_data = chip;
|
|
usb_psy_cfg.supplied_to = smb1351_usb_supplicants;
|
|
usb_psy_cfg.num_supplicants = ARRAY_SIZE(smb1351_usb_supplicants);
|
|
|
|
chip->usb_psy = devm_power_supply_register(chip->dev,
|
|
&chip->usb_psy_d, &usb_psy_cfg);
|
|
if (IS_ERR(chip->usb_psy)) {
|
|
pr_err("Unable to register usb_psy rc = %ld\n",
|
|
PTR_ERR(chip->usb_psy));
|
|
rc = PTR_ERR(chip->usb_psy);
|
|
return rc;
|
|
}
|
|
|
|
INIT_DELAYED_WORK(&chip->chg_remove_work, smb1351_chg_remove_work);
|
|
INIT_DELAYED_WORK(&chip->hvdcp_det_work, smb1351_hvdcp_det_work);
|
|
device_init_wakeup(chip->dev, true);
|
|
/* probe the device to check if its actually connected */
|
|
rc = smb1351_read_reg(chip, CHG_REVISION_REG, ®);
|
|
if (rc) {
|
|
pr_err("Failed to detect smb1351, device may be absent\n");
|
|
return -ENODEV;
|
|
}
|
|
pr_debug("smb1351 chip revision is %d\n", reg);
|
|
|
|
rc = smb1351_parse_dt(chip);
|
|
if (rc) {
|
|
pr_err("Couldn't parse DT nodes rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
i2c_set_clientdata(client, chip);
|
|
|
|
chip->batt_psy_d.name = "battery";
|
|
chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
|
|
chip->batt_psy_d.get_property = smb1351_battery_get_property;
|
|
chip->batt_psy_d.set_property = smb1351_battery_set_property;
|
|
chip->batt_psy_d.property_is_writeable =
|
|
smb1351_batt_property_is_writeable;
|
|
chip->batt_psy_d.properties = smb1351_battery_properties;
|
|
chip->batt_psy_d.num_properties =
|
|
ARRAY_SIZE(smb1351_battery_properties);
|
|
|
|
chip->resume_completed = true;
|
|
mutex_init(&chip->irq_complete);
|
|
|
|
batt_psy_cfg.drv_data = chip;
|
|
batt_psy_cfg.supplied_to = pm_batt_supplied_to;
|
|
batt_psy_cfg.num_supplicants = ARRAY_SIZE(pm_batt_supplied_to);
|
|
chip->batt_psy = devm_power_supply_register(chip->dev,
|
|
&chip->batt_psy_d,
|
|
&batt_psy_cfg);
|
|
if (IS_ERR(chip->batt_psy)) {
|
|
pr_err("Couldn't register batt psy rc=%ld\n",
|
|
PTR_ERR(chip->batt_psy));
|
|
return rc;
|
|
}
|
|
|
|
dump_regs(chip);
|
|
|
|
rc = smb1351_regulator_init(chip);
|
|
if (rc) {
|
|
pr_err("Couldn't initialize smb1351 ragulator rc=%d\n", rc);
|
|
goto fail_smb1351_regulator_init;
|
|
}
|
|
|
|
rc = smb1351_hw_init(chip);
|
|
if (rc) {
|
|
pr_err("Couldn't initialize hardware rc=%d\n", rc);
|
|
goto fail_smb1351_hw_init;
|
|
}
|
|
|
|
rc = smb1351_determine_initial_state(chip);
|
|
if (rc) {
|
|
pr_err("Couldn't determine initial state rc=%d\n", rc);
|
|
goto fail_smb1351_hw_init;
|
|
}
|
|
|
|
/* STAT irq configuration */
|
|
if (client->irq) {
|
|
rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
|
smb1351_chg_stat_handler,
|
|
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
|
"smb1351_chg_stat_irq", chip);
|
|
if (rc) {
|
|
pr_err("Failed STAT irq=%d request rc = %d\n",
|
|
client->irq, rc);
|
|
goto fail_smb1351_hw_init;
|
|
}
|
|
enable_irq_wake(client->irq);
|
|
}
|
|
|
|
create_debugfs_entries(chip);
|
|
|
|
dump_regs(chip);
|
|
|
|
pr_info("smb1351 successfully probed. charger=%d, batt=%d version=%s\n",
|
|
chip->chg_present,
|
|
smb1351_get_prop_batt_present(chip),
|
|
smb1351_version_str[chip->version]);
|
|
return 0;
|
|
|
|
fail_smb1351_hw_init:
|
|
regulator_unregister(chip->otg_vreg.rdev);
|
|
fail_smb1351_regulator_init:
|
|
return rc;
|
|
}
|
|
|
|
static int smb1351_parallel_charger_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int rc;
|
|
struct smb1351_charger *chip;
|
|
struct device_node *node = client->dev.of_node;
|
|
struct power_supply_config parallel_psy_cfg = {};
|
|
|
|
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
|
if (!chip)
|
|
return -ENOMEM;
|
|
|
|
chip->client = client;
|
|
chip->dev = &client->dev;
|
|
chip->parallel_charger = true;
|
|
chip->parallel_charger_suspended = true;
|
|
|
|
chip->usb_suspended_status = of_property_read_bool(node,
|
|
"qcom,charging-disabled");
|
|
rc = of_property_read_u32(node, "qcom,float-voltage-mv",
|
|
&chip->vfloat_mv);
|
|
if (rc)
|
|
chip->vfloat_mv = -EINVAL;
|
|
rc = of_property_read_u32(node, "qcom,recharge-mv",
|
|
&chip->recharge_mv);
|
|
if (rc)
|
|
chip->recharge_mv = -EINVAL;
|
|
|
|
rc = of_property_read_u32(node, "qcom,parallel-en-pin-polarity",
|
|
&chip->parallel_pin_polarity_setting);
|
|
if (rc)
|
|
chip->parallel_pin_polarity_setting = EN_BY_PIN_LOW_ENABLE;
|
|
else
|
|
chip->parallel_pin_polarity_setting =
|
|
chip->parallel_pin_polarity_setting ?
|
|
EN_BY_PIN_HIGH_ENABLE : EN_BY_PIN_LOW_ENABLE;
|
|
|
|
if (of_property_read_bool(node,
|
|
"qcom,parallel-external-current-sense"))
|
|
chip->parallel_mode = POWER_SUPPLY_PL_USBIN_USBIN_EXT;
|
|
else
|
|
chip->parallel_mode = POWER_SUPPLY_PL_USBIN_USBIN;
|
|
|
|
chip->pl_batfet_mode = POWER_SUPPLY_PL_NON_STACKED_BATFET;
|
|
if (of_property_read_bool(node, "qcom,stacked-batfet"))
|
|
chip->pl_batfet_mode = POWER_SUPPLY_PL_STACKED_BATFET;
|
|
|
|
i2c_set_clientdata(client, chip);
|
|
|
|
chip->parallel_psy_d.name = "parallel";
|
|
chip->parallel_psy_d.type = POWER_SUPPLY_TYPE_PARALLEL;
|
|
chip->parallel_psy_d.get_property = smb1351_parallel_get_property;
|
|
chip->parallel_psy_d.set_property = smb1351_parallel_set_property;
|
|
chip->parallel_psy_d.properties = smb1351_parallel_properties;
|
|
chip->parallel_psy_d.property_is_writeable
|
|
= smb1351_parallel_is_writeable;
|
|
chip->parallel_psy_d.num_properties
|
|
= ARRAY_SIZE(smb1351_parallel_properties);
|
|
|
|
parallel_psy_cfg.drv_data = chip;
|
|
parallel_psy_cfg.num_supplicants = 0;
|
|
chip->parallel_psy = devm_power_supply_register(chip->dev,
|
|
&chip->parallel_psy_d,
|
|
¶llel_psy_cfg);
|
|
if (IS_ERR(chip->parallel_psy)) {
|
|
pr_err("Couldn't register parallel psy rc=%ld\n",
|
|
PTR_ERR(chip->parallel_psy));
|
|
return rc;
|
|
}
|
|
|
|
chip->resume_completed = true;
|
|
mutex_init(&chip->irq_complete);
|
|
|
|
create_debugfs_entries(chip);
|
|
|
|
pr_info("smb1351 parallel successfully probed.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_charger_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
if (is_parallel_charger(client))
|
|
return smb1351_parallel_charger_probe(client, id);
|
|
else
|
|
return smb1351_main_charger_probe(client, id);
|
|
}
|
|
|
|
static int smb1351_charger_remove(struct i2c_client *client)
|
|
{
|
|
struct smb1351_charger *chip = i2c_get_clientdata(client);
|
|
|
|
cancel_delayed_work_sync(&chip->chg_remove_work);
|
|
|
|
mutex_destroy(&chip->irq_complete);
|
|
debugfs_remove_recursive(chip->debug_root);
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct smb1351_charger *chip = i2c_get_clientdata(client);
|
|
|
|
/* no suspend resume activities for parallel charger */
|
|
if (chip->parallel_charger)
|
|
return 0;
|
|
|
|
mutex_lock(&chip->irq_complete);
|
|
chip->resume_completed = false;
|
|
mutex_unlock(&chip->irq_complete);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_suspend_noirq(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct smb1351_charger *chip = i2c_get_clientdata(client);
|
|
|
|
/* no suspend resume activities for parallel charger */
|
|
if (chip->parallel_charger)
|
|
return 0;
|
|
|
|
if (chip->irq_waiting) {
|
|
pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
|
|
return -EBUSY;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int smb1351_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct smb1351_charger *chip = i2c_get_clientdata(client);
|
|
|
|
/* no suspend resume activities for parallel charger */
|
|
if (chip->parallel_charger)
|
|
return 0;
|
|
|
|
mutex_lock(&chip->irq_complete);
|
|
chip->resume_completed = true;
|
|
if (chip->irq_waiting) {
|
|
mutex_unlock(&chip->irq_complete);
|
|
smb1351_chg_stat_handler(client->irq, chip);
|
|
enable_irq(client->irq);
|
|
} else {
|
|
mutex_unlock(&chip->irq_complete);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops smb1351_pm_ops = {
|
|
.suspend = smb1351_suspend,
|
|
.suspend_noirq = smb1351_suspend_noirq,
|
|
.resume = smb1351_resume,
|
|
};
|
|
|
|
static const struct of_device_id smb1351_match_table[] = {
|
|
{ .compatible = "qcom,smb1351-charger",},
|
|
{ },
|
|
};
|
|
|
|
static const struct i2c_device_id smb1351_charger_id[] = {
|
|
{"smb1351-charger", 0},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, smb1351_charger_id);
|
|
|
|
static struct i2c_driver smb1351_charger_driver = {
|
|
.driver = {
|
|
.name = "smb1351-charger",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = smb1351_match_table,
|
|
.pm = &smb1351_pm_ops,
|
|
},
|
|
.probe = smb1351_charger_probe,
|
|
.remove = smb1351_charger_remove,
|
|
.id_table = smb1351_charger_id,
|
|
};
|
|
|
|
module_i2c_driver(smb1351_charger_driver);
|
|
|
|
MODULE_DESCRIPTION("smb1351 Charger");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("i2c:smb1351-charger");
|
|
|