summaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
authorVijay Hiremath <vijay.p.hiremath@intel.com>2016-04-16 01:08:55 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-05-24 15:46:01 -0700
commit313355302b2bc829e7ca16455ffa2a39b46ff4f4 (patch)
treeeb0f338c0c59156408c90402b61847665d157746 /driver
parentf4e617e11866c4ce353d42f8c83331017fbf7eb2 (diff)
downloadchrome-ec-313355302b2bc829e7ca16455ffa2a39b46ff4f4.tar.gz
Driver: BD99955: Enable BC1.2 support
BUG=none BRANCH=none TEST=Manually tested on Amenia. Connected Zinger, Type-C, DCP & CDP chargers. Device can negotiate to desired current & voltage and the battery can charge. USB2.0 sync device is detected by Kernel. Change-Id: I58cb69289eef9a966e06bef8fe31d35beaec5e27 Signed-off-by: Vijay Hiremath <vijay.p.hiremath@intel.com> Reviewed-on: https://chromium-review.googlesource.com/341030 Commit-Ready: Vijay P Hiremath <vijay.p.hiremath@intel.com> Tested-by: Vijay P Hiremath <vijay.p.hiremath@intel.com> Tested-by: Kevin K Wong <kevin.k.wong@intel.com> Reviewed-by: Shawn N <shawnn@chromium.org>
Diffstat (limited to 'driver')
-rw-r--r--driver/charger/bd99955.c274
-rw-r--r--driver/charger/bd99955.h42
2 files changed, 301 insertions, 15 deletions
diff --git a/driver/charger/bd99955.c b/driver/charger/bd99955.c
index 713a90be1c..c07cf70493 100644
--- a/driver/charger/bd99955.c
+++ b/driver/charger/bd99955.c
@@ -8,16 +8,28 @@
#include "battery.h"
#include "battery_smart.h"
#include "bd99955.h"
+#include "charge_manager.h"
#include "charger.h"
#include "console.h"
+#include "ec_commands.h"
#include "hooks.h"
#include "i2c.h"
#include "task.h"
+#include "time.h"
#include "util.h"
+#include "usb_charge.h"
+#include "usb_pd.h"
+
+#define OTPROM_LOAD_WAIT_RETRY 3
+
+#define BD99955_CHARGE_PORT_COUNT 2
/* Console output macros */
#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args)
+/* TODO: Add accurate timeout for detecting BC1.2 */
+#define BC12_DETECT_RETRY 10
+
/* Charger parameters */
static const struct charger_info bd99955_charger_info = {
.name = CHARGER_NAME,
@@ -37,6 +49,14 @@ static enum bd99955_command charger_map_cmd = BD99955_INVALID_COMMAND;
static struct mutex bd99955_map_mutex;
+#if defined(HAS_TASK_USB_CHG_P0) || defined(HAS_TASK_USB_CHG_P1)
+/* USB switch */
+static enum usb_switch usb_switch_state[BD99955_CHARGE_PORT_COUNT] = {
+ USB_SWITCH_DISCONNECT,
+ USB_SWITCH_DISCONNECT,
+};
+#endif
+
static inline int ch_raw_read16(int cmd, int *param,
enum bd99955_command map_cmd)
{
@@ -108,9 +128,34 @@ static int bd99955_charger_enable(int enable)
static int bd99955_por_reset(void)
{
- return ch_raw_write16(BD99955_CMD_SYSTEM_CTRL_SET,
- BD99955_CMD_SYSTEM_CTRL_SET_OTPLD |
- BD99955_CMD_SYSTEM_CTRL_SET_ALLRST,
+ int rv;
+ int reg;
+ int i;
+
+ rv = ch_raw_write16(BD99955_CMD_SYSTEM_CTRL_SET,
+ BD99955_CMD_SYSTEM_CTRL_SET_OTPLD |
+ BD99955_CMD_SYSTEM_CTRL_SET_ALLRST,
+ BD99955_EXTENDED_COMMAND);
+ if (rv)
+ return rv;
+
+ /* Wait until OTPROM loading is finished */
+ for (i = 0; i < OTPROM_LOAD_WAIT_RETRY; i++) {
+ msleep(10);
+ rv = ch_raw_read16(BD99955_CMD_SYSTEM_STATUS, &reg,
+ BD99955_EXTENDED_COMMAND);
+
+ if (!rv && (reg & BD99955_CMD_SYSTEM_STATUS_OTPLD_STATE) &&
+ (reg & BD99955_CMD_SYSTEM_STATUS_ALLRST_STATE))
+ break;
+ }
+
+ if (rv)
+ return rv;
+ if (i == OTPROM_LOAD_WAIT_RETRY)
+ return EC_ERROR_TIMEOUT;
+
+ return ch_raw_write16(BD99955_CMD_SYSTEM_CTRL_SET, 0,
BD99955_EXTENDED_COMMAND);
}
@@ -131,6 +176,125 @@ static int bd99955_get_charger_op_status(int *status)
BD99955_EXTENDED_COMMAND);
}
+#if defined(HAS_TASK_USB_CHG_P0) || defined(HAS_TASK_USB_CHG_P1)
+static int bd99955_get_bc12_device_type(enum bd99955_charge_port port)
+{
+ int rv;
+ int reg;
+
+ rv = ch_raw_read16((port == BD99955_CHARGE_PORT_VBUS) ?
+ BD99955_CMD_VBUS_UCD_STATUS :
+ BD99955_CMD_VCC_UCD_STATUS,
+ &reg, BD99955_EXTENDED_COMMAND);
+ if (rv)
+ return CHARGE_SUPPLIER_NONE;
+
+ switch (reg & BD99955_TYPE_MASK) {
+ case BD99955_TYPE_CDP:
+ return CHARGE_SUPPLIER_BC12_CDP;
+ case BD99955_TYPE_DCP:
+ return CHARGE_SUPPLIER_BC12_DCP;
+ case BD99955_TYPE_SDP:
+ return CHARGE_SUPPLIER_BC12_SDP;
+ case BD99955_TYPE_VBUS_OPEN:
+ case BD99955_TYPE_PUP_PORT:
+ case BD99955_TYPE_OPEN_PORT:
+ default:
+ return CHARGE_SUPPLIER_NONE;
+ }
+}
+
+static int bd99955_get_bc12_ilim(int charge_supplier)
+{
+ switch (charge_supplier) {
+ case CHARGE_SUPPLIER_BC12_CDP:
+ return 1500;
+ case CHARGE_SUPPLIER_BC12_DCP:
+ return 2000;
+ case CHARGE_SUPPLIER_BC12_SDP:
+ return 900;
+ default:
+ return 500;
+ }
+}
+
+static int bd99955_enable_usb_switch(enum bd99955_charge_port port,
+ enum usb_switch setting)
+{
+ int rv;
+ int reg;
+ int port_reg;
+
+ port_reg = (port == BD99955_CHARGE_PORT_VBUS) ?
+ BD99955_CMD_VBUS_UCD_SET : BD99955_CMD_VCC_UCD_SET;
+
+ rv = ch_raw_read16(port_reg, &reg, BD99955_EXTENDED_COMMAND);
+ if (rv)
+ return rv;
+
+ if (setting == USB_SWITCH_CONNECT)
+ reg |= BD99955_CMD_UCD_SET_USB_SW_EN;
+ else
+ reg &= ~BD99955_CMD_UCD_SET_USB_SW_EN;
+
+ return ch_raw_write16(port_reg, reg, BD99955_EXTENDED_COMMAND);
+}
+
+static int bd99955_bc12_detect(int port)
+{
+ int i;
+ int bc12_type;
+ struct charge_port_info charge;
+
+ /*
+ * BC1.2 detection starts 100ms after VBUS/VCC attach and typically
+ * completes 312ms after VBUS/VCC attach.
+ */
+ msleep(312);
+ for (i = 0; i < BC12_DETECT_RETRY; i++) {
+ /* get device type */
+ bc12_type = bd99955_get_bc12_device_type(port);
+
+ /* Detected BC1.2 */
+ if (bc12_type != CHARGE_SUPPLIER_NONE)
+ break;
+
+ /* TODO: Add accurate timeout for detecting BC1.2 */
+ msleep(100);
+ }
+
+ /* BC1.2 device attached */
+ if (bc12_type != CHARGE_SUPPLIER_NONE) {
+ /* Update charge manager */
+ charge.voltage = USB_CHARGER_VOLTAGE_MV;
+ charge.current = bd99955_get_bc12_ilim(bc12_type);
+ charge_manager_update_charge(bc12_type, port, &charge);
+
+ /* notify host of power info change */
+ pd_send_host_event(PD_EVENT_POWER_CHANGE);
+ }
+
+ return bc12_type;
+}
+
+static void bd99955_bc12_detach(int port, int type)
+{
+ struct charge_port_info charge = {
+ .voltage = USB_CHARGER_VOLTAGE_MV,
+ .current = 0,
+ };
+
+ /* Update charge manager */
+ charge_manager_update_charge(type, port, &charge);
+
+ /* Disable charging trigger by BC1.2 detection */
+ bd99955_bc12_enable_charging(port, 0);
+
+ /* notify host of power info change */
+ pd_send_host_event(PD_EVENT_POWER_CHANGE);
+}
+#endif /* defined(HAS_TASK_USB_CHG_P0) || defined(HAS_TASK_USB_CHG_P1) */
+
/* chip specific interfaces */
@@ -140,7 +304,6 @@ int charger_set_input_current(int input_current)
/* Input current step 32 mA */
input_current &= ~0x1F;
-
rv = ch_raw_write16(BD99955_CMD_IBUS_LIM_SET, input_current,
BD99955_BAT_CHG_COMMAND);
if (rv)
@@ -247,9 +410,7 @@ int charger_get_status(int *status)
*status |= CHARGER_POWER_FAIL;
/* Safety signal ranges & battery presence */
- ch_status = (reg & BD99955_CMD_CHGOP_STATUS_BATTEMP0) |
- ((reg & BD99955_CMD_CHGOP_STATUS_BATTEMP1) << 1) |
- ((reg & BD99955_CMD_CHGOP_STATUS_BATTEMP2) << 2);
+ ch_status = (reg & BD99955_BATTTEMP_MASK) >> 8;
*status |= CHARGER_BATTERY_PRESENT;
@@ -346,31 +507,44 @@ static void bd99995_init(void)
int reg;
const struct battery_info *bi = battery_get_info();
- /* Disable BC1.2 detection on VCC */
+ /* Enable BC1.2 detection on VCC */
if (ch_raw_read16(BD99955_CMD_VCC_UCD_SET, &reg,
BD99955_EXTENDED_COMMAND))
return;
- reg &= ~BD99955_CMD_UCD_SET_USBDETEN;
+ reg |= BD99955_CMD_UCD_SET_USBDETEN;
+ reg &= ~BD99955_CMD_UCD_SET_USB_SW_EN;
ch_raw_write16(BD99955_CMD_VCC_UCD_SET, reg,
BD99955_EXTENDED_COMMAND);
- /* Disable BC1.2 detection on VBUS */
+ /* Enable BC1.2 detection on VBUS */
if (ch_raw_read16(BD99955_CMD_VBUS_UCD_SET, &reg,
BD99955_EXTENDED_COMMAND))
return;
- reg &= ~BD99955_CMD_UCD_SET_USBDETEN;
+ reg |= BD99955_CMD_UCD_SET_USBDETEN;
+ reg &= ~BD99955_CMD_UCD_SET_USB_SW_EN;
ch_raw_write16(BD99955_CMD_VBUS_UCD_SET, reg,
BD99955_EXTENDED_COMMAND);
- /* Disable BC1.2 charge enable trigger */
+ /* Disable charging trigger by BC1.2 on VCC & VBUS. */
if (ch_raw_read16(BD99955_CMD_CHGOP_SET1, &reg,
BD99955_EXTENDED_COMMAND))
return;
- reg |= (BD99955_CMD_CHGOP_SET1_VCC_BC_DISEN |
- BD99955_CMD_CHGOP_SET1_VBUS_BC_DISEN);
+ reg |= (BD99955_CMD_CHGOP_SET1_SDP_CHG_TRIG_EN |
+ BD99955_CMD_CHGOP_SET1_SDP_CHG_TRIG |
+ BD99955_CMD_CHGOP_SET1_VBUS_BC_DISEN |
+ BD99955_CMD_CHGOP_SET1_VCC_BC_DISEN |
+ BD99955_CMD_CHGOP_SET1_ILIM_AUTO_DISEN);
ch_raw_write16(BD99955_CMD_CHGOP_SET1, reg,
BD99955_EXTENDED_COMMAND);
+ /* Enable BC1.2 USB charging and DC/DC converter */
+ if (ch_raw_read16(BD99955_CMD_CHGOP_SET2, &reg,
+ BD99955_EXTENDED_COMMAND))
+ return;
+ reg &= ~(BD99955_CMD_CHGOP_SET2_USB_SUS);
+ ch_raw_write16(BD99955_CMD_CHGOP_SET2, reg,
+ BD99955_EXTENDED_COMMAND);
+
/* Set battery OVP to 500 + maximum battery voltage */
ch_raw_write16(BD99955_CMD_VBATOVP_SET,
(bi->voltage_max + 500) & 0x7ff0,
@@ -449,6 +623,78 @@ int bd99955_select_input_port(enum bd99955_charge_port port)
BD99955_EXTENDED_COMMAND);
}
+#if defined(HAS_TASK_USB_CHG_P0) || defined(HAS_TASK_USB_CHG_P1)
+int bd99955_bc12_enable_charging(enum bd99955_charge_port port, int enable)
+{
+ int rv;
+ int reg;
+ int mask_val;
+
+ /*
+ * For BC1.2, enable VBUS/VCC_BC_DISEN charging trigger by BC1.2
+ * detection and disable SDP_CHG_TRIG, SDP_CHG_TRIG_EN. Vice versa
+ * for USB-C.
+ */
+ rv = ch_raw_read16(BD99955_CMD_CHGOP_SET1, &reg,
+ BD99955_EXTENDED_COMMAND);
+ if (rv)
+ return rv;
+
+ mask_val = (BD99955_CMD_CHGOP_SET1_SDP_CHG_TRIG_EN |
+ BD99955_CMD_CHGOP_SET1_SDP_CHG_TRIG |
+ ((port == BD99955_CHARGE_PORT_VBUS) ?
+ BD99955_CMD_CHGOP_SET1_VBUS_BC_DISEN :
+ BD99955_CMD_CHGOP_SET1_VCC_BC_DISEN));
+
+ if (enable)
+ reg &= ~mask_val;
+ else
+ reg |= mask_val;
+
+ return ch_raw_write16(BD99955_CMD_CHGOP_SET1, reg,
+ BD99955_EXTENDED_COMMAND);
+}
+
+void usb_charger_set_switches(int port, enum usb_switch setting)
+{
+ /* If switch is not changing then return */
+ if (setting == usb_switch_state[port] ||
+ pd_snk_is_vbus_provided(port))
+ return;
+
+ if (setting != USB_SWITCH_RESTORE)
+ usb_switch_state[port] = setting;
+ bd99955_enable_usb_switch(port, usb_switch_state[port]);
+}
+
+void usb_charger_task(void)
+{
+ int port = (task_get_current() == TASK_ID_USB_CHG_P0 ? 0 : 1);
+ int bc12_type = CHARGE_SUPPLIER_NONE;
+ int vbus_provided;
+
+ while (1) {
+ vbus_provided = pd_snk_is_vbus_provided(port);
+
+ if (vbus_provided) {
+ /* Charger/sync attached */
+ bc12_type = bd99955_bc12_detect(port);
+ } else if (bc12_type != CHARGE_SUPPLIER_NONE &&
+ !vbus_provided) {
+ /* Charger/sync detached */
+ bd99955_bc12_detach(port, bc12_type);
+ bc12_type = CHARGE_SUPPLIER_NONE;
+ }
+
+ /* Wait for interrupt */
+ task_wait_event(-1);
+ }
+}
+#endif /* defined(HAS_TASK_USB_CHG_P0) || defined(HAS_TASK_USB_CHG_P1) */
+
+
+/*** Console commands ***/
+
#ifdef CONFIG_CMD_CHARGER
static int read_bat(uint8_t cmd)
{
diff --git a/driver/charger/bd99955.h b/driver/charger/bd99955.h
index 0f3e4aa48d..69884dce13 100644
--- a/driver/charger/bd99955.h
+++ b/driver/charger/bd99955.h
@@ -58,6 +58,7 @@ enum bd99955_charge_port {
#define BD99955_CMD_CHGOP_STATUS_BATTEMP2 (1 << 10)
#define BD99955_CMD_CHGOP_STATUS_BATTEMP1 (1 << 9)
#define BD99955_CMD_CHGOP_STATUS_BATTEMP0 (1 << 8)
+#define BD99955_BATTTEMP_MASK 0x700
#define BD99955_CMD_CHGOP_STATUS_BATTEMP_ROOMTEMP 0
#define BD99955_CMD_CHGOP_STATUS_BATTEMP_HOT1 1
#define BD99955_CMD_CHGOP_STATUS_BATTEMP_HOT2 2
@@ -81,11 +82,16 @@ enum bd99955_charge_port {
#define BD99955_CMD_VIN_CTRL_SET_VCC_EN (1 << 5)
#define BD99955_CMD_CHGOP_SET1 0x0B
+#define BD99955_CMD_CHGOP_SET1_ILIM_AUTO_DISEN (1 << 13)
#define BD99955_CMD_CHGOP_SET1_VCC_BC_DISEN (1 << 11)
#define BD99955_CMD_CHGOP_SET1_VBUS_BC_DISEN (1 << 10)
+#define BD99955_CMD_CHGOP_SET1_SDP_CHG_TRIG_EN (1 << 9)
+#define BD99955_CMD_CHGOP_SET1_SDP_CHG_TRIG (1 << 8)
+
#define BD99955_CMD_CHGOP_SET2 0x0C
#define BD99955_CMD_CHGOP_SET2_BATT_LEARN (1 << 8)
#define BD99955_CMD_CHGOP_SET2_CHG_EN (1 << 7)
+#define BD99955_CMD_CHGOP_SET2_USB_SUS (1 << 6)
#define BD99955_CMD_VBUSCLPS_TH_SET 0x0D
#define BD99955_CMD_VCCCLPS_TH_SET 0x0E
@@ -121,10 +127,39 @@ enum bd99955_charge_port {
#define BD99955_CMD_PMON_DACIN_VAL 0x26
#define BD99955_CMD_IOUT_DACIN_VAL 0x27
#define BD99955_CMD_VCC_UCD_SET 0x28
-/* Bits for both VCC_UDC_SET and VBUS_UCD_SET regs */
+/* Bits for both VCC_UCD_SET and VBUS_UCD_SET regs */
#define BD99955_CMD_UCD_SET_BCSRETRY (1 << 12)
#define BD99955_CMD_UCD_SET_USBDETEN (1 << 7)
+#define BD99955_CMD_UCD_SET_USB_SW_EN (1 << 1)
+
#define BD99955_CMD_VCC_UCD_STATUS 0x29
+/* Bits for both VCC_UCD_STATUS and VBUS_UCD_STATUS regs */
+#define BD99955_CMD_UCD_STATUS_DCDFAIL (1 << 15)
+#define BD99955_CMD_UCD_STATUS_CHGPORT1 (1 << 13)
+#define BD99955_CMD_UCD_STATUS_CHGPORT0 (1 << 12)
+#define BD99955_CMD_UCD_STATUS_PUPDET (1 << 11)
+#define BD99955_CMD_UCD_STATUS_CHGDET (1 << 6)
+#define BD99955_TYPE_VBUS_OPEN 0
+#define BD99955_TYPE_SDP BD99955_CMD_UCD_STATUS_CHGPORT0
+/*
+ * TODO: For CDP detection, from the datasheet CHGDET & CHGPORT[1] bits need
+ * to be high and rest need to be low. However following bits are high CHGDET,
+ * DCDFAIL, CHGPORT[1], CHGPORT[0] and rest low.
+ */
+#define BD99955_TYPE_CDP (BD99955_CMD_UCD_STATUS_CHGDET | \
+ BD99955_CMD_UCD_STATUS_CHGPORT1 | \
+ BD99955_CMD_UCD_STATUS_CHGPORT0 | \
+ BD99955_CMD_UCD_STATUS_DCDFAIL)
+#define BD99955_TYPE_DCP (BD99955_CMD_UCD_STATUS_CHGDET | \
+ BD99955_CMD_UCD_STATUS_CHGPORT0 | \
+ BD99955_CMD_UCD_STATUS_CHGPORT1)
+#define BD99955_TYPE_PUP_PORT (BD99955_CMD_UCD_STATUS_PUPDET | \
+ BD99955_CMD_UCD_STATUS_DCDFAIL | \
+ BD99955_CMD_UCD_STATUS_CHGPORT0)
+#define BD99955_TYPE_OPEN_PORT (BD99955_CMD_UCD_STATUS_DCDFAIL | \
+ BD99955_CMD_UCD_STATUS_CHGPORT0)
+#define BD99955_TYPE_MASK 0xB840
+
#define BD99955_CMD_VCC_IDD_STATUS 0x2A
#define BD99955_CMD_VCC_UCD_FCTRL_SET 0x2B
#define BD99955_CMD_VCC_UCD_FCTRL_EN 0x2C
@@ -138,6 +173,9 @@ enum bd99955_charge_port {
#define BD99955_CMD_IC_SET1 0x3A
#define BD99955_CMD_IC_SET2 0x3B
#define BD99955_CMD_SYSTEM_STATUS 0x3C
+#define BD99955_CMD_SYSTEM_STATUS_OTPLD_STATE (1 << 1)
+#define BD99955_CMD_SYSTEM_STATUS_ALLRST_STATE (1 << 0)
+
#define BD99955_CMD_SYSTEM_CTRL_SET 0x3D
#define BD99955_CMD_SYSTEM_CTRL_SET_OTPLD (1 << 1)
#define BD99955_CMD_SYSTEM_CTRL_SET_ALLRST (1 << 0)
@@ -213,5 +251,7 @@ enum bd99955_charge_port {
int bd99955_extpower_is_present(void);
/* Select input port from {VCC, VBUS, VCC&VBUS, NONE}. */
int bd99955_select_input_port(enum bd99955_charge_port port);
+/* Enable/Disable charging triggered by BC1.2 */
+int bd99955_bc12_enable_charging(enum bd99955_charge_port port, int enable);
#endif /* __CROS_EC_BD99955_H */