extcon: usbc-tusb320: Add support for mode setting and reset

[ Upstream commit 70c55d6be634e5f9894169340f3fe5c73f53ac2d ]

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@protonmail.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Stable-dep-of: 581c848b610d ("extcon: usbc-tusb320: Update state on probe even if no IRQ pending")
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Yassine Oudjana 2021-09-25 05:45:39 +00:00 committed by Greg Kroah-Hartman
parent 4524599a6a
commit b9c8820d91

View File

@ -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[] = {
@ -62,6 +79,53 @@ 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;
}
/* Wait for chip to go through reset */
msleep(95);
return 0;
}
static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
{
struct tusb320_priv *priv = dev_id;
@ -96,6 +160,8 @@ static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
extcon_sync(priv->edev, EXTCON_USB);
extcon_sync(priv->edev, EXTCON_USB_HOST);
priv->state = state;
regmap_write(priv->regmap, TUSB320_REG9, reg);
return IRQ_HANDLED;
@ -145,6 +211,17 @@ static int tusb320_extcon_probe(struct i2c_client *client,
/* update initial state */
tusb320_irq_handler(client->irq, priv);
/* Reset chip to its default state */
ret = tusb320_reset(priv);
if (ret)
dev_warn(priv->dev, "failed to reset chip: %d\n", ret);
else
/*
* State and polarity might change after a reset, so update
* them again and make sure the interrupt status bit is cleared.
*/
tusb320_irq_handler(client->irq, priv);
ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,
tusb320_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,