diff options
Diffstat (limited to 'board/grunt/board.c')
-rw-r--r-- | board/grunt/board.c | 221 |
1 files changed, 218 insertions, 3 deletions
diff --git a/board/grunt/board.c b/board/grunt/board.c index 68d765dab4..23e8e0dad6 100644 --- a/board/grunt/board.c +++ b/board/grunt/board.c @@ -11,8 +11,11 @@ #include "charge_state.h" #include "charge_state_v2.h" #include "common.h" -#include "console.h" #include "compile_time_macros.h" +#include "console.h" +#include "driver/ppc/sn5s330.h" +#include "driver/tcpm/anx74xx.h" +#include "driver/tcpm/ps8xxx.h" #include "ec_commands.h" #include "extpower.h" #include "gpio.h" @@ -23,8 +26,9 @@ #include "power.h" #include "power_button.h" #include "registers.h" -#include "system.h" #include "switch.h" +#include "system.h" +#include "task.h" #include "tcpci.h" #include "usb_mux.h" #include "usb_pd_tcpm.h" @@ -32,8 +36,46 @@ static void tcpc_alert_event(enum gpio_signal signal) { - /* TODO(ecgh) */ + if ((signal == GPIO_USB_C0_PD_INT_ODL) && + !gpio_get_level(GPIO_USB_C0_PD_RST_L)) + return; + + if ((signal == GPIO_USB_C1_PD_INT_ODL) && + !gpio_get_level(GPIO_USB_C1_PD_RST_L)) + return; + +#ifdef HAS_TASK_PDCMD + /* Exchange status with TCPCs */ + host_command_pd_send_status(PD_CHARGE_NO_CHANGE); +#endif +} + +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER +static void anx74xx_cable_det_handler(void) +{ + int cable_det = gpio_get_level(GPIO_USB_C0_CABLE_DET); + int reset_n = gpio_get_level(GPIO_USB_C0_PD_RST_L); + + /* + * A cable_det low->high transition was detected. If following the + * debounce time, cable_det is high, and reset_n is low, then ANX3429 is + * currently in standby mode and needs to be woken up. Set the + * TCPC_RESET event which will bring the ANX3429 out of standby + * mode. Setting this event is gated on reset_n being low because the + * ANX3429 will always set cable_det when transitioning to normal mode + * and if in normal mode, then there is no need to trigger a tcpc reset. + */ + if (cable_det && !reset_n) + task_set_event(TASK_ID_PD_C0, PD_EVENT_TCPC_RESET, 0); +} +DECLARE_DEFERRED(anx74xx_cable_det_handler); + +void anx74xx_cable_det_interrupt(enum gpio_signal signal) +{ + /* debounce for 2 msec */ + hook_call_deferred(&anx74xx_cable_det_handler_data, (2 * MSEC)); } +#endif #include "gpio_list.h" @@ -72,6 +114,179 @@ const struct i2c_port_t i2c_ports[] = { }; const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); +#define USB_PD_PORT_ANX74XX 0 +#define USB_PD_PORT_PS8751 1 + +const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = { + [USB_PD_PORT_ANX74XX] = { + .i2c_host_port = I2C_PORT_TCPC0, + .i2c_slave_addr = 0x50, + .drv = &anx74xx_tcpm_drv, + .pol = TCPC_ALERT_ACTIVE_LOW, + }, + [USB_PD_PORT_PS8751] = { + .i2c_host_port = I2C_PORT_TCPC1, + .i2c_slave_addr = 0x16, + .drv = &ps8xxx_tcpm_drv, + .pol = TCPC_ALERT_ACTIVE_LOW, + }, +}; + +uint16_t tcpc_get_alert_status(void) +{ + uint16_t status = 0; + + if (!gpio_get_level(GPIO_USB_C0_PD_INT_ODL)) { + if (gpio_get_level(GPIO_USB_C0_PD_RST_L)) + status |= PD_STATUS_TCPC_ALERT_0; + } + + if (!gpio_get_level(GPIO_USB_C1_PD_INT_ODL)) { + if (gpio_get_level(GPIO_USB_C1_PD_RST_L)) + status |= PD_STATUS_TCPC_ALERT_1; + } + + return status; +} + +struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = { + { + .port_addr = USB_PD_PORT_ANX74XX, /* don't care / unused */ + .driver = &anx74xx_tcpm_usb_mux_driver, + .hpd_update = &anx74xx_tcpc_update_hpd_status, + }, + { + .port_addr = USB_PD_PORT_PS8751, + .driver = &tcpci_tcpm_usb_mux_driver, + .hpd_update = &ps8xxx_tcpc_update_hpd_status, + /* TODO(ecgh): ps8751_tune_mux needed? */ + } +}; + +const struct sn5s330_config sn5s330_chips[] = { + {I2C_PORT_TCPC0, SN5S330_ADDR0}, + {I2C_PORT_TCPC1, SN5S330_ADDR0}, +}; +const unsigned int sn5s330_cnt = ARRAY_SIZE(sn5s330_chips); + +const int usb_port_enable[CONFIG_USB_PORT_POWER_SMART_PORT_COUNT] = { + GPIO_EN_USB_A0_5V, + GPIO_EN_USB_A1_5V, +}; + +/** + * Power on (or off) a single TCPC. + * minimum on/off delays are included. + * + * @param port Port number of TCPC. + * @param mode 0: power off, 1: power on. + */ +void board_set_tcpc_power_mode(int port, int mode) +{ + if (port != USB_PD_PORT_ANX74XX) + return; + + switch (mode) { + case ANX74XX_NORMAL_MODE: + gpio_set_level(GPIO_EN_USB_C0_TCPC_PWR, 1); + msleep(ANX74XX_PWR_H_RST_H_DELAY_MS); + gpio_set_level(GPIO_USB_C0_PD_RST_L, 1); + break; + case ANX74XX_STANDBY_MODE: + gpio_set_level(GPIO_USB_C0_PD_RST_L, 0); + msleep(ANX74XX_RST_L_PWR_L_DELAY_MS); + gpio_set_level(GPIO_EN_USB_C0_TCPC_PWR, 0); + msleep(ANX74XX_PWR_L_PWR_H_DELAY_MS); + break; + default: + break; + } +} + +void board_reset_pd_mcu(void) +{ + /* Assert reset to TCPC1 (ps8751) */ + gpio_set_level(GPIO_USB_C1_PD_RST_L, 0); + + /* Assert reset to TCPC0 (anx3429) */ + gpio_set_level(GPIO_USB_C0_PD_RST_L, 0); + + /* TCPC1 (ps8751) requires 1ms reset down assertion */ + msleep(MAX(1, ANX74XX_RST_L_PWR_L_DELAY_MS)); + + /* Deassert reset to TCPC1 */ + gpio_set_level(GPIO_USB_C1_PD_RST_L, 1); + /* Disable TCPC0 power */ + gpio_set_level(GPIO_EN_USB_C0_TCPC_PWR, 0); + + /* + * anx3429 requires 10ms reset/power down assertion + */ + msleep(ANX74XX_PWR_L_PWR_H_DELAY_MS); + board_set_tcpc_power_mode(USB_PD_PORT_ANX74XX, 1); +} + +void board_tcpc_init(void) +{ + int port; + + /* TODO(ecgh): need to wait for disconnected battery? */ + + /* Only reset TCPC if not sysjump */ + if (!system_jumped_to_this_image()) + board_reset_pd_mcu(); + + /* Enable TCPC0 interrupt */ + gpio_enable_interrupt(GPIO_USB_C0_PD_INT_ODL); + + /* Enable TCPC1 interrupt */ + gpio_enable_interrupt(GPIO_USB_C1_PD_INT_ODL); + +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER + /* Enable CABLE_DET interrupt for ANX3429 wake from standby */ + gpio_enable_interrupt(GPIO_USB_C0_CABLE_DET); +#endif + /* + * Initialize HPD to low; after sysjump SOC needs to see + * HPD pulse to enable video path + */ + for (port = 0; port < CONFIG_USB_PD_PORT_COUNT; port++) { + const struct usb_mux *mux = &usb_muxes[port]; + + mux->hpd_update(port, 0, 0); + } +} + +int pd_snk_is_vbus_provided(int port) +{ + /* TODO(ecgh): Detect VBUS via SWCTL_INT and I2C to SN5S330 */ + return 0; +} + +int board_set_active_charge_port(int port) +{ + /* TODO(ecgh): how to disable/enable charge ports? */ + /* TODO(ecgh): how to check if port sourcing VBUS? */ + /* TODO(ecgh): disable/enable SN5S330 FETs? */ + return EC_SUCCESS; +} + +void board_set_charge_limit(int port, int supplier, int charge_ma, + int max_ma, int charge_mv) +{ + /* TODO(ecgh): check limits */ + /* + * To protect the charge inductor, at voltages above 18V we should + * set the current limit to 2.7A. + */ + if (charge_mv > 18000) + charge_ma = MIN(2700, charge_ma); + + charge_set_input_current_limit(MAX(charge_ma, + CONFIG_CHARGER_INPUT_CURRENT), + charge_mv); +} + /* Keyboard scan setting */ struct keyboard_scan_config keyscan_config = { /* Extra delay when KSO2 is tied to Cr50. */ |