| /* Copyright (c) 2015-2017 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) "SMB1351 %s: " fmt, __func__ |
| |
| #include <linux/i2c.h> |
| #include <linux/debugfs.h> |
| #include <linux/errno.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/qpnp/qpnp-adc.h> |
| #include <linux/pinctrl/consumer.h> |
| #include <linux/bitops.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_ACTIVE_HIGH BIT(7) |
| #define STAT_OUTPUT_ACTIVE_LOW 0 |
| #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 SWITCH_FREQ_SHIFT 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_CFG_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_CFG_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_INOK_POLARITY_INVERT BIT(7) |
| #define SYSOK_OPTIONS_MASK SMB1351_MASK(6, 4) |
| #define SYSOK_INOK_OPTION1 0x00 |
| #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_HC_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_DONE_BIT BIT(7) |
| #define STATUS_INPUT_MODE_MASK SMB1351_MASK(6, 5) |
| #define STATUS_INPUT_SUSPEND BIT(4) |
| #define STATUS_INPUT_CURRENT_LIMIT_MASK SMB1351_MASK(3, 0) |
| |
| #define STATUS_1_REG 0x37 |
| #define STATUS_INPUT_RANGE_MASK SMB1351_MASK(7, 4) |
| #define STATUS_INPUT_RANGE_12V BIT(7) |
| #define STATUS_INPUT_RANGE_5V_9V BIT(6) |
| #define STATUS_INPUT_RANGE_9V BIT(5) |
| #define STATUS_INPUT_RANGE_5V BIT(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 HVDCP_SEL_5V BIT(1) |
| #define HVDCP_SEL_9V BIT(2) |
| #define HVDCP_SEL_12V BIT(3) |
| |
| #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 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 ADC_TM_WARM_COOL_THR_ENABLE ADC_TM_HIGH_LOW_THR_ENABLE |
| |
| enum reason { |
| USER = BIT(0), |
| THERMAL = BIT(1), |
| CURRENT = BIT(2), |
| SOC = BIT(3), |
| PARALLEL = BIT(4), |
| ITERM = BIT(5), |
| }; |
| |
| 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", |
| }; |
| |
| enum workaround_flags { |
| CHG_FORCE_ESR_WA = BIT(0), |
| CHG_FG_NOTIFY_JEITA = BIT(1), |
| CHG_ITERM_CHECK_SOC = BIT(2), |
| CHG_SOC_BASED_RESUME = BIT(3), |
| }; |
| |
| enum wakeup_src { |
| I2C_ACCESS, |
| HVDCP_DETECT, |
| REMOVAL_DETECT, |
| RERUN_APSD, |
| PARALLEL_CHARGE, |
| PARALLEL_TAPER, |
| FORCE_ESR_PULSE, |
| ITERM_CHECK_SOC, |
| WAKEUP_SRC_MAX, |
| }; |
| #define WAKEUP_SRC_MASK GENMASK(WAKEUP_SRC_MAX, 0) |
| |
| struct smb1351_wakeup_source { |
| struct wakeup_source source; |
| unsigned long enabled_bitmap; |
| spinlock_t ws_lock; |
| }; |
| |
| /* parallel primary charger */ |
| struct parallel_main_cfg { |
| struct power_supply *psy; |
| struct mutex lock; |
| |
| bool avail; |
| bool slave_detected; |
| int min_current_thr_ma; |
| int min_9v_current_thr_ma; |
| int min_12v_current_thr_ma; |
| int allowed_lowering_ma; |
| int main_chg_fcc_percent; |
| int main_chg_icl_percent; |
| int total_icl_ma; |
| int slave_icl_ma; |
| |
| struct delayed_work parallel_work; |
| }; |
| |
| 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 switch_freq; |
| int chg_present; |
| int fake_battery_soc; |
| int resume_soc; |
| bool chg_autonomous_mode; |
| bool disable_apsd; |
| bool using_pmic_therm; |
| bool jeita_supported; |
| bool fg_notify_jeita; |
| 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; |
| struct delayed_work rerun_apsd_work; |
| struct delayed_work init_fg_work; |
| struct delayed_work iterm_check_soc_work; |
| struct smb1351_wakeup_source smb1351_ws; |
| |
| /* 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 main_fcc_ma_before_esr; |
| int slave_fcc_ma_before_esr; |
| int workaround_flags; |
| |
| struct mutex parallel_config_lock; |
| int parallel_pin_polarity_setting; |
| bool is_slave; |
| bool use_external_fg; |
| bool parallel_charger_present; |
| bool bms_controlled_charging; |
| bool apsd_rerun; |
| bool usbin_ov; |
| bool chg_remove_work_scheduled; |
| bool force_hvdcp_2p0; |
| bool check_parallel; |
| bool in_esr_pulse; |
| enum chip_version version; |
| u32 wa_flags; |
| int pre_soc; |
| |
| /* psy */ |
| struct power_supply *usb_psy; |
| int usb_psy_ma; |
| enum power_supply_type usb_psy_type; |
| struct power_supply *bms_psy; |
| struct power_supply batt_psy; |
| struct power_supply parallel_psy; |
| |
| struct smb1351_regulator otg_vreg; |
| struct mutex irq_complete; |
| struct mutex fcc_lock; |
| |
| struct dentry *debug_root; |
| u32 peek_poke_address; |
| |
| /* adc_tm parameters */ |
| struct qpnp_vadc_chip *vadc_dev; |
| struct qpnp_adc_tm_chip *adc_tm_dev; |
| struct qpnp_adc_tm_btm_param adc_param; |
| |
| /* jeita parameters */ |
| int batt_hot_decidegc; |
| int batt_cold_decidegc; |
| int batt_warm_decidegc; |
| int batt_cool_decidegc; |
| int batt_missing_decidegc; |
| unsigned int batt_warm_ma; |
| unsigned int batt_warm_mv; |
| unsigned int batt_cool_ma; |
| unsigned int batt_cool_mv; |
| |
| /* pinctrl parameters */ |
| const char *pinctrl_state_name; |
| struct pinctrl *smb_pinctrl; |
| |
| /* parallel primary */ |
| struct parallel_main_cfg parallel; |
| }; |
| |
| 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, |
| }; |
| |
| struct battery_status { |
| bool batt_hot; |
| bool batt_warm; |
| bool batt_cool; |
| bool batt_cold; |
| bool batt_present; |
| }; |
| |
| enum { |
| BATT_HOT = 0, |
| BATT_WARM, |
| BATT_NORMAL, |
| BATT_COOL, |
| BATT_COLD, |
| BATT_MISSING, |
| BATT_STATUS_MAX, |
| }; |
| |
| static struct battery_status batt_s[] = { |
| [BATT_HOT] = {1, 0, 0, 0, 1}, |
| [BATT_WARM] = {0, 1, 0, 0, 1}, |
| [BATT_NORMAL] = {0, 0, 0, 0, 1}, |
| [BATT_COOL] = {0, 0, 1, 0, 1}, |
| [BATT_COLD] = {0, 0, 0, 1, 1}, |
| [BATT_MISSING] = {0, 0, 0, 1, 0}, |
| }; |
| |
| static void smb1351_stay_awake(struct smb1351_wakeup_source *source, |
| enum wakeup_src wk_src) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&source->ws_lock, flags); |
| if (!__test_and_set_bit(wk_src, &source->enabled_bitmap)) |
| __pm_stay_awake(&source->source); |
| spin_unlock_irqrestore(&source->ws_lock, flags); |
| } |
| |
| static void smb1351_relax(struct smb1351_wakeup_source *source, |
| enum wakeup_src wk_src) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&source->ws_lock, flags); |
| if (__test_and_clear_bit(wk_src, &source->enabled_bitmap) && |
| !(source->enabled_bitmap & WAKEUP_SRC_MASK)) { |
| __pm_relax(&source->source); |
| } |
| spin_unlock_irqrestore(&source->ws_lock, flags); |
| |
| } |
| |
| static void smb1351_wakeup_src_init(struct smb1351_charger *chip) |
| { |
| spin_lock_init(&chip->smb1351_ws.ws_lock); |
| wakeup_source_init(&chip->smb1351_ws.source, "smb1351"); |
| } |
| |
| static int smb1351_read_reg(struct smb1351_charger *chip, int reg, u8 *val) |
| { |
| s32 ret; |
| |
| smb1351_stay_awake(&chip->smb1351_ws, I2C_ACCESS); |
| 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); |
| smb1351_relax(&chip->smb1351_ws, I2C_ACCESS); |
| return ret; |
| } else { |
| *val = ret; |
| } |
| smb1351_relax(&chip->smb1351_ws, I2C_ACCESS); |
| 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; |
| |
| smb1351_stay_awake(&chip->smb1351_ws, I2C_ACCESS); |
| 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); |
| smb1351_relax(&chip->smb1351_ws, I2C_ACCESS); |
| return ret; |
| } |
| smb1351_relax(&chip->smb1351_ws, I2C_ACCESS); |
| 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_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 battery-charging rc=%d\n", |
| disable ? "disable" : "enable", rc); |
| else |
| chip->battchg_disabled_status = disabled; |
| |
| return rc; |
| } |
| |
| #define INPUT_MODE_HC 0x00 |
| #define INPUT_MODE_USB100 (0x01 << 5) |
| #define INPUT_MODE_USB500 (0x02 << 5) |
| static int smb1351_get_usb_chg_current(struct smb1351_charger *chip, |
| int *icl_ma) |
| { |
| int rc, i; |
| bool is_usb3; |
| u8 icl_status, usb_mode; |
| |
| rc = smb1351_read_reg(chip, STATUS_0_REG, &icl_status); |
| if (rc) { |
| pr_err("read STATUS_0 failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| if (icl_status & STATUS_INPUT_SUSPEND) { |
| pr_debug("USB suspended!\n"); |
| *icl_ma = 0; |
| return rc; |
| } |
| |
| rc = smb1351_read_reg(chip, CMD_INPUT_LIMIT_REG, &usb_mode); |
| if (rc) { |
| pr_err("read CMD_IL failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| is_usb3 = !!(usb_mode & CMD_USB_2_3_SEL_BIT); |
| switch (icl_status & STATUS_INPUT_MODE_MASK) { |
| case INPUT_MODE_USB100: |
| if (is_usb3) |
| *icl_ma = USB3_MIN_CURRENT_MA; |
| else |
| *icl_ma = USB2_MIN_CURRENT_MA; |
| break; |
| case INPUT_MODE_USB500: |
| if (is_usb3) |
| *icl_ma = USB3_MAX_CURRENT_MA; |
| else |
| *icl_ma = USB2_MAX_CURRENT_MA; |
| break; |
| case INPUT_MODE_HC: |
| i = icl_status & STATUS_INPUT_CURRENT_LIMIT_MASK; |
| if (i >= ARRAY_SIZE(usb_chg_current)) |
| i = ARRAY_SIZE(usb_chg_current) - 1; |
| *icl_ma = usb_chg_current[i]; |
| break; |
| default: |
| break; |
| } |
| pr_debug("USB ICL status: %d\n", *icl_ma); |
| |
| return rc; |
| } |
| |
| static int smb1351_set_usb_chg_current(struct smb1351_charger *chip, |
| int current_ma) |
| { |
| int i, rc = 0, icl_result_ma = 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_HC_MODE; |
| rc = smb1351_get_usb_chg_current(chip, &icl_result_ma); |
| if (rc) { |
| pr_err("Get ICL result failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| 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; |
| } |
| current_ma = usb_chg_current[i]; |
| } |
| |
| /* 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); |
| /* |
| * AICL couldn't be rerun if the current being set is larger |
| * than the ICL result. Configure it to USB500 mode first |
| * and then to USB_HC mode to force AICL rerun. |
| */ |
| if ((icl_result_ma < current_ma) && (reg & CMD_USB_HC_MODE)) { |
| rc = smb1351_masked_write(chip, CMD_INPUT_LIMIT_REG, mask, |
| CMD_USB_2_MODE | CMD_USB_500_MODE | |
| CMD_INPUT_CURRENT_MODE_CMD); |
| if (rc) { |
| pr_err("Set USB500 for AICL rerun failed, rc=%d\n", rc); |
| return rc; |
| } |
| rc = smb1351_masked_write(chip, CMD_INPUT_LIMIT_REG, mask, reg); |
| if (rc) { |
| pr_err("Set USBAC for AICL rerun failed, rc=%d\n", rc); |
| return rc; |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int smb1351_fastchg_current_set(struct smb1351_charger *chip, |
| unsigned int fastchg_current) |
| { |
| int i, rc; |
| bool is_pre_chg = false; |
| |
| mutex_lock(&chip->fcc_lock); |
| if (fastchg_current < SMB1351_CHG_PRE_MIN_MA) |
| fastchg_current = SMB1351_CHG_PRE_MIN_MA; |
| |
| if (fastchg_current > SMB1351_CHG_FAST_MAX_MA) |
| fastchg_current = SMB1351_CHG_FAST_MAX_MA; |
| |
| pr_debug("set fastchg current mA=%d\n", fastchg_current); |
| |
| /* |
| * 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); |
| |
| rc = smb1351_masked_write(chip, VARIOUS_FUNC_2_REG, |
| PRECHG_TO_FASTCHG_BIT, PRECHG_TO_FASTCHG_BIT); |
| if (rc) |
| pr_err("Write VARIOUS_FUNC_2_REG failed, rc=%d\n", rc); |
| } else { |
| if (chip->version == SMB_UNKNOWN) { |
| rc = -EINVAL; |
| goto done; |
| } |
| |
| /* 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); |
| |
| rc = smb1351_masked_write(chip, CHG_CURRENT_CTRL_REG, |
| FAST_CHG_CURRENT_MASK, i); |
| if (rc) |
| pr_err("Write CURRENT_CTRL_REG failed, rc=%d\n", rc); |
| } |
| done: |
| mutex_unlock(&chip->fcc_lock); |
| return rc; |
| } |
| |
| #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_chg_set_appropriate_battery_current( |
| struct smb1351_charger *chip) |
| { |
| int rc; |
| unsigned int current_max = chip->target_fastchg_current_max_ma; |
| |
| if (chip->batt_cool) |
| current_max = min(current_max, chip->batt_cool_ma); |
| if (chip->batt_warm) |
| current_max = min(current_max, chip->batt_warm_ma); |
| |
| pr_debug("setting FCC to %dmA", current_max); |
| |
| rc = smb1351_fastchg_current_set(chip, current_max); |
| if (rc) |
| pr_err("Couldn't set charging current rc = %d\n", rc); |
| |
| return rc; |
| } |
| |
| static int smb1351_chg_set_appropriate_vfloat(struct smb1351_charger *chip) |
| { |
| int rc; |
| unsigned int vfloat = chip->vfloat_mv; |
| |
| if (chip->batt_cool) |
| vfloat = min(vfloat, chip->batt_cool_mv); |
| if (chip->batt_warm) |
| vfloat = min(vfloat, chip->batt_warm_mv); |
| |
| pr_debug("setting vfloat to %dmV\n", vfloat); |
| |
| rc = smb1351_float_voltage_set(chip, vfloat); |
| if (rc) |
| pr_err("Couldn't set float voltage rc = %d\n", rc); |
| |
| return rc; |
| } |
| |
| 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; |
| } |
| |
| #define N_TYPE_BIT 8 |
| static int smb1351_get_usb_supply_type(struct smb1351_charger *chip, |
| enum power_supply_type *type) |
| { |
| int rc, index; |
| u8 reg; |
| static const char * const usb_ps_type_str[] = { |
| "ACA_DOCK", |
| "ACA_C", |
| "ACA_B", |
| "ACA_A", |
| "SDP", |
| "OTHER", |
| "DCP", |
| "CDP", |
| }; |
| |
| rc = smb1351_read_reg(chip, STATUS_5_REG, ®); |
| if (rc) { |
| pr_err("Couldn't read STATUS_5 rc = %d\n", rc); |
| return rc; |
| } |
| |
| 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_UNKNOWN; |
| break; |
| } |
| |
| if (*type == POWER_SUPPLY_TYPE_UNKNOWN) { |
| pr_debug("Can't get correct power supply type!"); |
| } else { |
| index = find_first_bit((unsigned long *)®, N_TYPE_BIT); |
| pr_debug("STATUS_5_REG(0x3B)=%x, USB type = %s\n", |
| reg, usb_ps_type_str[index]); |
| } |
| |
| return rc; |
| } |
| |
| static int smb1351_set_bms_property(struct smb1351_charger *chip, |
| enum power_supply_property prop, int value) |
| { |
| int rc; |
| union power_supply_propval propval = {0, }; |
| |
| if (chip->bms_psy_name && !chip->bms_psy) |
| chip->bms_psy = |
| power_supply_get_by_name((char *)chip->bms_psy_name); |
| |
| if (IS_ERR_OR_NULL(chip->bms_psy)) { |
| pr_debug("BMS(fg) power supply not present\n"); |
| return -ENODEV; |
| } |
| |
| propval.intval = value; |
| rc = chip->bms_psy->set_property(chip->bms_psy, prop, &propval); |
| if (rc) { |
| pr_err("Set BMS property %d failed, rc=%d\n", prop, rc); |
| return rc; |
| } |
| |
| return rc; |
| } |
| |
| static int smb1351_get_bms_property(struct smb1351_charger *chip, |
| enum power_supply_property prop, int *val) |
| { |
| int rc; |
| union power_supply_propval propval = {0, }; |
| |
| if (chip->bms_psy_name && !chip->bms_psy) |
| chip->bms_psy = |
| power_supply_get_by_name((char *)chip->bms_psy_name); |
| |
| if (IS_ERR_OR_NULL(chip->bms_psy)) { |
| pr_debug("BMS(fg) power supply not present\n"); |
| return -ENODEV; |
| } |
| |
| rc = chip->bms_psy->get_property(chip->bms_psy, prop, &propval); |
| if (rc) { |
| pr_err("Set BMS property %d failed, rc=%d\n", prop, rc); |
| return rc; |
| } |
| *val = propval.intval; |
| |
| return rc; |
| } |
| |
| 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; |
| } |
| |
| 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_init_data *init_data; |
| struct regulator_config cfg = {}; |
| |
| init_data = of_get_regulator_init_data(chip->dev, chip->dev->of_node); |
| if (!init_data) { |
| pr_err("Unable to allocate memory\n"); |
| return -ENOMEM; |
| } |
| |
| if (init_data->constraints.name) { |
| 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 = init_data->constraints.name; |
| |
| cfg.dev = chip->dev; |
| cfg.init_data = init_data; |
| cfg.driver_data = chip; |
| cfg.of_node = chip->dev->of_node; |
| |
| init_data->constraints.valid_ops_mask |
| |= REGULATOR_CHANGE_STATUS; |
| |
| 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; |
| } |
| |
| #define INIT_FG_RETRY_MS 1000 |
| 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 charging before we move into register based CHG_EN control */ |
| rc = smb1351_battchg_disable(chip, USER, chip->usb_suspended_status); |
| if (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 | OTG_OC_BIT |
| | INPUT_OVLO_BIT | INPUT_UVLO_BIT | AICL_DONE_FAIL_BIT; |
| rc = smb1351_write_reg(chip, FAULT_INT_CFG_REG, reg); |
| if (rc) { |
| pr_err("Couldn't set FAULT_INT_CFG_REG rc=%d\n", rc); |
| return rc; |
| } |
| reg = CHG_OR_PRECHG_TIMEOUT_BIT | RID_CHANGE_BIT | BATT_OVP_BIT | |
| FAST_TERM_TAPER_RECHG_INHIBIT_BIT | BATT_MISSING_BIT | |
| BATT_LOW_BIT; |
| rc = smb1351_write_reg(chip, STATUS_INT_CFG_REG, reg); |
| if (rc) { |
| pr_err("Couldn't set STATUS_INT_CFG_REG rc=%d\n", rc); |
| return rc; |
| } |
| /* setup THERM Monitor */ |
| if (!chip->using_pmic_therm) { |
| 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; |
| } else { |
| 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; |
| } else { |
| 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 || |
| (chip->wa_flags & CHG_SOC_BASED_RESUME)) { |
| 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; |
| } |
| } |
| |
| /* Update switching frequency based on device tree entry */ |
| if (chip->switch_freq != -EINVAL) { |
| rc = smb1351_masked_write(chip, OTG_TLIM_CTRL_REG, |
| SWITCH_FREQ_MASK, |
| (chip->switch_freq << SWITCH_FREQ_SHIFT)); |
| if (rc) { |
| pr_err("Set switching frequency failed, r=%d\n", rc); |
| return rc; |
| } |
| } |
| |
| /* set STAT pin behavior: active low, indicate charging status */ |
| rc = smb1351_masked_write(chip, CHG_STAT_TIMERS_CTRL_REG, |
| STAT_OUTPUT_POLARITY_BIT | STAT_OUTPUT_MODE_BIT |
| | STAT_OUTPUT_CTRL_BIT, 0); |
| if (rc) { |
| pr_err("Set STAT pin polarity failed, r=%d\n", rc); |
| return rc; |
| } |
| |
| if (chip->parallel.avail) { |
| rc = smb1351_masked_write(chip, PON_OPTIONS_REG, |
| SYSOK_INOK_POLARITY_BIT | SYSOK_OPTIONS_MASK, |
| SYSOK_INOK_POLARITY_INVERT | SYSOK_INOK_OPTION1); |
| if (rc) { |
| pr_err("Program PON option failed, rc=%d\n", rc); |
| return rc; |
| } |
| } |
| |
| if (chip->use_external_fg) { |
| rc = smb1351_set_bms_property(chip, |
| POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE, 0); |
| if (rc) { |
| pr_debug("set IGNORE_FALSE_NEGATIVE_ISENSE failed, rc=%d\n", |
| rc); |
| /* start a delay worker to do it again if failed */ |
| schedule_delayed_work(&chip->init_fg_work, |
| msecs_to_jiffies(INIT_FG_RETRY_MS)); |
| } else if (chip->fg_notify_jeita) { |
| rc = smb1351_set_bms_property(chip, |
| POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION, 1); |
| if (rc) { |
| pr_err("Set ENABLE_JEITA_DETECTION failed, rc=%d\n", |
| rc); |
| return rc; |
| } |
| } |
| } |
| |
| /* Enable HVDCP */ |
| rc = smb1351_masked_write(chip, HVDCP_BATT_MISSING_CTRL_REG, |
| HVDCP_EN_BIT, HVDCP_EN_BIT); |
| if (rc) { |
| pr_err("Failed to enable HVDCP, 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 %s USB input. rc=%d\n", |
| chip->usb_suspended_status ? "suspend" : "enable", rc); |
| return rc; |
| } |
| |
| rc = smb1351_battchg_disable(chip, USER, chip->usb_suspended_status); |
| if (rc) |
| pr_err("Couldn't %s charging rc = %d\n", |
| chip->usb_suspended_status ? "disable" : "enable", 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) |
| { |
| int rc = 0, soc = 0; |
| |
| if (chip->fake_battery_soc >= 0) |
| return chip->fake_battery_soc; |
| |
| rc = smb1351_get_bms_property(chip, POWER_SUPPLY_PROP_CAPACITY, &soc); |
| if (rc) { |
| pr_debug("Get capacity from BMS failed, rc=%d\n", rc); |
| return DEFAULT_BATT_CAPACITY; |
| } |
| pr_debug("SoC = %d\n", soc); |
| |
| return soc; |
| } |
| |
| static int smb1351_get_prop_batt_temp(struct smb1351_charger *chip) |
| { |
| int rc = 0, temp = 0; |
| struct qpnp_vadc_result results; |
| |
| rc = smb1351_get_bms_property(chip, POWER_SUPPLY_PROP_TEMP, &temp); |
| if (rc) { |
| temp = DEFAULT_BATT_TEMP; |
| pr_debug("Get tempertory from BMS failed, rc=%d\n", rc); |
| } |
| if (chip->vadc_dev) { |
| rc = qpnp_vadc_read(chip->vadc_dev, |
| LR_MUX1_BATT_THERM, &results); |
| if (rc) |
| pr_debug("Unable to read adc batt temp rc=%d\n", rc); |
| else |
| temp = (int)results.physical; |
| } |
| pr_debug("temperature: %d\n", temp); |
| |
| return 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_get_usbid_status(struct smb1351_charger *chip, bool *gnd) |
| { |
| int rc; |
| u8 regval; |
| |
| rc = smb1351_read_reg(chip, STATUS_6_REG, ®val); |
| if (rc) { |
| pr_err("Read STATUS_6 failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| *gnd = !regval; |
| |
| return rc; |
| } |
| |
| static struct power_supply *smb1351_get_parallel_slave( |
| struct smb1351_charger *chip) |
| { |
| if (!chip->parallel.avail) |
| return NULL; |
| if (chip->parallel.psy) |
| return chip->parallel.psy; |
| |
| chip->parallel.psy = power_supply_get_by_name("usb-parallel"); |
| if (!chip->parallel.psy) |
| pr_debug("parallel slave not found\n"); |
| |
| return chip->parallel.psy; |
| } |
| |
| /* |
| * Get the mininum parallel charging current threshold according to |
| * the voltage provided on the charger adapter |
| */ |
| static int smb1351_get_min_parallel_current_ma(struct smb1351_charger *chip) |
| { |
| int rc; |
| u8 value; |
| |
| rc = smb1351_read_reg(chip, STATUS_1_REG, &value); |
| if (rc) { |
| pr_err("read STATUS_7_REG failed, rc = %d\n", rc); |
| return rc; |
| } |
| |
| switch (value & STATUS_INPUT_RANGE_MASK) { |
| case STATUS_INPUT_RANGE_5V: |
| case STATUS_INPUT_RANGE_5V_9V: |
| rc = chip->parallel.min_current_thr_ma; |
| break; |
| case STATUS_INPUT_RANGE_9V: |
| rc = chip->parallel.min_9v_current_thr_ma; |
| break; |
| case STATUS_INPUT_RANGE_12V: |
| rc = chip->parallel.min_12v_current_thr_ma; |
| break; |
| default: |
| rc = -EINVAL; |
| break; |
| } |
| |
| return rc; |
| } |
| |
| static int smb1351_parallel_charger_disable_slave( |
| struct smb1351_charger *chip) |
| { |
| int rc; |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| |
| if (!parallel_psy || !chip->parallel.slave_detected) |
| return 0; |
| |
| pr_debug("Disable parallel slave!\n"); |
| |
| chip->parallel.total_icl_ma = 0; |
| chip->parallel.slave_icl_ma = 0; |
| |
| /* Suspend USB and clear PRESENT to slave charger */ |
| power_supply_set_current_limit(parallel_psy, SUSPEND_CURRENT_MA * 1000); |
| power_supply_set_present(parallel_psy, false); |
| |
| /* Restore main charger FCC && ICL as the standalone */ |
| rc = smb1351_chg_set_appropriate_battery_current(chip); |
| if (rc) { |
| pr_err("restore FCC to %d failed\n", |
| chip->target_fastchg_current_max_ma); |
| return rc; |
| } |
| rc = smb1351_set_usb_chg_current(chip, chip->usb_psy_ma); |
| if (rc) { |
| pr_err("restore ICL to %d failed\n", chip->usb_psy_ma); |
| return rc; |
| } |
| pr_debug("Restore ICL %dmA to the main charger\n", chip->usb_psy_ma); |
| |
| return rc; |
| } |
| |
| /* |
| * Enable slave, and split the total input current to main charger |
| * and the slave according to the percent. |
| */ |
| static int smb1351_parallel_charger_enable_slave( |
| struct smb1351_charger *chip, |
| int total_icl_ma) |
| { |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| union power_supply_propval pval = {0, }; |
| int rc, slave_icl_ma, actual_slave_icl_ma, main_icl_ma; |
| int slave_fcc_ma, actual_slave_fcc_ma, main_fcc_ma; |
| |
| if (!parallel_psy || !chip->parallel.slave_detected) |
| return 0; |
| |
| pr_debug("Enable parallel slave!\n"); |
| /* |
| * Set slave's float voltage a little high main charger |
| * to avoid slave trigger taper before the main charger |
| */ |
| rc = power_supply_set_voltage_limit(parallel_psy, chip->vfloat_mv + 50); |
| if (rc) { |
| pr_err("Set slave VFLT failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* split the ICL for main and slave charger */ |
| slave_icl_ma = total_icl_ma * |
| (100 - chip->parallel.main_chg_icl_percent) / 100; |
| |
| /* set the allotted ICL for slave */ |
| rc = power_supply_set_present(parallel_psy, true); |
| if (rc) { |
| pr_err("Set slave present failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = power_supply_set_current_limit(parallel_psy, slave_icl_ma * 1000); |
| if (rc) { |
| pr_err("Set slave ICL to %dmA failed, rc=%d\n", |
| slave_icl_ma, rc); |
| return rc; |
| } |
| |
| chip->parallel.slave_icl_ma = slave_icl_ma; |
| |
| /* get the ICL real set in HW */ |
| parallel_psy->get_property(parallel_psy, |
| POWER_SUPPLY_PROP_CURRENT_MAX, &pval); |
| actual_slave_icl_ma = pval.intval / 1000; |
| pr_debug("parallel slave ICL, program %d, set %d\n", |
| slave_icl_ma, actual_slave_icl_ma); |
| |
| /* set the allotted ICL for main charger */ |
| main_icl_ma = max(total_icl_ma - actual_slave_icl_ma, 0); |
| |
| rc = smb1351_set_usb_chg_current(chip, main_icl_ma); |
| if (rc) { |
| pr_err("set main charger ICL %d failed, rc=%d\n", |
| main_icl_ma, rc); |
| return rc; |
| } |
| pr_debug("ICL split, total %d: main %d, slave %d\n", |
| total_icl_ma, main_icl_ma, actual_slave_icl_ma); |
| /* split the FCC for main and slave charger */ |
| slave_fcc_ma = chip->target_fastchg_current_max_ma * |
| (100 - chip->parallel.main_chg_fcc_percent) / 100; |
| |
| /* set the allotted FCC to slave charger */ |
| pval.intval = slave_fcc_ma * 1000; |
| parallel_psy->set_property(parallel_psy, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); |
| parallel_psy->get_property(parallel_psy, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); |
| actual_slave_fcc_ma = pval.intval / 1000; |
| |
| /* set the allotted FCC to main charger */ |
| main_fcc_ma = chip->target_fastchg_current_max_ma |
| - actual_slave_fcc_ma; |
| |
| rc = smb1351_fastchg_current_set(chip, main_fcc_ma); |
| if (rc) { |
| pr_err("Set main charger FCC %d failed, rc=%d\n", |
| main_fcc_ma, rc); |
| return rc; |
| } |
| pr_debug("FCC split, total %d: main %d, slave %d\n", |
| chip->target_fastchg_current_max_ma, |
| main_fcc_ma, actual_slave_fcc_ma); |
| return rc; |
| } |
| |
| /* |
| * Check conditions if it's able to enable the slave for parallel |
| * charging, and also calculate the max current could be drawn from |
| * the charger adapter. |
| */ |
| static bool smb1351_attempt_enable_parallel_slave( |
| struct smb1351_charger *chip, |
| int *ret_total_icl_ma) |
| { |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| union power_supply_propval pval = {0, }; |
| enum power_supply_type type; |
| int min_icl_thr_ma, main_current_icl_ma; |
| int slave_current_icl_ma, total_icl_ma; |
| int rc; |
| |
| pr_debug("Check if it is OK to enable slave!\n"); |
| if (!parallel_psy || !chip->parallel.slave_detected) |
| return false; |
| /* |
| * Report false to disable parallel charging if NOT in fastchg mode, |
| * only when battery is connected or parallel slave hasn't been |
| * enabled. |
| */ |
| if (smb1351_get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST |
| && (smb1351_get_prop_batt_present(chip) |
| || chip->parallel.slave_icl_ma == 0)) { |
| pr_debug("Not in fastchg mode, disable parallel!\n"); |
| return false; |
| } |
| /* If battery health NOT good */ |
| if (smb1351_get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) { |
| pr_debug("JEITA active, disable parallel!\n"); |
| return false; |
| } |
| /* Only start parallel charger in DCP/HVDCP */ |
| rc = smb1351_get_usb_supply_type(chip, &type); |
| if (rc) { |
| pr_err("Get USB power supply type failed, rc=%d\n", rc); |
| return false; |
| } else if (type == POWER_SUPPLY_TYPE_USB_CDP || |
| type == POWER_SUPPLY_TYPE_USB) { |
| pr_debug("USB type %d, disable parallel!\n", type); |
| return false; |
| } |
| |
| min_icl_thr_ma = smb1351_get_min_parallel_current_ma(chip); |
| if (min_icl_thr_ma <= 0) { |
| pr_debug("Get min ICL threshold failed, disable parallel!\n"); |
| return false; |
| } |
| |
| rc = smb1351_get_usb_chg_current(chip, &main_current_icl_ma); |
| if (rc) { |
| pr_debug("Get main charger ICL failed, skipping!\n"); |
| return false; |
| } |
| pr_debug("main charger ICL = %d\n", main_current_icl_ma); |
| /* |
| * If parallel is not enabled, and main charger ICL is less than min |
| * parallel ICL threshold, disable parallel. |
| * Record the total ICL at 1st place. |
| */ |
| if (chip->parallel.total_icl_ma == 0) { |
| if (main_current_icl_ma < min_icl_thr_ma) { |
| pr_debug("Current ICL %d lower than threshold %d, skipping!\n", |
| main_current_icl_ma, min_icl_thr_ma); |
| return false; |
| } |
| chip->parallel.total_icl_ma = main_current_icl_ma; |
| pr_debug("Not in parallel charging previously, total_icl = %d\n", |
| main_current_icl_ma); |
| } |
| |
| /* Check if parallel slave already draw some current */ |
| parallel_psy->get_property(parallel_psy, |
| POWER_SUPPLY_PROP_CURRENT_MAX, &pval); |
| slave_current_icl_ma = pval.intval / 1000; |
| if (slave_current_icl_ma == SUSPEND_CURRENT_MA) |
| slave_current_icl_ma = 0; |
| |
| pr_debug("slave charger ICL = %d\n", slave_current_icl_ma); |
| total_icl_ma = min(main_current_icl_ma + slave_current_icl_ma, |
| chip->usb_psy_ma); |
| pr_debug("calculated total ICL = %d\n", total_icl_ma); |
| /* |
| * If calculated total ICL is below than allowed_lowering, |
| * disable parallel |
| */ |
| if (total_icl_ma < |
| chip->parallel.total_icl_ma - |
| chip->parallel.allowed_lowering_ma) { |
| pr_debug("total ICL reduced to below the threshold: %d < (%d-%d)\n", |
| total_icl_ma, chip->parallel.total_icl_ma, |
| chip->parallel.allowed_lowering_ma); |
| return false; |
| } |
| |
| *ret_total_icl_ma = total_icl_ma; |
| |
| pr_debug("Final total ICL for parallel charging = %d\n", total_icl_ma); |
| return true; |
| } |
| |
| #define MIN_SLAVER_FCC_MA 500 |
| #define SLAVE_CHG_FCC_REDUCTION_PERCENTAGE 75 |
| #define SLAVER_FCC_REDUCTION_MAX_TRIES 3 |
| static int smb1351_parallel_charger_taper_attempt( |
| struct smb1351_charger *chip) |
| { |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| union power_supply_propval pval = {0, }; |
| int rc, slave_fcc_ma, tries = 0; |
| u8 value; |
| |
| if (!parallel_psy || !chip->parallel.slave_detected) |
| return 0; |
| |
| smb1351_stay_awake(&chip->smb1351_ws, PARALLEL_TAPER); |
| |
| try_again: |
| mutex_lock(&chip->parallel.lock); |
| if (chip->parallel.slave_icl_ma == 0) { |
| pr_debug("Parallel slave not enabled, skipping\n"); |
| rc = 0; |
| goto done; |
| } |
| rc = parallel_psy->get_property(parallel_psy, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); |
| if (rc) { |
| pr_err("Get slave FCC failed, rc=%d\n", rc); |
| goto done; |
| } |
| |
| tries += 1; |
| slave_fcc_ma = pval.intval / 1000; |
| |
| if (slave_fcc_ma < MIN_SLAVER_FCC_MA || |
| tries > SLAVER_FCC_REDUCTION_MAX_TRIES) { |
| pr_debug("reduce slave FCC can't exist taper, disable slave\n"); |
| smb1351_parallel_charger_disable_slave(chip); |
| goto done; |
| } |
| |
| pval.intval = slave_fcc_ma * SLAVE_CHG_FCC_REDUCTION_PERCENTAGE / 100; |
| pval.intval *= 1000; |
| rc = parallel_psy->set_property(parallel_psy, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); |
| if (rc) { |
| pr_err("Set slave FCC %duA failed, rc=%d\n", pval.intval, rc); |
| goto done; |
| } |
| |
| mutex_unlock(&chip->parallel.lock); |
| /* |
| * Sleep 100ms to make sure the charger has a chance to |
| * go back to fast charging state. |
| */ |
| msleep(100); |
| |
| mutex_lock(&chip->parallel.lock); |
| rc = smb1351_read_reg(chip, IRQ_C_REG, &value); |
| if (rc) { |
| pr_err("Read IRQ_C failed, rc=%d\n", rc); |
| goto done; |
| } |
| |
| if (value & IRQ_TAPER_BIT) { |
| mutex_unlock(&chip->parallel.lock); |
| pr_debug("recheck: %d\n", tries); |
| goto try_again; |
| } |
| done: |
| mutex_unlock(&chip->parallel.lock); |
| smb1351_relax(&chip->smb1351_ws, PARALLEL_TAPER); |
| return rc; |
| } |
| |
| static void smb1351_parallel_work(struct work_struct *work) |
| { |
| struct smb1351_charger *chip = container_of(work, |
| struct smb1351_charger, parallel.parallel_work.work); |
| |
| int rc, pre_icl_ma, icl_ma, total_icl_ma = 0; |
| |
| /* Get ICL twice with 500ms delays to check if ICl settled */ |
| rc = smb1351_get_usb_chg_current(chip, &pre_icl_ma); |
| if (rc) { |
| pr_err("Get ICL result failed, rc = %d\n", rc); |
| return; |
| } |
| |
| msleep(500); |
| |
| rc = smb1351_get_usb_chg_current(chip, &icl_ma); |
| if (rc) { |
| pr_err("Get ICL result failed, rc = %d\n", rc); |
| return; |
| } |
| |
| if ((pre_icl_ma > 0) && (pre_icl_ma == icl_ma)) { |
| pr_debug("AICL stable at %d\n", icl_ma); |
| } else { |
| pr_debug("AICL changed %d -> %d, double check\n", |
| pre_icl_ma, icl_ma); |
| goto recheck; |
| } |
| mutex_lock(&chip->parallel.lock); |
| if (smb1351_attempt_enable_parallel_slave(chip, &total_icl_ma)) { |
| pr_debug("Enable parallel slave, total_icl = %d!\n", |
| total_icl_ma); |
| smb1351_parallel_charger_enable_slave(chip, total_icl_ma); |
| } else { |
| if (chip->parallel.slave_icl_ma != 0) { |
| pr_debug("Disable parallel slave!\n"); |
| smb1351_parallel_charger_disable_slave(chip); |
| } |
| } |
| mutex_unlock(&chip->parallel.lock); |
| smb1351_relax(&chip->smb1351_ws, PARALLEL_CHARGE); |
| |
| return; |
| recheck: |
| pr_debug("reschedule!\n"); |
| schedule_delayed_work(&chip->parallel.parallel_work, 0); |
| } |
| |
| static void smb1351_parallel_check_start(struct smb1351_charger *chip) |
| { |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| |
| if (!parallel_psy || !chip->parallel.slave_detected) |
| return; |
| |
| cancel_delayed_work_sync(&chip->parallel.parallel_work); |
| smb1351_stay_awake(&chip->smb1351_ws, PARALLEL_CHARGE); |
| schedule_delayed_work(&chip->parallel.parallel_work, 0); |
| pr_debug("parallel work scheduled\n"); |
| } |
| |
| static void smb1351_init_fg_work(struct work_struct *work) |
| { |
| int rc; |
| struct smb1351_charger *chip = container_of(work, |
| struct smb1351_charger, init_fg_work.work); |
| |
| rc = smb1351_set_bms_property(chip, |
| POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE, 0); |
| if (rc) { |
| pr_debug("Disable isense patch failed, rc=%d\n", rc); |
| goto recheck; |
| } |
| |
| if (chip->fg_notify_jeita) { |
| rc = smb1351_set_bms_property(chip, |
| POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION, 1); |
| if (rc) { |
| pr_debug("Enable FG JEITA IRQ failed, rc=%d\n", rc); |
| goto recheck; |
| } |
| } |
| return; |
| recheck: |
| /* start a delay worker to do it again if failed */ |
| schedule_delayed_work(&chip->init_fg_work, |
| msecs_to_jiffies(INIT_FG_RETRY_MS)); |
| } |
| |
| static void smb1351_handle_jeita_from_fg(struct smb1351_charger *chip, |
| int health) |
| { |
| int rc; |
| bool disable; |
| |
| if (health <= POWER_SUPPLY_HEALTH_UNKNOWN |
| || health > POWER_SUPPLY_HEALTH_COOL) { |
| pr_err("health state not valid: %d\n", health); |
| return; |
| } |
| |
| switch (health) { |
| case POWER_SUPPLY_HEALTH_GOOD: |
| chip->batt_hot = false; |
| chip->batt_cold = false; |
| chip->batt_warm = false; |
| chip->batt_cool = false; |
| break; |
| case POWER_SUPPLY_HEALTH_OVERHEAT: |
| chip->batt_hot = true; |
| chip->batt_cold = false; |
| chip->batt_warm = false; |
| chip->batt_cool = false; |
| break; |
| case POWER_SUPPLY_HEALTH_COLD: |
| chip->batt_hot = false; |
| chip->batt_cold = true; |
| chip->batt_cool = false; |
| chip->batt_warm = false; |
| break; |
| case POWER_SUPPLY_HEALTH_WARM: |
| chip->batt_hot = false; |
| chip->batt_cold = false; |
| chip->batt_warm = true; |
| chip->batt_cool = false; |
| break; |
| case POWER_SUPPLY_HEALTH_COOL: |
| chip->batt_hot = false; |
| chip->batt_cold = false; |
| chip->batt_warm = false; |
| chip->batt_cool = true; |
| break; |
| default: |
| pr_debug("health: %d, not a JEITA state\n", health); |
| break; |
| } |
| pr_debug("hot: %d, cold: %d, warm = %d, cool = %d\n", |
| chip->batt_hot, chip->batt_cold, |
| chip->batt_warm, chip->batt_cool); |
| |
| disable = (chip->batt_hot || chip->batt_cold) ? true : false; |
| rc = smb1351_battchg_disable(chip, THERMAL, disable); |
| if (rc) { |
| pr_err("%s charging for THERMAL failed, rc=%d\n", |
| disable ? "Disable" : "Enable", rc); |
| return; |
| } |
| |
| rc = smb1351_chg_set_appropriate_battery_current(chip); |
| if (rc) { |
| pr_err("Set battery current failed\n"); |
| return; |
| } |
| |
| rc = smb1351_chg_set_appropriate_vfloat(chip); |
| if (rc) { |
| pr_err("Set float voltage failed\n"); |
| return; |
| } |
| |
| smb1351_parallel_check_start(chip); |
| |
| if (chip->use_external_fg) { |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_HEALTH, |
| smb1351_get_prop_batt_health(chip)); |
| } |
| |
| pr_debug("end!\n"); |
| } |
| |
| 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 = container_of(psy, |
| struct smb1351_charger, batt_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; |
| case POWER_SUPPLY_PROP_HEALTH: |
| if (chip->fg_notify_jeita) |
| smb1351_handle_jeita_from_fg(chip, val->intval); |
| 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 = container_of(psy, |
| struct smb1351_charger, batt_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_PRESENT, |
| POWER_SUPPLY_PROP_CURRENT_MAX, |
| POWER_SUPPLY_PROP_VOLTAGE_MAX, |
| POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, |
| }; |
| |
| static int smb1351_parallel_set_chg_present(struct smb1351_charger *chip, |
| int present) |
| { |
| int rc; |
| u8 reg, mask = 0; |
| |
| pr_debug("set slave present = %d\n", present); |
| if (present == chip->parallel_charger_present) { |
| pr_debug("present %d -> %d, skipping\n", |
| chip->parallel_charger_present, present); |
| return 0; |
| } |
| |
| chip->parallel_charger_present = present; |
| |
| if (present) { |
| /* Check if SMB1351 is present */ |
| rc = smb1351_read_reg(chip, CHG_REVISION_REG, ®); |
| if (rc) { |
| pr_debug("Failed to detect smb1351-parallel-charger, may be absent\n"); |
| return -ENODEV; |
| } |
| |
| 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; |
| } |
| } |
| |
| /* 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; |
| } |
| |
| /* control USB suspend via command bits */ |
| rc = smb1351_masked_write(chip, VARIOUS_FUNC_REG, |
| SUSPEND_MODE_CTRL_BIT, |
| SUSPEND_MODE_CTRL_BY_I2C); |
| if (rc) { |
| pr_err("Couldn't set USB suspend 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; |
| } |
| |
| /* set fast charging current limit */ |
| chip->target_fastchg_current_max_ma = SMB1351_CHG_FAST_MIN_MA; |
| 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; |
| } |
| /* |
| * Suspend USB input (CURRENT reason) to avoid slave start |
| * charging before any SW logic been run. USB input will be |
| * un-suspended (CURRENT reason) after allotted ICL being set. |
| */ |
| chip->usb_psy_ma = SUSPEND_CURRENT_MA; |
| rc = smb1351_usb_suspend(chip, CURRENT, true); |
| if (rc) { |
| pr_err("Suspend USB (CURRENT) failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = smb1351_usb_suspend(chip, PARALLEL, false); |
| if (rc) { |
| pr_err("Suspend USB (PARALLEL) failed, rc=%d\n", rc); |
| return rc; |
| } |
| } else { |
| rc = smb1351_usb_suspend(chip, PARALLEL, true); |
| if (rc) { |
| pr_debug("Suspend USB (PARALLEL) failed, rc=%d\n", rc); |
| return rc; |
| } |
| } |
| |
| 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 int smb1351_parallel_set_property(struct power_supply *psy, |
| enum power_supply_property prop, |
| const union power_supply_propval *val) |
| { |
| int rc = 0; |
| struct smb1351_charger *chip = container_of(psy, |
| struct smb1351_charger, parallel_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_present) { |
| rc = smb1351_usb_suspend(chip, USER, !val->intval); |
| if (rc) |
| pr_err("%suspend charger failed\n", |
| val->intval ? "Un-s" : "S"); |
| } |
| break; |
| case POWER_SUPPLY_PROP_PRESENT: |
| mutex_lock(&chip->parallel_config_lock); |
| rc = smb1351_parallel_set_chg_present(chip, val->intval); |
| if (rc) |
| pr_err("Set charger %spresent failed\n", |
| val->intval ? "" : "un-"); |
| mutex_unlock(&chip->parallel_config_lock); |
| break; |
| case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: |
| if (chip->parallel_charger_present) { |
| chip->target_fastchg_current_max_ma = |
| val->intval / 1000; |
| rc = smb1351_fastchg_current_set(chip, |
| chip->target_fastchg_current_max_ma); |
| } |
| break; |
| case POWER_SUPPLY_PROP_CURRENT_MAX: |
| if (chip->parallel_charger_present) { |
| rc = smb1351_set_usb_chg_current(chip, |
| val->intval / 1000); |
| } |
| break; |
| case POWER_SUPPLY_PROP_VOLTAGE_MAX: |
| if (chip->parallel_charger_present && |
| (chip->vfloat_mv != val->intval)) { |
| rc = smb1351_float_voltage_set(chip, val->intval); |
| if (!rc) |
| chip->vfloat_mv = val->intval; |
| } else { |
| chip->vfloat_mv = 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 = container_of(psy, |
| struct smb1351_charger, parallel_psy); |
| int rc, icl; |
| |
| switch (prop) { |
| case POWER_SUPPLY_PROP_CHARGING_ENABLED: |
| val->intval = !chip->usb_suspended_status; |
| break; |
| case POWER_SUPPLY_PROP_CURRENT_MAX: |
| if (chip->parallel_charger_present) { |
| rc = smb1351_get_usb_chg_current(chip, &icl); |
| if (rc) { |
| pr_err("Get ICL result failed, rc=%d\n", rc); |
| return rc; |
| } |
| if (icl > 0) |
| val->intval = icl * 1000; |
| else |
| val->intval = 0; |
| } else { |
| val->intval = 0; |
| } |
| break; |
| case POWER_SUPPLY_PROP_VOLTAGE_MAX: |
| val->intval = chip->vfloat_mv; |
| break; |
| case POWER_SUPPLY_PROP_PRESENT: |
| val->intval = chip->parallel_charger_present; |
| break; |
| case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: |
| if (chip->parallel_charger_present) |
| val->intval = chip->fastchg_current_max_ma * 1000; |
| else |
| val->intval = 0; |
| break; |
| case POWER_SUPPLY_PROP_STATUS: |
| if (chip->parallel_charger_present) |
| 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_present) |
| val->intval = |
| smb1351_is_input_current_limited(chip) ? 1 : 0; |
| else |
| val->intval = 0; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static void smb1351_chg_ctrl_in_jeita(struct smb1351_charger *chip) |
| { |
| union power_supply_propval ret = {0, }; |
| int rc; |
| |
| /* enable the iterm to prevent the reverse boost */ |
| if (chip->iterm_disabled) { |
| if (chip->batt_cool || chip->batt_warm) { |
| rc = smb1351_iterm_set(chip, 100); |
| pr_debug("set the iterm due to JEITA\n"); |
| } else { |
| rc = smb1351_masked_write(chip, CHG_CTRL_REG, |
| ITERM_EN_BIT, ITERM_DISABLE); |
| pr_debug("disable the iterm when exits warm/cool\n"); |
| } |
| if (rc) { |
| pr_err("Couldn't set iterm rc = %d\n", rc); |
| return; |
| } |
| } |
| /* |
| * When JEITA back to normal, the charging maybe disabled due to |
| * the current termination. So re-enable the charging if the soc |
| * is less than 100 in the normal mode. A 200ms delay is requred |
| * before the disabe and enable operation. |
| */ |
| if (chip->bms_psy) { |
| rc = chip->bms_psy->get_property(chip->bms_psy, |
| POWER_SUPPLY_PROP_CAPACITY, &ret); |
| if (rc) { |
| pr_err("Couldn't read the bms capacity rc = %d\n", |
| rc); |
| return; |
| } |
| if (!chip->batt_cool && !chip->batt_warm |
| && !chip->batt_cold && !chip->batt_hot |
| && ret.intval < 100) { |
| rc = smb1351_battchg_disable(chip, THERMAL, true); |
| if (rc) { |
| pr_err("Couldn't disable charging rc = %d\n", |
| rc); |
| return; |
| } |
| /* delay for resetting the charging */ |
| msleep(200); |
| rc = smb1351_battchg_disable(chip, THERMAL, false); |
| if (rc) { |
| pr_err("Couldn't enable charging rc = %d\n", |
| rc); |
| return; |
| } else { |
| chip->batt_full = false; |
| pr_debug("re-enable charging, batt_full = %d\n", |
| chip->batt_full); |
| } |
| pr_debug("batt psy changed\n"); |
| power_supply_changed(&chip->batt_psy); |
| } |
| } |
| } |
| |
| #define HYSTERESIS_DECIDEGC 20 |
| static void smb1351_chg_adc_notification(enum qpnp_tm_state state, void *ctx) |
| { |
| struct smb1351_charger *chip = ctx; |
| struct battery_status *cur; |
| int temp; |
| |
| if (state >= ADC_TM_STATE_NUM) { |
| pr_err("invalid state parameter %d\n", state); |
| return; |
| } |
| |
| temp = smb1351_get_prop_batt_temp(chip); |
| |
| pr_debug("temp = %d state = %s\n", temp, |
| state == ADC_TM_WARM_STATE ? "hot" : "cold"); |
| |
| /* reset the adc status request */ |
| chip->adc_param.state_request = ADC_TM_WARM_COOL_THR_ENABLE; |
| |
| /* temp from low to high */ |
| if (state == ADC_TM_WARM_STATE) { |
| /* WARM -> HOT */ |
| if (temp >= chip->batt_hot_decidegc) { |
| cur = &batt_s[BATT_HOT]; |
| chip->adc_param.low_temp = |
| chip->batt_hot_decidegc - HYSTERESIS_DECIDEGC; |
| chip->adc_param.state_request = ADC_TM_COOL_THR_ENABLE; |
| /* NORMAL -> WARM */ |
| } else if (temp >= chip->batt_warm_decidegc && |
| chip->jeita_supported) { |
| cur = &batt_s[BATT_WARM]; |
| chip->adc_param.low_temp = |
| chip->batt_warm_decidegc - HYSTERESIS_DECIDEGC; |
| chip->adc_param.high_temp = chip->batt_hot_decidegc; |
| /* COOL -> NORMAL */ |
| } else if (temp >= chip->batt_cool_decidegc && |
| chip->jeita_supported) { |
| cur = &batt_s[BATT_NORMAL]; |
| chip->adc_param.low_temp = |
| chip->batt_cool_decidegc - HYSTERESIS_DECIDEGC; |
| chip->adc_param.high_temp = chip->batt_warm_decidegc; |
| /* COLD -> COOL */ |
| } else if (temp >= chip->batt_cold_decidegc) { |
| cur = &batt_s[BATT_COOL]; |
| chip->adc_param.low_temp = |
| chip->batt_cold_decidegc - HYSTERESIS_DECIDEGC; |
| if (chip->jeita_supported) |
| chip->adc_param.high_temp = |
| chip->batt_cool_decidegc; |
| else |
| chip->adc_param.high_temp = |
| chip->batt_hot_decidegc; |
| /* MISSING -> COLD */ |
| } else if (temp >= chip->batt_missing_decidegc) { |
| cur = &batt_s[BATT_COLD]; |
| chip->adc_param.high_temp = chip->batt_cold_decidegc; |
| chip->adc_param.low_temp = chip->batt_missing_decidegc |
| - HYSTERESIS_DECIDEGC; |
| |
| } |
| /* temp from high to low */ |
| } else { |
| /* COLD -> MISSING */ |
| if (temp <= chip->batt_missing_decidegc) { |
| cur = &batt_s[BATT_MISSING]; |
| chip->adc_param.high_temp = chip->batt_missing_decidegc |
| + HYSTERESIS_DECIDEGC; |
| chip->adc_param.state_request = ADC_TM_WARM_THR_ENABLE; |
| /* COOL -> COLD */ |
| } else if (temp <= chip->batt_cold_decidegc) { |
| cur = &batt_s[BATT_COLD]; |
| chip->adc_param.high_temp = |
| chip->batt_cold_decidegc + HYSTERESIS_DECIDEGC; |
| /* add low_temp to enable batt present check */ |
| chip->adc_param.low_temp = chip->batt_missing_decidegc; |
| /* NORMAL -> COOL */ |
| } else if (temp <= chip->batt_cool_decidegc && |
| chip->jeita_supported) { |
| cur = &batt_s[BATT_COOL]; |
| chip->adc_param.high_temp = |
| chip->batt_cool_decidegc + HYSTERESIS_DECIDEGC; |
| chip->adc_param.low_temp = chip->batt_cold_decidegc; |
| /* WARM -> NORMAL */ |
| } else if (temp <= chip->batt_warm_decidegc && |
| chip->jeita_supported) { |
| cur = &batt_s[BATT_NORMAL]; |
| chip->adc_param.high_temp = |
| chip->batt_warm_decidegc + HYSTERESIS_DECIDEGC; |
| chip->adc_param.low_temp = chip->batt_cool_decidegc; |
| /* HOT -> WARM */ |
| } else if (temp <= chip->batt_hot_decidegc) { |
| cur = &batt_s[BATT_WARM]; |
| if (chip->jeita_supported) |
| chip->adc_param.low_temp = |
| chip->batt_warm_decidegc; |
| else |
| chip->adc_param.low_temp = |
| chip->batt_cold_decidegc; |
| chip->adc_param.high_temp = |
| chip->batt_hot_decidegc + HYSTERESIS_DECIDEGC; |
| } |
| } |
| |
| if (cur->batt_present) |
| chip->battery_missing = false; |
| else |
| chip->battery_missing = true; |
| |
| if (cur->batt_hot ^ chip->batt_hot || |
| cur->batt_cold ^ chip->batt_cold) { |
| chip->batt_hot = cur->batt_hot; |
| chip->batt_cold = cur->batt_cold; |
| /* stop charging explicitly since we use PMIC thermal pin*/ |
| if (cur->batt_hot || cur->batt_cold || |
| chip->battery_missing) |
| smb1351_battchg_disable(chip, THERMAL, 1); |
| else |
| smb1351_battchg_disable(chip, THERMAL, 0); |
| } |
| |
| if ((chip->batt_warm ^ cur->batt_warm || |
| chip->batt_cool ^ cur->batt_cool) |
| && chip->jeita_supported) { |
| chip->batt_warm = cur->batt_warm; |
| chip->batt_cool = cur->batt_cool; |
| smb1351_chg_set_appropriate_battery_current(chip); |
| smb1351_chg_set_appropriate_vfloat(chip); |
| smb1351_chg_ctrl_in_jeita(chip); |
| } |
| |
| pr_debug("hot %d, cold %d, warm %d, cool %d, soft jeita supported %d, missing %d, low = %d deciDegC, high = %d deciDegC\n", |
| chip->batt_hot, chip->batt_cold, chip->batt_warm, |
| chip->batt_cool, chip->jeita_supported, |
| chip->battery_missing, chip->adc_param.low_temp, |
| chip->adc_param.high_temp); |
| if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->adc_param)) |
| pr_err("request ADC error\n"); |
| } |
| |
| static void smb1351_rerun_apsd_work(struct work_struct *work) |
| { |
| int rc; |
| struct smb1351_charger *chip = container_of(work, |
| struct smb1351_charger, |
| rerun_apsd_work.work); |
| |
| pr_debug("Rerunning APSD\n"); |
| |
| chip->apsd_rerun = true; |
| 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, rc = %d\n", rc); |
| |
| smb1351_relax(&chip->smb1351_ws, RERUN_APSD); |
| } |
| |
| static int smb1351_notify_usb_supply_type(struct smb1351_charger *chip, |
| enum power_supply_type type) |
| { |
| int rc; |
| union power_supply_propval pval = {0, }; |
| |
| pval.intval = type; |
| rc = chip->usb_psy->set_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_REAL_TYPE, &pval); |
| if (rc < 0) { |
| if (rc == -EINVAL) { |
| rc = chip->usb_psy->set_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_TYPE, &pval); |
| if (!rc) |
| return 0; |
| } |
| pr_err("notify charger type to usb_psy failed, rc=%d\n", rc); |
| } |
| |
| return rc; |
| } |
| |
| static void smb1351_hvdcp_det_work(struct work_struct *work) |
| { |
| int rc; |
| u8 reg; |
| bool is_hvdcp; |
| 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); |
| |
| is_hvdcp = !!(reg & (HVDCP_SEL_5V | HVDCP_SEL_9V | HVDCP_SEL_12V)); |
| if (is_hvdcp) { |
| pr_debug("HVDCP detected; notifying USB PSY\n"); |
| smb1351_notify_usb_supply_type(chip, |
| POWER_SUPPLY_TYPE_USB_HVDCP); |
| } |
| end: |
| smb1351_relax(&chip->smb1351_ws, HVDCP_DETECT); |
| } |
| |
| #define HVDCP_NOTIFY_MS 3500 |
| static int smb1351_apsd_complete_handler(struct smb1351_charger *chip, |
| u8 status) |
| { |
| int rc = 0; |
| enum power_supply_type type = POWER_SUPPLY_TYPE_UNKNOWN; |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| |
| /* |
| * 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; |
| } |
| |
| if (status) { |
| chip->chg_present = true; |
| rc = smb1351_get_usb_supply_type(chip, &type); |
| if (rc) { |
| pr_err("Get USB power supply type failed, rc=%d\n", rc); |
| return rc; |
| } |
| pr_debug("APSD complete. USB type detected=%d chg_present=%d\n", |
| type, chip->chg_present); |
| /* |
| * If defined force hvdcp 2p0 property, |
| * we force to hvdcp 2p0 in the APSD handler. |
| */ |
| if (type == POWER_SUPPLY_TYPE_USB_DCP) { |
| 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 { |
| pr_debug("schedule hvdcp detection worker\n"); |
| smb1351_stay_awake(&chip->smb1351_ws, |
| HVDCP_DETECT); |
| schedule_delayed_work(&chip->hvdcp_det_work, |
| msecs_to_jiffies(HVDCP_NOTIFY_MS)); |
| } |
| } |
| smb1351_notify_usb_supply_type(chip, type); |
| /* |
| * SMB is now done sampling the D+/D- lines, |
| * indicate USB driver |
| */ |
| pr_debug("updating usb_psy present=%d\n", chip->chg_present); |
| power_supply_set_present(chip->usb_psy, chip->chg_present); |
| chip->apsd_rerun = false; |
| /* set parallel slave PRESENT */ |
| if (parallel_psy) { |
| pr_debug("set parallel charger present!\n"); |
| rc = power_supply_set_present(parallel_psy, true); |
| if (rc) |
| pr_debug("parallel slave absent!\n"); |
| else |
| chip->parallel.slave_detected = true; |
| } |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * 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_MS 100 |
| #define SECOND_CHECK_DELAY_MS 1000 |
| #define RERUN_CHG_REMOVE_DELAY_MS 1500 |
| 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); |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| |
| 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"); |
| chip->chg_present = false; |
| /* clear parallel slave PRESENT */ |
| if (parallel_psy && chip->parallel.slave_detected) { |
| pr_debug("set parallel charger un-present!\n"); |
| power_supply_set_present(parallel_psy, false); |
| } |
| smb1351_notify_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN); |
| power_supply_set_present(chip->usb_psy, |
| chip->chg_present); |
| pr_debug("Set usb psy dp=r dm=r\n"); |
| power_supply_set_dp_dm(chip->usb_psy, |
| POWER_SUPPLY_DP_DM_DPR_DMR); |
| chip->apsd_rerun = false; |
| } 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; |
| smb1351_relax(&chip->smb1351_ws, REMOVAL_DETECT); |
| return; |
| |
| reschedule: |
| pr_debug("reschedule after 1s\n"); |
| schedule_delayed_work(&chip->chg_remove_work, |
| msecs_to_jiffies(SECOND_CHECK_DELAY_MS)); |
| } |
| |
| static int smb1351_usbin_uv_handler(struct smb1351_charger *chip, u8 status) |
| { |
| int chg_remove_delay = FIRST_CHECK_DELAY_MS; |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| int rc; |
| |
| pr_debug("enter, status = %d\n", !!status); |
| /* use this to detect USB insertion only if !apsd */ |
| if (chip->disable_apsd) { |
| /* |
| * If APSD is disabled, src det interrupt won't trigger. |
| * Hence use usbin_uv for removal and insertion notification |
| */ |
| if (status == 0) { |
| chip->chg_present = true; |
| pr_debug("updating usb_psy present=%d\n", |
| chip->chg_present); |
| smb1351_notify_usb_supply_type(chip, |
| POWER_SUPPLY_TYPE_USB); |
| power_supply_set_present(chip->usb_psy, |
| chip->chg_present); |
| /* set parallel slave PRESENT */ |
| if (parallel_psy) { |
| rc = power_supply_set_present( |
| parallel_psy, true); |
| if (rc) |
| pr_debug("parallel slave absent!\n"); |
| else |
| chip->parallel.slave_detected = true; |
| } |
| } else { |
| chip->chg_present = false; |
| /* clear parallel slave PRESENT */ |
| if (parallel_psy && chip->parallel.slave_detected) |
| power_supply_set_present(parallel_psy, false); |
| smb1351_notify_usb_supply_type(chip, |
| POWER_SUPPLY_TYPE_UNKNOWN); |
| power_supply_set_present(chip->usb_psy, |
| chip->chg_present); |
| pr_debug("updating usb_psy present=%d\n", |
| chip->chg_present); |
| } |
| return 0; |
| } |
| |
| if (status) { |
| cancel_delayed_work_sync(&chip->hvdcp_det_work); |
| if (chip->wa_flags & CHG_ITERM_CHECK_SOC) |
| cancel_delayed_work_sync(&chip->iterm_check_soc_work); |
| smb1351_relax(&chip->smb1351_ws, REMOVAL_DETECT); |
| pr_debug("schedule charger remove worker\n"); |
| if (chip->apsd_rerun) |
| chg_remove_delay = RERUN_CHG_REMOVE_DELAY_MS; |
| smb1351_stay_awake(&chip->smb1351_ws, REMOVAL_DETECT); |
| schedule_delayed_work(&chip->chg_remove_work, |
| msecs_to_jiffies(chg_remove_delay)); |
| } else { |
| cancel_delayed_work_sync(&chip->chg_remove_work); |
| smb1351_relax(&chip->smb1351_ws, REMOVAL_DETECT); |
| pr_debug("Set usb psy dp=f dm=f\n"); |
| power_supply_set_dp_dm(chip->usb_psy, |
| POWER_SUPPLY_DP_DM_DPF_DMF); |
| } |
| |
| pr_debug("chip->chg_present = %d\n", chip->chg_present); |
| |
| return 0; |
| } |
| |
| static int smb1351_usbin_ov_handler(struct smb1351_charger *chip, u8 status) |
| { |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| int health; |
| int rc; |
| u8 reg; |
| |
| pr_debug("enter, status = %d\n", !!status); |
| 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->chg_present = false; |
| chip->usbin_ov = true; |
| /* clear parallel slave PRESENT */ |
| if (parallel_psy && chip->parallel.slave_detected) |
| power_supply_set_present(parallel_psy, false); |
| smb1351_notify_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN); |
| power_supply_set_present(chip->usb_psy, chip->chg_present); |
| } 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); |
| } |
| |
| if (chip->usb_psy) { |
| health = status ? POWER_SUPPLY_HEALTH_OVERVOLTAGE |
| : POWER_SUPPLY_HEALTH_GOOD; |
| power_supply_set_health_state(chip->usb_psy, health); |
| pr_debug("chip ov status is %d\n", health); |
| } |
| pr_debug("chip->chg_present = %d\n", chip->chg_present); |
| |
| return 0; |
| } |
| |
| static int smb1351_usbid_handler(struct smb1351_charger *chip, u8 status) |
| { |
| bool usbid_gnd; |
| int rc; |
| |
| if (!!status) { |
| rc = smb1351_get_usbid_status(chip, &usbid_gnd); |
| if (rc) { |
| pr_err("Get usbid failed!"); |
| return 0; |
| } |
| pr_debug("usbid grounded: %d\n", usbid_gnd); |
| if (chip->usb_psy) |
| power_supply_set_usb_otg(chip->usb_psy, usbid_gnd); |
| } |
| if (chip->use_external_fg) |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| |
| return 0; |
| } |
| |
| static int smb1351_fast_chg_handler(struct smb1351_charger *chip, u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| chip->check_parallel = true; |
| if (chip->use_external_fg) |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| return 0; |
| } |
| |
| static int smb1351_taper_handler(struct smb1351_charger *chip, u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| if (!!status) |
| smb1351_parallel_charger_taper_attempt(chip); |
| |
| if (chip->use_external_fg) |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| return 0; |
| } |
| |
| static int smb1351_recharge_handler(struct smb1351_charger *chip, u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| if (!chip->bms_controlled_charging) |
| chip->batt_full = !status; |
| chip->check_parallel = true; |
| |
| if (chip->use_external_fg) |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| return 0; |
| } |
| |
| #define FULL_SOC 100 |
| #define ITERM_CHECK_MS 10000 |
| #define ITERM_RECHECK_COUNT 3 |
| static void smb1351_iterm_check_soc_work(struct work_struct *work) |
| { |
| int rc, current_ua, soc; |
| struct smb1351_charger *chip = container_of(work, |
| struct smb1351_charger, iterm_check_soc_work.work); |
| static int retry; |
| |
| if (!chip->chg_present) { |
| pr_debug("Plugged out, ignore!\n"); |
| goto done; |
| } |
| |
| rc = smb1351_set_bms_property(chip, |
| POWER_SUPPLY_PROP_UPDATE_NOW, 1); |
| if (rc) { |
| pr_err("Update bms status failed, rc=%d\n", rc); |
| goto recheck; |
| } |
| |
| rc = smb1351_get_bms_property(chip, |
| POWER_SUPPLY_PROP_CURRENT_NOW, ¤t_ua); |
| if (rc) { |
| pr_err("Get bms current now failed, rc=%d\n", rc); |
| goto recheck; |
| } |
| |
| pr_debug("current: %d ua!", current_ua); |
| if (current_ua >= 0) { |
| pr_debug("Terminated? large current consumption?"); |
| retry++; |
| goto recheck; |
| } |
| |
| if (retry > ITERM_RECHECK_COUNT) { |
| pr_debug("retry timed out!\n"); |
| goto done; |
| } |
| |
| if (-1 * current_ua / 1000 < chip->iterm_ma) { |
| pr_debug("Charging current is less than iterm, terminate!"); |
| chip->batt_full = true; |
| goto done; |
| } |
| |
| rc = smb1351_get_bms_property(chip, POWER_SUPPLY_PROP_CAPACITY, &soc); |
| if (rc) { |
| pr_err("Get bms capacity failed, rc=%d\n", rc); |
| goto recheck; |
| } |
| |
| if (soc == FULL_SOC) { |
| pr_debug("FG has terminated, terminate charger!\n"); |
| chip->batt_full = true; |
| goto done; |
| } |
| done: |
| if (chip->batt_full) { |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| if (chip->wa_flags & CHG_SOC_BASED_RESUME) { |
| rc = smb1351_battchg_disable(chip, SOC, true); |
| if (rc) { |
| pr_err("Disable charger for SOC failed, rc=%d\n", |
| rc); |
| goto recheck; |
| } |
| } |
| } |
| |
| rc = smb1351_masked_write(chip, CHG_CTRL_REG, |
| ITERM_EN_BIT, ITERM_ENABLE); |
| if (rc) { |
| pr_err("Re-enable iterm failed, rc=%d\n", rc); |
| goto recheck; |
| } |
| |
| chip->iterm_disabled = false; |
| smb1351_relax(&chip->smb1351_ws, ITERM_CHECK_SOC); |
| |
| return; |
| |
| recheck: |
| schedule_delayed_work(&chip->iterm_check_soc_work, |
| msecs_to_jiffies(ITERM_CHECK_MS)); |
| } |
| |
| static int smb1351_chg_term_handler(struct smb1351_charger *chip, u8 status) |
| { |
| int rc, soc; |
| |
| pr_debug("enter, status = 0x%02x\n", status); |
| if (!chip->bms_controlled_charging && |
| !(chip->wa_flags & CHG_ITERM_CHECK_SOC)) |
| chip->batt_full = !!status; |
| |
| /* Only check parallel when falling */ |
| if (!status) |
| chip->check_parallel = true; |
| |
| if (!(chip->wa_flags & CHG_ITERM_CHECK_SOC)) |
| return 0; |
| |
| if (!!status && !chip->batt_full) { |
| rc = smb1351_get_bms_property(chip, |
| POWER_SUPPLY_PROP_CAPACITY, &soc); |
| if (rc) { |
| pr_err("Get capacity failed, rc=%d\n", rc); |
| return rc; |
| } |
| if (soc != FULL_SOC) { |
| pr_debug("FG haven't reported FULL, soc=%d\n", soc); |
| /* |
| * disable termination, restart charging, |
| * hold a wakelock and start a timer for |
| * checking SOC until the SOC reach to |
| * FULL_SOC. |
| */ |
| rc = smb1351_masked_write(chip, CHG_CTRL_REG, |
| ITERM_EN_BIT, ITERM_DISABLE); |
| if (rc) { |
| pr_err("disable iterm failed, rc=%d", rc); |
| return rc; |
| } |
| chip->iterm_disabled = true; |
| /* toggle charging disble bit for re-charge */ |
| rc = smb1351_battchg_disable(chip, ITERM, true); |
| if (rc) { |
| pr_err("Disable charger for ITERM failed, rc=%d\n", |
| rc); |
| return rc; |
| } |
| rc = smb1351_battchg_disable(chip, ITERM, false); |
| if (rc) { |
| pr_err("Enable charger for ITERM failed, rc=%d\n", |
| rc); |
| return rc; |
| } |
| smb1351_stay_awake(&chip->smb1351_ws, ITERM_CHECK_SOC); |
| schedule_delayed_work(&chip->iterm_check_soc_work, 0); |
| } else { |
| pr_debug("Terminated, FG report full\n"); |
| chip->batt_full = true; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int smb1351_safety_timeout_handler(struct smb1351_charger *chip, |
| u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| chip->check_parallel = true; |
| if (chip->use_external_fg) |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| return 0; |
| } |
| |
| static int smb1351_chg_error_handler(struct smb1351_charger *chip, |
| u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| chip->check_parallel = true; |
| if (chip->use_external_fg) |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| return 0; |
| } |
| |
| static int smb1351_aicl_done_handler(struct smb1351_charger *chip, u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| chip->check_parallel = true; |
| return 0; |
| } |
| |
| static int smb1351_hot_hard_handler(struct smb1351_charger *chip, u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| chip->batt_hot = !!status; |
| chip->check_parallel = true; |
| return 0; |
| } |
| static int smb1351_cold_hard_handler(struct smb1351_charger *chip, u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| chip->batt_cold = !!status; |
| chip->check_parallel = true; |
| return 0; |
| } |
| static int smb1351_hot_soft_handler(struct smb1351_charger *chip, u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| chip->batt_warm = !!status; |
| chip->check_parallel = true; |
| return 0; |
| } |
| static int smb1351_cold_soft_handler(struct smb1351_charger *chip, u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| chip->batt_cool = !!status; |
| chip->check_parallel = true; |
| return 0; |
| } |
| |
| static int smb1351_battery_missing_handler(struct smb1351_charger *chip, |
| u8 status) |
| { |
| pr_debug("enter, status = 0x%02x\n", status); |
| if (status) |
| chip->battery_missing = true; |
| else |
| chip->battery_missing = false; |
| |
| if (chip->use_external_fg) |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| 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", |
| .smb_irq = smb1351_taper_handler, |
| }, |
| { .name = "recharge", |
| .smb_irq = smb1351_recharge_handler, |
| }, |
| { .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", |
| .smb_irq = smb1351_chg_error_handler, |
| }, |
| { .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_usbid_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; |
| } |
| if (chip->check_parallel) { |
| smb1351_parallel_check_start(chip); |
| chip->check_parallel = false; |
| } |
| 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 DEFAULT_SDP_MA 500 |
| #define DEFAULT_CDP_MA 1500 |
| #define DEFAULT_DCP_MA 1800 |
| #define DEFAULT_HVDCP_MA 1800 |
| #define DEFAULT_HVDCP3_MA 3000 |
| static int smb1351_update_usb_supply_icl(struct smb1351_charger *chip) |
| { |
| int rc, type, icl; |
| union power_supply_propval pval = {0, }; |
| |
| rc = chip->usb_psy->get_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_REAL_TYPE, &pval); |
| if (rc == -EINVAL) { |
| rc = chip->usb_psy->get_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_TYPE, &pval); |
| if (rc < 0) { |
| pr_err("Get USB supply TYPE failed, rc=%d\n", rc); |
| return rc; |
| } |
| } else if (rc < 0) { |
| pr_err("Get USB supply REAL_TYPE failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| type = pval.intval; |
| chip->usb_psy_type = type; |
| rc = chip->usb_psy->get_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_CURRENT_MAX, &pval); |
| if (!rc) |
| icl = pval.intval / 1000; |
| else |
| return rc; |
| /* |
| * Only update ICL when DCP/HVDCP/HVDCP3 being detected |
| * For other types such as SDP/CDP, keep the ICL set in |
| * USB PHY to avoid violating USB spec. |
| */ |
| switch (type) { |
| case POWER_SUPPLY_TYPE_USB_DCP: |
| icl = DEFAULT_DCP_MA; |
| break; |
| case POWER_SUPPLY_TYPE_USB_HVDCP: |
| icl = DEFAULT_HVDCP_MA; |
| break; |
| case POWER_SUPPLY_TYPE_USB_HVDCP_3: |
| icl = DEFAULT_HVDCP3_MA; |
| break; |
| default: |
| break; |
| } |
| chip->usb_psy_ma = icl; |
| pr_debug("type = %d, icl = %d\n", type, icl); |
| |
| return rc; |
| } |
| |
| #define ESR_PULSE_DELTA_MA 200 |
| static int smb1351_force_esr_pulse_en(struct smb1351_charger *chip, bool en) |
| { |
| int rc = 0, current_in_esr = 0, fg_current_now = 0; |
| int main_fcc_in_esr = 0, slave_fcc_in_esr = 0; |
| int actual_slave_fcc_in_esr = 0; |
| union power_supply_propval pval = {0,}; |
| struct power_supply *parallel_psy = smb1351_get_parallel_slave(chip); |
| |
| if (chip->in_esr_pulse == en) { |
| pr_err("ESR pulse is already %s\n", |
| en ? "enabled" : "disabled"); |
| return 0; |
| } |
| |
| if (en) { |
| rc = smb1351_get_bms_property(chip, |
| POWER_SUPPLY_PROP_CURRENT_NOW, |
| &fg_current_now); |
| if (rc) { |
| pr_debug("Get battery current from BMS(fg) failed, rc=%d\n", |
| rc); |
| return rc; |
| } |
| pr_debug("ibatt from FG reading: %duA\n", fg_current_now); |
| fg_current_now = abs(fg_current_now); |
| fg_current_now /= 1000; |
| chip->main_fcc_ma_before_esr = chip->fastchg_current_max_ma; |
| current_in_esr = max(chip->iterm_ma + ESR_PULSE_DELTA_MA, |
| fg_current_now - ESR_PULSE_DELTA_MA); |
| pr_debug("Force battery current to %d in ESR pulse\n", |
| current_in_esr); |
| } |
| |
| mutex_lock(&chip->parallel.lock); |
| /* |
| * If parallel charging is not enabled, set the current_in_esr |
| * to main charger to achieve the ESR pulse |
| */ |
| if (!parallel_psy || !chip->parallel.slave_detected |
| || chip->parallel.slave_icl_ma == 0) { |
| rc = smb1351_fastchg_current_set(chip, |
| en ? current_in_esr : |
| chip->main_fcc_ma_before_esr); |
| if (!rc) |
| chip->in_esr_pulse = en; |
| else |
| pr_err("set FCC for ESR failed, rc=%d\n", rc); |
| |
| goto unlock; |
| } |
| |
| /* |
| * If parallel charging is enabled, split current_in_esr and |
| * set it to main and slave charger accordingly |
| */ |
| if (en) { |
| rc = parallel_psy->get_property(parallel_psy, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); |
| if (rc) { |
| pr_err("get slave fcc failed, rc=%d\n", rc); |
| goto unlock; |
| } |
| |
| chip->slave_fcc_ma_before_esr = pval.intval / 1000; |
| slave_fcc_in_esr = current_in_esr * |
| (100 - chip->parallel.main_chg_fcc_percent) / 100; |
| pval.intval = slave_fcc_in_esr * 1000; |
| rc = parallel_psy->set_property(parallel_psy, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); |
| if (rc) { |
| pr_err("set slave fcc for ESR failed, rc=%d\n", rc); |
| goto unlock; |
| } |
| |
| rc = parallel_psy->get_property(parallel_psy, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); |
| if (rc) { |
| pr_err("get slave fcc for ESR failed, rc=%d\n", rc); |
| goto unlock; |
| } |
| |
| actual_slave_fcc_in_esr = pval.intval / 1000; |
| pr_debug("slave_fcc_in_esr = %dmA, actual_slave_fcc_in_esr = %dmA\n", |
| slave_fcc_in_esr, actual_slave_fcc_in_esr); |
| main_fcc_in_esr = current_in_esr - actual_slave_fcc_in_esr; |
| rc = smb1351_fastchg_current_set(chip, main_fcc_in_esr); |
| if (rc) { |
| pr_err("Set main fcc for ESR failed, rc=%d\n", |
| rc); |
| goto unlock; |
| } |
| pr_debug("main_fcc_in_esr = %dmA\n", main_fcc_in_esr); |
| } else { |
| pval.intval = chip->slave_fcc_ma_before_esr * 1000; |
| rc = parallel_psy->set_property(parallel_psy, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, |
| &pval); |
| if (rc) { |
| pr_err("restore fcc for slave failed, rc=%d\n", rc); |
| goto unlock; |
| } |
| rc = smb1351_fastchg_current_set(chip, |
| chip->main_fcc_ma_before_esr); |
| if (rc) { |
| pr_err("restore fcc for main failed, rc=%d\n", rc); |
| goto unlock; |
| } |
| pr_debug("restore, main_fcc = %dmA, slave_fcc = %dmA\n", |
| chip->main_fcc_ma_before_esr, |
| chip->slave_fcc_ma_before_esr); |
| } |
| |
| chip->in_esr_pulse = en; |
| unlock: |
| mutex_unlock(&chip->parallel.lock); |
| return rc; |
| } |
| |
| #define ESR_PULSE_TIME_MS 1500 |
| static void smb1351_force_esr_for_fg(struct smb1351_charger *chip) |
| { |
| int rc = 0, esr_count = 0; |
| |
| if (!chip->chg_present) { |
| pr_debug("No force ESR pulse when no charging\n"); |
| return; |
| } |
| |
| rc = smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1); |
| if (rc) { |
| pr_debug("Update BMS(fg) status failed, rc=%d\n", rc); |
| return; |
| } |
| rc = smb1351_get_bms_property(chip, POWER_SUPPLY_PROP_ESR_COUNT, |
| &esr_count); |
| if (rc) { |
| pr_debug("Get ESR count from BMS(fg) failed, rc=%d\n", rc); |
| return; |
| } |
| if (esr_count != 0) { |
| pr_debug("ESR count is not zero: %d, skipping\n", |
| esr_count); |
| return; |
| } |
| smb1351_stay_awake(&chip->smb1351_ws, FORCE_ESR_PULSE); |
| rc = smb1351_force_esr_pulse_en(chip, true); |
| if (rc) { |
| pr_err("Force ESR pulse enable failed, rc=%d\n", rc); |
| smb1351_relax(&chip->smb1351_ws, FORCE_ESR_PULSE); |
| return; |
| } |
| |
| msleep(ESR_PULSE_TIME_MS); |
| rc = smb1351_force_esr_pulse_en(chip, false); |
| if (rc) |
| pr_err("Force ESR pulse disable failed, rc=%d\n", rc); |
| |
| smb1351_relax(&chip->smb1351_ws, FORCE_ESR_PULSE); |
| } |
| |
| static void battery_soc_changed(struct smb1351_charger *chip) |
| { |
| int rc, soc; |
| |
| soc = smb1351_get_prop_batt_capacity(chip); |
| if (chip->pre_soc == soc) |
| return; |
| |
| if (chip->wa_flags & CHG_FORCE_ESR_WA) |
| smb1351_force_esr_for_fg(chip); |
| |
| if ((chip->wa_flags & CHG_SOC_BASED_RESUME) |
| && chip->batt_full && (soc <= chip->resume_soc)) { |
| rc = smb1351_battchg_disable(chip, SOC, false); |
| if (rc) { |
| pr_err("Enable charge for SOC failed, rc=%d\n", |
| rc); |
| return; |
| } |
| chip->batt_full = false; |
| smb1351_set_bms_property(chip, POWER_SUPPLY_PROP_STATUS, |
| smb1351_get_prop_batt_status(chip)); |
| } |
| |
| chip->pre_soc = soc; |
| } |
| |
| static void smb1351_external_power_changed(struct power_supply *psy) |
| { |
| struct smb1351_charger *chip = container_of(psy, |
| struct smb1351_charger, batt_psy); |
| struct power_supply *parallel_psy = |
| smb1351_get_parallel_slave(chip); |
| union power_supply_propval prop = {0,}; |
| int rc, online = 0, slave_present = 0; |
| |
| battery_soc_changed(chip); |
| |
| if (parallel_psy) { |
| parallel_psy->get_property(parallel_psy, |
| POWER_SUPPLY_PROP_PRESENT, &prop); |
| slave_present = prop.intval; |
| /* Don't update main charger ICL if slave is enabled */ |
| if (chip->parallel.slave_detected && slave_present |
| && chip->parallel.slave_icl_ma != 0) { |
| pr_debug("Ignore ICL setting as parallel-slave is enabled\n"); |
| return; |
| } |
| } |
| |
| rc = chip->usb_psy->get_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_ONLINE, &prop); |
| if (rc) { |
| pr_err("Couldn't read USB online property, rc=%d\n", rc); |
| return; |
| } |
| |
| online = prop.intval; |
| rc = smb1351_update_usb_supply_icl(chip); |
| if (rc) { |
| pr_err("Update USB supply ICL failed!\n"); |
| return; |
| } |
| |
| pr_debug("online = %d, current_limit = %d\n", online, chip->usb_psy_ma); |
| |
| smb1351_enable_volatile_writes(chip); |
| smb1351_set_usb_chg_current(chip, chip->usb_psy_ma); |
| |
| pr_debug("updating batt psy\n"); |
| } |
| |
| #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 |
| |
| #define MAIN_CHG_DEFAULT_FCC_PERCENT 50 |
| #define MAIN_CHG_DEFAULT_ICL_PERCENT 50 |
| static int smb1351_parse_parallel_charger_dt(struct smb1351_charger *chip) |
| { |
| int rc; |
| struct device_node *node = chip->dev->of_node; |
| |
| rc = of_property_read_u32(node, "qcom,parallel-usb-min-current-ma", |
| &chip->parallel.min_current_thr_ma); |
| if (rc && rc != -EINVAL) |
| goto bail_out; |
| |
| rc = of_property_read_u32(node, "qcom,parallel-usb-9v-min-current-ma", |
| &chip->parallel.min_9v_current_thr_ma); |
| if (rc && rc != -EINVAL) |
| goto bail_out; |
| |
| rc = of_property_read_u32(node, "qcom,parallel-usb-12v-min-current-ma", |
| &chip->parallel.min_12v_current_thr_ma); |
| if (rc && rc != -EINVAL) |
| goto bail_out; |
| |
| rc = of_property_read_u32(node, "qcom,parallel-allowed-lowering-ma", |
| &chip->parallel.allowed_lowering_ma); |
| if (rc && rc != -EINVAL) |
| goto bail_out; |
| |
| rc = of_property_read_u32(node, "qcom,parallel-main-chg-fcc-percent", |
| &chip->parallel.main_chg_fcc_percent); |
| if (rc < 0) |
| chip->parallel.main_chg_fcc_percent = |
| MAIN_CHG_DEFAULT_FCC_PERCENT; |
| |
| rc = of_property_read_u32(node, "qcom,parallel-main-chg-icl-percent", |
| &chip->parallel.main_chg_icl_percent); |
| if (rc < 0) |
| chip->parallel.main_chg_icl_percent = |
| MAIN_CHG_DEFAULT_ICL_PERCENT; |
| |
| chip->parallel.avail = true; |
| pr_debug("parallel main charger settings: min_curr = %d, min_9v_curr = %d, min_12v_curr = %d, allowed_lowering = %d, fcc_percent = %d, icl_percent = %d\n", |
| chip->parallel.min_current_thr_ma, |
| chip->parallel.min_9v_current_thr_ma, |
| chip->parallel.min_12v_current_thr_ma, |
| chip->parallel.allowed_lowering_ma, |
| chip->parallel.main_chg_fcc_percent, |
| chip->parallel.main_chg_icl_percent); |
| |
| return 0; |
| |
| bail_out: |
| chip->parallel.min_current_thr_ma = -EINVAL; |
| chip->parallel.min_9v_current_thr_ma = -EINVAL; |
| chip->parallel.min_12v_current_thr_ma = -EINVAL; |
| chip->parallel.allowed_lowering_ma = -EINVAL; |
| pr_debug("parallel charger not support!\n"); |
| |
| return 0; |
| } |
| |
| 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->chg_autonomous_mode = of_property_read_bool(node, |
| "qcom,chg-autonomous-mode"); |
| |
| chip->disable_apsd = of_property_read_bool(node, "qcom,disable-apsd"); |
| |
| chip->using_pmic_therm = of_property_read_bool(node, |
| "qcom,using-pmic-therm"); |
| 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; |
| chip->resume_soc = -EINVAL; |
| if (of_property_read_bool(node, "qcom,use-external-fg")) { |
| chip->use_external_fg = true; |
| chip->wa_flags |= CHG_FORCE_ESR_WA | CHG_FG_NOTIFY_JEITA |
| | CHG_ITERM_CHECK_SOC; |
| rc = of_property_read_u32(node, "qcom,resume-soc", |
| &chip->resume_soc); |
| if (!rc && chip->resume_soc > 0) |
| chip->wa_flags |= CHG_SOC_BASED_RESUME; |
| } |
| 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"); |
| |
| rc = of_property_read_u32(node, "qcom,switch-freq", |
| &chip->switch_freq); |
| if (rc < 0) |
| chip->switch_freq = -EINVAL; |
| /* thermal and jeita support */ |
| rc = of_property_read_u32(node, "qcom,batt-cold-decidegc", |
| &chip->batt_cold_decidegc); |
| if (rc < 0) |
| chip->batt_cold_decidegc = -EINVAL; |
| |
| rc = of_property_read_u32(node, "qcom,batt-hot-decidegc", |
| &chip->batt_hot_decidegc); |
| if (rc < 0) |
| chip->batt_hot_decidegc = -EINVAL; |
| |
| rc = of_property_read_u32(node, "qcom,batt-warm-decidegc", |
| &chip->batt_warm_decidegc); |
| if (rc < 0) |
| chip->batt_warm_decidegc = -EINVAL; |
| |
| rc = of_property_read_u32(node, "qcom,batt-cool-decidegc", |
| &chip->batt_cool_decidegc); |
| if (rc < 0) |
| chip->batt_cool_decidegc = -EINVAL; |
| |
| if ((chip->batt_warm_decidegc != -EINVAL |
| && chip->batt_cool_decidegc != -EINVAL) |
| || (chip->wa_flags & CHG_FG_NOTIFY_JEITA)) { |
| rc = of_property_read_u32(node, "qcom,batt-cool-mv", |
| &chip->batt_cool_mv); |
| |
| rc |= of_property_read_u32(node, "qcom,batt-warm-mv", |
| &chip->batt_warm_mv); |
| |
| rc |= of_property_read_u32(node, "qcom,batt-cool-ma", |
| &chip->batt_cool_ma); |
| |
| rc |= of_property_read_u32(node, "qcom,batt-warm-ma", |
| &chip->batt_warm_ma); |
| if (rc) |
| chip->jeita_supported = false; |
| else if (chip->wa_flags & CHG_FG_NOTIFY_JEITA) |
| chip->fg_notify_jeita = true; |
| else |
| chip->jeita_supported = true; |
| } |
| |
| pr_debug("fg_notify_jeita = %d, jeita_supported = %d\n", |
| chip->fg_notify_jeita, chip->jeita_supported); |
| |
| rc = of_property_read_u32(node, "qcom,batt-missing-decidegc", |
| &chip->batt_missing_decidegc); |
| |
| chip->pinctrl_state_name = of_get_property(node, "pinctrl-names", NULL); |
| |
| smb1351_parse_parallel_charger_dt(chip); |
| |
| return 0; |
| } |
| |
| #define RERUN_APSD_DELAY_MS 1000 |
| 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_stay_awake(&chip->smb1351_ws, RERUN_APSD); |
| schedule_delayed_work(&chip->rerun_apsd_work, |
| msecs_to_jiffies(RERUN_APSD_DELAY_MS)); |
| } |
| |
| 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_slave(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; |
| char str[16]; |
| |
| snprintf(str, ARRAY_SIZE(str), "smb1351_%s", |
| chip->is_slave ? "slave" : "main"); |
| chip->debug_root = debugfs_create_dir(str, NULL); |
| if (!chip->debug_root) { |
| pr_err("Couldn't create debug dir\n"); |
| } else { |
| ent = debugfs_create_file("config_registers", S_IFREG | S_IRUGO, |
| 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 | S_IRUGO, |
| 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 | S_IRUGO, |
| 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 | S_IWUSR | S_IRUGO, |
| 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 | S_IWUSR | S_IRUGO, |
| chip->debug_root, chip, |
| &poke_poke_debug_ops); |
| if (!ent) |
| pr_err("Couldn't create data debug file\n"); |
| if (!chip->is_slave) { |
| ent = debugfs_create_file("force_irq", |
| S_IFREG | S_IWUSR | S_IRUGO, |
| 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 | S_IRUGO, |
| 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 *usb_psy; |
| u8 reg = 0; |
| |
| usb_psy = power_supply_get_by_name("usb"); |
| if (!usb_psy) { |
| pr_debug("USB psy not found; deferring probe\n"); |
| return -EPROBE_DEFER; |
| } |
| |
| chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
| if (!chip) { |
| pr_err("Couldn't allocate memory\n"); |
| return -ENOMEM; |
| } |
| |
| chip->client = client; |
| chip->dev = &client->dev; |
| chip->usb_psy = usb_psy; |
| chip->fake_battery_soc = -EINVAL; |
| INIT_DELAYED_WORK(&chip->chg_remove_work, smb1351_chg_remove_work); |
| INIT_DELAYED_WORK(&chip->hvdcp_det_work, smb1351_hvdcp_det_work); |
| INIT_DELAYED_WORK(&chip->rerun_apsd_work, smb1351_rerun_apsd_work); |
| smb1351_wakeup_src_init(chip); |
| |
| /* 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"); |
| rc = -ENODEV; |
| goto trash_ws; |
| } |
| 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); |
| goto trash_ws; |
| } |
| |
| /* using vadc and adc_tm for implementing pmic therm */ |
| if (chip->using_pmic_therm) { |
| chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg"); |
| if (IS_ERR(chip->vadc_dev)) { |
| rc = PTR_ERR(chip->vadc_dev); |
| if (rc != -EPROBE_DEFER) |
| pr_err("vadc property missing\n"); |
| goto trash_ws; |
| } |
| chip->adc_tm_dev = qpnp_get_adc_tm(chip->dev, "chg"); |
| if (IS_ERR(chip->adc_tm_dev)) { |
| rc = PTR_ERR(chip->adc_tm_dev); |
| if (rc != -EPROBE_DEFER) |
| pr_err("adc_tm property missing\n"); |
| goto trash_ws; |
| } |
| } |
| |
| i2c_set_clientdata(client, chip); |
| |
| chip->batt_psy.name = "battery"; |
| chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY; |
| chip->batt_psy.get_property = smb1351_battery_get_property; |
| chip->batt_psy.set_property = smb1351_battery_set_property; |
| chip->batt_psy.property_is_writeable = |
| smb1351_batt_property_is_writeable; |
| chip->batt_psy.properties = smb1351_battery_properties; |
| chip->batt_psy.num_properties = |
| ARRAY_SIZE(smb1351_battery_properties); |
| chip->batt_psy.external_power_changed = |
| smb1351_external_power_changed; |
| chip->batt_psy.supplied_to = pm_batt_supplied_to; |
| chip->batt_psy.num_supplicants = ARRAY_SIZE(pm_batt_supplied_to); |
| |
| chip->resume_completed = true; |
| mutex_init(&chip->irq_complete); |
| mutex_init(&chip->fcc_lock); |
| mutex_init(&chip->parallel.lock); |
| |
| rc = power_supply_register(chip->dev, &chip->batt_psy); |
| if (rc) { |
| pr_err("Couldn't register batt psy rc=%d\n", rc); |
| goto destroy_mutex; |
| } |
| INIT_DELAYED_WORK(&chip->parallel.parallel_work, smb1351_parallel_work); |
| INIT_DELAYED_WORK(&chip->init_fg_work, smb1351_init_fg_work); |
| INIT_DELAYED_WORK(&chip->iterm_check_soc_work, |
| smb1351_iterm_check_soc_work); |
| |
| 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 intialize 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_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); |
| } |
| |
| if (chip->using_pmic_therm) { |
| if (!chip->jeita_supported) { |
| /* add hot/cold temperature monitor */ |
| chip->adc_param.low_temp = chip->batt_cold_decidegc; |
| chip->adc_param.high_temp = chip->batt_hot_decidegc; |
| } else { |
| chip->adc_param.low_temp = chip->batt_cool_decidegc; |
| chip->adc_param.high_temp = chip->batt_warm_decidegc; |
| } |
| chip->adc_param.timer_interval = ADC_MEAS1_INTERVAL_500MS; |
| chip->adc_param.state_request = ADC_TM_WARM_COOL_THR_ENABLE; |
| chip->adc_param.btm_ctx = chip; |
| chip->adc_param.threshold_notification = |
| smb1351_chg_adc_notification; |
| chip->adc_param.channel = LR_MUX1_BATT_THERM; |
| |
| rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev, |
| &chip->adc_param); |
| if (rc) { |
| pr_err("requesting ADC error %d\n", rc); |
| goto fail_smb1351_hw_init; |
| } |
| } |
| |
| 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: |
| power_supply_unregister(&chip->batt_psy); |
| destroy_mutex: |
| mutex_destroy(&chip->irq_complete); |
| mutex_destroy(&chip->fcc_lock); |
| mutex_destroy(&chip->parallel.lock); |
| trash_ws: |
| wakeup_source_trash(&chip->smb1351_ws.source); |
| |
| return rc; |
| } |
| |
| static int smb1351_parallel_slave_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; |
| |
| chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
| if (!chip) { |
| pr_err("Couldn't allocate memory\n"); |
| return -ENOMEM; |
| } |
| |
| chip->client = client; |
| chip->dev = &client->dev; |
| chip->is_slave = 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; |
| |
| i2c_set_clientdata(client, chip); |
| mutex_init(&chip->parallel_config_lock); |
| |
| chip->parallel_psy.name = "parallel"; |
| chip->parallel_psy.type = POWER_SUPPLY_TYPE_PARALLEL; |
| chip->parallel_psy.get_property = smb1351_parallel_get_property; |
| chip->parallel_psy.set_property = smb1351_parallel_set_property; |
| chip->parallel_psy.properties = smb1351_parallel_properties; |
| chip->parallel_psy.property_is_writeable |
| = smb1351_parallel_is_writeable; |
| chip->parallel_psy.num_properties |
| = ARRAY_SIZE(smb1351_parallel_properties); |
| |
| mutex_init(&chip->fcc_lock); |
| mutex_init(&chip->irq_complete); |
| smb1351_wakeup_src_init(chip); |
| |
| rc = power_supply_register(chip->dev, &chip->parallel_psy); |
| if (rc) { |
| pr_err("Couldn't register parallel psy rc=%d\n", rc); |
| goto fail_register_psy; |
| } |
| |
| chip->resume_completed = true; |
| create_debugfs_entries(chip); |
| |
| pr_info("smb1351 parallel successfully probed.\n"); |
| |
| return 0; |
| |
| fail_register_psy: |
| wakeup_source_trash(&chip->smb1351_ws.source); |
| mutex_destroy(&chip->irq_complete); |
| mutex_destroy(&chip->fcc_lock); |
| mutex_destroy(&chip->parallel_config_lock); |
| return rc; |
| } |
| |
| static int smb1351_charger_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| if (is_parallel_slave(client)) |
| return smb1351_parallel_slave_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); |
| power_supply_unregister(&chip->batt_psy); |
| |
| wakeup_source_trash(&chip->smb1351_ws.source); |
| mutex_destroy(&chip->irq_complete); |
| mutex_destroy(&chip->fcc_lock); |
| if (is_parallel_slave(client)) { |
| mutex_destroy(&chip->parallel_config_lock); |
| mutex_destroy(&chip->parallel.lock); |
| } |
| 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->is_slave) |
| 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->is_slave) |
| 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->is_slave) |
| 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 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"); |