struct mutex lock; /* protect non-concurrent retimer & switch */
enum typec_orientation orientation;
- u8 cfg0;
- u8 cfg1;
- u8 cfg2;
+ bool in_reset;
};
+static int ps883x_enable_vregs(struct ps883x_retimer *retimer)
+{
+ struct device *dev = &retimer->client->dev;
+ int ret;
+
+ ret = regulator_enable(retimer->vdd33_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(retimer->vdd33_cap_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret);
+ goto err_vdd33_disable;
+ }
+
+ usleep_range(4000, 10000);
+
+ ret = regulator_enable(retimer->vdd_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD regulator: %d\n", ret);
+ goto err_vdd33_cap_disable;
+ }
+
+ ret = regulator_enable(retimer->vddar_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret);
+ goto err_vdd_disable;
+ }
+
+ ret = regulator_enable(retimer->vddat_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret);
+ goto err_vddar_disable;
+ }
+
+ ret = regulator_enable(retimer->vddio_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret);
+ goto err_vddat_disable;
+ }
+
+ return 0;
+
+err_vddat_disable:
+ regulator_disable(retimer->vddat_supply);
+err_vddar_disable:
+ regulator_disable(retimer->vddar_supply);
+err_vdd_disable:
+ regulator_disable(retimer->vdd_supply);
+err_vdd33_cap_disable:
+ regulator_disable(retimer->vdd33_cap_supply);
+err_vdd33_disable:
+ regulator_disable(retimer->vdd33_supply);
+
+ return ret;
+}
+
+static void ps883x_disable_vregs(struct ps883x_retimer *retimer)
+{
+ regulator_disable(retimer->vddio_supply);
+ regulator_disable(retimer->vddat_supply);
+ regulator_disable(retimer->vddar_supply);
+ regulator_disable(retimer->vdd_supply);
+ regulator_disable(retimer->vdd33_cap_supply);
+ regulator_disable(retimer->vdd33_supply);
+}
+
+static void ps883x_reset(struct ps883x_retimer *retimer)
+{
+ if (retimer->in_reset)
+ return;
+
+ gpiod_set_value(retimer->reset_gpio, 1);
+ ps883x_disable_vregs(retimer);
+ retimer->in_reset = true;
+}
+
static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0,
- int cfg1, int cfg2)
+ int cfg1, int cfg2, bool reset)
{
struct device *dev = &retimer->client->dev;
int ret;
- if (retimer->cfg0 == cfg0 && retimer->cfg1 == cfg1 && retimer->cfg2 == cfg2)
+ if (reset) {
+ ps883x_reset(retimer);
+
return 0;
+ } else if (retimer->in_reset) {
+ ret = ps883x_enable_vregs(retimer);
+ if (ret)
+ return ret;
+
+ gpiod_set_value(retimer->reset_gpio, 0);
+
+ /* firmware initialization delay */
+ msleep(60);
+
+ retimer->in_reset = false;
+ }
ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0);
if (ret) {
return ret;
}
- retimer->cfg0 = cfg0;
- retimer->cfg1 = cfg1;
- retimer->cfg2 = cfg2;
-
return 0;
}
int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT;
int cfg1 = 0x00;
int cfg2 = 0x00;
+ bool reset = false;
if (retimer->orientation == TYPEC_ORIENTATION_REVERSE)
cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED;
}
} else {
switch (state->mode) {
+ /* SAFE can be transient or point to an actual disconnect */
case TYPEC_STATE_SAFE:
+ reset = retimer->orientation == TYPEC_ORIENTATION_NONE;
+ break;
/* USB2 pins don't even go through this chip */
case TYPEC_MODE_USB2:
+ reset = true;
break;
case TYPEC_STATE_USB:
case TYPEC_MODE_USB3:
}
}
- return ps883x_configure(retimer, cfg0, cfg1, cfg2);
+ return ps883x_configure(retimer, cfg0, cfg1, cfg2, reset);
}
static int ps883x_sw_set(struct typec_switch_dev *sw,
if (ret)
return ret;
- mutex_lock(&retimer->lock);
+ guard(mutex)(&retimer->lock);
if (retimer->orientation != orientation) {
retimer->orientation = orientation;
+ /*
+ * Orientation notifications usually come prior to mode switch
+ * events. If the retimer is already in reset, we still want to
+ * cache the new orientation value for the subsequent ps883x_set().
+ */
+ if (retimer->in_reset)
+ return 0;
+
ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
CONN_STATUS_0_ORIENTATION_REVERSED,
orientation == TYPEC_ORIENTATION_REVERSE);
dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret);
}
- mutex_unlock(&retimer->lock);
-
return ret;
}
return typec_mux_set(retimer->typec_mux, &mux_state);
}
-static int ps883x_enable_vregs(struct ps883x_retimer *retimer)
-{
- struct device *dev = &retimer->client->dev;
- int ret;
-
- ret = regulator_enable(retimer->vdd33_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret);
- return ret;
- }
-
- ret = regulator_enable(retimer->vdd33_cap_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret);
- goto err_vdd33_disable;
- }
-
- usleep_range(4000, 10000);
-
- ret = regulator_enable(retimer->vdd_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD regulator: %d\n", ret);
- goto err_vdd33_cap_disable;
- }
-
- ret = regulator_enable(retimer->vddar_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret);
- goto err_vdd_disable;
- }
-
- ret = regulator_enable(retimer->vddat_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret);
- goto err_vddar_disable;
- }
-
- ret = regulator_enable(retimer->vddio_supply);
- if (ret) {
- dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret);
- goto err_vddat_disable;
- }
-
- return 0;
-
-err_vddat_disable:
- regulator_disable(retimer->vddat_supply);
-err_vddar_disable:
- regulator_disable(retimer->vddar_supply);
-err_vdd_disable:
- regulator_disable(retimer->vdd_supply);
-err_vdd33_cap_disable:
- regulator_disable(retimer->vdd33_cap_supply);
-err_vdd33_disable:
- regulator_disable(retimer->vdd33_supply);
-
- return ret;
-}
-
-static void ps883x_disable_vregs(struct ps883x_retimer *retimer)
-{
- regulator_disable(retimer->vddio_supply);
- regulator_disable(retimer->vddat_supply);
- regulator_disable(retimer->vddar_supply);
- regulator_disable(retimer->vdd_supply);
- regulator_disable(retimer->vdd33_cap_supply);
- regulator_disable(retimer->vdd33_supply);
-}
-
static int ps883x_get_vregs(struct ps883x_retimer *retimer)
{
struct device *dev = &retimer->client->dev;
}
}
+ /* Keep the retimer in reset until a Type-C notification comes */
+ ps883x_reset(retimer);
+
sw_desc.drvdata = retimer;
sw_desc.fwnode = dev_fwnode(dev);
sw_desc.set = ps883x_sw_set;