diff options
Diffstat (limited to 'driver/charger/sm5803.c')
-rw-r--r-- | driver/charger/sm5803.c | 1933 |
1 files changed, 0 insertions, 1933 deletions
diff --git a/driver/charger/sm5803.c b/driver/charger/sm5803.c deleted file mode 100644 index 460dcf4093..0000000000 --- a/driver/charger/sm5803.c +++ /dev/null @@ -1,1933 +0,0 @@ -/* Copyright 2020 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * Silicon Mitus SM5803 Buck-Boost Charger - */ -#include "atomic.h" -#include "battery.h" -#include "battery_smart.h" -#include "charge_state_v2.h" -#include "charger.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "i2c.h" -#include "sm5803.h" -#include "system.h" -#include "stdbool.h" -#include "throttle_ap.h" -#include "timer.h" -#include "usb_charge.h" -#include "usb_pd.h" -#include "usbc_ocp.h" -#include "util.h" -#include "watchdog.h" - -#ifndef CONFIG_CHARGER_NARROW_VDC -#error "SM5803 is a NVDC charger, please enable CONFIG_CHARGER_NARROW_VDC." -#endif - -/* Console output macros */ -#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args) -#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) - -#define UNKNOWN_DEV_ID -1 -static int dev_id = UNKNOWN_DEV_ID; - -static const struct charger_info sm5803_charger_info = { - .name = CHARGER_NAME, - .voltage_max = CHARGE_V_MAX, - .voltage_min = CHARGE_V_MIN, - .voltage_step = CHARGE_V_STEP, - .current_max = CHARGE_I_MAX, - .current_min = CHARGE_I_MIN, - .current_step = CHARGE_I_STEP, - .input_current_max = INPUT_I_MAX, - .input_current_min = INPUT_I_MIN, - .input_current_step = INPUT_I_STEP, -}; - -static uint32_t irq_pending; /* Bitmask of chips with interrupts pending */ - -static struct mutex flow1_access_lock[CHARGER_NUM]; -static struct mutex flow2_access_lock[CHARGER_NUM]; - -static int charger_vbus[CHARGER_NUM]; - -/* Tracker for charging failures per port */ -struct { - int count; - timestamp_t time; -} failure_tracker[CHARGER_NUM] = {}; - -/* Port to restart charging on */ -static int active_restart_port = CHARGE_PORT_NONE; - -/* - * If powered from the sub board port, we need to attempt to enable the BFET - * before proceeding with charging. - */ -static int attempt_bfet_enable; - -/* - * Note if auto fast charge for the primary port is disabled due to a - * disconnected battery, at re-enable auto fast charge later when the battery - * has connected. - */ -static bool fast_charge_disabled; - - -#define CHARGING_FAILURE_MAX_COUNT 5 -#define CHARGING_FAILURE_INTERVAL MINUTE - -static int sm5803_is_sourcing_otg_power(int chgnum, int port); -static enum ec_error_list sm5803_get_dev_id(int chgnum, int *id); -static enum ec_error_list sm5803_set_current(int chgnum, int current); - -static inline enum ec_error_list chg_read8(int chgnum, int offset, int *value) -{ - return i2c_read8(chg_chips[chgnum].i2c_port, - chg_chips[chgnum].i2c_addr_flags, - offset, value); -} - -static inline enum ec_error_list chg_write8(int chgnum, int offset, int value) -{ - return i2c_write8(chg_chips[chgnum].i2c_port, - chg_chips[chgnum].i2c_addr_flags, - offset, value); -} - -static inline enum ec_error_list meas_read8(int chgnum, int offset, int *value) -{ - return i2c_read8(chg_chips[chgnum].i2c_port, - SM5803_ADDR_MEAS_FLAGS, - offset, value); -} - -static inline enum ec_error_list meas_write8(int chgnum, int offset, int value) -{ - return i2c_write8(chg_chips[chgnum].i2c_port, - SM5803_ADDR_MEAS_FLAGS, - offset, value); -} - -static inline enum ec_error_list main_read8(int chgnum, int offset, int *value) -{ - return i2c_read8(chg_chips[chgnum].i2c_port, - SM5803_ADDR_MAIN_FLAGS, - offset, value); -} - -static inline enum ec_error_list main_write8(int chgnum, int offset, int value) -{ - return i2c_write8(chg_chips[chgnum].i2c_port, - SM5803_ADDR_MAIN_FLAGS, - offset, value); -} - -static inline enum ec_error_list test_write8(int chgnum, int offset, int value) -{ - return i2c_write8(chg_chips[chgnum].i2c_port, - SM5803_ADDR_TEST_FLAGS, - offset, value); -} - -static inline enum ec_error_list test_update8(int chgnum, const int offset, - const uint8_t mask, - const enum mask_update_action action) -{ - return i2c_update8(chg_chips[chgnum].i2c_port, - SM5803_ADDR_TEST_FLAGS, offset, mask, action); -} - -static enum ec_error_list sm5803_flow1_update(int chgnum, const uint8_t mask, - const enum mask_update_action action) -{ - int rv; - - /* Safety checks done, onto the actual register update */ - mutex_lock(&flow1_access_lock[chgnum]); - - rv = i2c_update8(chg_chips[chgnum].i2c_port, - chg_chips[chgnum].i2c_addr_flags, - SM5803_REG_FLOW1, - mask, action); - - mutex_unlock(&flow1_access_lock[chgnum]); - - return rv; -} - -static enum ec_error_list sm5803_flow2_update(int chgnum, const uint8_t mask, - const enum mask_update_action action) -{ - int rv; - - mutex_lock(&flow2_access_lock[chgnum]); - - rv = i2c_update8(chg_chips[chgnum].i2c_port, - chg_chips[chgnum].i2c_addr_flags, - SM5803_REG_FLOW2, - mask, action); - - mutex_unlock(&flow2_access_lock[chgnum]); - - return rv; -} - -static bool is_platform_id_2s(uint32_t platform_id) -{ - return platform_id >= 0x06 && platform_id <= 0x0D; -} - -static bool is_platform_id_3s(uint32_t platform_id) -{ - return platform_id >= 0x0E && platform_id <= 0x16; -} - -int sm5803_is_vbus_present(int chgnum) -{ - return charger_vbus[chgnum]; -} - -enum ec_error_list sm5803_configure_gpio0(int chgnum, - enum sm5803_gpio0_modes mode, int od) -{ - enum ec_error_list rv; - int reg; - - rv = main_read8(chgnum, SM5803_REG_GPIO0_CTRL, ®); - if (rv) - return rv; - - reg &= ~SM5803_GPIO0_MODE_MASK; - reg |= mode << 1; - - if (od) - reg |= SM5803_GPIO0_OPEN_DRAIN_EN; - else - reg &= ~SM5803_GPIO0_OPEN_DRAIN_EN; - - rv = main_write8(chgnum, SM5803_REG_GPIO0_CTRL, reg); - return rv; -} - -enum ec_error_list sm5803_set_gpio0_level(int chgnum, int level) -{ - enum ec_error_list rv; - int reg; - - rv = main_read8(chgnum, SM5803_REG_GPIO0_CTRL, ®); - if (rv) - return rv; - - if (level) - reg |= SM5803_GPIO0_VAL; - else - reg &= ~SM5803_GPIO0_VAL; - - rv = main_write8(chgnum, SM5803_REG_GPIO0_CTRL, reg); - return rv; -} - -enum ec_error_list sm5803_configure_chg_det_od(int chgnum, int enable) -{ - enum ec_error_list rv; - int reg; - - rv = main_read8(chgnum, SM5803_REG_GPIO0_CTRL, ®); - if (rv) - return rv; - - if (enable) - reg |= SM5803_CHG_DET_OPEN_DRAIN_EN; - else - reg &= ~SM5803_CHG_DET_OPEN_DRAIN_EN; - - rv = main_write8(chgnum, SM5803_REG_GPIO0_CTRL, reg); - return rv; -} - -enum ec_error_list sm5803_get_chg_det(int chgnum, int *chg_det) -{ - enum ec_error_list rv; - int reg; - - rv = main_read8(chgnum, SM5803_REG_STATUS1, ®); - if (rv) - return rv; - - *chg_det = (reg & SM5803_STATUS1_CHG_DET) != 0; - - return EC_SUCCESS; -} - -enum ec_error_list sm5803_set_vbus_disch(int chgnum, int enable) -{ - enum ec_error_list rv; - int reg; - - rv = main_read8(chgnum, SM5803_REG_PORTS_CTRL, ®); - if (rv) - return rv; - - if (enable) - reg |= SM5803_PORTS_VBUS_DISCH; - else - reg &= ~SM5803_PORTS_VBUS_DISCH; - - rv = main_write8(chgnum, SM5803_REG_PORTS_CTRL, reg); - return rv; -} - -enum ec_error_list sm5803_vbus_sink_enable(int chgnum, int enable) -{ - enum ec_error_list rv; - int regval; - - rv = sm5803_get_dev_id(chgnum, &dev_id); - if (rv) - return rv; - - if (enable) { - if (chgnum == CHARGER_PRIMARY) { - /* Magic for new silicon */ - if (dev_id >= 3) { - rv |= main_write8(chgnum, 0x1F, 0x1); - rv |= test_write8(chgnum, 0x44, 0x2); - rv |= main_write8(chgnum, 0x1F, 0); - } - /* - * Only enable auto fast charge when a battery is - * connected and out of cutoff. - */ - if (battery_get_disconnect_state() == - BATTERY_NOT_DISCONNECTED) { - rv = sm5803_flow2_update(chgnum, - SM5803_FLOW2_AUTO_ENABLED, - MASK_SET); - fast_charge_disabled = false; - } else { - rv = sm5803_flow2_update(chgnum, - SM5803_FLOW2_AUTO_TRKL_EN | - SM5803_FLOW2_AUTO_PRECHG_EN, - MASK_SET); - fast_charge_disabled = true; - } - } else { - if (dev_id >= 3) { - /* Touch of magic on the primary charger */ - rv |= main_write8(CHARGER_PRIMARY, 0x1F, 0x1); - rv |= test_write8(CHARGER_PRIMARY, 0x44, 0x20); - rv |= main_write8(CHARGER_PRIMARY, 0x1F, 0x0); - - /* - * Disable linear, pre-charge, and linear fast - * charge for primary charger. - */ - rv = chg_read8(CHARGER_PRIMARY, - SM5803_REG_FLOW3, ®val); - regval &= ~(BIT(6) | BIT(5) | BIT(4)); - - rv |= chg_write8(CHARGER_PRIMARY, - SM5803_REG_FLOW3, regval); - } - } - - /* Last but not least, enable sinking */ - rv |= sm5803_flow1_update(chgnum, CHARGER_MODE_SINK, MASK_SET); - } else { - if (chgnum == CHARGER_PRIMARY) - rv |= sm5803_flow2_update(chgnum, - SM5803_FLOW2_AUTO_ENABLED, - MASK_CLR); - - if (chgnum == CHARGER_SECONDARY) { - rv |= sm5803_flow1_update(CHARGER_PRIMARY, - SM5803_FLOW1_LINEAR_CHARGE_EN, - MASK_CLR); - - rv = chg_read8(CHARGER_PRIMARY, SM5803_REG_FLOW3, - ®val); - regval &= ~(BIT(6) | BIT(5) | BIT(4)); - rv |= chg_write8(CHARGER_PRIMARY, SM5803_REG_FLOW3, - regval); - } - - - /* Disable sink mode, unless currently sourcing out */ - if (!sm5803_is_sourcing_otg_power(chgnum, chgnum)) - rv |= sm5803_flow1_update(chgnum, CHARGER_MODE_SINK, - MASK_CLR); - } - - return rv; - -} - -/* - * Track and store whether we've initialized the charger chips already on this - * boot. This should prevent us from re-running inits after sysjumps. - */ -static bool chip_inited[CHARGER_NUM]; -#define SM5803_SYSJUMP_TAG 0x534D /* SM */ -#define SM5803_HOOK_VERSION 1 - -static void init_status_preserve(void) -{ - system_add_jump_tag(SM5803_SYSJUMP_TAG, SM5803_HOOK_VERSION, - sizeof(chip_inited), &chip_inited); -} -DECLARE_HOOK(HOOK_SYSJUMP, init_status_preserve, HOOK_PRIO_DEFAULT); - -static void init_status_retrieve(void) -{ - const uint8_t *tag_contents; - int version, size; - - tag_contents = system_get_jump_tag(SM5803_SYSJUMP_TAG, - &version, &size); - if (tag_contents && (version == SM5803_HOOK_VERSION) && - (size == sizeof(chip_inited))) - /* Valid init status found, restore before charger chip init */ - memcpy(&chip_inited, tag_contents, size); -} -DECLARE_HOOK(HOOK_INIT, init_status_retrieve, HOOK_PRIO_FIRST); - -static void sm5803_init(int chgnum) -{ - enum ec_error_list rv; - int reg; - int vbus_mv; - const struct battery_info *batt_info; - int pre_term; - int cells; - - /* - * If a charger is not currently present, disable switching per OCPC - * requirements - */ - rv = charger_get_vbus_voltage(chgnum, &vbus_mv); - if (rv == EC_SUCCESS) { - if (vbus_mv < 4000) { - /* - * No charger connected, disable CHG_EN - * (note other bits default to 0) - */ - rv = chg_write8(chgnum, SM5803_REG_FLOW1, 0); - } else if (!sm5803_is_sourcing_otg_power(chgnum, chgnum)) { - charger_vbus[chgnum] = 1; - } - } else { - CPRINTS("%s %d: Failed to read VBUS voltage during init", - CHARGER_NAME, chgnum); - return; - } - - /* - * A previous boot already ran inits, safe to return now that we've - * checked i2c communication to the chip and cached Vbus presence - */ - if (chip_inited[chgnum]) { - CPRINTS("%s %d: Already initialized", CHARGER_NAME, chgnum); - return; - } - - rv |= charger_device_id(®); - if (reg == 0x02) { - /* --- Special register init --- - * For early silicon (ID 2) with 3S batteries - */ - rv |= main_write8(chgnum, 0x20, 0x08); - rv |= main_write8(chgnum, 0x30, 0xC0); - rv |= main_write8(chgnum, 0x80, 0x01); - - rv |= meas_write8(chgnum, 0x08, 0xC2); - - rv |= chg_write8(chgnum, 0x1D, 0x40); - rv |= chg_write8(chgnum, 0x1F, 0x09); - - rv |= chg_write8(chgnum, 0x22, 0xB3); - rv |= chg_write8(chgnum, 0x23, 0x81); - rv |= chg_write8(chgnum, 0x28, 0xB7); - - rv |= chg_write8(chgnum, 0x4A, 0x82); - rv |= chg_write8(chgnum, 0x4B, 0xA3); - rv |= chg_write8(chgnum, 0x4C, 0xA8); - rv |= chg_write8(chgnum, 0x4D, 0xCA); - rv |= chg_write8(chgnum, 0x4E, 0x07); - rv |= chg_write8(chgnum, 0x4F, 0xFF); - - rv |= chg_write8(chgnum, 0x50, 0x98); - rv |= chg_write8(chgnum, 0x51, 0x00); - rv |= chg_write8(chgnum, 0x52, 0x77); - rv |= chg_write8(chgnum, 0x53, 0xD2); - rv |= chg_write8(chgnum, 0x54, 0x02); - rv |= chg_write8(chgnum, 0x55, 0xD1); - rv |= chg_write8(chgnum, 0x56, 0x7F); - rv |= chg_write8(chgnum, 0x57, 0x02); - rv |= chg_write8(chgnum, 0x58, 0xD1); - rv |= chg_write8(chgnum, 0x59, 0x7F); - rv |= chg_write8(chgnum, 0x5A, 0x13); - rv |= chg_write8(chgnum, 0x5B, 0x50); - rv |= chg_write8(chgnum, 0x5C, 0x5B); - rv |= chg_write8(chgnum, 0x5D, 0xB0); - rv |= chg_write8(chgnum, 0x5E, 0x3C); - rv |= chg_write8(chgnum, 0x5F, 0x3C); - - rv |= chg_write8(chgnum, 0x60, 0x44); - rv |= chg_write8(chgnum, 0x61, 0x20); - rv |= chg_write8(chgnum, 0x65, 0x35); - rv |= chg_write8(chgnum, 0x66, 0x29); - rv |= chg_write8(chgnum, 0x67, 0x64); - rv |= chg_write8(chgnum, 0x68, 0x88); - rv |= chg_write8(chgnum, 0x69, 0xC7); - - /* Inits to access page 0x37 and enable trickle charging */ - rv |= main_write8(chgnum, 0x1F, 0x01); - rv |= test_update8(chgnum, 0x8E, BIT(5), MASK_SET); - rv |= main_write8(chgnum, 0x1F, 0x00); - } else if (reg == 0x03) { - uint32_t platform_id; - - rv = main_read8(chgnum, SM5803_REG_PLATFORM, &platform_id); - if (rv) { - CPRINTS("%s %d: Failed to read platform during init", - CHARGER_NAME, chgnum); - return; - } - platform_id &= SM5803_PLATFORM_ID; - - if (is_platform_id_3s(platform_id)) { - /* 3S Battery inits */ - /* set 13.3V VBAT_SNSP TH GPADC THRESHOLD*/ - rv |= meas_write8(chgnum, 0x26, - SM5803_VBAT_SNSP_MAXTH_3S_LEVEL); - /* OV_VBAT HW second level (14.1V) */ - rv |= chg_write8(chgnum, 0x21, - SM5803_VBAT_PWR_MINTH_3S_LEVEL); - rv |= main_write8(chgnum, 0x30, 0xC0); - rv |= main_write8(chgnum, 0x80, 0x01); - rv |= main_write8(chgnum, 0x1A, 0x08); - - rv |= meas_write8(chgnum, 0x08, 0xC2); - - rv |= chg_write8(chgnum, 0x1D, 0x40); - - rv |= chg_write8(chgnum, 0x22, 0xB3); - - rv |= chg_write8(chgnum, 0x3E, 0x3C); - - rv |= chg_write8(chgnum, 0x4B, 0xA6); - rv |= chg_write8(chgnum, 0x4F, 0xBF); - - rv |= chg_write8(chgnum, 0x52, 0x77); - rv |= chg_write8(chgnum, 0x53, 0xD2); - rv |= chg_write8(chgnum, 0x54, 0x02); - rv |= chg_write8(chgnum, 0x55, 0xD1); - rv |= chg_write8(chgnum, 0x56, 0x7F); - rv |= chg_write8(chgnum, 0x57, 0x01); - rv |= chg_write8(chgnum, 0x58, 0x50); - rv |= chg_write8(chgnum, 0x59, 0x7F); - rv |= chg_write8(chgnum, 0x5A, 0x13); - rv |= chg_write8(chgnum, 0x5B, 0x50); - rv |= chg_write8(chgnum, 0x5D, 0xB0); - - rv |= chg_write8(chgnum, 0x60, 0x44); - rv |= chg_write8(chgnum, 0x65, 0x35); - rv |= chg_write8(chgnum, 0x66, 0x29); - - rv |= chg_write8(chgnum, 0x7D, 0x67); - rv |= chg_write8(chgnum, 0x7E, 0x04); - - rv |= chg_write8(chgnum, 0x33, 0x3C); - - rv |= chg_write8(chgnum, 0x5C, 0x7A); - } else if (is_platform_id_2s(platform_id)) { - /* 2S Battery inits */ - - /* - * Set 9V as higher threshold for VBATSNSP_MAX_TH GPADC - * threshold for interrupt generation. - */ - rv |= meas_write8(chgnum, 0x26, - SM5803_VBAT_SNSP_MAXTH_2S_LEVEL); - - /* Set OV_VBAT HW second level threshold as 9.4V */ - rv |= chg_write8(chgnum, 0x21, - SM5803_VBAT_PWR_MINTH_2S_LEVEL); - - rv |= main_write8(chgnum, 0x30, 0xC0); - rv |= main_write8(chgnum, 0x80, 0x01); - rv |= main_write8(chgnum, 0x1A, 0x08); - - rv |= meas_write8(chgnum, 0x08, 0xC2); - - rv |= chg_write8(chgnum, 0x1D, 0x40); - - rv |= chg_write8(chgnum, 0x22, 0xB3); - - rv |= chg_write8(chgnum, 0x3E, 0x3C); - - rv |= chg_write8(chgnum, 0x4F, 0xBF); - - rv |= chg_write8(chgnum, 0x52, 0x77); - rv |= chg_write8(chgnum, 0x53, 0xD2); - rv |= chg_write8(chgnum, 0x54, 0x02); - rv |= chg_write8(chgnum, 0x55, 0xD1); - rv |= chg_write8(chgnum, 0x56, 0x7F); - rv |= chg_write8(chgnum, 0x57, 0x01); - rv |= chg_write8(chgnum, 0x58, 0x50); - rv |= chg_write8(chgnum, 0x59, 0x7F); - rv |= chg_write8(chgnum, 0x5A, 0x13); - rv |= chg_write8(chgnum, 0x5B, 0x52); - rv |= chg_write8(chgnum, 0x5D, 0xD0); - - rv |= chg_write8(chgnum, 0x60, 0x44); - rv |= chg_write8(chgnum, 0x65, 0x35); - rv |= chg_write8(chgnum, 0x66, 0x29); - - rv |= chg_write8(chgnum, 0x7D, 0x97); - rv |= chg_write8(chgnum, 0x7E, 0x07); - - rv |= chg_write8(chgnum, 0x33, 0x3C); - - rv |= chg_write8(chgnum, 0x5C, 0x7A); - } - - rv |= chg_write8(chgnum, 0x73, 0x22); - rv |= chg_write8(chgnum, 0x50, 0x88); - rv |= chg_read8(chgnum, 0x34, ®); - reg |= BIT(7); - rv |= chg_write8(chgnum, 0x34, reg); - rv |= main_write8(chgnum, 0x1F, 0x1); - rv |= test_write8(chgnum, 0x43, 0x10); - rv |= test_write8(chgnum, 0x47, 0x10); - rv |= test_write8(chgnum, 0x48, 0x04); - rv |= main_write8(chgnum, 0x1F, 0x0); - } - - /* Enable LDO bits */ - rv |= main_read8(chgnum, SM5803_REG_REFERENCE, ®); - reg &= ~(BIT(0) | BIT(1)); - rv |= main_write8(chgnum, SM5803_REG_REFERENCE, reg); - - /* Set a higher clock speed in case it was lowered for z-state */ - rv |= main_read8(chgnum, SM5803_REG_CLOCK_SEL, ®); - reg &= ~SM5803_CLOCK_SEL_LOW; - rv |= main_write8(chgnum, SM5803_REG_CLOCK_SEL, reg); - - /* - * Turn on GPADCs to default. Enable the IBAT_CHG ADC in order to - * measure battery current and calculate system resistance. - */ - reg = SM5803_GPADCC1_TINT_EN | - SM5803_GPADCC1_VSYS_EN | - SM5803_GPADCC1_VCHGPWR_EN | - SM5803_GPADCC1_VBUS_EN | - SM5803_GPADCC1_IBAT_CHG_EN | - SM5803_GPADCC1_IBAT_DIS_EN | - SM5803_GPADCC1_VBATSNSP_EN; - rv |= meas_write8(chgnum, SM5803_REG_GPADC_CONFIG1, reg); - - /* Enable Psys DAC */ - rv |= meas_read8(chgnum, SM5803_REG_PSYS1, ®); - reg |= SM5803_PSYS1_DAC_EN; - rv |= meas_write8(chgnum, SM5803_REG_PSYS1, reg); - - /* Enable ADC sigma delta */ - rv |= chg_read8(chgnum, SM5803_REG_CC_CONFIG1, ®); - reg |= SM5803_CC_CONFIG1_SD_PWRUP; - rv |= chg_write8(chgnum, SM5803_REG_CC_CONFIG1, reg); - - /* Enable PROCHOT comparators except Ibus */ - rv |= chg_read8(chgnum, SM5803_REG_PHOT1, ®); - reg |= SM5803_PHOT1_COMPARATOR_EN; - reg &= ~SM5803_PHOT1_IBUS_PHOT_COMP_EN; - rv |= chg_write8(chgnum, SM5803_REG_PHOT1, reg); - - /* Set DPM Voltage to 4200 mv, see b:172173517 */ - reg = SM5803_VOLTAGE_TO_REG(4200); - rv = chg_write8(chgnum, SM5803_REG_DPM_VL_SET_MSB, (reg >> 3)); - rv |= chg_write8(chgnum, SM5803_REG_DPM_VL_SET_LSB, (reg & 0x7)); - - /* Set default input current */ - reg = SM5803_CURRENT_TO_REG(CONFIG_CHARGER_INPUT_CURRENT) - & SM5803_CHG_ILIM_RAW; - rv |= chg_write8(chgnum, SM5803_REG_CHG_ILIM, reg); - - /* Configure charger insertion interrupts */ - rv |= main_write8(chgnum, SM5803_REG_INT1_EN, SM5803_INT1_CHG); - /* Enable end of charge interrupts for logging */ - rv |= main_write8(chgnum, SM5803_REG_INT4_EN, SM5803_INT4_CHG_FAIL | - SM5803_INT4_CHG_DONE | - SM5803_INT4_OTG_FAIL); - - /* Set TINT interrupts for higher threshold 360 K */ - rv |= meas_write8(chgnum, SM5803_REG_TINT_HIGH_TH, - SM5803_TINT_HIGH_LEVEL); - /* - * Set TINT interrupts for lower threshold to 0 when not - * throttled to prevent trigger interrupts continually - */ - rv |= meas_write8(chgnum, SM5803_REG_TINT_LOW_TH, - SM5803_TINT_MIN_LEVEL); - - /* - * Configure VBAT_SNSP high interrupt to fire after thresholds are set. - */ - rv |= main_read8(chgnum, SM5803_REG_INT2_EN, ®); - reg |= SM5803_INT2_VBATSNSP; - rv |= main_write8(chgnum, SM5803_REG_INT2_EN, reg); - - - /* Configure TINT interrupts to fire after thresholds are set */ - rv |= main_write8(chgnum, SM5803_REG_INT2_EN, SM5803_INT2_TINT); - - /* - * Configure CHG_ENABLE to only be set through I2C by setting - * HOST_MODE_EN bit (all other register bits are 0 by default) - */ - rv |= chg_write8(chgnum, SM5803_REG_FLOW2, SM5803_FLOW2_HOST_MODE_EN); - - if (chgnum == CHARGER_PRIMARY) { - int ibat_eoc_ma; - - /* Set end of fast charge threshold */ - batt_info = battery_get_info(); - ibat_eoc_ma = batt_info->precharge_current - 50; - ibat_eoc_ma /= 100; - ibat_eoc_ma = CLAMP(ibat_eoc_ma, 0, SM5803_CONF5_IBAT_EOC_TH); - rv |= chg_read8(chgnum, SM5803_REG_FAST_CONF5, ®); - reg &= ~SM5803_CONF5_IBAT_EOC_TH; - reg |= ibat_eoc_ma; - rv |= chg_write8(CHARGER_PRIMARY, SM5803_REG_FAST_CONF5, reg); - - /* Setup the proper precharge thresholds. */ - cells = batt_info->voltage_max / 4; - pre_term = batt_info->voltage_min / cells; - pre_term /= 100; /* Convert to decivolts. */ - pre_term = CLAMP(pre_term, SM5803_VBAT_PRE_TERM_MIN_DV, - SM5803_VBAT_PRE_TERM_MAX_DV); - pre_term -= SM5803_VBAT_PRE_TERM_MIN_DV; /* Convert to regval */ - - rv |= chg_read8(chgnum, SM5803_REG_PRE_FAST_CONF_REG1, ®); - reg &= ~SM5803_VBAT_PRE_TERM; - reg |= pre_term << SM5803_VBAT_PRE_TERM_SHIFT; - rv |= chg_write8(chgnum, SM5803_REG_PRE_FAST_CONF_REG1, reg); - - /* - * Set up precharge current - * Note it is preferred to under-shoot the precharge current - * requested. Upper bits of this register are read/write 1 to - * clear - */ - reg = SM5803_CURRENT_TO_REG(batt_info->precharge_current); - reg = MIN(reg, SM5803_PRECHG_ICHG_PRE_SET); - rv |= chg_write8(chgnum, SM5803_REG_PRECHG, reg); - - /* - * Set up BFET alerts - * - * We'll set the soft limit at 1.5W and the hard limit at 6W. - * - * The register is 29.2 mW per bit. - */ - reg = (1500 * 10) / 292; - rv |= meas_write8(chgnum, SM5803_REG_BFET_PWR_MAX_TH, reg); - reg = (6000 * 10) / 292; - rv |= meas_write8(chgnum, SM5803_REG_BFET_PWR_HWSAFE_MAX_TH, - reg); - rv |= main_read8(chgnum, SM5803_REG_INT3_EN, ®); - reg |= SM5803_INT3_BFET_PWR_LIMIT | - SM5803_INT3_BFET_PWR_HWSAFE_LIMIT; - rv |= main_write8(chgnum, SM5803_REG_INT3_EN, reg); - - rv |= chg_read8(chgnum, SM5803_REG_FLOW3, ®); - reg &= ~SM5803_FLOW3_SWITCH_BCK_BST; - rv |= chg_write8(chgnum, SM5803_REG_FLOW3, reg); - - rv |= chg_read8(chgnum, SM5803_REG_SWITCHER_CONF, ®); - reg |= SM5803_SW_BCK_BST_CONF_AUTO; - rv |= chg_write8(chgnum, SM5803_REG_SWITCHER_CONF, reg); - } - - if (rv) - CPRINTS("%s %d: Failed initialization", CHARGER_NAME, chgnum); - else - chip_inited[chgnum] = true; -} - -static enum ec_error_list sm5803_post_init(int chgnum) -{ - /* Nothing to do, charger is always powered */ - return EC_SUCCESS; -} - -void sm5803_hibernate(int chgnum) -{ - enum ec_error_list rv; - int reg; - - rv = main_read8(chgnum, SM5803_REG_REFERENCE, ®); - if (rv) { - CPRINTS("%s %d: Failed to read REFERENCE reg", CHARGER_NAME, - chgnum); - return; - } - - /* Disable LDO bits - note the primary LDO should not be disabled */ - if (chgnum != CHARGER_PRIMARY) { - reg |= (BIT(0) | BIT(1)); - rv |= main_write8(chgnum, SM5803_REG_REFERENCE, reg); - } - - /* Slow the clock speed */ - rv |= main_read8(chgnum, SM5803_REG_CLOCK_SEL, ®); - reg |= SM5803_CLOCK_SEL_LOW; - rv |= main_write8(chgnum, SM5803_REG_CLOCK_SEL, reg); - - /* Turn off GPADCs */ - rv |= meas_write8(chgnum, SM5803_REG_GPADC_CONFIG1, 0); - rv |= meas_write8(chgnum, SM5803_REG_GPADC_CONFIG2, 0); - - /* Disable Psys DAC */ - rv |= meas_read8(chgnum, SM5803_REG_PSYS1, ®); - reg &= ~SM5803_PSYS1_DAC_EN; - rv |= meas_write8(chgnum, SM5803_REG_PSYS1, reg); - - /* Disable ADC sigma delta */ - rv |= chg_read8(chgnum, SM5803_REG_CC_CONFIG1, ®); - reg &= ~SM5803_CC_CONFIG1_SD_PWRUP; - rv |= chg_write8(chgnum, SM5803_REG_CC_CONFIG1, reg); - - /* Disable PROCHOT comparators */ - rv |= chg_read8(chgnum, SM5803_REG_PHOT1, ®); - reg &= ~SM5803_PHOT1_COMPARATOR_EN; - rv |= chg_write8(chgnum, SM5803_REG_PHOT1, reg); - - if (rv) - CPRINTS("%s %d: Failed to set hibernate", CHARGER_NAME, chgnum); -} - -static void sm5803_disable_runtime_low_power_mode(void) -{ - enum ec_error_list rv; - int reg; - int chgnum = TASK_ID_TO_PD_PORT(task_get_current()); - - CPRINTS("%s %d: disable runtime low power mode", CHARGER_NAME, chgnum); - rv = main_read8(chgnum, SM5803_REG_REFERENCE, ®); - if (rv) { - CPRINTS("%s %d: Failed to read REFERENCE reg", CHARGER_NAME, - chgnum); - return; - } - /* Set a higher clock speed */ - rv |= main_read8(chgnum, SM5803_REG_CLOCK_SEL, ®); - reg &= ~SM5803_CLOCK_SEL_LOW; - rv |= main_write8(chgnum, SM5803_REG_CLOCK_SEL, reg); - - /* Enable ADC sigma delta */ - rv |= chg_read8(chgnum, SM5803_REG_CC_CONFIG1, ®); - reg |= SM5803_CC_CONFIG1_SD_PWRUP; - rv |= chg_write8(chgnum, SM5803_REG_CC_CONFIG1, reg); - - if (rv) - CPRINTS("%s %d: Failed to set in disable runtime LPM", - CHARGER_NAME, chgnum); -} -DECLARE_HOOK(HOOK_USB_PD_CONNECT, - sm5803_disable_runtime_low_power_mode, - HOOK_PRIO_FIRST); - -static enum ec_error_list sm5803_enable_linear_charge(int chgnum, bool enable) -{ - int rv; - int regval; - const struct battery_info *batt_info; - - if (enable) { - /* - * We need to wait for the BFET enable attempt to complete, - * otherwise we may end up disabling linear charge. - */ - if (!attempt_bfet_enable) - return EC_ERROR_TRY_AGAIN; - rv = main_write8(chgnum, 0x1F, 0x1); - rv |= test_write8(chgnum, 0x44, 0x20); - rv |= main_write8(chgnum, 0x1F, 0); - - /* - * Precharge thresholds have already been set up as a part of - * init, however set fast charge current equal to the precharge - * current in case the battery moves beyond that threshold. - */ - batt_info = battery_get_info(); - rv |= sm5803_set_current(CHARGER_PRIMARY, - batt_info->precharge_current); - - /* Enable linear charge mode. */ - rv |= sm5803_flow1_update(chgnum, - SM5803_FLOW1_LINEAR_CHARGE_EN, - MASK_SET); - rv |= chg_read8(chgnum, SM5803_REG_FLOW3, ®val); - regval |= BIT(6) | BIT(5) | BIT(4); - rv |= chg_write8(chgnum, SM5803_REG_FLOW3, regval); - } else { - rv = sm5803_flow1_update(chgnum, - SM5803_FLOW1_LINEAR_CHARGE_EN, - MASK_CLR); - rv |= sm5803_flow2_update(chgnum, - SM5803_FLOW2_AUTO_ENABLED, - MASK_CLR); - rv |= chg_read8(chgnum, SM5803_REG_FLOW3, ®val); - regval &= ~(BIT(6) | BIT(5) | BIT(4) | - SM5803_FLOW3_SWITCH_BCK_BST); - rv |= chg_write8(chgnum, SM5803_REG_FLOW3, regval); - rv |= chg_read8(chgnum, SM5803_REG_SWITCHER_CONF, ®val); - regval |= SM5803_SW_BCK_BST_CONF_AUTO; - rv |= chg_write8(chgnum, SM5803_REG_SWITCHER_CONF, regval); - } - - return rv; -} - -static void sm5803_enable_runtime_low_power_mode(void) -{ - enum ec_error_list rv; - int reg; - int chgnum = TASK_ID_TO_PD_PORT(task_get_current()); - - CPRINTS("%s %d: enable runtime low power mode", CHARGER_NAME, chgnum); - rv = main_read8(chgnum, SM5803_REG_REFERENCE, ®); - if (rv) { - CPRINTS("%s %d: Failed to read REFERENCE reg", CHARGER_NAME, - chgnum); - return; - } - /* Slow the clock speed */ - rv |= main_read8(chgnum, SM5803_REG_CLOCK_SEL, ®); - reg |= SM5803_CLOCK_SEL_LOW; - rv |= main_write8(chgnum, SM5803_REG_CLOCK_SEL, reg); - - /* Disable ADC sigma delta */ - rv |= chg_read8(chgnum, SM5803_REG_CC_CONFIG1, ®); - reg &= ~SM5803_CC_CONFIG1_SD_PWRUP; - rv |= chg_write8(chgnum, SM5803_REG_CC_CONFIG1, reg); - - /* If the system is off, all PROCHOT comparators may be turned off */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF | - CHIPSET_STATE_ANY_SUSPEND)) { - rv |= chg_read8(chgnum, SM5803_REG_PHOT1, ®); - reg &= ~SM5803_PHOT1_COMPARATOR_EN; - rv |= chg_write8(chgnum, SM5803_REG_PHOT1, reg); - } - - if (rv) - CPRINTS("%s %d: Failed to set in enable runtime LPM", - CHARGER_NAME, chgnum); -} -DECLARE_HOOK(HOOK_USB_PD_DISCONNECT, - sm5803_enable_runtime_low_power_mode, - HOOK_PRIO_LAST); - -void sm5803_disable_low_power_mode(int chgnum) -{ - enum ec_error_list rv; - int reg; - - CPRINTS("%s %d: disable low power mode", CHARGER_NAME, chgnum); - rv = main_read8(chgnum, SM5803_REG_REFERENCE, ®); - if (rv) { - CPRINTS("%s %d: Failed to read REFERENCE reg", CHARGER_NAME, - chgnum); - return; - } - /* Enable Psys DAC */ - rv |= meas_read8(chgnum, SM5803_REG_PSYS1, ®); - reg |= SM5803_PSYS1_DAC_EN; - rv |= meas_write8(chgnum, SM5803_REG_PSYS1, reg); - - /* Enable PROCHOT comparators except Ibus */ - rv |= chg_read8(chgnum, SM5803_REG_PHOT1, ®); - reg |= SM5803_PHOT1_COMPARATOR_EN; - reg &= ~SM5803_PHOT1_IBUS_PHOT_COMP_EN; - rv |= chg_write8(chgnum, SM5803_REG_PHOT1, reg); - - if (rv) - CPRINTS("%s %d: Failed to set in disable low power mode", - CHARGER_NAME, chgnum); -} - -void sm5803_enable_low_power_mode(int chgnum) -{ - enum ec_error_list rv; - int reg; - - CPRINTS("%s %d: enable low power mode", CHARGER_NAME, chgnum); - rv = main_read8(chgnum, SM5803_REG_REFERENCE, ®); - if (rv) { - CPRINTS("%s %d: Failed to read REFERENCE reg", CHARGER_NAME, - chgnum); - return; - } - /* Disable Psys DAC */ - rv |= meas_read8(chgnum, SM5803_REG_PSYS1, ®); - reg &= ~SM5803_PSYS1_DAC_EN; - rv |= meas_write8(chgnum, SM5803_REG_PSYS1, reg); - - /* - * Disable all PROCHOT comparators only if port is inactive. Vbus - * sourcing requires that the Vbus comparator be enabled, and it - * cannot be enabled from HOOK_USB_PD_CONNECT since that is - * called after Vbus has turned on. - */ - rv |= chg_read8(chgnum, SM5803_REG_PHOT1, ®); - reg &= ~SM5803_PHOT1_COMPARATOR_EN; - if (pd_is_connected(chgnum)) - reg |= SM5803_PHOT1_VBUS_MON_EN; - rv |= chg_write8(chgnum, SM5803_REG_PHOT1, reg); - - - if (rv) - CPRINTS("%s %d: Failed to set in enable low power mode", - CHARGER_NAME, chgnum); -} - -/* - * Restart charging on the active port, if it's still active and it hasn't - * exceeded our maximum number of restarts. - */ -void sm5803_restart_charging(void) -{ - int act_chg = charge_manager_get_active_charge_port(); - timestamp_t now = get_time(); - - if (act_chg == active_restart_port) { - if (timestamp_expired(failure_tracker[act_chg].time, &now)) { - /* - * Enough time has passed since our last failure, - * restart the timing and count from now. - */ - failure_tracker[act_chg].time.val = now.val + - CHARGING_FAILURE_INTERVAL; - failure_tracker[act_chg].count = 1; - - sm5803_vbus_sink_enable(act_chg, 1); - } else if (++failure_tracker[act_chg].count > - CHARGING_FAILURE_MAX_COUNT) { - CPRINTS("%s %d: Exceeded charging failure retries", - CHARGER_NAME, act_chg); - } else { - sm5803_vbus_sink_enable(act_chg, 1); - } - } - - active_restart_port = CHARGE_PORT_NONE; -} -DECLARE_DEFERRED(sm5803_restart_charging); - -/* - * Process interrupt registers and report any Vbus changes. Alert the AP if the - * charger has become too hot. - */ -void sm5803_handle_interrupt(int chgnum) -{ - enum ec_error_list rv; - int int_reg, meas_reg; - static bool throttled; - struct batt_params bp; - int act_chg, val; - - /* Note: Interrupt registers are clear on read */ - rv = main_read8(chgnum, SM5803_REG_INT1_REQ, &int_reg); - if (rv) { - CPRINTS("%s %d: Failed read int1 register", CHARGER_NAME, - chgnum); - return; - } - - if (int_reg & SM5803_INT1_CHG) { - rv = main_read8(chgnum, SM5803_REG_STATUS1, &meas_reg); - if (!(meas_reg & SM5803_STATUS1_CHG_DET)) { - charger_vbus[chgnum] = 0; - if (IS_ENABLED(CONFIG_USB_CHARGER)) - usb_charger_vbus_change(chgnum, 0); - } else { - charger_vbus[chgnum] = 1; - if (IS_ENABLED(CONFIG_USB_CHARGER)) - usb_charger_vbus_change(chgnum, 1); - } - board_check_extpower(); - } - - rv = main_read8(chgnum, SM5803_REG_INT2_REQ, &int_reg); - if (rv) { - CPRINTS("%s %d: Failed read int2 register", CHARGER_NAME, - chgnum); - return; - } - - if (int_reg & SM5803_INT2_TINT) { - rv = meas_read8(chgnum, SM5803_REG_TINT_MEAS_MSB, &meas_reg); - if ((meas_reg <= SM5803_TINT_LOW_LEVEL) && throttled) { - throttled = false; - throttle_ap(THROTTLE_OFF, THROTTLE_HARD, - THROTTLE_SRC_THERMAL); - /* - * Set back higher threshold to 360 K and set lower - * threshold to 0. - */ - rv |= meas_write8(chgnum, SM5803_REG_TINT_LOW_TH, - SM5803_TINT_MIN_LEVEL); - rv |= meas_write8(chgnum, SM5803_REG_TINT_HIGH_TH, - SM5803_TINT_HIGH_LEVEL); - } else if (meas_reg >= SM5803_TINT_HIGH_LEVEL) { - throttled = true; - throttle_ap(THROTTLE_ON, THROTTLE_HARD, - THROTTLE_SRC_THERMAL); - /* - * Set back lower threshold to 330 K and set higher - * threshold to maximum. - */ - rv |= meas_write8(chgnum, SM5803_REG_TINT_HIGH_TH, - SM5803_TINT_MAX_LEVEL); - rv |= meas_write8(chgnum, SM5803_REG_TINT_LOW_TH, - SM5803_TINT_LOW_LEVEL); - } - /* - * If the interrupt came in and we're not currently throttling - * or the level is below the upper threshold, it can likely be - * ignored. - */ - } - - if (int_reg & SM5803_INT2_VBATSNSP) { - int meas_volt; - uint32_t platform_id; - - rv = main_read8(chgnum, SM5803_REG_PLATFORM, &platform_id); - if (rv) { - CPRINTS("%s %d: Failed to read platform in interrupt", - CHARGER_NAME, chgnum); - return; - } - platform_id &= SM5803_PLATFORM_ID; - act_chg = charge_manager_get_active_charge_port(); - rv = meas_read8(CHARGER_PRIMARY, - SM5803_REG_VBATSNSP_MEAS_MSB, - &meas_reg); - if (rv) - return; - meas_volt = meas_reg << 2; - rv = meas_read8(CHARGER_PRIMARY, - SM5803_REG_VBATSNSP_MEAS_LSB, - &meas_reg); - if (rv) - return; - meas_volt |= meas_reg & 0x03; - rv = meas_read8(CHARGER_PRIMARY, - SM5803_REG_VBATSNSP_MAX_TH, &meas_reg); - if (rv) - return; - - if (is_platform_id_2s(platform_id)) { - /* 2S Battery */ - CPRINTS("%s %d : VBAT_SNSP_HIGH_TH: %d mV ! - " - "VBAT %d mV", - CHARGER_NAME, CHARGER_PRIMARY, - meas_reg * 408/10, - meas_volt * 102/10); - } - - if (is_platform_id_3s(platform_id)) { - /* 3S Battery */ - CPRINTS("%s %d : VBAT_SNSP_HIGH_TH: %d mV ! " - "- VBAT %d mV", - CHARGER_NAME, CHARGER_PRIMARY, - meas_reg * 616/10, - meas_volt * 154/10); - } - - /* Set Vbat Threshold to Max value to re-arm the interrupt */ - rv = meas_write8(CHARGER_PRIMARY, - SM5803_REG_VBATSNSP_MAX_TH, 0xFF); - - /* Disable battery charge */ - rv |= sm5803_flow1_update(chgnum, CHARGER_MODE_DISABLED, - MASK_CLR); - if (is_platform_id_2s(platform_id)) { - /* 2S battery: set VBAT_SENSP TH 9V */ - rv |= meas_write8(CHARGER_PRIMARY, - SM5803_REG_VBATSNSP_MAX_TH, - SM5803_VBAT_SNSP_MAXTH_2S_LEVEL); - } - if (is_platform_id_3s(platform_id)) { - /* 3S battery: set VBAT_SENSP TH 13.3V */ - rv |= meas_write8(CHARGER_PRIMARY, - SM5803_REG_VBATSNSP_MAX_TH, - SM5803_VBAT_SNSP_MAXTH_3S_LEVEL); - } - - active_restart_port = act_chg; - hook_call_deferred(&sm5803_restart_charging_data, 1 * SECOND); - } - - /* TODO(b/159376384): Take action on fatal BFET power alert. */ - rv = main_read8(chgnum, SM5803_REG_INT3_REQ, &int_reg); - if (rv) { - CPRINTS("%s %d: Failed to read int3 register", CHARGER_NAME, - chgnum); - return; - } - - if ((int_reg & SM5803_INT3_BFET_PWR_LIMIT) || - (int_reg & SM5803_INT3_BFET_PWR_HWSAFE_LIMIT)) { - battery_get_params(&bp); - act_chg = charge_manager_get_active_charge_port(); - CPRINTS("%s BFET power limit reached! (%s)", CHARGER_NAME, - (int_reg & SM5803_INT3_BFET_PWR_LIMIT) ? "warn" : - "FATAL"); - CPRINTS("\tVbat: %dmV", bp.voltage); - CPRINTS("\tIbat: %dmA", bp.current); - charger_get_voltage(act_chg, &val); - CPRINTS("\tVsys(aux): %dmV", val); - charger_get_current(act_chg, &val); - CPRINTS("\tIsys: %dmA", val); - cflush(); - } - - rv = main_read8(chgnum, SM5803_REG_INT4_REQ, &int_reg); - if (rv) { - CPRINTS("%s %d: Failed to read int4 register", CHARGER_NAME, - chgnum); - return; - } - - if (int_reg & SM5803_INT4_CHG_FAIL) { - int status_reg; - - act_chg = charge_manager_get_active_charge_port(); - chg_read8(chgnum, SM5803_REG_STATUS_CHG_REG, &status_reg); - CPRINTS("%s %d: CHG_FAIL_INT fired. Status 0x%02x", - CHARGER_NAME, chgnum, status_reg); - - /* Write 1 to clear status interrupts */ - chg_write8(chgnum, SM5803_REG_STATUS_CHG_REG, status_reg); - - /* - * If a survivable fault happened, re-start sinking on the - * active charger after an appropriate delay. - */ - if (status_reg & SM5803_STATUS_CHG_OV_ITEMP) { - active_restart_port = act_chg; - hook_call_deferred(&sm5803_restart_charging_data, - 30 * SECOND); - } else if ((status_reg & SM5803_STATUS_CHG_OV_VBAT) && - act_chg == CHARGER_PRIMARY) { - active_restart_port = act_chg; - hook_call_deferred(&sm5803_restart_charging_data, - 1 * SECOND); - } - } - - if (int_reg & SM5803_INT4_CHG_DONE) - CPRINTS("%s %d: CHG_DONE_INT fired!!!", CHARGER_NAME, chgnum); - - if (int_reg & SM5803_INT4_OTG_FAIL) { - int status_reg; - - /* - * Gather status to detect if this was overcurrent - * - * Note: a status of 0 with this interrupt also indicates an - * overcurrent (see b/170517117) - */ - chg_read8(chgnum, SM5803_REG_STATUS_DISCHG, &status_reg); - CPRINTS("%s %d: OTG_FAIL_INT fired. Status 0x%02x", - CHARGER_NAME, chgnum, status_reg); - if ((status_reg == 0) || - (status_reg == SM5803_STATUS_DISCHG_VBUS_SHORT)) { - pd_handle_overcurrent(chgnum); - } - - /* - * Clear source mode here when status is 0, since OTG disable - * will detect us as sinking in this failure case. - */ - if (status_reg == 0) - rv = sm5803_flow1_update(chgnum, CHARGER_MODE_SOURCE | - SM5803_FLOW1_DIRECTCHG_SRC_EN, - MASK_CLR); - } - -} - -static void sm5803_irq_deferred(void) -{ - int i; - uint32_t pending = atomic_clear(&irq_pending); - - for (i = 0; i < CHARGER_NUM; i++) - if (BIT(i) & pending) - sm5803_handle_interrupt(i); -} -DECLARE_DEFERRED(sm5803_irq_deferred); - -void sm5803_interrupt(int chgnum) -{ - atomic_or(&irq_pending, BIT(chgnum)); - hook_call_deferred(&sm5803_irq_deferred_data, 0); -} - -static enum ec_error_list sm5803_get_dev_id(int chgnum, int *id) -{ - int rv = EC_SUCCESS; - - if (dev_id == UNKNOWN_DEV_ID) - rv = main_read8(chgnum, SM5803_REG_CHIP_ID, &dev_id); - - if (!rv) - *id = dev_id; - - return rv; - -} - -static const struct charger_info *sm5803_get_info(int chgnum) -{ - return &sm5803_charger_info; -} - -static enum ec_error_list sm5803_get_status(int chgnum, int *status) -{ - enum ec_error_list rv; - int reg; - - /* Charger obeys smart battery requests - making it level 2 */ - *status = CHARGER_LEVEL_2; - - rv = chg_read8(chgnum, SM5803_REG_FLOW1, ®); - if (rv) - return rv; - - - if ((reg & SM5803_FLOW1_MODE) == CHARGER_MODE_DISABLED && - !(reg & SM5803_FLOW1_LINEAR_CHARGE_EN)) - *status |= CHARGER_CHARGE_INHIBITED; - - return EC_SUCCESS; -} - -static enum ec_error_list sm5803_set_mode(int chgnum, int mode) -{ - enum ec_error_list rv = EC_SUCCESS; - - if (mode & CHARGE_FLAG_INHIBIT_CHARGE) { - rv = sm5803_flow1_update(chgnum, 0xFF, MASK_CLR); - rv |= sm5803_flow2_update(chgnum, SM5803_FLOW2_AUTO_ENABLED, - MASK_CLR); - } - - return rv; -} - -static enum ec_error_list sm5803_get_actual_current(int chgnum, int *current) -{ - enum ec_error_list rv; - int reg; - int curr; - - rv = meas_read8(chgnum, SM5803_REG_IBAT_CHG_AVG_MEAS_MSB, ®); - if (rv) - return rv; - curr = reg << 2; - - rv = meas_read8(chgnum, SM5803_REG_IBAT_CHG_AVG_MEAS_LSB, ®); - if (rv) - return rv; - curr |= reg & SM5803_IBAT_CHG_MEAS_LSB; - - /* The LSB is 7.32mA */ - *current = curr * 732 / 100; - return EC_SUCCESS; -} - -static enum ec_error_list sm5803_get_current(int chgnum, int *current) -{ - enum ec_error_list rv; - int reg; - - rv = chg_read8(chgnum, SM5803_REG_FAST_CONF4, ®); - if (rv) - return rv; - - reg &= SM5803_CONF4_ICHG_FAST; - *current = SM5803_REG_TO_CURRENT(reg); - - return EC_SUCCESS; -} - -static enum ec_error_list sm5803_set_current(int chgnum, int current) -{ - enum ec_error_list rv; - int reg; - - rv = chg_read8(chgnum, SM5803_REG_FAST_CONF4, ®); - if (rv) - return rv; - - reg &= ~SM5803_CONF4_ICHG_FAST; - reg |= SM5803_CURRENT_TO_REG(current); - - rv = chg_write8(chgnum, SM5803_REG_FAST_CONF4, reg); - return rv; -} - -static enum ec_error_list sm5803_get_actual_voltage(int chgnum, int *voltage) -{ - enum ec_error_list rv; - int reg; - int volt_bits; - - rv = meas_read8(chgnum, SM5803_REG_VSYS_AVG_MEAS_MSB, ®); - if (rv) - return rv; - volt_bits = reg << 2; - - rv = meas_read8(chgnum, SM5803_REG_VSYS_AVG_MEAS_LSB, ®); - if (rv) - return rv; - volt_bits |= reg & 0x3; - - /* The LSB is 23.4mV */ - *voltage = volt_bits * 234 / 10; - - return EC_SUCCESS; -} - -static enum ec_error_list sm5803_get_voltage(int chgnum, int *voltage) -{ - enum ec_error_list rv; - int regval; - int v; - - rv = chg_read8(chgnum, SM5803_REG_VBAT_FAST_MSB, ®val); - v = regval << 3; - rv |= chg_read8(chgnum, SM5803_REG_VBAT_FAST_LSB, ®val); - v |= (regval & 0x3); - - *voltage = SM5803_REG_TO_VOLTAGE(v); - - if (rv) - return EC_ERROR_UNKNOWN; - - return EC_SUCCESS; -} - -static enum ec_error_list sm5803_set_voltage(int chgnum, int voltage) -{ - enum ec_error_list rv; - int regval; - - regval = SM5803_VOLTAGE_TO_REG(voltage); - - /* - * Note: Set both voltages on both chargers. Vbat will only be used on - * primary, which enables charging. - */ - rv = chg_write8(chgnum, SM5803_REG_VSYS_PREREG_MSB, (regval >> 3)); - rv |= chg_write8(chgnum, SM5803_REG_VSYS_PREREG_LSB, (regval & 0x7)); - rv |= chg_write8(chgnum, SM5803_REG_VBAT_FAST_MSB, (regval >> 3)); - rv |= chg_write8(chgnum, SM5803_REG_VBAT_FAST_LSB, (regval & 0x7)); - - /* Once battery is connected, set up fast charge enable */ - if (fast_charge_disabled && chgnum == CHARGER_PRIMARY && - battery_get_disconnect_state() == BATTERY_NOT_DISCONNECTED) { - rv = sm5803_flow2_update(chgnum, - SM5803_FLOW2_AUTO_ENABLED, - MASK_SET); - fast_charge_disabled = false; - } - - if (IS_ENABLED(CONFIG_OCPC) && chgnum != CHARGER_PRIMARY) { - /* - * Check to see if the BFET is enabled. If not, enable it by - * toggling linear mode on the primary charger. The BFET can be - * disabled if the system is powered up from an auxiliary charge - * port and the battery is dead. - */ - rv |= chg_read8(CHARGER_PRIMARY, SM5803_REG_LOG1, ®val); - if (!(regval & SM5803_BATFET_ON) && !attempt_bfet_enable) { - CPRINTS("SM5803: Attempting to turn on BFET"); - cflush(); - rv |= sm5803_flow1_update(CHARGER_PRIMARY, - SM5803_FLOW1_LINEAR_CHARGE_EN, - MASK_SET); - rv |= sm5803_flow1_update(CHARGER_PRIMARY, - SM5803_FLOW1_LINEAR_CHARGE_EN, - MASK_CLR); - attempt_bfet_enable = 1; - sm5803_vbus_sink_enable(chgnum, 1); - } - /* There's no need to attempt it if the BFET's already on. */ - if (regval & SM5803_BATFET_ON) - attempt_bfet_enable = 1; - } - - return rv; -} - -static enum ec_error_list sm5803_discharge_on_ac(int chgnum, int enable) -{ - enum ec_error_list rv = EC_SUCCESS; - - if (enable) { - rv = sm5803_vbus_sink_enable(chgnum, 0); - } else { - if (chgnum == charge_manager_get_active_charge_port()) - rv = sm5803_vbus_sink_enable(chgnum, 1); - } - - return rv; -} - -static enum ec_error_list sm5803_get_vbus_voltage(int chgnum, int port, - int *voltage) -{ - enum ec_error_list rv; - int reg; - int volt_bits; - - rv = meas_read8(chgnum, SM5803_REG_VBUS_MEAS_MSB, ®); - if (rv) - return rv; - - volt_bits = reg << 2; - - rv = meas_read8(chgnum, SM5803_REG_VBUS_MEAS_LSB, ®); - - volt_bits |= reg & SM5803_VBUS_MEAS_LSB; - - /* Vbus ADC is in 23.4 mV steps */ - *voltage = (volt_bits * 234) / 10; - return rv; -} - -static enum ec_error_list sm5803_set_input_current_limit(int chgnum, - int input_current) -{ - int reg; - - reg = SM5803_CURRENT_TO_REG(input_current) & SM5803_CHG_ILIM_RAW; - - return chg_write8(chgnum, SM5803_REG_CHG_ILIM, reg); -} - -static enum ec_error_list sm5803_get_input_current_limit(int chgnum, - int *input_current) -{ - int rv; - int val; - - rv = chg_read8(chgnum, SM5803_REG_CHG_ILIM, &val); - if (rv) - return rv; - - *input_current = SM5803_REG_TO_CURRENT(val & SM5803_CHG_ILIM_RAW); - return rv; -} - -static enum ec_error_list sm5803_get_input_current(int chgnum, - int *input_current) -{ - enum ec_error_list rv; - int reg; - int curr; - - rv = meas_read8(chgnum, SM5803_REG_IBUS_CHG_MEAS_MSB, ®); - if (rv) - return rv; - curr = reg << 2; - - rv = meas_read8(chgnum, SM5803_REG_IBUS_CHG_MEAS_LSB, ®); - if (rv) - return rv; - curr |= reg & 0x3; - - /* The LSB is 7.32mA */ - *input_current = curr * 732 / 100; - return EC_SUCCESS; -} - -static enum ec_error_list sm5803_get_option(int chgnum, int *option) -{ - enum ec_error_list rv; - uint32_t control; - int reg; - - rv = chg_read8(chgnum, SM5803_REG_FLOW1, ®); - control = reg; - - rv |= chg_read8(chgnum, SM5803_REG_FLOW2, ®); - control |= reg << 8; - - rv |= chg_read8(chgnum, SM5803_REG_FLOW3, ®); - control |= reg << 16; - - return rv; -} - -enum ec_error_list sm5803_is_acok(int chgnum, bool *acok) -{ - int rv; - int reg, vbus_mv; - - rv = main_read8(chgnum, SM5803_REG_STATUS1, ®); - - if (rv) - return rv; - - /* If we're not sinking, then AC can't be OK. */ - if (!(reg & SM5803_STATUS1_CHG_DET)) { - *acok = false; - return EC_SUCCESS; - } - - /* - * Okay, we're sinking. Check that VBUS has some voltage. This - * should indicate that the path is good. - */ - rv = charger_get_vbus_voltage(chgnum, &vbus_mv); - - if (rv) - return rv; - - /* Assume that ACOK would be asserted if VBUS is higher than ~4V. */ - *acok = vbus_mv >= 4000; - - return EC_SUCCESS; -} - -static enum ec_error_list sm5803_is_input_current_limit_reached(int chgnum, - bool *reached) -{ - enum ec_error_list rv; - int reg; - - rv = chg_read8(chgnum, SM5803_REG_LOG2, ®); - if (rv) - return rv; - - *reached = (reg & SM5803_ISOLOOP_ON) ? true : false; - - return EC_SUCCESS; -} - -static enum ec_error_list sm5803_set_option(int chgnum, int option) -{ - enum ec_error_list rv; - int reg; - - mutex_lock(&flow1_access_lock[chgnum]); - - reg = option & 0xFF; - rv = chg_write8(chgnum, SM5803_REG_FLOW1, reg); - - mutex_unlock(&flow1_access_lock[chgnum]); - if (rv) - return rv; - - reg = (option >> 8) & 0xFF; - rv = chg_write8(chgnum, SM5803_REG_FLOW2, reg); - if (rv) - return rv; - - reg = (option >> 16) & 0xFF; - rv = chg_write8(chgnum, SM5803_REG_FLOW3, reg); - - return rv; -} - -static enum ec_error_list sm5803_set_otg_current_voltage(int chgnum, - int output_current, - int output_voltage) -{ - enum ec_error_list rv; - int reg; - - rv = chg_read8(chgnum, SM5803_REG_DISCH_CONF5, ®); - if (rv) - return rv; - - reg &= ~SM5803_DISCH_CONF5_CLS_LIMIT; - reg |= MIN((output_current / SM5803_CLS_CURRENT_STEP), - SM5803_DISCH_CONF5_CLS_LIMIT); - rv |= chg_write8(chgnum, SM5803_REG_DISCH_CONF5, reg); - - reg = SM5803_VOLTAGE_TO_REG(output_voltage); - rv = chg_write8(chgnum, SM5803_REG_VPWR_MSB, (reg >> 3)); - rv |= chg_write8(chgnum, SM5803_REG_DISCH_CONF2, - reg & SM5803_DISCH_CONF5_VPWR_LSB); - - return rv; -} - -static enum ec_error_list sm5803_enable_otg_power(int chgnum, int enabled) -{ - enum ec_error_list rv; - int reg, status; - - if (enabled) { - int selected_current; - - rv = chg_read8(chgnum, SM5803_REG_ANA_EN1, ®); - if (rv) - return rv; - - /* Enable current limit */ - reg &= ~SM5803_ANA_EN1_CLS_DISABLE; - rv = chg_write8(chgnum, SM5803_REG_ANA_EN1, reg); - - /* Disable ramps on current set in discharge */ - rv |= chg_read8(chgnum, SM5803_REG_DISCH_CONF6, ®); - reg |= SM5803_DISCH_CONF6_RAMPS_DIS; - rv |= chg_write8(chgnum, SM5803_REG_DISCH_CONF6, reg); - - /* - * In order to ensure the Vbus output doesn't overshoot too - * much, turn the starting voltage down to 4.8 V and ramp up - * after 4 ms - */ - rv = chg_read8(chgnum, SM5803_REG_DISCH_CONF5, ®); - if (rv) - return rv; - - selected_current = (reg & SM5803_DISCH_CONF5_CLS_LIMIT) * - SM5803_CLS_CURRENT_STEP; - sm5803_set_otg_current_voltage(chgnum, selected_current, 4800); - - /* - * Enable: SOURCE_MODE - enable sourcing out - * DIRECTCHG_SOURCE_EN - enable current loop - * (for designs with no external Vbus FET) - */ - rv = sm5803_flow1_update(chgnum, CHARGER_MODE_SOURCE | - SM5803_FLOW1_DIRECTCHG_SRC_EN, - MASK_SET); - usleep(4000); - - sm5803_set_otg_current_voltage(chgnum, selected_current, 5000); - } else { - /* Always clear out discharge status before clearing FLOW1 */ - rv = chg_read8(chgnum, SM5803_REG_STATUS_DISCHG, &status); - if (rv) - return rv; - - if (status) - CPRINTS("%s %d: Discharge failure 0x%02x", CHARGER_NAME, - chgnum, status); - - rv |= chg_write8(chgnum, SM5803_REG_STATUS_DISCHG, status); - - /* Re-enable ramps on current set in discharge */ - rv |= chg_read8(chgnum, SM5803_REG_DISCH_CONF6, ®); - reg &= ~SM5803_DISCH_CONF6_RAMPS_DIS; - rv |= chg_write8(chgnum, SM5803_REG_DISCH_CONF6, reg); - - /* - * PD tasks will always turn off previous sourcing on init. - * Protect ourselves from brown out on init by checking if we're - * sinking right now. The init process should only leave sink - * mode enabled if a charger is plugged in; otherwise it's - * expected to be 0. - * - * Always clear out sourcing if the previous source-out failed. - */ - rv |= chg_read8(chgnum, SM5803_REG_FLOW1, ®); - if (rv) - return rv; - - if ((reg & SM5803_FLOW1_MODE) != CHARGER_MODE_SINK || status) - rv = sm5803_flow1_update(chgnum, CHARGER_MODE_SOURCE | - SM5803_FLOW1_DIRECTCHG_SRC_EN, - MASK_CLR); - } - - return rv; -} - -static int sm5803_is_sourcing_otg_power(int chgnum, int port) -{ - enum ec_error_list rv; - int reg; - - rv = chg_read8(chgnum, SM5803_REG_FLOW1, ®); - if (rv) - return 0; - - /* - * Note: In linear mode, MB charger will read a reserved mode when - * sourcing, so bit 1 is the most reliable way to detect sourcing. - */ - return (reg & BIT(1)); -} - -static enum ec_error_list sm5803_set_vsys_compensation(int chgnum, - struct ocpc_data *ocpc, - int current_ma, - int voltage_mv) -{ - - int rv; - int regval; - int r; - - /* Set IR drop compensation */ - r = ocpc->combined_rsys_rbatt_mo * 100 / 167; /* 1.67mOhm steps */ - r = MAX(0, r); - rv = chg_write8(chgnum, SM5803_REG_IR_COMP2, r & 0x7F); - rv |= chg_read8(chgnum, SM5803_REG_IR_COMP1, ®val); - regval &= ~SM5803_IR_COMP_RES_SET_MSB; - r = r >> 8; /* Bits 9:8 */ - regval |= (r & 0x3) << SM5803_IR_COMP_RES_SET_MSB_SHIFT; - regval |= SM5803_IR_COMP_EN; - rv |= chg_write8(chgnum, SM5803_REG_IR_COMP1, regval); - - if (rv) - return EC_ERROR_UNKNOWN; - - return EC_ERROR_UNIMPLEMENTED; -} - -/* Hardware current ramping (aka DPM: Dynamic Power Management) */ - -#ifdef CONFIG_CHARGE_RAMP_HW -static enum ec_error_list sm5803_set_hw_ramp(int chgnum, int enable) -{ - enum ec_error_list rv; - int reg; - - rv = chg_read8(chgnum, SM5803_REG_CHG_MON_REG, ®); - - if (enable) - reg |= SM5803_DPM_LOOP_EN; - else - reg &= ~SM5803_DPM_LOOP_EN; - - rv |= chg_write8(chgnum, SM5803_REG_CHG_MON_REG, reg); - - return rv; -} - -static int sm5803_ramp_is_stable(int chgnum) -{ - /* - * There is no way to read current limit that the ramp has - * settled on with sm5803, so we don't consider the ramp stable, - * because we never know what the stable limit is. - */ - return 0; -} - -static int sm5803_ramp_is_detected(int chgnum) -{ - return 1; -} - -static int sm5803_ramp_get_current_limit(int chgnum) -{ - int rv; - int input_current = 0; - - rv = sm5803_get_input_current_limit(chgnum, &input_current); - - return rv ? -1 : input_current; -} -#endif /* CONFIG_CHARGE_RAMP_HW */ - -#ifdef CONFIG_CMD_CHARGER_DUMP -static int command_sm5803_dump(int argc, char **argv) -{ - int reg; - int regval; - int chgnum = 0; - - if (argc > 1) - chgnum = atoi(argv[1]); - - /* Dump base regs */ - ccprintf("BASE regs\n"); - for (reg = 0x01; reg <= 0x30; reg++) { - if (!main_read8(chgnum, reg, ®val)) - ccprintf("[0x%02X] = 0x%02x\n", reg, regval); - if (reg & 0xf) { - cflush(); /* Flush periodically */ - watchdog_reload(); - } - } - - /* Dump measure regs */ - ccprintf("MEAS regs\n"); - for (reg = 0x01; reg <= 0xED; reg++) { - if (!meas_read8(chgnum, reg, ®val)) - ccprintf("[0x%02X] = 0x%02x\n", reg, regval); - if (reg & 0xf) { - cflush(); /* Flush periodically */ - watchdog_reload(); - } - } - - /* Dump Charger regs from 0x1C to 0x7F */ - ccprintf("CHG regs\n"); - for (reg = 0x1C; reg <= 0x7F; reg++) { - if (!chg_read8(chgnum, reg, ®val)) - ccprintf("[0x%02X] = 0x%02x\n", reg, regval); - if (reg & 0xf) { - cflush(); /* Flush periodically */ - watchdog_reload(); - } - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(charger_dump, command_sm5803_dump, - "charger_dump [chgnum]", "Dumps SM5803 registers"); -#endif /* CONFIG_CMD_CHARGER_DUMP */ - -const struct charger_drv sm5803_drv = { - .init = &sm5803_init, - .post_init = &sm5803_post_init, - .get_info = &sm5803_get_info, - .get_status = &sm5803_get_status, - .set_mode = &sm5803_set_mode, - .get_actual_current = &sm5803_get_actual_current, - .get_current = &sm5803_get_current, - .set_current = &sm5803_set_current, - .get_actual_voltage = &sm5803_get_actual_voltage, - .get_voltage = &sm5803_get_voltage, - .set_voltage = &sm5803_set_voltage, - .discharge_on_ac = &sm5803_discharge_on_ac, - .get_vbus_voltage = &sm5803_get_vbus_voltage, - .set_input_current_limit = &sm5803_set_input_current_limit, - .get_input_current_limit = &sm5803_get_input_current_limit, - .get_input_current = &sm5803_get_input_current, - .device_id = &sm5803_get_dev_id, - .get_option = &sm5803_get_option, - .set_option = &sm5803_set_option, - .set_otg_current_voltage = &sm5803_set_otg_current_voltage, - .enable_otg_power = &sm5803_enable_otg_power, - .is_sourcing_otg_power = &sm5803_is_sourcing_otg_power, - .set_vsys_compensation = &sm5803_set_vsys_compensation, - .is_icl_reached = &sm5803_is_input_current_limit_reached, - .enable_linear_charge = &sm5803_enable_linear_charge, -#ifdef CONFIG_CHARGE_RAMP_HW - .set_hw_ramp = &sm5803_set_hw_ramp, - .ramp_is_stable = &sm5803_ramp_is_stable, - .ramp_is_detected = &sm5803_ramp_is_detected, - .ramp_get_current_limit = &sm5803_ramp_get_current_limit, -#endif -}; |