summaryrefslogtreecommitdiff
path: root/driver/ppc/nx20p348x.c
diff options
context:
space:
mode:
authorScott Collyer <scollyer@google.com>2018-08-16 10:06:19 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-09-13 18:58:35 -0700
commita68497b87a0fcdfa1e4130600687fdb40430df84 (patch)
tree944ff0753b669173ea153f212e5299ee0ac21652 /driver/ppc/nx20p348x.c
parentab54765cf1c71d1f1a91ab08d68521b29d8fec76 (diff)
downloadchrome-ec-a68497b87a0fcdfa1e4130600687fdb40430df84.tar.gz
ppc: Add support for nx20p3481
This CL is an incremental change to the nx20p348x driver to add support for the nx20p3481 ppc. Sink/source modes are controlled via the switch control register instead of gpio signals. Another difference is that the values of mode in register 0x1 are slightly different between the 3481 and 3483. The 3481 needs to use the switch status register to verify whether it's in sink or source mode. This register is now checked for both the 3483 and 3481. A delay is required for the switch status register to reflect the control setting just applied. In addition, the nx20p3481 supports Fast Role Swap (FRS). For FRS, only the detection is supported, and it's assumed that it's caused by the removal of an external charger, not an actual FRS event. BUG=b:111281797 BRANCH=none TEST=Verified on DragonEgg that port acts correctly as a sink. Have not been able to verify source operation. Change-Id: I2fb4200a5d9c3ce460e9b913a5b09441e458bb7e Signed-off-by: Scott Collyer <scollyer@google.com> Reviewed-on: https://chromium-review.googlesource.com/1178995 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: Scott Collyer <scollyer@chromium.org> Reviewed-by: Furquan Shaikh <furquan@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org>
Diffstat (limited to 'driver/ppc/nx20p348x.c')
-rw-r--r--driver/ppc/nx20p348x.c105
1 files changed, 95 insertions, 10 deletions
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) */
};