diff options
-rw-r--r-- | driver/build.mk | 3 | ||||
-rw-r--r-- | driver/ppc/nx20p348x.c | 105 | ||||
-rw-r--r-- | driver/ppc/nx20p348x.h | 26 | ||||
-rw-r--r-- | include/config.h | 1 |
4 files changed, 121 insertions, 14 deletions
diff --git a/driver/build.mk b/driver/build.mk index 74bf4c573a..50b478ef0d 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -127,6 +127,9 @@ driver-$(CONFIG_USB_MUX_VIRTUAL)+=usb_mux_virtual.o # Type-C Power Path Controllers (PPC) driver-$(CONFIG_USBC_PPC_SN5S330)+=ppc/sn5s330.o +ifeq ($(CONFIG_USBC_PPC_NX20P3481)$(CONFIG_USBC_PPC_NX20P3483),y) +driver-y += ppc/nx20p348x.o +endif driver-$(CONFIG_USBC_PPC_SYV682X)+=ppc/syv682x.o driver-$(CONFIG_USBC_PPC_NX20P3483)+=ppc/nx20p348x.o diff --git a/driver/ppc/nx20p348x.c b/driver/ppc/nx20p348x.c index a5ee9d5e31..48ee5ca9b2 100644 --- a/driver/ppc/nx20p348x.c +++ b/driver/ppc/nx20p348x.c @@ -130,25 +130,36 @@ static int nx20p348x_vbus_sink_enable(int port, int enable) { int status; int rv; - int desired_mode = enable ? NX20P348X_MODE_HV_SNK : - NX20P348X_MODE_STANDBY; + int control = enable ? NX20P348X_SWITCH_CONTROL_HVSNK : 0; enable = !!enable; +#if defined(CONFIG_USBC_PPC_NX20P3481) + rv = write_reg(port, NX20P348X_SWITCH_CONTROL_REG, control); +#elif defined(CONFIG_USBC_PPC_NX20P3483) /* * We cannot use an EC GPIO for EN_SNK since an EC reset will float the * GPIO thus browning out the board (without a battery). */ rv = tcpm_set_snk_ctrl(port, enable); +#else +#error "Either the NX20P3481 or NX20P3483 must be selected" +#endif if (rv) return rv; - /* Read device status register to get mode */ - rv = read_reg(port, NX20P348X_DEVICE_STATUS_REG, &status); + /* + * Read switch status register. The bit definitions for switch control + * and switch status resister are identical, so the control value can be + * compared against the status value. The control switch has a debounce + * (15 msec) before the status will reflect the control command. + */ + msleep(NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC); + rv = read_reg(port, NX20P348X_SWITCH_STATUS_REG, &status); if (rv) return rv; - return ((status & NX20P348X_DEVICE_MODE_MASK) == desired_mode) ? + return (status & NX20P348X_SWITCH_STATUS_MASK) == control ? EC_SUCCESS : EC_ERROR_UNKNOWN; } @@ -156,25 +167,36 @@ static int nx20p348x_vbus_source_enable(int port, int enable) { int status; int rv; - int desired_mode = enable ? NX20P348X_MODE_5V_SRC : - NX20P348X_MODE_STANDBY; + int control = enable ? NX20P348X_SWITCH_CONTROL_5VSRC : 0; enable = !!enable; +#if defined(CONFIG_USBC_PPC_NX20P3481) + rv = write_reg(port, NX20P348X_SWITCH_CONTROL_REG, control); +#elif defined(CONFIG_USBC_PPC_NX20P3483) /* * For parity's sake, we should not use an EC GPIO for EN_SRC since we * cannot use it for EN_SNK (for brown out reason listed above). */ rv = tcpm_set_src_ctrl(port, enable); +#else +#error "Either the NX20P3481 or NX20P3483 must be selected" +#endif if (rv) return rv; - /* Read device status register to get mode */ - rv = read_reg(port, NX20P348X_DEVICE_STATUS_REG, &status); + /* + * Read switch status register. The bit definitions for switch control + * and switch status resister are identical, so the control value can be + * compared against the status value. The control switch has a debounce + * (15 msec) before the status will reflect the control command. + */ + msleep(NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC); + rv = read_reg(port, NX20P348X_SWITCH_STATUS_REG, &status); if (rv) return rv; - if ((status & NX20P348X_DEVICE_MODE_MASK) != desired_mode) + if ((status & NX20P348X_SWITCH_STATUS_MASK) != control) return EC_ERROR_UNKNOWN; /* Cache the Vbus state */ @@ -201,6 +223,10 @@ static int nx20p348x_init(int port) /* Mask interrupts for interrupt 1 register */ mask = ~(NX20P348X_INT1_OC_5VSRC | NX20P348X_INT1_DBEXIT_ERR); +#ifdef CONFIG_USBC_PPC_NX20P3481 + /* Unmask Fast Role Swap detect interrupt */ + mask &= ~NX20P348X_INT1_FRS_DET; +#endif rv = write_reg(port, NX20P348X_INTERRUPT1_MASK_REG, mask); if (rv) return rv; @@ -297,6 +323,28 @@ static void nx20p348x_handle_interrupt(int port) */ } +#ifdef CONFIG_USBC_PPC_NX20P3481 + /* Check for FRS detection */ + if (reg & NX20P348X_INT1_FRS_DET) { + /* + * TODO(b/113069469): Need to check for CC status and verifiy + * that a sink is attached to continue with FRS. If a sink is + * not attached, then this FRS detect is a false detect which is + * triggered when removing an external charger. If FRS was + * detected by the PPC, then it has automatically enabled the + * 5V SRC mode and this must be undone for a proper detach. + */ + /* Check CC status */ + + /* + * False detect, disable SRC mode which was enabled by + * NX20P3481. + */ + CPRINTS("C%d: PPC FRS false detect, disabling SRC mode!", port); + nx20p348x_vbus_source_enable(port, 0); + } +#endif + /* * Read interrupt 2 status register. Note, interrupt register is * automatically cleared by reading. @@ -354,6 +402,34 @@ static int nx20p348x_dump(int port) } #endif /* defined(CONFIG_CMD_PPC_DUMP) */ +/* + * TODO (b/112697473): The NX20P348x PPCs do not support vbus detection or vconn + * generation. However, if a different PPC does support these features and needs + * these config options, then these functions do need to exist. The + * configuration for what each PPC supports should be converted to bits within + * a flags variable that is part of the ppc_config_t struct. + */ +#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC +static int nx20p348x_is_vbus_present(int port) +{ + return EC_ERROR_UNIMPLEMENTED; +} +#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ + +#ifdef CONFIG_USBC_PPC_POLARITY +static int nx20p348x_set_polarity(int port, int polarity) +{ + return EC_ERROR_UNIMPLEMENTED; +} +#endif + +#ifdef CONFIG_USBC_PPC_VCONN +static int nx20p348x_set_vconn(int port, int enable) +{ + return EC_ERROR_UNIMPLEMENTED; +} +#endif + const struct ppc_drv nx20p348x_drv = { .init = &nx20p348x_init, .is_sourcing_vbus = &nx20p348x_is_sourcing_vbus, @@ -365,4 +441,13 @@ const struct ppc_drv nx20p348x_drv = { .set_vbus_source_current_limit = &nx20p348x_set_vbus_source_current_limit, .discharge_vbus = &nx20p348x_discharge_vbus, +#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC + .is_vbus_present = &nx20p348x_is_vbus_present, +#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ +#ifdef CONFIG_USBC_PPC_POLARITY + .set_polarity = &nx20p348x_set_polarity, +#endif /* defined(CONFIG_USBC_PPC_POLARITY) */ +#ifdef CONFIG_USBC_PPC_VCONN + .set_vconn = &nx20p348x_set_vconn, +#endif /* defined(CONFIG_USBC_PPC_VCONN) */ }; diff --git a/driver/ppc/nx20p348x.h b/driver/ppc/nx20p348x.h index 52224b6a8e..68048be6ea 100644 --- a/driver/ppc/nx20p348x.h +++ b/driver/ppc/nx20p348x.h @@ -13,6 +13,11 @@ #define NX20P3483_ADDR2 0xE4 #define NX20P3483_ADDR3 0xE6 +#define NX20P3481_ADDR0 0xE8 +#define NX20P3481_ADDR1 0xEA +#define NX20P3481_ADDR2 0xEC +#define NX20P3481_ADDR3 0xEE + /* * This PPC hard-codes the over voltage protect of Vbus at 6.8V in dead-battery * mode. If we ever are every going to drop the PD rail, we need to first ensure @@ -43,15 +48,27 @@ /* Device Status Modes */ #define NX20P348X_DEVICE_MODE_MASK 0x7 #define NX20P348X_MODE_DEAD_BATTERY 0 -#define NX20P348X_MODE_HV_SNK 1 -#define NX20P348X_MODE_5V_SRC 2 -#define NX20P348X_MODE_HV_SRC 3 -#define NX20P348X_MODE_STANDBY 4 +/* After dead battery, mode values are different between 3481 and 3483 */ +#define NX20P3481_MODE_NORMAL 1 +#define NX20P3481_MODE_FRS 2 +#define NX20P3481_MODE_STANDBY 3 + +#define NX20P3483_MODE_HV_SNK 1 +#define NX20P3483_MODE_5V_SRC 2 +#define NX20P3483_MODE_HV_SRC 3 +#define NX20P3483_MODE_STANDBY 4 + +/* Switch Control Register */ +#define NX20P348X_SWITCH_CONTROL_HVSNK (1 << 0) +#define NX20P348X_SWITCH_CONTROL_HVSRC (1 << 1) +#define NX20P348X_SWITCH_CONTROL_5VSRC (1 << 2) /* Switch Status Register */ #define NX20P348X_HVSNK_STS (1 << 0) #define NX20P348X_HVSRC_STS (1 << 1) #define NX20P348X_5VSRC_STS (1 << 2) +#define NX20P348X_SWITCH_STATUS_DEBOUNCE_MSEC 25 +#define NX20P348X_SWITCH_STATUS_MASK 0x7 /* Internal 5V VBUS Switch Current Limit Settings (min) */ #define NX20P348X_ILIM_MASK 0xF @@ -84,6 +101,7 @@ /* Interrupt 1 Register Bits */ #define NX20P348X_INT1_DBEXIT_ERR (1 << 7) +#define NX20P348X_INT1_FRS_DET (1 << 6) #define NX20P348X_INT1_OV_5VSRC (1 << 4) #define NX20P348X_INT1_RCP_5VSRC (1 << 3) #define NX20P348X_INT1_SC_5VSRC (1 << 2) diff --git a/include/config.h b/include/config.h index b4e2935bd8..179f7cfb51 100644 --- a/include/config.h +++ b/include/config.h @@ -3249,6 +3249,7 @@ #undef CONFIG_USBC_PPC_POLARITY /* USB Type-C Power Path Controllers (PPC) */ +#undef CONFIG_USBC_PPC_NX20P3481 #undef CONFIG_USBC_PPC_NX20P3483 #undef CONFIG_USBC_PPC_SN5S330 #undef CONFIG_USBC_PPC_SYV682X |