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 #define TUSB320_ATTACHED_STATE_NONE 0x0 23 #define TUSB320_ATTACHED_STATE_DFP 0x1 24 #define TUSB320_ATTACHED_STATE_UFP 0x2 25 #define TUSB320_ATTACHED_STATE_ACC 0x3 26 27 struct tusb320_priv { 28 struct device *dev; 29 struct regmap *regmap; 30 struct extcon_dev *edev; 31 }; 32 33 static const char * const tusb_attached_states[] = { 34 [TUSB320_ATTACHED_STATE_NONE] = "not attached", 35 [TUSB320_ATTACHED_STATE_DFP] = "downstream facing port", 36 [TUSB320_ATTACHED_STATE_UFP] = "upstream facing port", 37 [TUSB320_ATTACHED_STATE_ACC] = "accessory", 38 }; 39 40 static const unsigned int tusb320_extcon_cable[] = { 41 EXTCON_USB, 42 EXTCON_USB_HOST, 43 EXTCON_NONE, 44 }; 45 46 static int tusb320_check_signature(struct tusb320_priv *priv) 47 { 48 static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' }; 49 unsigned val; 50 int i, ret; 51 52 for (i = 0; i < sizeof(sig); i++) { 53 ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val); 54 if (ret < 0) 55 return ret; 56 if (val != sig[i]) { 57 dev_err(priv->dev, "signature mismatch!\n"); 58 return -ENODEV; 59 } 60 } 61 62 return 0; 63 } 64 65 static irqreturn_t tusb320_irq_handler(int irq, void *dev_id) 66 { 67 struct tusb320_priv *priv = dev_id; 68 int state, polarity; 69 unsigned reg; 70 71 if (regmap_read(priv->regmap, TUSB320_REG9, ®)) { 72 dev_err(priv->dev, "error during i2c read!\n"); 73 return IRQ_NONE; 74 } 75 76 if (!(reg & TUSB320_REG9_INTERRUPT_STATUS)) 77 return IRQ_NONE; 78 79 state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) & 80 TUSB320_REG9_ATTACHED_STATE_MASK; 81 polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION); 82 83 dev_dbg(priv->dev, "attached state: %s, polarity: %d\n", 84 tusb_attached_states[state], polarity); 85 86 extcon_set_state(priv->edev, EXTCON_USB, 87 state == TUSB320_ATTACHED_STATE_UFP); 88 extcon_set_state(priv->edev, EXTCON_USB_HOST, 89 state == TUSB320_ATTACHED_STATE_DFP); 90 extcon_set_property(priv->edev, EXTCON_USB, 91 EXTCON_PROP_USB_TYPEC_POLARITY, 92 (union extcon_property_value)polarity); 93 extcon_set_property(priv->edev, EXTCON_USB_HOST, 94 EXTCON_PROP_USB_TYPEC_POLARITY, 95 (union extcon_property_value)polarity); 96 extcon_sync(priv->edev, EXTCON_USB); 97 extcon_sync(priv->edev, EXTCON_USB_HOST); 98 99 regmap_write(priv->regmap, TUSB320_REG9, reg); 100 101 return IRQ_HANDLED; 102 } 103 104 static const struct regmap_config tusb320_regmap_config = { 105 .reg_bits = 8, 106 .val_bits = 8, 107 }; 108 109 static int tusb320_extcon_probe(struct i2c_client *client, 110 const struct i2c_device_id *id) 111 { 112 struct tusb320_priv *priv; 113 int ret; 114 115 priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); 116 if (!priv) 117 return -ENOMEM; 118 priv->dev = &client->dev; 119 120 priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config); 121 if (IS_ERR(priv->regmap)) 122 return PTR_ERR(priv->regmap); 123 124 ret = tusb320_check_signature(priv); 125 if (ret) 126 return ret; 127 128 priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable); 129 if (IS_ERR(priv->edev)) { 130 dev_err(priv->dev, "failed to allocate extcon device\n"); 131 return PTR_ERR(priv->edev); 132 } 133 134 ret = devm_extcon_dev_register(priv->dev, priv->edev); 135 if (ret < 0) { 136 dev_err(priv->dev, "failed to register extcon device\n"); 137 return ret; 138 } 139 140 extcon_set_property_capability(priv->edev, EXTCON_USB, 141 EXTCON_PROP_USB_TYPEC_POLARITY); 142 extcon_set_property_capability(priv->edev, EXTCON_USB_HOST, 143 EXTCON_PROP_USB_TYPEC_POLARITY); 144 145 /* update initial state */ 146 tusb320_irq_handler(client->irq, priv); 147 148 ret = devm_request_threaded_irq(priv->dev, client->irq, NULL, 149 tusb320_irq_handler, 150 IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 151 client->name, priv); 152 153 return ret; 154 } 155 156 static const struct of_device_id tusb320_extcon_dt_match[] = { 157 { .compatible = "ti,tusb320", }, 158 { } 159 }; 160 MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match); 161 162 static struct i2c_driver tusb320_extcon_driver = { 163 .probe = tusb320_extcon_probe, 164 .driver = { 165 .name = "extcon-tusb320", 166 .of_match_table = tusb320_extcon_dt_match, 167 }, 168 }; 169 170 static int __init tusb320_init(void) 171 { 172 return i2c_add_driver(&tusb320_extcon_driver); 173 } 174 subsys_initcall(tusb320_init); 175 176 static void __exit tusb320_exit(void) 177 { 178 i2c_del_driver(&tusb320_extcon_driver); 179 } 180 module_exit(tusb320_exit); 181 182 MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>"); 183 MODULE_DESCRIPTION("TI TUSB320 extcon driver"); 184 MODULE_LICENSE("GPL v2"); 185