1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // extcon-ptn5150.c - PTN5150 CC logic extcon driver to support USB detection 4 // 5 // Based on extcon-sm5502.c driver 6 // Copyright (c) 2018-2019 by Vijai Kumar K 7 // Author: Vijai Kumar K <vijaikumar.kanagarajan@gmail.com> 8 9 #include <linux/err.h> 10 #include <linux/i2c.h> 11 #include <linux/interrupt.h> 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/regmap.h> 15 #include <linux/slab.h> 16 #include <linux/extcon-provider.h> 17 #include <linux/gpio/consumer.h> 18 19 /* PTN5150 registers */ 20 enum ptn5150_reg { 21 PTN5150_REG_DEVICE_ID = 0x01, 22 PTN5150_REG_CONTROL, 23 PTN5150_REG_INT_STATUS, 24 PTN5150_REG_CC_STATUS, 25 PTN5150_REG_CON_DET = 0x09, 26 PTN5150_REG_VCONN_STATUS, 27 PTN5150_REG_RESET, 28 PTN5150_REG_INT_MASK = 0x18, 29 PTN5150_REG_INT_REG_STATUS, 30 PTN5150_REG_END, 31 }; 32 33 #define PTN5150_DFP_ATTACHED 0x1 34 #define PTN5150_UFP_ATTACHED 0x2 35 36 /* Define PTN5150 MASK/SHIFT constant */ 37 #define PTN5150_REG_DEVICE_ID_VENDOR_SHIFT 0 38 #define PTN5150_REG_DEVICE_ID_VENDOR_MASK \ 39 (0x3 << PTN5150_REG_DEVICE_ID_VENDOR_SHIFT) 40 41 #define PTN5150_REG_DEVICE_ID_VERSION_SHIFT 3 42 #define PTN5150_REG_DEVICE_ID_VERSION_MASK \ 43 (0x1f << PTN5150_REG_DEVICE_ID_VERSION_SHIFT) 44 45 #define PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT 2 46 #define PTN5150_REG_CC_PORT_ATTACHMENT_MASK \ 47 (0x7 << PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT) 48 49 #define PTN5150_REG_CC_VBUS_DETECTION_SHIFT 7 50 #define PTN5150_REG_CC_VBUS_DETECTION_MASK \ 51 (0x1 << PTN5150_REG_CC_VBUS_DETECTION_SHIFT) 52 53 #define PTN5150_REG_INT_CABLE_ATTACH_SHIFT 0 54 #define PTN5150_REG_INT_CABLE_ATTACH_MASK \ 55 (0x1 << PTN5150_REG_INT_CABLE_ATTACH_SHIFT) 56 57 #define PTN5150_REG_INT_CABLE_DETACH_SHIFT 1 58 #define PTN5150_REG_INT_CABLE_DETACH_MASK \ 59 (0x1 << PTN5150_REG_CC_CABLE_DETACH_SHIFT) 60 61 struct ptn5150_info { 62 struct device *dev; 63 struct extcon_dev *edev; 64 struct i2c_client *i2c; 65 struct regmap *regmap; 66 struct gpio_desc *int_gpiod; 67 struct gpio_desc *vbus_gpiod; 68 int irq; 69 struct work_struct irq_work; 70 struct mutex mutex; 71 }; 72 73 /* List of detectable cables */ 74 static const unsigned int ptn5150_extcon_cable[] = { 75 EXTCON_USB, 76 EXTCON_USB_HOST, 77 EXTCON_NONE, 78 }; 79 80 static const struct regmap_config ptn5150_regmap_config = { 81 .reg_bits = 8, 82 .val_bits = 8, 83 .max_register = PTN5150_REG_END, 84 }; 85 86 static void ptn5150_irq_work(struct work_struct *work) 87 { 88 struct ptn5150_info *info = container_of(work, 89 struct ptn5150_info, irq_work); 90 int ret = 0; 91 unsigned int reg_data; 92 unsigned int int_status; 93 94 if (!info->edev) 95 return; 96 97 mutex_lock(&info->mutex); 98 99 ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, ®_data); 100 if (ret) { 101 dev_err(info->dev, "failed to read CC STATUS %d\n", ret); 102 mutex_unlock(&info->mutex); 103 return; 104 } 105 106 /* Clear interrupt. Read would clear the register */ 107 ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &int_status); 108 if (ret) { 109 dev_err(info->dev, "failed to read INT STATUS %d\n", ret); 110 mutex_unlock(&info->mutex); 111 return; 112 } 113 114 if (int_status) { 115 unsigned int cable_attach; 116 117 cable_attach = int_status & PTN5150_REG_INT_CABLE_ATTACH_MASK; 118 if (cable_attach) { 119 unsigned int port_status; 120 unsigned int vbus; 121 122 port_status = ((reg_data & 123 PTN5150_REG_CC_PORT_ATTACHMENT_MASK) >> 124 PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT); 125 126 switch (port_status) { 127 case PTN5150_DFP_ATTACHED: 128 extcon_set_state_sync(info->edev, 129 EXTCON_USB_HOST, false); 130 gpiod_set_value(info->vbus_gpiod, 0); 131 extcon_set_state_sync(info->edev, EXTCON_USB, 132 true); 133 break; 134 case PTN5150_UFP_ATTACHED: 135 extcon_set_state_sync(info->edev, EXTCON_USB, 136 false); 137 vbus = ((reg_data & 138 PTN5150_REG_CC_VBUS_DETECTION_MASK) >> 139 PTN5150_REG_CC_VBUS_DETECTION_SHIFT); 140 if (vbus) 141 gpiod_set_value(info->vbus_gpiod, 0); 142 else 143 gpiod_set_value(info->vbus_gpiod, 1); 144 145 extcon_set_state_sync(info->edev, 146 EXTCON_USB_HOST, true); 147 break; 148 default: 149 dev_err(info->dev, 150 "Unknown Port status : %x\n", 151 port_status); 152 break; 153 } 154 } else { 155 extcon_set_state_sync(info->edev, 156 EXTCON_USB_HOST, false); 157 extcon_set_state_sync(info->edev, 158 EXTCON_USB, false); 159 gpiod_set_value(info->vbus_gpiod, 0); 160 } 161 } 162 163 /* Clear interrupt. Read would clear the register */ 164 ret = regmap_read(info->regmap, PTN5150_REG_INT_REG_STATUS, 165 &int_status); 166 if (ret) { 167 dev_err(info->dev, 168 "failed to read INT REG STATUS %d\n", ret); 169 mutex_unlock(&info->mutex); 170 return; 171 } 172 173 mutex_unlock(&info->mutex); 174 } 175 176 177 static irqreturn_t ptn5150_irq_handler(int irq, void *data) 178 { 179 struct ptn5150_info *info = data; 180 181 schedule_work(&info->irq_work); 182 183 return IRQ_HANDLED; 184 } 185 186 static int ptn5150_init_dev_type(struct ptn5150_info *info) 187 { 188 unsigned int reg_data, vendor_id, version_id; 189 int ret; 190 191 ret = regmap_read(info->regmap, PTN5150_REG_DEVICE_ID, ®_data); 192 if (ret) { 193 dev_err(info->dev, "failed to read DEVICE_ID %d\n", ret); 194 return -EINVAL; 195 } 196 197 vendor_id = ((reg_data & PTN5150_REG_DEVICE_ID_VENDOR_MASK) >> 198 PTN5150_REG_DEVICE_ID_VENDOR_SHIFT); 199 version_id = ((reg_data & PTN5150_REG_DEVICE_ID_VERSION_MASK) >> 200 PTN5150_REG_DEVICE_ID_VERSION_SHIFT); 201 202 dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n", 203 version_id, vendor_id); 204 205 /* Clear any existing interrupts */ 206 ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, ®_data); 207 if (ret) { 208 dev_err(info->dev, 209 "failed to read PTN5150_REG_INT_STATUS %d\n", 210 ret); 211 return -EINVAL; 212 } 213 214 ret = regmap_read(info->regmap, PTN5150_REG_INT_REG_STATUS, ®_data); 215 if (ret) { 216 dev_err(info->dev, 217 "failed to read PTN5150_REG_INT_REG_STATUS %d\n", ret); 218 return -EINVAL; 219 } 220 221 return 0; 222 } 223 224 static int ptn5150_i2c_probe(struct i2c_client *i2c, 225 const struct i2c_device_id *id) 226 { 227 struct device *dev = &i2c->dev; 228 struct device_node *np = i2c->dev.of_node; 229 struct ptn5150_info *info; 230 int ret; 231 232 if (!np) 233 return -EINVAL; 234 235 info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); 236 if (!info) 237 return -ENOMEM; 238 i2c_set_clientdata(i2c, info); 239 240 info->dev = &i2c->dev; 241 info->i2c = i2c; 242 info->int_gpiod = devm_gpiod_get(&i2c->dev, "int", GPIOD_IN); 243 if (IS_ERR(info->int_gpiod)) { 244 dev_err(dev, "failed to get INT GPIO\n"); 245 return PTR_ERR(info->int_gpiod); 246 } 247 info->vbus_gpiod = devm_gpiod_get(&i2c->dev, "vbus", GPIOD_IN); 248 if (IS_ERR(info->vbus_gpiod)) { 249 dev_err(dev, "failed to get VBUS GPIO\n"); 250 return PTR_ERR(info->vbus_gpiod); 251 } 252 ret = gpiod_direction_output(info->vbus_gpiod, 0); 253 if (ret) { 254 dev_err(dev, "failed to set VBUS GPIO direction\n"); 255 return -EINVAL; 256 } 257 258 mutex_init(&info->mutex); 259 260 INIT_WORK(&info->irq_work, ptn5150_irq_work); 261 262 info->regmap = devm_regmap_init_i2c(i2c, &ptn5150_regmap_config); 263 if (IS_ERR(info->regmap)) { 264 ret = PTR_ERR(info->regmap); 265 dev_err(info->dev, "failed to allocate register map: %d\n", 266 ret); 267 return ret; 268 } 269 270 if (info->int_gpiod) { 271 info->irq = gpiod_to_irq(info->int_gpiod); 272 if (info->irq < 0) { 273 dev_err(dev, "failed to get INTB IRQ\n"); 274 return info->irq; 275 } 276 277 ret = devm_request_threaded_irq(dev, info->irq, NULL, 278 ptn5150_irq_handler, 279 IRQF_TRIGGER_FALLING | 280 IRQF_ONESHOT, 281 i2c->name, info); 282 if (ret < 0) { 283 dev_err(dev, "failed to request handler for INTB IRQ\n"); 284 return ret; 285 } 286 } 287 288 /* Allocate extcon device */ 289 info->edev = devm_extcon_dev_allocate(info->dev, ptn5150_extcon_cable); 290 if (IS_ERR(info->edev)) { 291 dev_err(info->dev, "failed to allocate memory for extcon\n"); 292 return -ENOMEM; 293 } 294 295 /* Register extcon device */ 296 ret = devm_extcon_dev_register(info->dev, info->edev); 297 if (ret) { 298 dev_err(info->dev, "failed to register extcon device\n"); 299 return ret; 300 } 301 302 /* Initialize PTN5150 device and print vendor id and version id */ 303 ret = ptn5150_init_dev_type(info); 304 if (ret) 305 return -EINVAL; 306 307 return 0; 308 } 309 310 static const struct of_device_id ptn5150_dt_match[] = { 311 { .compatible = "nxp,ptn5150" }, 312 { }, 313 }; 314 MODULE_DEVICE_TABLE(of, ptn5150_dt_match); 315 316 static const struct i2c_device_id ptn5150_i2c_id[] = { 317 { "ptn5150", 0 }, 318 { } 319 }; 320 MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id); 321 322 static struct i2c_driver ptn5150_i2c_driver = { 323 .driver = { 324 .name = "ptn5150", 325 .of_match_table = ptn5150_dt_match, 326 }, 327 .probe = ptn5150_i2c_probe, 328 .id_table = ptn5150_i2c_id, 329 }; 330 331 static int __init ptn5150_i2c_init(void) 332 { 333 return i2c_add_driver(&ptn5150_i2c_driver); 334 } 335 subsys_initcall(ptn5150_i2c_init); 336 337 MODULE_DESCRIPTION("NXP PTN5150 CC logic Extcon driver"); 338 MODULE_AUTHOR("Vijai Kumar K <vijaikumar.kanagarajan@gmail.com>"); 339 MODULE_LICENSE("GPL v2"); 340