From a4ef74039104fb7da4137960c2c023f45dda6924 Mon Sep 17 00:00:00 2001 From: Shawn Nematbakhsh Date: Thu, 12 May 2016 16:06:05 -0700 Subject: usb_charger: Move part-specific code to usb_switch driver Previously usb_charger.c supported only pi3usb9281, but now support for additional parts is required. Move pericom-specific code (including the usb_charger tasks that handles various quirks of that part) to the pi3usb9281 usb_switch driver. Going forward, usb_switch drivers must implement usb_charger_set_switches() and must have some method (such as a task or interrupt handler) to update charge_manager with information about attached chargers. BUG=chrome-os-partner:53363 BRANCH=None TEST=`make buildall -j` Signed-off-by: Shawn Nematbakhsh Change-Id: I4df74e043d8cf2e532d48c39c73b7dc2930f7d3b Reviewed-on: https://chromium-review.googlesource.com/344289 Commit-Ready: Shawn N Tested-by: Shawn N Reviewed-by: Vincent Palatin --- common/usb_charger.c | 195 ++----------------------------------- driver/pi3usb9281.h | 19 +--- driver/usb_switch_pi3usb9281.c | 216 +++++++++++++++++++++++++++++++++++++---- include/config.h | 2 +- 4 files changed, 204 insertions(+), 228 deletions(-) diff --git a/common/usb_charger.c b/common/usb_charger.c index c56e0f4d0f..8e938233a3 100644 --- a/common/usb_charger.c +++ b/common/usb_charger.c @@ -4,40 +4,23 @@ */ /* - * USB charger / BC1.2 task. This code assumes that CONFIG_CHARGE_MANAGER - * is defined and implemented. PI3USB9281 is the only charger detector - * currently supported. + * USB charger interface routines. This code assumes that CONFIG_CHARGE_MANAGER + * is defined and implemented. + * usb_charger_set_switches() must be implemented by a companion + * usb_switch driver. + * In addition, USB switch-specific usb_charger task or interrupt routine + * is necessary to update charge_manager with detected charger attributes. */ #include "charge_manager.h" #include "common.h" #include "console.h" -#include "ec_commands.h" #include "gpio.h" #include "hooks.h" -#include "pi3usb9281.h" #include "task.h" -#include "timer.h" #include "usb_charge.h" #include "usb_pd.h" -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -/* Wait after a charger is detected to debounce pin contact order */ -#define USB_CHG_DEBOUNCE_DELAY_MS 1000 -/* - * Wait after reset, before re-enabling attach interrupt, so that the - * spurious attach interrupt from certain ports is ignored. - */ -#define USB_CHG_RESET_DELAY_MS 100 - -/* - * Store the state of our USB data switches so that they can be restored - * after pericom reset. - */ -static int usb_switch_state[CONFIG_USB_PD_PORT_COUNT]; -static struct mutex usb_switch_lock[CONFIG_USB_PD_PORT_COUNT]; - static void update_vbus_supplier(int port, int vbus_level) { struct charge_port_info charge; @@ -67,19 +50,6 @@ int usb_charger_port_is_sourcing_vbus(int port) return 0; } -void usb_charger_set_switches(int port, enum usb_switch setting) -{ - /* If switch is not changing then return */ - if (setting == usb_switch_state[port]) - return; - - mutex_lock(&usb_switch_lock[port]); - if (setting != USB_SWITCH_RESTORE) - usb_switch_state[port] = setting; - pi3usb9281_set_switches(port, usb_switch_state[port]); - mutex_unlock(&usb_switch_lock[port]); -} - void usb_charger_vbus_change(int port, int vbus_level) { /* If VBUS has transitioned low, notify PD module directly */ @@ -94,159 +64,6 @@ void usb_charger_vbus_change(int port, int vbus_level) #endif } -static void usb_charger_bc12_detect(int port) -{ - int device_type, charger_status; - struct charge_port_info charge; - int type; - - charge.voltage = USB_CHARGER_VOLTAGE_MV; - - if (usb_charger_port_is_sourcing_vbus(port)) { - /* If we're sourcing VBUS then we're not charging */ - device_type = charger_status = 0; - } else { - /* Set device type */ - device_type = pi3usb9281_get_device_type(port); - charger_status = pi3usb9281_get_charger_status(port); - } - - /* Debounce pin plug order if we detect a charger */ - if (device_type || PI3USB9281_CHG_STATUS_ANY(charger_status)) { - /* next operation might trigger a detach interrupt */ - pi3usb9281_disable_interrupts(port); - /* - * Ensure D+/D- are open before resetting - * Note: we can't simply call pi3usb9281_set_switches() because - * another task might override it and set the switches closed. - */ - pi3usb9281_set_switch_manual(port, 1); - pi3usb9281_set_pins(port, 0); - - /* Delay to debounce pin attach order */ - msleep(USB_CHG_DEBOUNCE_DELAY_MS); - - /* - * Trigger chip reset to refresh detection registers. - * WARNING: This reset is acceptable for samus_pd, - * but may not be acceptable for devices that have - * an OTG / device mode, as we may be interrupting - * the connection. - */ - pi3usb9281_reset(port); - /* - * Restore data switch settings - switches return to - * closed on reset until restored. - */ - usb_charger_set_switches(port, USB_SWITCH_RESTORE); - /* Clear possible disconnect interrupt */ - pi3usb9281_get_interrupts(port); - /* Mask attach interrupt */ - pi3usb9281_set_interrupt_mask(port, - 0xff & - ~PI3USB9281_INT_ATTACH); - /* Re-enable interrupts */ - pi3usb9281_enable_interrupts(port); - msleep(USB_CHG_RESET_DELAY_MS); - - /* Clear possible attach interrupt */ - pi3usb9281_get_interrupts(port); - /* Re-enable attach interrupt */ - pi3usb9281_set_interrupt_mask(port, 0xff); - - /* Re-read ID registers */ - device_type = pi3usb9281_get_device_type(port); - charger_status = pi3usb9281_get_charger_status(port); - } - - /* Attachment: decode + update available charge */ - if (device_type || PI3USB9281_CHG_STATUS_ANY(charger_status)) { - if (PI3USB9281_CHG_STATUS_ANY(charger_status)) - type = CHARGE_SUPPLIER_PROPRIETARY; - else if (device_type & PI3USB9281_TYPE_CDP) - type = CHARGE_SUPPLIER_BC12_CDP; - else if (device_type & PI3USB9281_TYPE_DCP) - type = CHARGE_SUPPLIER_BC12_DCP; - else if (device_type & PI3USB9281_TYPE_SDP) - type = CHARGE_SUPPLIER_BC12_SDP; - else - type = CHARGE_SUPPLIER_OTHER; - - charge.current = pi3usb9281_get_ilim(device_type, - charger_status); - charge_manager_update_charge(type, port, &charge); - } else { /* Detachment: update available charge to 0 */ - charge.current = 0; - charge_manager_update_charge( - CHARGE_SUPPLIER_PROPRIETARY, - port, - &charge); - charge_manager_update_charge( - CHARGE_SUPPLIER_BC12_CDP, - port, - &charge); - charge_manager_update_charge( - CHARGE_SUPPLIER_BC12_DCP, - port, - &charge); - charge_manager_update_charge( - CHARGE_SUPPLIER_BC12_SDP, - port, - &charge); - charge_manager_update_charge( - CHARGE_SUPPLIER_OTHER, - port, - &charge); - } - - /* notify host of power info change */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} - -void usb_charger_task(void) -{ - const int attach_mask = PI3USB9281_INT_ATTACH | PI3USB9281_INT_DETACH; - int port = (task_get_current() == TASK_ID_USB_CHG_P0 ? 0 : 1); - int interrupt; - uint32_t evt; - - /* Initialize chip and enable interrupts */ - pi3usb9281_init(port); - - usb_charger_bc12_detect(port); - - while (1) { - /* Wait for interrupt */ - evt = task_wait_event(-1); - - /* Interrupt from the Pericom chip, determine charger type */ - if (evt & USB_CHG_EVENT_BC12) { - /* Read interrupt register to clear on chip */ - pi3usb9281_get_interrupts(port); - usb_charger_bc12_detect(port); - } else if (evt & USB_CHG_EVENT_INTR) { - /* Check the interrupt register, and clear on chip */ - interrupt = pi3usb9281_get_interrupts(port); - if (interrupt & attach_mask) - usb_charger_bc12_detect(port); - } - - /* - * Re-enable interrupts on pericom charger detector since the - * chip may periodically reset itself, and come back up with - * registers in default state. TODO(crosbug.com/p/33823): Fix - * these unwanted resets. - */ - if (evt & USB_CHG_EVENT_VBUS) { - pi3usb9281_enable_interrupts(port); -#ifndef CONFIG_USB_PD_TCPM_VBUS - CPRINTS("VBUS p%d %d", port, - pd_snk_is_vbus_provided(port)); -#endif - } - } -} - static void usb_charger_init(void) { int i; diff --git a/driver/pi3usb9281.h b/driver/pi3usb9281.h index 929476c81b..fc642435c6 100644 --- a/driver/pi3usb9281.h +++ b/driver/pi3usb9281.h @@ -71,6 +71,7 @@ struct pi3usb9281_config { /* Configuration struct defined at board level */ extern struct pi3usb9281_config pi3usb9281_chips[]; +/* TODO: Make many of these functions static after Oak board changes. */ /* Initialize chip and enable interrupts */ void pi3usb9281_init(int port); @@ -80,9 +81,6 @@ int pi3usb9281_enable_interrupts(int port); /* Disable all interrupts. */ int pi3usb9281_disable_interrupts(int port); -/* Set interrupt mask. */ -int pi3usb9281_set_interrupt_mask(int port, uint8_t mask); - /* Get and clear current interrupt status. */ int pi3usb9281_get_interrupts(int port); @@ -92,19 +90,4 @@ int pi3usb9281_get_device_type(int port); /* Get attached charger status. */ int pi3usb9281_get_charger_status(int port); -/* Get charger current limit based on device type and charger status. */ -int pi3usb9281_get_ilim(int device_type, int charger_status); - -/* Set switch configuration to manual. */ -int pi3usb9281_set_switch_manual(int port, int val); - -/* Set bits to enable pins in manual switch register. */ -int pi3usb9281_set_pins(int port, uint8_t mask); - -/* Set D+/D-/Vbus switches to open or closed/auto-control. */ -int pi3usb9281_set_switches(int port, int open); - -/* Reset PI3USB9281. */ -int pi3usb9281_reset(int port); - #endif /* __CROS_EC_PI3USB9281_H */ diff --git a/driver/usb_switch_pi3usb9281.c b/driver/usb_switch_pi3usb9281.c index 063d7d3983..569438b761 100644 --- a/driver/usb_switch_pi3usb9281.c +++ b/driver/usb_switch_pi3usb9281.c @@ -5,13 +5,18 @@ * Pericom PI3USB3281 USB port switch driver. */ +#include "charge_manager.h" +#include "common.h" #include "console.h" +#include "ec_commands.h" #include "gpio.h" #include "hooks.h" #include "i2c.h" +#include "pi3usb9281.h" #include "task.h" #include "timer.h" -#include "pi3usb9281.h" +#include "usb_charge.h" +#include "usb_pd.h" #include "util.h" /* Console output macros */ @@ -24,6 +29,18 @@ /* Delay values */ #define PI3USB9281_SW_RESET_DELAY 20 +/* Wait after a charger is detected to debounce pin contact order */ +#define PI3USB9281_DETECT_DEBOUNCE_DELAY_MS 1000 +/* + * Wait after reset, before re-enabling attach interrupt, so that the + * spurious attach interrupt from certain ports is ignored. + */ +#define PI3USB9281_RESET_DEBOUNCE_DELAY_MS 100 + +/* Store the state of our USB data switches so that they can be restored. */ +static int usb_switch_state[CONFIG_USB_PD_PORT_COUNT]; +static struct mutex usb_switch_lock[CONFIG_USB_PD_PORT_COUNT]; + static void select_chip(int port) { struct pi3usb9281_config *chip = &pi3usb9281_chips[port]; @@ -81,6 +98,11 @@ static int pi3usb9281_write_ctrl(int port, uint8_t ctrl) PI3USB9281_CTRL_RSVD_1); } +static int pi3usb9281_set_interrupt_mask(int port, uint8_t mask) +{ + return pi3usb9281_write(port, PI3USB9281_REG_INT_MASK, ~mask); +} + void pi3usb9281_init(int port) { uint8_t dev_id; @@ -118,11 +140,6 @@ int pi3usb9281_disable_interrupts(int port) return rv; } -int pi3usb9281_set_interrupt_mask(int port, uint8_t mask) -{ - return pi3usb9281_write(port, PI3USB9281_REG_INT_MASK, ~mask); -} - int pi3usb9281_get_interrupts(int port) { return pi3usb9281_read(port, PI3USB9281_REG_INT); @@ -138,7 +155,7 @@ int pi3usb9281_get_charger_status(int port) return pi3usb9281_read(port, PI3USB9281_REG_CHG_STATUS) & 0x1f; } -int pi3usb9281_get_ilim(int device_type, int charger_status) +static int pi3usb9281_get_ilim(int device_type, int charger_status) { /* Limit USB port current. 500mA for not listed types. */ int current_limit_ma = 500; @@ -160,16 +177,7 @@ int pi3usb9281_get_ilim(int device_type, int charger_status) return current_limit_ma; } -int pi3usb9281_get_vbus(int port) -{ - int vbus = pi3usb9281_read(port, PI3USB9281_REG_VBUS); - if (vbus == 0xee) - return EC_ERROR_UNKNOWN; - - return !!(vbus & 0x2); -} - -int pi3usb9281_reset(int port) +static int pi3usb9281_reset(int port) { int rv = pi3usb9281_write(port, PI3USB9281_REG_RESET, 0x1); @@ -180,7 +188,7 @@ int pi3usb9281_reset(int port) return rv; } -int pi3usb9281_set_switch_manual(int port, int val) +static int pi3usb9281_set_switch_manual(int port, int val) { uint8_t ctrl = pi3usb9281_read(port, PI3USB9281_REG_CONTROL); @@ -195,12 +203,12 @@ int pi3usb9281_set_switch_manual(int port, int val) return pi3usb9281_write_ctrl(port, ctrl); } -int pi3usb9281_set_pins(int port, uint8_t val) +static int pi3usb9281_set_pins(int port, uint8_t val) { return pi3usb9281_write(port, PI3USB9281_REG_MANUAL, val); } -int pi3usb9281_set_switches(int port, int open) +static int pi3usb9281_set_switches(int port, int open) { uint8_t ctrl = pi3usb9281_read(port, PI3USB9281_REG_CONTROL); @@ -214,3 +222,171 @@ int pi3usb9281_set_switches(int port, int open) return pi3usb9281_write_ctrl(port, ctrl); } + +void usb_charger_set_switches(int port, enum usb_switch setting) +{ + /* If switch is not changing then return */ + if (setting == usb_switch_state[port]) + return; + + mutex_lock(&usb_switch_lock[port]); + if (setting != USB_SWITCH_RESTORE) + usb_switch_state[port] = setting; + + pi3usb9281_set_switches(port, usb_switch_state[port]); + + mutex_unlock(&usb_switch_lock[port]); +} + +static void bc12_detect(int port) +{ + int device_type, charger_status; + struct charge_port_info charge; + int type; + + charge.voltage = USB_CHARGER_VOLTAGE_MV; + + if (usb_charger_port_is_sourcing_vbus(port)) { + /* If we're sourcing VBUS then we're not charging */ + device_type = charger_status = 0; + } else { + /* Set device type */ + device_type = pi3usb9281_get_device_type(port); + charger_status = pi3usb9281_get_charger_status(port); + } + + /* Debounce pin plug order if we detect a charger */ + if (device_type || PI3USB9281_CHG_STATUS_ANY(charger_status)) { + /* next operation might trigger a detach interrupt */ + pi3usb9281_disable_interrupts(port); + /* + * Ensure D+/D- are open before resetting + * Note: we can't simply call pi3usb9281_set_switches() because + * another task might override it and set the switches closed. + */ + pi3usb9281_set_switch_manual(port, 1); + pi3usb9281_set_pins(port, 0); + + /* Delay to debounce pin attach order */ + msleep(PI3USB9281_DETECT_DEBOUNCE_DELAY_MS); + + /* + * Trigger chip reset to refresh detection registers. + * WARNING: This reset is acceptable for samus_pd, + * but may not be acceptable for devices that have + * an OTG / device mode, as we may be interrupting + * the connection. + */ + pi3usb9281_reset(port); + /* + * Restore data switch settings - switches return to + * closed on reset until restored. + */ + usb_charger_set_switches(port, USB_SWITCH_RESTORE); + /* Clear possible disconnect interrupt */ + pi3usb9281_get_interrupts(port); + /* Mask attach interrupt */ + pi3usb9281_set_interrupt_mask(port, + 0xff & + ~PI3USB9281_INT_ATTACH); + /* Re-enable interrupts */ + pi3usb9281_enable_interrupts(port); + msleep(PI3USB9281_RESET_DEBOUNCE_DELAY_MS); + + /* Clear possible attach interrupt */ + pi3usb9281_get_interrupts(port); + /* Re-enable attach interrupt */ + pi3usb9281_set_interrupt_mask(port, 0xff); + + /* Re-read ID registers */ + device_type = pi3usb9281_get_device_type(port); + charger_status = pi3usb9281_get_charger_status(port); + } + + /* Attachment: decode + update available charge */ + if (device_type || PI3USB9281_CHG_STATUS_ANY(charger_status)) { + if (PI3USB9281_CHG_STATUS_ANY(charger_status)) + type = CHARGE_SUPPLIER_PROPRIETARY; + else if (device_type & PI3USB9281_TYPE_CDP) + type = CHARGE_SUPPLIER_BC12_CDP; + else if (device_type & PI3USB9281_TYPE_DCP) + type = CHARGE_SUPPLIER_BC12_DCP; + else if (device_type & PI3USB9281_TYPE_SDP) + type = CHARGE_SUPPLIER_BC12_SDP; + else + type = CHARGE_SUPPLIER_OTHER; + + charge.current = pi3usb9281_get_ilim(device_type, + charger_status); + charge_manager_update_charge(type, port, &charge); + } else { /* Detachment: update available charge to 0 */ + charge.current = 0; + charge_manager_update_charge( + CHARGE_SUPPLIER_PROPRIETARY, + port, + &charge); + charge_manager_update_charge( + CHARGE_SUPPLIER_BC12_CDP, + port, + &charge); + charge_manager_update_charge( + CHARGE_SUPPLIER_BC12_DCP, + port, + &charge); + charge_manager_update_charge( + CHARGE_SUPPLIER_BC12_SDP, + port, + &charge); + charge_manager_update_charge( + CHARGE_SUPPLIER_OTHER, + port, + &charge); + } + + /* notify host of power info change */ + pd_send_host_event(PD_EVENT_POWER_CHANGE); +} + +void usb_charger_task(void) +{ + const int attach_mask = PI3USB9281_INT_ATTACH | PI3USB9281_INT_DETACH; + int port = (task_get_current() == TASK_ID_USB_CHG_P0 ? 0 : 1); + int interrupt; + uint32_t evt; + + /* Initialize chip and enable interrupts */ + pi3usb9281_init(port); + + bc12_detect(port); + + while (1) { + /* Wait for interrupt */ + evt = task_wait_event(-1); + + /* Interrupt from the Pericom chip, determine charger type */ + if (evt & USB_CHG_EVENT_BC12) { + /* Read interrupt register to clear on chip */ + pi3usb9281_get_interrupts(port); + bc12_detect(port); + } else if (evt & USB_CHG_EVENT_INTR) { + /* Check the interrupt register, and clear on chip */ + interrupt = pi3usb9281_get_interrupts(port); + if (interrupt & attach_mask) + bc12_detect(port); + } + + /* + * Re-enable interrupts on pericom charger detector since the + * chip may periodically reset itself, and come back up with + * registers in default state. TODO(crosbug.com/p/33823): Fix + * these unwanted resets. + */ + if (evt & USB_CHG_EVENT_VBUS) { + pi3usb9281_enable_interrupts(port); +#ifndef CONFIG_USB_PD_TCPM_VBUS + CPRINTS("VBUS p%d %d", port, + pd_snk_is_vbus_provided(port)); +#endif + } + } +} diff --git a/include/config.h b/include/config.h index 73c42f08d5..709ee2e41e 100644 --- a/include/config.h +++ b/include/config.h @@ -1947,7 +1947,7 @@ /* Support USB blob handler. */ #undef CONFIG_USB_BLOB -/* Common USB / BC1.2 charger task */ +/* Common USB / BC1.2 charger detection routines */ #undef CONFIG_USB_CHARGER /* Enable USB serial console module. */ -- cgit v1.2.1