626 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * drivers/extcon/extcon-tusb320.c - TUSB320 extcon driver4 *5 * Copyright (C) 2020 National Instruments Corporation6 * Author: Michael Auchter <michael.auchter@ni.com>7 */8 9#include <linux/bitfield.h>10#include <linux/extcon-provider.h>11#include <linux/i2c.h>12#include <linux/init.h>13#include <linux/interrupt.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/regmap.h>17#include <linux/usb/typec.h>18#include <linux/usb/typec_altmode.h>19#include <linux/usb/role.h>20#include <linux/irq.h>21 22#define TUSB320_REG8 0x823#define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6)24#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB 0x025#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A 0x126#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A 0x227#define TUSB320_REG8_CURRENT_MODE_DETECT GENMASK(5, 4)28#define TUSB320_REG8_CURRENT_MODE_DETECT_DEF 0x029#define TUSB320_REG8_CURRENT_MODE_DETECT_MED 0x130#define TUSB320_REG8_CURRENT_MODE_DETECT_ACC 0x231#define TUSB320_REG8_CURRENT_MODE_DETECT_HI 0x332#define TUSB320_REG8_ACCESSORY_CONNECTED GENMASK(3, 1)33#define TUSB320_REG8_ACCESSORY_CONNECTED_NONE 0x034#define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO 0x435#define TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG 0x536#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP 0x637#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP 0x738#define TUSB320_REG8_ACTIVE_CABLE_DETECTION BIT(0)39 40#define TUSB320_REG9 0x941#define TUSB320_REG9_ATTACHED_STATE GENMASK(7, 6)42#define TUSB320_REG9_CABLE_DIRECTION BIT(5)43#define TUSB320_REG9_INTERRUPT_STATUS BIT(4)44 45#define TUSB320_REGA 0xa46#define TUSB320L_REGA_DISABLE_TERM BIT(0)47#define TUSB320_REGA_I2C_SOFT_RESET BIT(3)48#define TUSB320_REGA_MODE_SELECT_SHIFT 449#define TUSB320_REGA_MODE_SELECT_MASK 0x350 51#define TUSB320L_REGA0_REVISION 0xa052 53enum tusb320_attached_state {54 TUSB320_ATTACHED_STATE_NONE,55 TUSB320_ATTACHED_STATE_DFP,56 TUSB320_ATTACHED_STATE_UFP,57 TUSB320_ATTACHED_STATE_ACC,58};59 60enum tusb320_mode {61 TUSB320_MODE_PORT,62 TUSB320_MODE_UFP,63 TUSB320_MODE_DFP,64 TUSB320_MODE_DRP,65};66 67struct tusb320_priv;68 69struct tusb320_ops {70 int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode);71 int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision);72};73 74struct tusb320_priv {75 struct device *dev;76 struct regmap *regmap;77 struct extcon_dev *edev;78 struct tusb320_ops *ops;79 enum tusb320_attached_state state;80 struct typec_port *port;81 struct typec_capability cap;82 enum typec_port_type port_type;83 enum typec_pwr_opmode pwr_opmode;84 struct fwnode_handle *connector_fwnode;85 struct usb_role_switch *role_sw;86};87 88static const char * const tusb_attached_states[] = {89 [TUSB320_ATTACHED_STATE_NONE] = "not attached",90 [TUSB320_ATTACHED_STATE_DFP] = "downstream facing port",91 [TUSB320_ATTACHED_STATE_UFP] = "upstream facing port",92 [TUSB320_ATTACHED_STATE_ACC] = "accessory",93};94 95static const unsigned int tusb320_extcon_cable[] = {96 EXTCON_USB,97 EXTCON_USB_HOST,98 EXTCON_NONE,99};100 101static int tusb320_check_signature(struct tusb320_priv *priv)102{103 static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' };104 unsigned val;105 int i, ret;106 107 for (i = 0; i < sizeof(sig); i++) {108 ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val);109 if (ret < 0)110 return ret;111 if (val != sig[i]) {112 dev_err(priv->dev, "signature mismatch!\n");113 return -ENODEV;114 }115 }116 117 return 0;118}119 120static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)121{122 int ret;123 124 /* Mode cannot be changed while cable is attached */125 if (priv->state != TUSB320_ATTACHED_STATE_NONE)126 return -EBUSY;127 128 /* Write mode */129 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,130 TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,131 mode << TUSB320_REGA_MODE_SELECT_SHIFT);132 if (ret) {133 dev_err(priv->dev, "failed to write mode: %d\n", ret);134 return ret;135 }136 137 return 0;138}139 140static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)141{142 int ret;143 144 /* Disable CC state machine */145 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,146 TUSB320L_REGA_DISABLE_TERM, 1);147 if (ret) {148 dev_err(priv->dev,149 "failed to disable CC state machine: %d\n", ret);150 return ret;151 }152 153 /* Write mode */154 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,155 TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,156 mode << TUSB320_REGA_MODE_SELECT_SHIFT);157 if (ret) {158 dev_err(priv->dev, "failed to write mode: %d\n", ret);159 goto err;160 }161 162 msleep(5);163err:164 /* Re-enable CC state machine */165 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,166 TUSB320L_REGA_DISABLE_TERM, 0);167 if (ret)168 dev_err(priv->dev,169 "failed to re-enable CC state machine: %d\n", ret);170 171 return ret;172}173 174static int tusb320_reset(struct tusb320_priv *priv)175{176 int ret;177 178 /* Set mode to default (follow PORT pin) */179 ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT);180 if (ret && ret != -EBUSY) {181 dev_err(priv->dev,182 "failed to set mode to PORT: %d\n", ret);183 return ret;184 }185 186 /* Perform soft reset */187 ret = regmap_write_bits(priv->regmap, TUSB320_REGA,188 TUSB320_REGA_I2C_SOFT_RESET, 1);189 if (ret) {190 dev_err(priv->dev,191 "failed to write soft reset bit: %d\n", ret);192 return ret;193 }194 195 /* Wait for chip to go through reset */196 msleep(95);197 198 return 0;199}200 201static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision)202{203 return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision);204}205 206static struct tusb320_ops tusb320_ops = {207 .set_mode = tusb320_set_mode,208};209 210static struct tusb320_ops tusb320l_ops = {211 .set_mode = tusb320l_set_mode,212 .get_revision = tusb320l_get_revision,213};214 215static int tusb320_set_adv_pwr_mode(struct tusb320_priv *priv)216{217 u8 mode;218 219 if (priv->pwr_opmode == TYPEC_PWR_MODE_USB)220 mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB;221 else if (priv->pwr_opmode == TYPEC_PWR_MODE_1_5A)222 mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A;223 else if (priv->pwr_opmode == TYPEC_PWR_MODE_3_0A)224 mode = TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A;225 else /* No other mode is supported. */226 return -EINVAL;227 228 return regmap_write_bits(priv->regmap, TUSB320_REG8,229 TUSB320_REG8_CURRENT_MODE_ADVERTISE,230 FIELD_PREP(TUSB320_REG8_CURRENT_MODE_ADVERTISE,231 mode));232}233 234static int tusb320_port_type_set(struct typec_port *port,235 enum typec_port_type type)236{237 struct tusb320_priv *priv = typec_get_drvdata(port);238 239 if (type == TYPEC_PORT_SRC)240 return priv->ops->set_mode(priv, TUSB320_MODE_DFP);241 else if (type == TYPEC_PORT_SNK)242 return priv->ops->set_mode(priv, TUSB320_MODE_UFP);243 else if (type == TYPEC_PORT_DRP)244 return priv->ops->set_mode(priv, TUSB320_MODE_DRP);245 else246 return priv->ops->set_mode(priv, TUSB320_MODE_PORT);247}248 249static const struct typec_operations tusb320_typec_ops = {250 .port_type_set = tusb320_port_type_set,251};252 253static void tusb320_extcon_irq_handler(struct tusb320_priv *priv, u8 reg)254{255 int state, polarity;256 257 state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg);258 polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION);259 260 dev_dbg(priv->dev, "attached state: %s, polarity: %d\n",261 tusb_attached_states[state], polarity);262 263 extcon_set_state(priv->edev, EXTCON_USB,264 state == TUSB320_ATTACHED_STATE_UFP);265 extcon_set_state(priv->edev, EXTCON_USB_HOST,266 state == TUSB320_ATTACHED_STATE_DFP);267 extcon_set_property(priv->edev, EXTCON_USB,268 EXTCON_PROP_USB_TYPEC_POLARITY,269 (union extcon_property_value)polarity);270 extcon_set_property(priv->edev, EXTCON_USB_HOST,271 EXTCON_PROP_USB_TYPEC_POLARITY,272 (union extcon_property_value)polarity);273 extcon_sync(priv->edev, EXTCON_USB);274 extcon_sync(priv->edev, EXTCON_USB_HOST);275 276 priv->state = state;277}278 279static void tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9)280{281 struct typec_port *port = priv->port;282 struct device *dev = priv->dev;283 int typec_mode;284 enum usb_role usb_role;285 enum typec_role pwr_role;286 enum typec_data_role data_role;287 u8 state, mode, accessory;288 int ret, reg8;289 bool ori;290 291 ret = regmap_read(priv->regmap, TUSB320_REG8, ®8);292 if (ret) {293 dev_err(dev, "error during reg8 i2c read, ret=%d!\n", ret);294 return;295 }296 297 ori = reg9 & TUSB320_REG9_CABLE_DIRECTION;298 typec_set_orientation(port, ori ? TYPEC_ORIENTATION_REVERSE :299 TYPEC_ORIENTATION_NORMAL);300 301 state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg9);302 accessory = FIELD_GET(TUSB320_REG8_ACCESSORY_CONNECTED, reg8);303 304 switch (state) {305 case TUSB320_ATTACHED_STATE_DFP:306 typec_mode = TYPEC_MODE_USB2;307 usb_role = USB_ROLE_HOST;308 pwr_role = TYPEC_SOURCE;309 data_role = TYPEC_HOST;310 break;311 case TUSB320_ATTACHED_STATE_UFP:312 typec_mode = TYPEC_MODE_USB2;313 usb_role = USB_ROLE_DEVICE;314 pwr_role = TYPEC_SINK;315 data_role = TYPEC_DEVICE;316 break;317 case TUSB320_ATTACHED_STATE_ACC:318 /*319 * Accessory detected. For debug accessories, just make some320 * qualified guesses as to the role for lack of a better option.321 */322 if (accessory == TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO ||323 accessory == TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG) {324 typec_mode = TYPEC_MODE_AUDIO;325 usb_role = USB_ROLE_NONE;326 pwr_role = TYPEC_SINK;327 data_role = TYPEC_DEVICE;328 break;329 } else if (accessory ==330 TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP) {331 typec_mode = TYPEC_MODE_DEBUG;332 pwr_role = TYPEC_SOURCE;333 usb_role = USB_ROLE_HOST;334 data_role = TYPEC_HOST;335 break;336 } else if (accessory ==337 TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP) {338 typec_mode = TYPEC_MODE_DEBUG;339 pwr_role = TYPEC_SINK;340 usb_role = USB_ROLE_DEVICE;341 data_role = TYPEC_DEVICE;342 break;343 }344 345 dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n",346 accessory);347 348 fallthrough;349 default:350 typec_mode = TYPEC_MODE_USB2;351 usb_role = USB_ROLE_NONE;352 pwr_role = TYPEC_SINK;353 data_role = TYPEC_DEVICE;354 break;355 }356 357 typec_set_vconn_role(port, pwr_role);358 typec_set_pwr_role(port, pwr_role);359 typec_set_data_role(port, data_role);360 typec_set_mode(port, typec_mode);361 usb_role_switch_set_role(priv->role_sw, usb_role);362 363 mode = FIELD_GET(TUSB320_REG8_CURRENT_MODE_DETECT, reg8);364 if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_DEF)365 typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB);366 else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_MED)367 typec_set_pwr_opmode(port, TYPEC_PWR_MODE_1_5A);368 else if (mode == TUSB320_REG8_CURRENT_MODE_DETECT_HI)369 typec_set_pwr_opmode(port, TYPEC_PWR_MODE_3_0A);370 else /* Charge through accessory */371 typec_set_pwr_opmode(port, TYPEC_PWR_MODE_USB);372}373 374static irqreturn_t tusb320_state_update_handler(struct tusb320_priv *priv,375 bool force_update)376{377 unsigned int reg;378 379 if (regmap_read(priv->regmap, TUSB320_REG9, ®)) {380 dev_err(priv->dev, "error during i2c read!\n");381 return IRQ_NONE;382 }383 384 if (!force_update && !(reg & TUSB320_REG9_INTERRUPT_STATUS))385 return IRQ_NONE;386 387 tusb320_extcon_irq_handler(priv, reg);388 389 /*390 * Type-C support is optional. Only call the Type-C handler if a391 * port had been registered previously.392 */393 if (priv->port)394 tusb320_typec_irq_handler(priv, reg);395 396 regmap_write(priv->regmap, TUSB320_REG9, reg);397 398 return IRQ_HANDLED;399}400 401static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)402{403 struct tusb320_priv *priv = dev_id;404 405 return tusb320_state_update_handler(priv, false);406}407 408static const struct regmap_config tusb320_regmap_config = {409 .reg_bits = 8,410 .val_bits = 8,411};412 413static int tusb320_extcon_probe(struct tusb320_priv *priv)414{415 int ret;416 417 priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);418 if (IS_ERR(priv->edev)) {419 dev_err(priv->dev, "failed to allocate extcon device\n");420 return PTR_ERR(priv->edev);421 }422 423 ret = devm_extcon_dev_register(priv->dev, priv->edev);424 if (ret < 0) {425 dev_err(priv->dev, "failed to register extcon device\n");426 return ret;427 }428 429 extcon_set_property_capability(priv->edev, EXTCON_USB,430 EXTCON_PROP_USB_TYPEC_POLARITY);431 extcon_set_property_capability(priv->edev, EXTCON_USB_HOST,432 EXTCON_PROP_USB_TYPEC_POLARITY);433 434 return 0;435}436 437static int tusb320_typec_probe(struct i2c_client *client,438 struct tusb320_priv *priv)439{440 struct fwnode_handle *connector;441 const char *cap_str;442 int ret;443 444 /* The Type-C connector is optional, for backward compatibility. */445 connector = device_get_named_child_node(&client->dev, "connector");446 if (!connector)447 return 0;448 449 /* Type-C connector found. */450 ret = typec_get_fw_cap(&priv->cap, connector);451 if (ret)452 goto err_put;453 454 priv->port_type = priv->cap.type;455 456 /* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */457 ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str);458 if (ret)459 goto err_put;460 461 ret = typec_find_pwr_opmode(cap_str);462 if (ret < 0)463 goto err_put;464 465 priv->pwr_opmode = ret;466 467 /* Initialize the hardware with the devicetree settings. */468 ret = tusb320_set_adv_pwr_mode(priv);469 if (ret)470 goto err_put;471 472 priv->cap.revision = USB_TYPEC_REV_1_1;473 priv->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO;474 priv->cap.accessory[1] = TYPEC_ACCESSORY_DEBUG;475 priv->cap.orientation_aware = true;476 priv->cap.driver_data = priv;477 priv->cap.ops = &tusb320_typec_ops;478 priv->cap.fwnode = connector;479 480 priv->port = typec_register_port(&client->dev, &priv->cap);481 if (IS_ERR(priv->port)) {482 ret = PTR_ERR(priv->port);483 goto err_put;484 }485 486 /* Find any optional USB role switch that needs reporting to */487 priv->role_sw = fwnode_usb_role_switch_get(connector);488 if (IS_ERR(priv->role_sw)) {489 ret = PTR_ERR(priv->role_sw);490 goto err_unreg;491 }492 493 priv->connector_fwnode = connector;494 495 return 0;496 497err_unreg:498 typec_unregister_port(priv->port);499 500err_put:501 fwnode_handle_put(connector);502 503 return ret;504}505 506static void tusb320_typec_remove(struct tusb320_priv *priv)507{508 usb_role_switch_put(priv->role_sw);509 typec_unregister_port(priv->port);510 fwnode_handle_put(priv->connector_fwnode);511}512 513static int tusb320_probe(struct i2c_client *client)514{515 struct tusb320_priv *priv;516 const void *match_data;517 unsigned int revision;518 int ret;519 u32 irq_trigger_type = IRQF_TRIGGER_FALLING;520 struct irq_data *irq_d;521 522 priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);523 if (!priv)524 return -ENOMEM;525 526 priv->dev = &client->dev;527 i2c_set_clientdata(client, priv);528 529 priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config);530 if (IS_ERR(priv->regmap))531 return PTR_ERR(priv->regmap);532 533 ret = tusb320_check_signature(priv);534 if (ret)535 return ret;536 537 match_data = device_get_match_data(&client->dev);538 if (!match_data)539 return -EINVAL;540 541 priv->ops = (struct tusb320_ops*)match_data;542 543 if (priv->ops->get_revision) {544 ret = priv->ops->get_revision(priv, &revision);545 if (ret)546 dev_warn(priv->dev,547 "failed to read revision register: %d\n", ret);548 else549 dev_info(priv->dev, "chip revision %d\n", revision);550 }551 552 ret = tusb320_extcon_probe(priv);553 if (ret)554 return ret;555 556 ret = tusb320_typec_probe(client, priv);557 if (ret)558 return ret;559 560 /* update initial state */561 tusb320_state_update_handler(priv, true);562 563 /* Reset chip to its default state */564 ret = tusb320_reset(priv);565 if (ret)566 dev_warn(priv->dev, "failed to reset chip: %d\n", ret);567 else568 /*569 * State and polarity might change after a reset, so update570 * them again and make sure the interrupt status bit is cleared.571 */572 tusb320_state_update_handler(priv, true);573 574 irq_d = irq_get_irq_data(client->irq);575 if (irq_d)576 irq_trigger_type = irqd_get_trigger_type(irq_d);577 578 ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,579 tusb320_irq_handler,580 IRQF_ONESHOT | irq_trigger_type,581 client->name, priv);582 if (ret)583 tusb320_typec_remove(priv);584 585 return ret;586}587 588static void tusb320_remove(struct i2c_client *client)589{590 struct tusb320_priv *priv = i2c_get_clientdata(client);591 592 tusb320_typec_remove(priv);593}594 595static const struct of_device_id tusb320_extcon_dt_match[] = {596 { .compatible = "ti,tusb320", .data = &tusb320_ops, },597 { .compatible = "ti,tusb320l", .data = &tusb320l_ops, },598 { }599};600MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);601 602static struct i2c_driver tusb320_extcon_driver = {603 .probe = tusb320_probe,604 .remove = tusb320_remove,605 .driver = {606 .name = "extcon-tusb320",607 .of_match_table = tusb320_extcon_dt_match,608 },609};610 611static int __init tusb320_init(void)612{613 return i2c_add_driver(&tusb320_extcon_driver);614}615subsys_initcall(tusb320_init);616 617static void __exit tusb320_exit(void)618{619 i2c_del_driver(&tusb320_extcon_driver);620}621module_exit(tusb320_exit);622 623MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>");624MODULE_DESCRIPTION("TI TUSB320 extcon driver");625MODULE_LICENSE("GPL v2");626