Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

extcon: usbc-tusb320: Add support for TUSB320L

TUSB320L is a newer chip with additional features, and it has additional steps
in its mode changing sequence:
- Disable CC state machine,
- Write to mode register,
- Wait for 5 ms,
- Re-enable CC state machine.
It also has an additional register that a revision number can be read from.

Add support for the mode changing sequence, and read the revision number during
probe and print it as info.

Signed-off-by: Yassine Oudjana <y.oudjana@protonmail.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>

authored by

Yassine Oudjana and committed by
Chanwoo Choi
ce0320bd 70c55d6b

+79 -3
+79 -3
drivers/extcon/extcon-usbc-tusb320.c
··· 21 21 #define TUSB320_REG9_INTERRUPT_STATUS BIT(4) 22 22 23 23 #define TUSB320_REGA 0xa 24 + #define TUSB320L_REGA_DISABLE_TERM BIT(0) 24 25 #define TUSB320_REGA_I2C_SOFT_RESET BIT(3) 25 26 #define TUSB320_REGA_MODE_SELECT_SHIFT 4 26 27 #define TUSB320_REGA_MODE_SELECT_MASK 0x3 28 + 29 + #define TUSB320L_REGA0_REVISION 0xa0 27 30 28 31 enum tusb320_attached_state { 29 32 TUSB320_ATTACHED_STATE_NONE, ··· 42 39 TUSB320_MODE_DRP, 43 40 }; 44 41 42 + struct tusb320_priv; 43 + 44 + struct tusb320_ops { 45 + int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode); 46 + int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision); 47 + }; 48 + 45 49 struct tusb320_priv { 46 50 struct device *dev; 47 51 struct regmap *regmap; 48 52 struct extcon_dev *edev; 49 - 53 + struct tusb320_ops *ops; 50 54 enum tusb320_attached_state state; 51 55 }; 52 56 ··· 109 99 return 0; 110 100 } 111 101 102 + static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode) 103 + { 104 + int ret; 105 + 106 + /* Disable CC state machine */ 107 + ret = regmap_write_bits(priv->regmap, TUSB320_REGA, 108 + TUSB320L_REGA_DISABLE_TERM, 1); 109 + if (ret) { 110 + dev_err(priv->dev, 111 + "failed to disable CC state machine: %d\n", ret); 112 + return ret; 113 + } 114 + 115 + /* Write mode */ 116 + ret = regmap_write_bits(priv->regmap, TUSB320_REGA, 117 + TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT, 118 + mode << TUSB320_REGA_MODE_SELECT_SHIFT); 119 + if (ret) { 120 + dev_err(priv->dev, "failed to write mode: %d\n", ret); 121 + goto err; 122 + } 123 + 124 + msleep(5); 125 + err: 126 + /* Re-enable CC state machine */ 127 + ret = regmap_write_bits(priv->regmap, TUSB320_REGA, 128 + TUSB320L_REGA_DISABLE_TERM, 0); 129 + if (ret) 130 + dev_err(priv->dev, 131 + "failed to re-enable CC state machine: %d\n", ret); 132 + 133 + return ret; 134 + } 135 + 112 136 static int tusb320_reset(struct tusb320_priv *priv) 113 137 { 114 138 int ret; 115 139 116 140 /* Set mode to default (follow PORT pin) */ 117 - ret = tusb320_set_mode(priv, TUSB320_MODE_PORT); 141 + ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT); 118 142 if (ret && ret != -EBUSY) { 119 143 dev_err(priv->dev, 120 144 "failed to set mode to PORT: %d\n", ret); ··· 169 125 170 126 return 0; 171 127 } 128 + 129 + static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision) 130 + { 131 + return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision); 132 + } 133 + 134 + static struct tusb320_ops tusb320_ops = { 135 + .set_mode = tusb320_set_mode, 136 + }; 137 + 138 + static struct tusb320_ops tusb320l_ops = { 139 + .set_mode = tusb320l_set_mode, 140 + .get_revision = tusb320l_get_revision, 141 + }; 172 142 173 143 static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) 174 144 { ··· 234 176 const struct i2c_device_id *id) 235 177 { 236 178 struct tusb320_priv *priv; 179 + const void *match_data; 180 + unsigned int revision; 237 181 int ret; 238 182 239 183 priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ··· 251 191 if (ret) 252 192 return ret; 253 193 194 + match_data = device_get_match_data(&client->dev); 195 + if (!match_data) 196 + return -EINVAL; 197 + 198 + priv->ops = (struct tusb320_ops*)match_data; 199 + 254 200 priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable); 255 201 if (IS_ERR(priv->edev)) { 256 202 dev_err(priv->dev, "failed to allocate extcon device\n"); 257 203 return PTR_ERR(priv->edev); 204 + } 205 + 206 + if (priv->ops->get_revision) { 207 + ret = priv->ops->get_revision(priv, &revision); 208 + if (ret) 209 + dev_warn(priv->dev, 210 + "failed to read revision register: %d\n", ret); 211 + else 212 + dev_info(priv->dev, "chip revision %d\n", revision); 258 213 } 259 214 260 215 ret = devm_extcon_dev_register(priv->dev, priv->edev); ··· 306 231 } 307 232 308 233 static const struct of_device_id tusb320_extcon_dt_match[] = { 309 - { .compatible = "ti,tusb320", }, 234 + { .compatible = "ti,tusb320", .data = &tusb320_ops, }, 235 + { .compatible = "ti,tusb320l", .data = &tusb320l_ops, }, 310 236 { } 311 237 }; 312 238 MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);