[PATCH v2 1/3] extcon: usbc-tusb320: Add support for mode setting and reset

From: Yassine Oudjana
Date: Tue Jul 27 2021 - 05:56:50 EST


Reset the chip and set its mode to default (maintain mode set by PORT pin)
during probe to make sure it comes up in the default state.

Signed-off-by: Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx>
---
drivers/extcon/extcon-usbc-tusb320.c | 111 +++++++++++++++++++++++----
1 file changed, 97 insertions(+), 14 deletions(-)

diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c
index 805af73b4152..c8d931abbf9f 100644
--- a/drivers/extcon/extcon-usbc-tusb320.c
+++ b/drivers/extcon/extcon-usbc-tusb320.c
@@ -19,15 +19,32 @@
#define TUSB320_REG9_ATTACHED_STATE_MASK 0x3
#define TUSB320_REG9_CABLE_DIRECTION BIT(5)
#define TUSB320_REG9_INTERRUPT_STATUS BIT(4)
-#define TUSB320_ATTACHED_STATE_NONE 0x0
-#define TUSB320_ATTACHED_STATE_DFP 0x1
-#define TUSB320_ATTACHED_STATE_UFP 0x2
-#define TUSB320_ATTACHED_STATE_ACC 0x3
+
+#define TUSB320_REGA 0xa
+#define TUSB320_REGA_I2C_SOFT_RESET BIT(3)
+#define TUSB320_REGA_MODE_SELECT_SHIFT 4
+#define TUSB320_REGA_MODE_SELECT_MASK 0x3
+
+enum tusb320_attached_state {
+ TUSB320_ATTACHED_STATE_NONE,
+ TUSB320_ATTACHED_STATE_DFP,
+ TUSB320_ATTACHED_STATE_UFP,
+ TUSB320_ATTACHED_STATE_ACC,
+};
+
+enum tusb320_mode {
+ TUSB320_MODE_PORT,
+ TUSB320_MODE_UFP,
+ TUSB320_MODE_DFP,
+ TUSB320_MODE_DRP,
+};

struct tusb320_priv {
struct device *dev;
struct regmap *regmap;
struct extcon_dev *edev;
+
+ enum tusb320_attached_state state;
};

static const char * const tusb_attached_states[] = {
@@ -37,6 +54,13 @@ static const char * const tusb_attached_states[] = {
[TUSB320_ATTACHED_STATE_ACC] = "accessory",
};

+static const char * const tusb_modes[] = {
+ [TUSB320_MODE_PORT] = "maintain mode set by PORT pin",
+ [TUSB320_MODE_UFP] = "upstream facing port",
+ [TUSB320_MODE_DFP] = "downstream facing port",
+ [TUSB320_MODE_DRP] = "dual role port",
+};
+
static const unsigned int tusb320_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
@@ -62,26 +86,77 @@ static int tusb320_check_signature(struct tusb320_priv *priv)
return 0;
}

+static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
+{
+ int ret;
+
+ /* Mode cannot be changed while cable is attached */
+ if(priv->state != TUSB320_ATTACHED_STATE_NONE)
+ return -EBUSY;
+
+ /* Write mode */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
+ mode << TUSB320_REGA_MODE_SELECT_SHIFT);
+ if(ret) {
+ dev_err(priv->dev, "failed to write mode: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tusb320_reset(struct tusb320_priv *priv)
+{
+ int ret;
+
+ /* Set mode to default (follow PORT pin) */
+ ret = tusb320_set_mode(priv, TUSB320_MODE_PORT);
+ if(ret && ret != -EBUSY) {
+ dev_err(priv->dev,
+ "failed to set mode to PORT: %d\n", ret);
+ return ret;
+ }
+
+ /* Perform soft reset */
+ ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
+ TUSB320_REGA_I2C_SOFT_RESET, 1);
+ if(ret) {
+ dev_err(priv->dev,
+ "failed to write soft reset bit: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
{
struct tusb320_priv *priv = dev_id;
- int state, polarity;
- unsigned reg;
+ int state, polarity, mode;
+ unsigned reg9, rega;
+
+ if (regmap_read(priv->regmap, TUSB320_REG9, &reg9)) {
+ dev_err(priv->dev, "error during register 0x9 i2c read!\n");
+ return IRQ_NONE;
+ }

- if (regmap_read(priv->regmap, TUSB320_REG9, &reg)) {
- dev_err(priv->dev, "error during i2c read!\n");
+ if (regmap_read(priv->regmap, TUSB320_REGA, &rega)) {
+ dev_err(priv->dev, "error during register 0xa i2c read!\n");
return IRQ_NONE;
}

- if (!(reg & TUSB320_REG9_INTERRUPT_STATUS))
+ if (!(reg9 & TUSB320_REG9_INTERRUPT_STATUS))
return IRQ_NONE;

- state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) &
+ state = (reg9 >> TUSB320_REG9_ATTACHED_STATE_SHIFT) &
TUSB320_REG9_ATTACHED_STATE_MASK;
- polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION);
+ polarity = !!(reg9 & TUSB320_REG9_CABLE_DIRECTION);
+ mode = (rega >> TUSB320_REGA_MODE_SELECT_SHIFT) &
+ TUSB320_REGA_MODE_SELECT_MASK;

- dev_dbg(priv->dev, "attached state: %s, polarity: %d\n",
- tusb_attached_states[state], polarity);
+ dev_dbg(priv->dev, "mode: %s, attached state: %s, polarity: %d\n",
+ tusb_modes[mode], tusb_attached_states[state], polarity);

extcon_set_state(priv->edev, EXTCON_USB,
state == TUSB320_ATTACHED_STATE_UFP);
@@ -96,7 +171,10 @@ static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
extcon_sync(priv->edev, EXTCON_USB);
extcon_sync(priv->edev, EXTCON_USB_HOST);

- regmap_write(priv->regmap, TUSB320_REG9, reg);
+ priv->state = state;
+
+ regmap_write(priv->regmap, TUSB320_REG9, reg9);
+ regmap_write(priv->regmap, TUSB320_REGA, rega);

return IRQ_HANDLED;
}
@@ -137,6 +215,11 @@ static int tusb320_extcon_probe(struct i2c_client *client,
return ret;
}

+ /* Reset chip to its default state */
+ ret = tusb320_reset(priv);
+ if(ret)
+ dev_warn(priv->dev, "failed to reset chip: %d\n", ret);
+
extcon_set_property_capability(priv->edev, EXTCON_USB,
EXTCON_PROP_USB_TYPEC_POLARITY);
extcon_set_property_capability(priv->edev, EXTCON_USB_HOST,
--
2.32.0