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

Configure Feed

Select the types of activity you want to include in your feed.

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