summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--driver/build.mk3
-rw-r--r--driver/ppc/nx20p348x.c105
-rw-r--r--driver/ppc/nx20p348x.h26
-rw-r--r--include/config.h1
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