/* Copyright 2018 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. */ /* ANX7447 port manager */ #include "common.h" #include "anx7447.h" #include "console.h" #include "hooks.h" #include "tcpm/tcpci.h" #include "tcpm/tcpm.h" #include "timer.h" #include "usb_mux.h" #include "usb_pd.h" #include "util.h" #define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) #define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args) #define vsafe5v_min (3800/25) #define vsafe0v_max (800/25) /* * These interface are workable while ADC is enabled, before * calling them should make sure ec driver finished chip initilization. */ #define is_equal_greater_safe5v(port) \ (((anx7447_get_vbus_voltage(port))) > vsafe5v_min) #define is_equal_greater_safe0v(port) \ (((anx7447_get_vbus_voltage(port))) > vsafe0v_max) struct anx_state { uint16_t i2c_addr_flags; }; struct anx_usb_mux { int state; }; static int anx7447_mux_set(const struct usb_mux *me, mux_state_t mux_state, bool *ack_required); static struct anx_state anx[CONFIG_USB_PD_PORT_MAX_COUNT]; static struct anx_usb_mux mux[CONFIG_USB_PD_PORT_MAX_COUNT]; /* * ANX7447 has two co-existence I2C addresses, TCPC address and * SPI address. The registers of TCPC address are partly compliant * with standard USB TCPC specification, and the registers in SPI * address controls the other functions (ex, hpd_level, mux_switch, and * so on). It can't use tcpc_read() and tcpc_write() to access SPI * address because its address has been set as TCPC in the structure * tcpc_config_t. * anx7447_reg_write() and anx7447_reg_read() are implemented here to access * ANX7447 SPI address. */ const struct anx7447_i2c_addr anx7447_i2c_addrs_flags[] = { {AN7447_TCPC0_I2C_ADDR_FLAGS, AN7447_SPI0_I2C_ADDR_FLAGS}, {AN7447_TCPC1_I2C_ADDR_FLAGS, AN7447_SPI1_I2C_ADDR_FLAGS}, {AN7447_TCPC2_I2C_ADDR_FLAGS, AN7447_SPI2_I2C_ADDR_FLAGS}, {AN7447_TCPC3_I2C_ADDR_FLAGS, AN7447_SPI3_I2C_ADDR_FLAGS} }; static inline int anx7447_reg_write(int port, int reg, int val) { int rv = i2c_write8(tcpc_config[port].i2c_info.port, anx[port].i2c_addr_flags, reg, val); #ifdef CONFIG_USB_PD_TCPC_LOW_POWER pd_device_accessed(port); #endif return rv; } static inline int anx7447_reg_read(int port, int reg, int *val) { int rv = i2c_read8(tcpc_config[port].i2c_info.port, anx[port].i2c_addr_flags, reg, val); #ifdef CONFIG_USB_PD_TCPC_LOW_POWER pd_device_accessed(port); #endif return rv; } void anx7447_hpd_mode_init(int port) { int reg, rv; rv = anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, ®); if (rv) return; /* * Set ANX7447_REG_HPD_MODE bit as 0, then the TCPC will generate the * HPD pulse from internal timer (by using ANX7447_REG_HPD_IRQ0) * instead of using the ANX7447_REG_HPD_OUT to set the HPD IRQ signal. */ reg &= ~(ANX7447_REG_HPD_MODE | ANX7447_REG_HPD_PLUG | ANX7447_REG_HPD_UNPLUG); anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg); } void anx7447_hpd_output_en(int port) { int reg, rv; rv = anx7447_reg_read(port, ANX7447_REG_HPD_DEGLITCH_H, ®); if (rv) return; reg |= ANX7447_REG_HPD_OEN; anx7447_reg_write(port, ANX7447_REG_HPD_DEGLITCH_H, reg); } void anx7447_set_hpd_level(int port, int hpd_lvl) { int reg, rv; rv = anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, ®); if (rv) return; /* * When ANX7447_REG_HPD_MODE is 1, use ANX7447_REG_HPD_OUT * to generate HPD event, otherwise use ANX7447_REG_HPD_UNPLUG * and ANX7447_REG_HPD_PLUG. */ if (hpd_lvl) { reg &= ~ANX7447_REG_HPD_UNPLUG; reg |= ANX7447_REG_HPD_PLUG; } else { reg &= ~ANX7447_REG_HPD_PLUG; reg |= ANX7447_REG_HPD_UNPLUG; } anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg); } #ifdef CONFIG_USB_PD_TCPM_ANX7447_OCM_ERASE_COMMAND static inline void anx7447_reg_write_and(int port, int reg, int v_and) { int val; if (!anx7447_reg_read(port, reg, &val)) anx7447_reg_write(port, reg, (val & v_and)); } static inline void anx7447_reg_write_or(int port, int reg, int v_or) { int val; if (!anx7447_reg_read(port, reg, &val)) anx7447_reg_write(port, reg, (val | v_or)); } #define ANX7447_FLASH_DONE_TIMEOUT_US (100 * MSEC) static int anx7447_wait_for_flash_done(int port) { timestamp_t deadline; int rv; int r; deadline.val = get_time().val + ANX7447_FLASH_DONE_TIMEOUT_US; do { if (timestamp_expired(deadline, NULL)) return EC_ERROR_TIMEOUT; rv = anx7447_reg_read(port, ANX7447_REG_R_RAM_CTRL, &r); if (rv) return rv; } while (!(r & ANX7447_R_RAM_CTRL_FLASH_DONE)); return EC_SUCCESS; } static int anx7447_flash_write_en(int port) { anx7447_reg_write(port, ANX7447_REG_FLASH_INST_TYPE, ANX7447_FLASH_INST_TYPE_WRITEENABLE); anx7447_reg_write_or(port, ANX7447_REG_R_FLASH_RW_CTRL, ANX7447_R_FLASH_RW_CTRL_GENERAL_INST_EN); return anx7447_wait_for_flash_done(port); } static int anx7447_flash_op_init(int port) { int rv; anx7447_reg_write_or(port, ANX7447_REG_OCM_CTRL_0, ANX7447_OCM_CTRL_OCM_RESET); anx7447_reg_write_or(port, ANX7447_REG_ADDR_GPIO_CTRL_0, ANX7447_ADDR_GPIO_CTRL_0_SPI_WP); rv = anx7447_flash_write_en(port); if (rv) return rv; anx7447_reg_write_and(port, ANX7447_REG_R_FLASH_STATUS_0, ANX7447_FLASH_STATUS_SPI_STATUS_0); anx7447_reg_write_or(port, ANX7447_REG_R_FLASH_RW_CTRL, ANX7447_R_FLASH_RW_CTRL_WRITE_STATUS_EN); return anx7447_wait_for_flash_done(port); } static int anx7447_flash_is_empty(int port) { int r; anx7447_reg_read(port, ANX7447_REG_OCM_VERSION, &r); return ((r == 0) ? 1 : 0); } static int anx7447_flash_erase_internal(int port, int write_console_if_empty) { int rv; int r; tcpc_read(port, TCPC_REG_COMMAND, &r); usleep(ANX7447_DELAY_IN_US); if (anx7447_flash_is_empty(port) == 1) { if (write_console_if_empty) CPRINTS("C%d: Nothing to erase!", port); return EC_SUCCESS; } CPRINTS("C%d: Erasing OCM flash...", port); rv = anx7447_flash_op_init(port); if (rv) return rv; usleep(ANX7447_DELAY_IN_US); rv = anx7447_flash_write_en(port); if (rv) return rv; anx7447_reg_write(port, ANX7447_REG_FLASH_ERASE_TYPE, ANX7447_FLASH_ERASE_TYPE_CHIPERASE); anx7447_reg_write_or(port, ANX7447_REG_R_FLASH_RW_CTRL, ANX7447_R_FLASH_RW_CTRL_FLASH_ERASE_EN); return anx7447_wait_for_flash_done(port); } int anx7447_flash_erase(int port) { return anx7447_flash_erase_internal(port, 0 /* suppress console if empty */); } /* Add console command to erase OCM flash if needed. */ static int command_anx_ocm(int argc, char **argv) { char *e = NULL; int port; if (argc < 2) return EC_ERROR_PARAM_COUNT; /* Get port number from first parameter */ port = strtoi(argv[1], &e, 0); if (*e) return EC_ERROR_PARAM1; if (argc > 2) { int rv; if (strcasecmp(argv[2], "erase")) return EC_ERROR_PARAM2; rv = anx7447_flash_erase_internal( port, 1 /* write to console if empty */); if (rv) ccprintf("C%d: Failed to erase OCM flash (%d)\n", port, rv); } ccprintf("C%d: OCM flash is %sempty.\n", port, anx7447_flash_is_empty(port) ? "" : "not "); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(anx_ocm, command_anx_ocm, "port [erase]", "Print OCM status or erases OCM for a given port."); #endif static int anx7447_init(int port) { int rv, reg, i; const struct usb_mux *me = &usb_muxes[port]; bool unused; ASSERT(port < CONFIG_USB_PD_PORT_MAX_COUNT); memset(&anx[port], 0, sizeof(struct anx_state)); /* * find corresponding anx7447 SPI address according to * specified TCPC address */ for (i = 0; i < ARRAY_SIZE(anx7447_i2c_addrs_flags); i++) { if (I2C_STRIP_FLAGS(tcpc_config[port].i2c_info.addr_flags) == I2C_STRIP_FLAGS( anx7447_i2c_addrs_flags[i].tcpc_addr_flags)) { anx[port].i2c_addr_flags = anx7447_i2c_addrs_flags[i].spi_addr_flags; break; } } if (!I2C_STRIP_FLAGS(anx[port].i2c_addr_flags)) { ccprintf("TCPC I2C addr 0x%x is invalid for ANX7447\n", I2C_STRIP_FLAGS(tcpc_config[port] .i2c_info.addr_flags)); return EC_ERROR_UNKNOWN; } rv = tcpci_tcpm_init(port); if (rv) return rv; #ifdef CONFIG_USB_PD_TCPM_ANX7447_OCM_ERASE_COMMAND /* Check and print OCM status to console. */ CPRINTS("C%d: OCM flash is %sempty", port, anx7447_flash_is_empty(port) ? "" : "not "); #endif /* * 7447 has a physical pin to detect the presence of VBUS, VBUS_SENSE * , and 7447 has a VBUS current protection mechanism through another * pin input VBUS_OCP. To enable VBUS OCP, OVP protection, driver needs * to set the threshold to the registers VBUS_VOLTAGE_ALARM_HI_CFG * (0x76 & 0x77) and VBUS_OCP_HI_THRESHOLD (0xDD &0xDE). These values * could be customized based on different platform design. * Disable VBUS protection here since the default values of * VBUS_VOLTAGE_ALARM_HI_CFG and VBUS_OCP_HI_THRESHOLD are zero. */ rv = tcpc_read(port, ANX7447_REG_TCPC_CTRL_2, ®); if (rv) return rv; reg &= ~ANX7447_REG_ENABLE_VBUS_PROTECT; rv = tcpc_write(port, ANX7447_REG_TCPC_CTRL_2, reg); if (rv) return rv; /* * Specifically disable voltage alarms, as VBUS_VOLTAGE_ALARM_HI may * trigger repeatedly despite being masked (b/153989733) */ rv = tcpc_update16(port, TCPC_REG_POWER_CTRL, TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS, MASK_SET); if (rv) return rv; /* ADC enable, use to monitor VBUS voltage */ rv = tcpc_read(port, ANX7447_REG_ADC_CTRL_1, ®); if (rv) return rv; reg |= ANX7447_REG_ADCFSM_EN; rv = tcpc_write(port, ANX7447_REG_ADC_CTRL_1, reg); if (rv) return rv; /* Set VCONN OCP(Over Current Protection) threshold */ rv = tcpc_read(port, ANX7447_REG_ANALOG_CTRL_8, ®); if (rv) return rv; reg &= ~ANX7447_REG_VCONN_OCP_MASK; reg |= ANX7447_REG_VCONN_OCP_440mA; rv = tcpc_write(port, ANX7447_REG_ANALOG_CTRL_8, reg); /* Vconn SW protection time of inrush current */ rv = tcpc_read(port, ANX7447_REG_ANALOG_CTRL_10, ®); if (rv) return rv; reg &= ~ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_MASK; reg |= ANX7447_REG_R_VCONN_PWR_PRT_INRUSH_TIME_2430US; rv = tcpc_write(port, ANX7447_REG_ANALOG_CTRL_10, reg); if (rv) return rv; #ifdef CONFIG_USB_PD_TCPM_MUX /* * Run mux_set() here for considering CCD(Case-Closed Debugging) case * If this TCPC is not also the MUX then don't initialize to NONE */ while ((me != NULL) && (me->driver != &anx7447_usb_mux_driver)) me = me->next_mux; /* * Note that bypassing the usb_mux API is okay for internal driver calls * since the task calling init already holds this port's mux lock. */ if (me != NULL && !(me->flags & USB_MUX_FLAG_NOT_TCPC)) rv = anx7447_mux_set(me, USB_PD_MUX_NONE, &unused); #endif /* CONFIG_USB_PD_TCPM_MUX */ return rv; } static int anx7447_release(int port) { return EC_SUCCESS; } #ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC static int anx7447_get_vbus_voltage(int port) { int vbus_volt = 0; tcpc_read16(port, TCPC_REG_VBUS_VOLTAGE, &vbus_volt); return vbus_volt; } int anx7447_set_power_supply_ready(int port) { int count = 0; while (is_equal_greater_safe0v(port)) { if (count >= 10) break; msleep(100); count++; } return tcpc_write(port, TCPC_REG_COMMAND, 0x77); } #endif /* CONFIG_USB_PD_VBUS_DETECT_TCPC */ int anx7447_power_supply_reset(int port) { return tcpc_write(port, TCPC_REG_COMMAND, 0x66); } int anx7447_board_charging_enable(int port, int enable) { return tcpc_write(port, TCPC_REG_COMMAND, enable ? 0x55 : 0x44); } static void anx7447_tcpc_alert(int port) { /* process and clear alert status */ tcpci_tcpc_alert(port); } /* * timestamp of the next possible toggle to ensure the 2-ms spacing * between IRQ_HPD. */ static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_MAX_COUNT]; void anx7447_tcpc_update_hpd_status(const struct usb_mux *me, mux_state_t mux_state) { int reg = 0; int port = me->usb_port; int hpd_lvl = (mux_state & USB_PD_MUX_HPD_LVL) ? 1 : 0; int hpd_irq = (mux_state & USB_PD_MUX_HPD_IRQ) ? 1 : 0; /* * All calls within this method need to update to a mux_read/write calls * that use the secondary address. This is a non-trival change and no * one is using the anx7447 as a mux only (and probably never will since * it doesn't have a re-driver). If that changes, we need to update this * code. */ ASSERT(!(me->flags & USB_MUX_FLAG_NOT_TCPC)); anx7447_set_hpd_level(port, hpd_lvl); if (hpd_irq) { uint64_t now = get_time().val; /* wait for the minimum spacing between IRQ_HPD if needed */ if (now < hpd_deadline[port]) usleep(hpd_deadline[port] - now); /* * For generate hardware HPD IRQ, need clear bit * ANX7447_REG_HPD_IRQ0 first, then set it. This bit is not * write clear. */ anx7447_reg_read(port, ANX7447_REG_HPD_CTRL_0, ®); reg &= ~ANX7447_REG_HPD_IRQ0; anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg); reg |= ANX7447_REG_HPD_IRQ0; anx7447_reg_write(port, ANX7447_REG_HPD_CTRL_0, reg); } /* enforce 2-ms delay between HPD pulses */ hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; } void anx7447_tcpc_clear_hpd_status(int port) { anx7447_hpd_output_en(port); anx7447_set_hpd_level(port, 0); } #ifdef CONFIG_USB_PD_TCPM_MUX static int anx7447_mux_init(const struct usb_mux *me) { int port = me->usb_port; bool unused; ASSERT(port < CONFIG_USB_PD_PORT_MAX_COUNT); memset(&mux[port], 0, sizeof(struct anx_usb_mux)); /* init hpd status */ anx7447_hpd_mode_init(port); anx7447_set_hpd_level(port, 0); anx7447_hpd_output_en(port); /* * ANX initializes its muxes to (USB_PD_MUX_USB_ENABLED | * USB_PD_MUX_DP_ENABLED) when reinitialized, we need to force * initialize it to USB_PD_MUX_NONE */ return anx7447_mux_set(me, USB_PD_MUX_NONE, &unused); } #ifdef CONFIG_USB_PD_TCPM_ANX7447_AUX_PU_PD static void anx7447_mux_safemode(const struct usb_mux *me, int on_off) { int reg; mux_read(me, ANX7447_REG_ANALOG_CTRL_9, ®); if (on_off) reg |= ANX7447_REG_SAFE_MODE; else reg &= ~(ANX7447_REG_SAFE_MODE); mux_write(me, ANX7447_REG_ANALOG_CTRL_9, reg); CPRINTS("C%d set mux to safemode %s, reg = 0x%x", me->usb_port, (on_off) ? "on" : "off", reg); } static inline void anx7447_configure_aux_src(const struct usb_mux *me, int on_off) { int reg; mux_read(me, ANX7447_REG_ANALOG_CTRL_9, ®); if (on_off) reg |= ANX7447_REG_R_AUX_RES_PULL_SRC; else reg &= ~(ANX7447_REG_R_AUX_RES_PULL_SRC); mux_write(me, ANX7447_REG_ANALOG_CTRL_9, reg); CPRINTS("C%d set aux_src to %s, reg = 0x%x", me->usb_port, (on_off) ? "on" : "off", reg); } #endif /* * Set mux. * * sstx and ssrx are the USB superspeed transmit and receive pairs. ml is the * DisplayPort Main Link. There are four lanes total. For example, DP cases * connect them all and dock cases connect 2 DP and USB. * * a2, a3, a10, a11, b2, b3, b10, b11 are pins on the USB-C connector. */ static int anx7447_mux_set(const struct usb_mux *me, mux_state_t mux_state, bool *ack_required) { int cc_direction; mux_state_t mux_type; int sw_sel = 0x00, aux_sw = 0x00; int rv; int port = me->usb_port; /* This driver does not use host command ACKs */ *ack_required = false; cc_direction = mux_state & USB_PD_MUX_POLARITY_INVERTED; mux_type = mux_state & USB_PD_MUX_DOCK; CPRINTS("C%d mux_state = 0x%x, mux_type = 0x%x", port, mux_state, mux_type); if (cc_direction == 0) { /* cc1 connection */ if (mux_type == USB_PD_MUX_DOCK) { /* ml0-a10/11, ml1-b2/b3, sstx-a2/a3, ssrx-b10/11 */ sw_sel = 0x21; /* aux+ <-> sbu1, aux- <-> sbu2 */ aux_sw = 0x03; } else if (mux_type == USB_PD_MUX_DP_ENABLED) { /* ml0-a10/11, ml1-b2/b3, ml2-a2/a3, ml3-b10/11 */ sw_sel = 0x09; /* aux+ <-> sbu1, aux- <-> sbu2 */ aux_sw = 0x03; } else if (mux_type == USB_PD_MUX_USB_ENABLED) { /* ssrxp<->b11, ssrxn<->b10, sstxp<->a2, sstxn<->a3 */ sw_sel = 0x20; } } else { /* cc2 connection */ if (mux_type == USB_PD_MUX_DOCK) { /* ml0-b10/11, ml1-a2/b3, sstx-b2/a3, ssrx-a10/11 */ sw_sel = 0x12; /* aux+ <-> sbu2, aux- <-> sbu1 */ aux_sw = 0x0C; } else if (mux_type == USB_PD_MUX_DP_ENABLED) { /* ml0-b10/11, ml1-a2/b3, ml2-b2/a3, ml3-a10/11 */ sw_sel = 0x06; /* aux+ <-> sbu2, aux- <-> sbu1 */ aux_sw = 0x0C; } else if (mux_type == USB_PD_MUX_USB_ENABLED) { /* ssrxp<->a11, ssrxn<->a10, sstxp<->b2, sstxn<->b3 */ sw_sel = 0x10; } } /* * Once need to configure the Mux, should set the mux to safe mode * first. After the mux configured, should set mux to normal mode. */ #ifdef CONFIG_USB_PD_TCPM_ANX7447_AUX_PU_PD anx7447_mux_safemode(me, 1); #endif rv = mux_write(me, ANX7447_REG_TCPC_SWITCH_0, sw_sel); rv |= mux_write(me, ANX7447_REG_TCPC_SWITCH_1, sw_sel); rv |= mux_write(me, ANX7447_REG_TCPC_AUX_SWITCH, aux_sw); mux[port].state = mux_state; #ifdef CONFIG_USB_PD_TCPM_ANX7447_AUX_PU_PD /* * DP and Dock mode: after configured the Mux, change the Mux to * normal mode, otherwise: keep safe mode. */ if (mux_type != USB_PD_MUX_NONE) { anx7447_configure_aux_src(me, 1); anx7447_mux_safemode(me, 0); } else anx7447_configure_aux_src(me, 0); #endif return rv; } /* current mux state */ static int anx7447_mux_get(const struct usb_mux *me, mux_state_t *mux_state) { int port = me->usb_port; *mux_state = mux[port].state; return EC_SUCCESS; } #endif /* CONFIG_USB_PD_TCPM_MUX */ #ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE static int anx7447_tcpc_drp_toggle(int port) { int rv, reg; rv = tcpc_read(port, ANX7447_REG_ANALOG_CTRL_10, ®); if (rv) return rv; /* * When using Look4Connection command to toggle CC under normal mode * the CABLE_DET_DIG shall be clear first. */ if (reg & ANX7447_REG_CABLE_DET_DIG) { reg &= ~ANX7447_REG_CABLE_DET_DIG; rv = tcpc_write(port, ANX7447_REG_ANALOG_CTRL_10, reg); if (rv) return rv; } return tcpci_tcpc_drp_toggle(port); } #endif /* Override for tcpci_tcpm_set_cc */ static int anx7447_set_cc(int port, int pull) { int rp, reg; rp = tcpc_read(port, ANX7447_REG_ANALOG_CTRL_10, ®); if (rp) return rp; /* * When setting CC status, should be confirm that the CC toggling * process is stopped, the CABLE_DET_DIG shall be set to one. */ if ((reg & ANX7447_REG_CABLE_DET_DIG) == 0) { reg |= ANX7447_REG_CABLE_DET_DIG; rp = tcpc_write(port, ANX7447_REG_ANALOG_CTRL_10, reg); if (rp) return rp; } rp = tcpci_get_cached_rp(port); /* Set manual control, and set both CC lines to the same pull */ return tcpc_write(port, TCPC_REG_ROLE_CTRL, TCPC_REG_ROLE_CTRL_SET(0, rp, pull, pull)); } /* Override for tcpci_tcpm_set_polarity */ static int anx7447_set_polarity(int port, enum tcpc_cc_polarity polarity) { return tcpc_update8(port, TCPC_REG_TCPC_CTRL, TCPC_REG_TCPC_CTRL_SET(1), polarity_rm_dts(polarity) ? MASK_SET : MASK_CLR); } #ifdef CONFIG_CMD_TCPC_DUMP static const struct tcpc_reg_dump_map anx7447_regs[] = { { .addr = ANX7447_REG_TCPC_SWITCH_0, .name = "SWITCH_0", .size = 1, }, { .addr = ANX7447_REG_TCPC_SWITCH_1, .name = "SWITCH_1", .size = 1, }, { .addr = ANX7447_REG_TCPC_AUX_SWITCH, .name = "AUX_SWITCH", .size = 1, }, { .addr = ANX7447_REG_ADC_CTRL_1, .name = "ADC_CTRL_1", .size = 1, }, { .addr = ANX7447_REG_ANALOG_CTRL_8, .name = "ANALOG_CTRL_8", .size = 1, }, { .addr = ANX7447_REG_ANALOG_CTRL_10, .name = "ANALOG_CTRL_10", .size = 1, }, { .addr = ANX7447_REG_TCPC_CTRL_2, .name = "TCPC_CTRL_2", .size = 1, }, }; const struct { const char *name; uint8_t addr; } anx7447_alt_regs[] = { { .name = "HPD_CTRL_0", .addr = ANX7447_REG_HPD_CTRL_0, }, { .name = "HPD_DEGLITCH_H", .addr = ANX7447_REG_HPD_DEGLITCH_H, }, { .name = "INTP_SOURCE_0", .addr = ANX7447_REG_INTP_SOURCE_0, }, { .name = "INTP_MASK_0", .addr = ANX7447_REG_INTP_MASK_0, }, { .name = "INTP_CTRL_0", .addr = ANX7447_REG_INTP_CTRL_0, }, { .name = "PAD_INTP_CTRL", .addr = ANX7447_REG_PAD_INTP_CTRL, }, }; /* * Dump registers for debug command. */ static void anx7447_dump_registers(int port) { int i, val; tcpc_dump_std_registers(port); tcpc_dump_registers(port, anx7447_regs, ARRAY_SIZE(anx7447_regs)); for (i = 0; i < ARRAY_SIZE(anx7447_alt_regs); i++) { anx7447_reg_read(port, anx7447_alt_regs[i].addr, &val); ccprintf(" %-26s(ALT/0x%02x) = 0x%02x\n", anx7447_alt_regs[i].name, anx7447_alt_regs[i].addr, (uint8_t)val); cflush(); } } #endif /* defined(CONFIG_CMD_TCPC_DUMP) */ /* * ANX7447 is a TCPCI compatible port controller, with some caveats. * It seems to require both CC lines to be set always, instead of just * one at a time, according to TCPCI spec. Thus, now that the TCPCI * driver more closely follows the spec, this driver requires * overrides for set_cc and set_polarity. */ const struct tcpm_drv anx7447_tcpm_drv = { .init = &anx7447_init, .release = &anx7447_release, .get_cc = &tcpci_tcpm_get_cc, #ifdef CONFIG_USB_PD_VBUS_DETECT_TCPC .check_vbus_level = &tcpci_tcpm_check_vbus_level, #endif .select_rp_value = &tcpci_tcpm_select_rp_value, .set_cc = &anx7447_set_cc, .set_polarity = &anx7447_set_polarity, #ifdef CONFIG_USB_PD_DECODE_SOP .sop_prime_enable = &tcpci_tcpm_sop_prime_enable, #endif .set_vconn = &tcpci_tcpm_set_vconn, .set_msg_header = &tcpci_tcpm_set_msg_header, .set_rx_enable = &tcpci_tcpm_set_rx_enable, .get_message_raw = &tcpci_tcpm_get_message_raw, .transmit = &tcpci_tcpm_transmit, .tcpc_alert = &anx7447_tcpc_alert, #ifdef CONFIG_USB_PD_DISCHARGE_TCPC .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus, #endif #ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE .drp_toggle = anx7447_tcpc_drp_toggle, #endif .get_chip_info = &tcpci_get_chip_info, #ifdef CONFIG_USB_PD_PPC .set_snk_ctrl = &tcpci_tcpm_set_snk_ctrl, .set_src_ctrl = &tcpci_tcpm_set_src_ctrl, #endif #ifdef CONFIG_USB_PD_TCPC_LOW_POWER .enter_low_power_mode = &tcpci_enter_low_power_mode, #endif .set_bist_test_mode = &tcpci_set_bist_test_mode, #ifdef CONFIG_CMD_TCPC_DUMP .dump_registers = &anx7447_dump_registers, #endif }; #ifdef CONFIG_USB_PD_TCPM_MUX const struct usb_mux_driver anx7447_usb_mux_driver = { .init = anx7447_mux_init, .set = anx7447_mux_set, .get = anx7447_mux_get, }; #endif /* CONFIG_USB_PD_TCPM_MUX */