extcon-usbc-tusb320.c (8519B)
1// SPDX-License-Identifier: GPL-2.0 2/** 3 * drivers/extcon/extcon-tusb320.c - TUSB320 extcon driver 4 * 5 * Copyright (C) 2020 National Instruments Corporation 6 * Author: Michael Auchter <michael.auchter@ni.com> 7 */ 8 9#include <linux/extcon-provider.h> 10#include <linux/i2c.h> 11#include <linux/init.h> 12#include <linux/interrupt.h> 13#include <linux/kernel.h> 14#include <linux/module.h> 15#include <linux/regmap.h> 16 17#define TUSB320_REG9 0x9 18#define TUSB320_REG9_ATTACHED_STATE_SHIFT 6 19#define TUSB320_REG9_ATTACHED_STATE_MASK 0x3 20#define TUSB320_REG9_CABLE_DIRECTION BIT(5) 21#define TUSB320_REG9_INTERRUPT_STATUS BIT(4) 22 23#define TUSB320_REGA 0xa 24#define TUSB320L_REGA_DISABLE_TERM BIT(0) 25#define TUSB320_REGA_I2C_SOFT_RESET BIT(3) 26#define TUSB320_REGA_MODE_SELECT_SHIFT 4 27#define TUSB320_REGA_MODE_SELECT_MASK 0x3 28 29#define TUSB320L_REGA0_REVISION 0xa0 30 31enum tusb320_attached_state { 32 TUSB320_ATTACHED_STATE_NONE, 33 TUSB320_ATTACHED_STATE_DFP, 34 TUSB320_ATTACHED_STATE_UFP, 35 TUSB320_ATTACHED_STATE_ACC, 36}; 37 38enum tusb320_mode { 39 TUSB320_MODE_PORT, 40 TUSB320_MODE_UFP, 41 TUSB320_MODE_DFP, 42 TUSB320_MODE_DRP, 43}; 44 45struct tusb320_priv; 46 47struct tusb320_ops { 48 int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode); 49 int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision); 50}; 51 52struct tusb320_priv { 53 struct device *dev; 54 struct regmap *regmap; 55 struct extcon_dev *edev; 56 struct tusb320_ops *ops; 57 enum tusb320_attached_state state; 58}; 59 60static const char * const tusb_attached_states[] = { 61 [TUSB320_ATTACHED_STATE_NONE] = "not attached", 62 [TUSB320_ATTACHED_STATE_DFP] = "downstream facing port", 63 [TUSB320_ATTACHED_STATE_UFP] = "upstream facing port", 64 [TUSB320_ATTACHED_STATE_ACC] = "accessory", 65}; 66 67static const unsigned int tusb320_extcon_cable[] = { 68 EXTCON_USB, 69 EXTCON_USB_HOST, 70 EXTCON_NONE, 71}; 72 73static int tusb320_check_signature(struct tusb320_priv *priv) 74{ 75 static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' }; 76 unsigned val; 77 int i, ret; 78 79 for (i = 0; i < sizeof(sig); i++) { 80 ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val); 81 if (ret < 0) 82 return ret; 83 if (val != sig[i]) { 84 dev_err(priv->dev, "signature mismatch!\n"); 85 return -ENODEV; 86 } 87 } 88 89 return 0; 90} 91 92static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode) 93{ 94 int ret; 95 96 /* Mode cannot be changed while cable is attached */ 97 if (priv->state != TUSB320_ATTACHED_STATE_NONE) 98 return -EBUSY; 99 100 /* Write mode */ 101 ret = regmap_write_bits(priv->regmap, TUSB320_REGA, 102 TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT, 103 mode << TUSB320_REGA_MODE_SELECT_SHIFT); 104 if (ret) { 105 dev_err(priv->dev, "failed to write mode: %d\n", ret); 106 return ret; 107 } 108 109 return 0; 110} 111 112static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode) 113{ 114 int ret; 115 116 /* Disable CC state machine */ 117 ret = regmap_write_bits(priv->regmap, TUSB320_REGA, 118 TUSB320L_REGA_DISABLE_TERM, 1); 119 if (ret) { 120 dev_err(priv->dev, 121 "failed to disable CC state machine: %d\n", ret); 122 return ret; 123 } 124 125 /* Write mode */ 126 ret = regmap_write_bits(priv->regmap, TUSB320_REGA, 127 TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT, 128 mode << TUSB320_REGA_MODE_SELECT_SHIFT); 129 if (ret) { 130 dev_err(priv->dev, "failed to write mode: %d\n", ret); 131 goto err; 132 } 133 134 msleep(5); 135err: 136 /* Re-enable CC state machine */ 137 ret = regmap_write_bits(priv->regmap, TUSB320_REGA, 138 TUSB320L_REGA_DISABLE_TERM, 0); 139 if (ret) 140 dev_err(priv->dev, 141 "failed to re-enable CC state machine: %d\n", ret); 142 143 return ret; 144} 145 146static int tusb320_reset(struct tusb320_priv *priv) 147{ 148 int ret; 149 150 /* Set mode to default (follow PORT pin) */ 151 ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT); 152 if (ret && ret != -EBUSY) { 153 dev_err(priv->dev, 154 "failed to set mode to PORT: %d\n", ret); 155 return ret; 156 } 157 158 /* Perform soft reset */ 159 ret = regmap_write_bits(priv->regmap, TUSB320_REGA, 160 TUSB320_REGA_I2C_SOFT_RESET, 1); 161 if (ret) { 162 dev_err(priv->dev, 163 "failed to write soft reset bit: %d\n", ret); 164 return ret; 165 } 166 167 /* Wait for chip to go through reset */ 168 msleep(95); 169 170 return 0; 171} 172 173static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision) 174{ 175 return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision); 176} 177 178static struct tusb320_ops tusb320_ops = { 179 .set_mode = tusb320_set_mode, 180}; 181 182static struct tusb320_ops tusb320l_ops = { 183 .set_mode = tusb320l_set_mode, 184 .get_revision = tusb320l_get_revision, 185}; 186 187static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) 188{ 189 struct tusb320_priv *priv = dev_id; 190 int state, polarity; 191 unsigned reg; 192 193 if (regmap_read(priv->regmap, TUSB320_REG9, ®)) { 194 dev_err(priv->dev, "error during i2c read!\n"); 195 return IRQ_NONE; 196 } 197 198 if (!(reg & TUSB320_REG9_INTERRUPT_STATUS)) 199 return IRQ_NONE; 200 201 state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) & 202 TUSB320_REG9_ATTACHED_STATE_MASK; 203 polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION); 204 205 dev_dbg(priv->dev, "attached state: %s, polarity: %d\n", 206 tusb_attached_states[state], polarity); 207 208 extcon_set_state(priv->edev, EXTCON_USB, 209 state == TUSB320_ATTACHED_STATE_UFP); 210 extcon_set_state(priv->edev, EXTCON_USB_HOST, 211 state == TUSB320_ATTACHED_STATE_DFP); 212 extcon_set_property(priv->edev, EXTCON_USB, 213 EXTCON_PROP_USB_TYPEC_POLARITY, 214 (union extcon_property_value)polarity); 215 extcon_set_property(priv->edev, EXTCON_USB_HOST, 216 EXTCON_PROP_USB_TYPEC_POLARITY, 217 (union extcon_property_value)polarity); 218 extcon_sync(priv->edev, EXTCON_USB); 219 extcon_sync(priv->edev, EXTCON_USB_HOST); 220 221 priv->state = state; 222 223 regmap_write(priv->regmap, TUSB320_REG9, reg); 224 225 return IRQ_HANDLED; 226} 227 228static const struct regmap_config tusb320_regmap_config = { 229 .reg_bits = 8, 230 .val_bits = 8, 231}; 232 233static int tusb320_extcon_probe(struct i2c_client *client, 234 const struct i2c_device_id *id) 235{ 236 struct tusb320_priv *priv; 237 const void *match_data; 238 unsigned int revision; 239 int ret; 240 241 priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); 242 if (!priv) 243 return -ENOMEM; 244 priv->dev = &client->dev; 245 246 priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config); 247 if (IS_ERR(priv->regmap)) 248 return PTR_ERR(priv->regmap); 249 250 ret = tusb320_check_signature(priv); 251 if (ret) 252 return ret; 253 254 match_data = device_get_match_data(&client->dev); 255 if (!match_data) 256 return -EINVAL; 257 258 priv->ops = (struct tusb320_ops*)match_data; 259 260 priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable); 261 if (IS_ERR(priv->edev)) { 262 dev_err(priv->dev, "failed to allocate extcon device\n"); 263 return PTR_ERR(priv->edev); 264 } 265 266 if (priv->ops->get_revision) { 267 ret = priv->ops->get_revision(priv, &revision); 268 if (ret) 269 dev_warn(priv->dev, 270 "failed to read revision register: %d\n", ret); 271 else 272 dev_info(priv->dev, "chip revision %d\n", revision); 273 } 274 275 ret = devm_extcon_dev_register(priv->dev, priv->edev); 276 if (ret < 0) { 277 dev_err(priv->dev, "failed to register extcon device\n"); 278 return ret; 279 } 280 281 extcon_set_property_capability(priv->edev, EXTCON_USB, 282 EXTCON_PROP_USB_TYPEC_POLARITY); 283 extcon_set_property_capability(priv->edev, EXTCON_USB_HOST, 284 EXTCON_PROP_USB_TYPEC_POLARITY); 285 286 /* update initial state */ 287 tusb320_irq_handler(client->irq, priv); 288 289 /* Reset chip to its default state */ 290 ret = tusb320_reset(priv); 291 if (ret) 292 dev_warn(priv->dev, "failed to reset chip: %d\n", ret); 293 else 294 /* 295 * State and polarity might change after a reset, so update 296 * them again and make sure the interrupt status bit is cleared. 297 */ 298 tusb320_irq_handler(client->irq, priv); 299 300 ret = devm_request_threaded_irq(priv->dev, client->irq, NULL, 301 tusb320_irq_handler, 302 IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 303 client->name, priv); 304 305 return ret; 306} 307 308static const struct of_device_id tusb320_extcon_dt_match[] = { 309 { .compatible = "ti,tusb320", .data = &tusb320_ops, }, 310 { .compatible = "ti,tusb320l", .data = &tusb320l_ops, }, 311 { } 312}; 313MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match); 314 315static struct i2c_driver tusb320_extcon_driver = { 316 .probe = tusb320_extcon_probe, 317 .driver = { 318 .name = "extcon-tusb320", 319 .of_match_table = tusb320_extcon_dt_match, 320 }, 321}; 322 323static int __init tusb320_init(void) 324{ 325 return i2c_add_driver(&tusb320_extcon_driver); 326} 327subsys_initcall(tusb320_init); 328 329static void __exit tusb320_exit(void) 330{ 331 i2c_del_driver(&tusb320_extcon_driver); 332} 333module_exit(tusb320_exit); 334 335MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>"); 336MODULE_DESCRIPTION("TI TUSB320 extcon driver"); 337MODULE_LICENSE("GPL v2");