/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "common.h" #include "compile_time_macros.h" #include "console.h" #include "driver/bc12/pi3usb9201_public.h" #include "driver/ppc/nx20p348x.h" #include "driver/tcpm/ps8xxx_public.h" #include "driver/tcpm/tcpci.h" #include "ec_commands.h" #include "gpio.h" #include "gpio_signal.h" #include "hooks.h" #include "system.h" #include "task.h" #include "task_id.h" #include "timer.h" #include "usb_charge.h" #include "usb_mux.h" #include "usb_pd.h" #include "usb_pd_tcpm.h" #include "usbc_config.h" #include "usbc_ppc.h" #include #include #define CPRINTF(format, args...) cprintf(CC_USBPD, format, ##args) #define CPRINTS(format, args...) cprints(CC_USBPD, format, ##args) /* USBC TCPC configuration */ const struct tcpc_config_t tcpc_config[] = { [USBC_PORT_C0] = { .bus_type = EC_BUS_TYPE_I2C, .i2c_info = { .port = I2C_PORT_USB_C0_TCPC, .addr_flags = PS8XXX_I2C_ADDR1_FLAGS, }, .drv = &ps8xxx_tcpm_drv, .flags = TCPC_FLAGS_TCPCI_REV2_0 | TCPC_FLAGS_TCPCI_REV2_0_NO_VSAFE0V, }, [USBC_PORT_C1] = { .bus_type = EC_BUS_TYPE_I2C, .i2c_info = { .port = I2C_PORT_USB_C1_TCPC, .addr_flags = PS8XXX_I2C_ADDR2_FLAGS, }, .drv = &ps8xxx_tcpm_drv, .flags = TCPC_FLAGS_TCPCI_REV2_0 | TCPC_FLAGS_TCPCI_REV2_0_NO_VSAFE0V, }, }; BUILD_ASSERT(ARRAY_SIZE(tcpc_config) == USBC_PORT_COUNT); BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT == USBC_PORT_COUNT); /* USBC PPC configuration */ struct ppc_config_t ppc_chips[] = { [USBC_PORT_C0] = { /* Compatible with Silicon Mitus SM5360A */ .i2c_port = I2C_PORT_USB_C0_PPC, .i2c_addr_flags = NX20P3483_ADDR2_FLAGS, .drv = &nx20p348x_drv, }, [USBC_PORT_C1] = { /* Compatible with Silicon Mitus SM5360A */ .i2c_port = I2C_PORT_USB_C1_PPC, .i2c_addr_flags = NX20P3483_ADDR3_FLAGS, .drv = &nx20p348x_drv, }, }; BUILD_ASSERT(ARRAY_SIZE(ppc_chips) == USBC_PORT_COUNT); unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips); /* USBC mux configuration - Alder Lake includes internal mux */ static const struct usb_mux_chain usbc0_tcss_usb_mux = { .mux = &(const struct usb_mux){ .usb_port = USBC_PORT_C0, .driver = &tcpci_tcpm_usb_mux_driver, .hpd_update = &ps8xxx_tcpc_update_hpd_status, }, }; static const struct usb_mux_chain usbc1_tcss_usb_mux = { .mux = &(const struct usb_mux){ .usb_port = USBC_PORT_C1, .driver = &tcpci_tcpm_usb_mux_driver, .hpd_update = &ps8xxx_tcpc_update_hpd_status, }, }; const struct usb_mux_chain usb_muxes[] = { [USBC_PORT_C0] = { .mux = &(const struct usb_mux) { .usb_port = USBC_PORT_C0, .driver = &virtual_usb_mux_driver, .hpd_update = &virtual_hpd_update, }, .next = &usbc0_tcss_usb_mux, }, [USBC_PORT_C1] = { .mux = &(const struct usb_mux) { .usb_port = USBC_PORT_C1, .driver = &virtual_usb_mux_driver, .hpd_update = &virtual_hpd_update, }, .next = &usbc1_tcss_usb_mux, }, }; BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == USBC_PORT_COUNT); /* BC1.2 charger detect configuration */ const struct pi3usb9201_config_t pi3usb9201_bc12_chips[] = { [USBC_PORT_C0] = { .i2c_port = I2C_PORT_USB_C0_BC12, .i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS, }, [USBC_PORT_C1] = { .i2c_port = I2C_PORT_USB_C1_BC12, .i2c_addr_flags = PI3USB9201_I2C_ADDR_2_FLAGS, }, }; BUILD_ASSERT(ARRAY_SIZE(pi3usb9201_bc12_chips) == USBC_PORT_COUNT); static void ps8815_reset(int port) { int val; int i2c_port; uint16_t i2c_addr_flags; enum gpio_signal ps8xxx_rst_odl; if (port == USBC_PORT_C0) { ps8xxx_rst_odl = GPIO_USB_C0_RT_RST_L; i2c_port = I2C_PORT_USB_C0_TCPC; i2c_addr_flags = PS8XXX_I2C_ADDR1_FLAGS; } else if (port == USBC_PORT_C1) { ps8xxx_rst_odl = GPIO_USB_C1_RT_RST_L; i2c_port = I2C_PORT_USB_C1_TCPC; i2c_addr_flags = PS8XXX_I2C_ADDR2_FLAGS; } else { return; } gpio_set_level(ps8xxx_rst_odl, 0); msleep(GENERIC_MAX(PS8XXX_RESET_DELAY_MS, PS8815_PWR_H_RST_H_DELAY_MS)); gpio_set_level(ps8xxx_rst_odl, 1); msleep(PS8815_FW_INIT_DELAY_MS); CPRINTS("[C%d] %s: patching ps8815 registers", port, __func__); if (i2c_read8(i2c_port, i2c_addr_flags, 0x0f, &val) == EC_SUCCESS) CPRINTS("ps8815: reg 0x0f was %02x", val); else { CPRINTS("delay 10ms to make sure ps8815 is waken from idle"); msleep(10); } if (i2c_write8(i2c_port, i2c_addr_flags, 0x0f, 0x31) == EC_SUCCESS) CPRINTS("ps8815: reg 0x0f set to 0x31"); if (i2c_read8(i2c_port, i2c_addr_flags, 0x0f, &val) == EC_SUCCESS) CPRINTS("ps8815: reg 0x0f now %02x", val); } void board_reset_pd_mcu(void) { ps8815_reset(USBC_PORT_C0); usb_mux_hpd_update(USBC_PORT_C0, USB_PD_MUX_HPD_LVL_DEASSERTED | USB_PD_MUX_HPD_IRQ_DEASSERTED); ps8815_reset(USBC_PORT_C1); usb_mux_hpd_update(USBC_PORT_C1, USB_PD_MUX_HPD_LVL_DEASSERTED | USB_PD_MUX_HPD_IRQ_DEASSERTED); } static void board_tcpc_init(void) { /* Don't reset TCPCs after initial reset */ if (!system_jumped_late()) { board_reset_pd_mcu(); } /* Enable PPC interrupts. */ gpio_enable_interrupt(GPIO_USB_C0_PPC_INT_ODL); gpio_enable_interrupt(GPIO_USB_C1_PPC_INT_ODL); /* Enable TCPC interrupts. */ gpio_enable_interrupt(GPIO_USB_C0_TCPC_INT_ODL); gpio_enable_interrupt(GPIO_USB_C1_TCPC_INT_ODL); /* Enable BC1.2 interrupts. */ gpio_enable_interrupt(GPIO_USB_C0_BC12_INT_ODL); gpio_enable_interrupt(GPIO_USB_C1_BC12_INT_ODL); } DECLARE_HOOK(HOOK_INIT, board_tcpc_init, HOOK_PRIO_INIT_CHIPSET); uint16_t tcpc_get_alert_status(void) { uint16_t status = 0; if (gpio_get_level(GPIO_USB_C0_TCPC_INT_ODL) == 0) status |= PD_STATUS_TCPC_ALERT_0; if (gpio_get_level(GPIO_USB_C1_TCPC_INT_ODL) == 0) status |= PD_STATUS_TCPC_ALERT_1; return status; } int ppc_get_alert_status(int port) { if (port == USBC_PORT_C0) return gpio_get_level(GPIO_USB_C0_PPC_INT_ODL) == 0; else if (port == USBC_PORT_C1) return gpio_get_level(GPIO_USB_C1_PPC_INT_ODL) == 0; return 0; } void tcpc_alert_event(enum gpio_signal signal) { switch (signal) { case GPIO_USB_C0_TCPC_INT_ODL: schedule_deferred_pd_interrupt(USBC_PORT_C0); break; case GPIO_USB_C1_TCPC_INT_ODL: schedule_deferred_pd_interrupt(USBC_PORT_C1); break; default: break; } } void bc12_interrupt(enum gpio_signal signal) { switch (signal) { case GPIO_USB_C0_BC12_INT_ODL: usb_charger_task_set_event(0, USB_CHG_EVENT_BC12); break; case GPIO_USB_C1_BC12_INT_ODL: usb_charger_task_set_event(1, USB_CHG_EVENT_BC12); break; default: break; } } void ppc_interrupt(enum gpio_signal signal) { switch (signal) { case GPIO_USB_C0_PPC_INT_ODL: nx20p348x_interrupt(USBC_PORT_C0); break; case GPIO_USB_C1_PPC_INT_ODL: nx20p348x_interrupt(USBC_PORT_C1); break; default: break; } } void retimer_interrupt(enum gpio_signal signal) { /* * TODO(b/179513527): add USB-C support */ } __override bool board_is_dts_port(int port) { return port == USBC_PORT_C0; }