Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // extcon-ptn5150.c - PTN5150 CC logic extcon driver to support USB detection
0004 //
0005 // Based on extcon-sm5502.c driver
0006 // Copyright (c) 2018-2019 by Vijai Kumar K
0007 // Author: Vijai Kumar K <vijaikumar.kanagarajan@gmail.com>
0008 // Copyright (c) 2020 Krzysztof Kozlowski <krzk@kernel.org>
0009 
0010 #include <linux/bitfield.h>
0011 #include <linux/err.h>
0012 #include <linux/i2c.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <linux/regmap.h>
0017 #include <linux/slab.h>
0018 #include <linux/extcon-provider.h>
0019 #include <linux/gpio/consumer.h>
0020 #include <linux/usb/role.h>
0021 
0022 /* PTN5150 registers */
0023 #define PTN5150_REG_DEVICE_ID           0x01
0024 #define PTN5150_REG_CONTROL         0x02
0025 #define PTN5150_REG_INT_STATUS          0x03
0026 #define PTN5150_REG_CC_STATUS           0x04
0027 #define PTN5150_REG_CON_DET         0x09
0028 #define PTN5150_REG_VCONN_STATUS        0x0a
0029 #define PTN5150_REG_RESET           0x0b
0030 #define PTN5150_REG_INT_MASK            0x18
0031 #define PTN5150_REG_INT_REG_STATUS      0x19
0032 #define PTN5150_REG_END             PTN5150_REG_INT_REG_STATUS
0033 
0034 #define PTN5150_DFP_ATTACHED            0x1
0035 #define PTN5150_UFP_ATTACHED            0x2
0036 
0037 /* Define PTN5150 MASK/SHIFT constant */
0038 #define PTN5150_REG_DEVICE_ID_VERSION       GENMASK(7, 3)
0039 #define PTN5150_REG_DEVICE_ID_VENDOR        GENMASK(2, 0)
0040 
0041 #define PTN5150_REG_CC_PORT_ATTACHMENT      GENMASK(4, 2)
0042 #define PTN5150_REG_CC_VBUS_DETECTION       BIT(7)
0043 #define PTN5150_REG_INT_CABLE_ATTACH_MASK   BIT(0)
0044 #define PTN5150_REG_INT_CABLE_DETACH_MASK   BIT(1)
0045 
0046 struct ptn5150_info {
0047     struct device *dev;
0048     struct extcon_dev *edev;
0049     struct i2c_client *i2c;
0050     struct regmap *regmap;
0051     struct gpio_desc *int_gpiod;
0052     struct gpio_desc *vbus_gpiod;
0053     int irq;
0054     struct work_struct irq_work;
0055     struct mutex mutex;
0056     struct usb_role_switch *role_sw;
0057 };
0058 
0059 /* List of detectable cables */
0060 static const unsigned int ptn5150_extcon_cable[] = {
0061     EXTCON_USB,
0062     EXTCON_USB_HOST,
0063     EXTCON_NONE,
0064 };
0065 
0066 static const struct regmap_config ptn5150_regmap_config = {
0067     .reg_bits   = 8,
0068     .val_bits   = 8,
0069     .max_register   = PTN5150_REG_END,
0070 };
0071 
0072 static void ptn5150_check_state(struct ptn5150_info *info)
0073 {
0074     unsigned int port_status, reg_data, vbus;
0075     enum usb_role usb_role = USB_ROLE_NONE;
0076     int ret;
0077 
0078     ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, &reg_data);
0079     if (ret) {
0080         dev_err(info->dev, "failed to read CC STATUS %d\n", ret);
0081         return;
0082     }
0083 
0084     port_status = FIELD_GET(PTN5150_REG_CC_PORT_ATTACHMENT, reg_data);
0085 
0086     switch (port_status) {
0087     case PTN5150_DFP_ATTACHED:
0088         extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
0089         gpiod_set_value_cansleep(info->vbus_gpiod, 0);
0090         extcon_set_state_sync(info->edev, EXTCON_USB, true);
0091         usb_role = USB_ROLE_DEVICE;
0092         break;
0093     case PTN5150_UFP_ATTACHED:
0094         extcon_set_state_sync(info->edev, EXTCON_USB, false);
0095         vbus = FIELD_GET(PTN5150_REG_CC_VBUS_DETECTION, reg_data);
0096         if (vbus)
0097             gpiod_set_value_cansleep(info->vbus_gpiod, 0);
0098         else
0099             gpiod_set_value_cansleep(info->vbus_gpiod, 1);
0100 
0101         extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
0102         usb_role = USB_ROLE_HOST;
0103         break;
0104     default:
0105         break;
0106     }
0107 
0108     if (usb_role) {
0109         ret = usb_role_switch_set_role(info->role_sw, usb_role);
0110         if (ret)
0111             dev_err(info->dev, "failed to set %s role: %d\n",
0112                 usb_role_string(usb_role), ret);
0113     }
0114 }
0115 
0116 static void ptn5150_irq_work(struct work_struct *work)
0117 {
0118     struct ptn5150_info *info = container_of(work,
0119             struct ptn5150_info, irq_work);
0120     int ret = 0;
0121     unsigned int int_status;
0122 
0123     if (!info->edev)
0124         return;
0125 
0126     mutex_lock(&info->mutex);
0127 
0128     /* Clear interrupt. Read would clear the register */
0129     ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &int_status);
0130     if (ret) {
0131         dev_err(info->dev, "failed to read INT STATUS %d\n", ret);
0132         mutex_unlock(&info->mutex);
0133         return;
0134     }
0135 
0136     if (int_status) {
0137         unsigned int cable_attach;
0138 
0139         cable_attach = int_status & PTN5150_REG_INT_CABLE_ATTACH_MASK;
0140         if (cable_attach) {
0141             ptn5150_check_state(info);
0142         } else {
0143             extcon_set_state_sync(info->edev,
0144                     EXTCON_USB_HOST, false);
0145             extcon_set_state_sync(info->edev,
0146                     EXTCON_USB, false);
0147             gpiod_set_value_cansleep(info->vbus_gpiod, 0);
0148 
0149             ret = usb_role_switch_set_role(info->role_sw,
0150                                USB_ROLE_NONE);
0151             if (ret)
0152                 dev_err(info->dev,
0153                     "failed to set none role: %d\n",
0154                     ret);
0155         }
0156     }
0157 
0158     /* Clear interrupt. Read would clear the register */
0159     ret = regmap_read(info->regmap, PTN5150_REG_INT_REG_STATUS,
0160             &int_status);
0161     if (ret) {
0162         dev_err(info->dev,
0163             "failed to read INT REG STATUS %d\n", ret);
0164         mutex_unlock(&info->mutex);
0165         return;
0166     }
0167 
0168     mutex_unlock(&info->mutex);
0169 }
0170 
0171 
0172 static irqreturn_t ptn5150_irq_handler(int irq, void *data)
0173 {
0174     struct ptn5150_info *info = data;
0175 
0176     schedule_work(&info->irq_work);
0177 
0178     return IRQ_HANDLED;
0179 }
0180 
0181 static int ptn5150_init_dev_type(struct ptn5150_info *info)
0182 {
0183     unsigned int reg_data, vendor_id, version_id;
0184     int ret;
0185 
0186     ret = regmap_read(info->regmap, PTN5150_REG_DEVICE_ID, &reg_data);
0187     if (ret) {
0188         dev_err(info->dev, "failed to read DEVICE_ID %d\n", ret);
0189         return -EINVAL;
0190     }
0191 
0192     vendor_id = FIELD_GET(PTN5150_REG_DEVICE_ID_VENDOR, reg_data);
0193     version_id = FIELD_GET(PTN5150_REG_DEVICE_ID_VERSION, reg_data);
0194     dev_dbg(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
0195         version_id, vendor_id);
0196 
0197     /* Clear any existing interrupts */
0198     ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &reg_data);
0199     if (ret) {
0200         dev_err(info->dev,
0201             "failed to read PTN5150_REG_INT_STATUS %d\n",
0202             ret);
0203         return -EINVAL;
0204     }
0205 
0206     ret = regmap_read(info->regmap, PTN5150_REG_INT_REG_STATUS, &reg_data);
0207     if (ret) {
0208         dev_err(info->dev,
0209             "failed to read PTN5150_REG_INT_REG_STATUS %d\n", ret);
0210         return -EINVAL;
0211     }
0212 
0213     return 0;
0214 }
0215 
0216 static void ptn5150_work_sync_and_put(void *data)
0217 {
0218     struct ptn5150_info *info = data;
0219 
0220     cancel_work_sync(&info->irq_work);
0221     usb_role_switch_put(info->role_sw);
0222 }
0223 
0224 static int ptn5150_i2c_probe(struct i2c_client *i2c)
0225 {
0226     struct device *dev = &i2c->dev;
0227     struct device_node *np = i2c->dev.of_node;
0228     struct ptn5150_info *info;
0229     int ret;
0230 
0231     if (!np)
0232         return -EINVAL;
0233 
0234     info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);
0235     if (!info)
0236         return -ENOMEM;
0237     i2c_set_clientdata(i2c, info);
0238 
0239     info->dev = &i2c->dev;
0240     info->i2c = i2c;
0241     info->vbus_gpiod = devm_gpiod_get(&i2c->dev, "vbus", GPIOD_OUT_LOW);
0242     if (IS_ERR(info->vbus_gpiod)) {
0243         ret = PTR_ERR(info->vbus_gpiod);
0244         if (ret == -ENOENT) {
0245             dev_info(dev, "No VBUS GPIO, ignoring VBUS control\n");
0246             info->vbus_gpiod = NULL;
0247         } else {
0248             return dev_err_probe(dev, ret, "failed to get VBUS GPIO\n");
0249         }
0250     }
0251 
0252     mutex_init(&info->mutex);
0253 
0254     INIT_WORK(&info->irq_work, ptn5150_irq_work);
0255 
0256     info->regmap = devm_regmap_init_i2c(i2c, &ptn5150_regmap_config);
0257     if (IS_ERR(info->regmap)) {
0258         return dev_err_probe(info->dev, PTR_ERR(info->regmap),
0259                      "failed to allocate register map\n");
0260     }
0261 
0262     if (i2c->irq > 0) {
0263         info->irq = i2c->irq;
0264     } else {
0265         info->int_gpiod = devm_gpiod_get(&i2c->dev, "int", GPIOD_IN);
0266         if (IS_ERR(info->int_gpiod)) {
0267             return dev_err_probe(dev, PTR_ERR(info->int_gpiod),
0268                          "failed to get INT GPIO\n");
0269         }
0270 
0271         info->irq = gpiod_to_irq(info->int_gpiod);
0272         if (info->irq < 0) {
0273             dev_err(dev, "failed to get INTB IRQ\n");
0274             return info->irq;
0275         }
0276     }
0277 
0278     ret = devm_request_threaded_irq(dev, info->irq, NULL,
0279                     ptn5150_irq_handler,
0280                     IRQF_TRIGGER_FALLING |
0281                     IRQF_ONESHOT,
0282                     i2c->name, info);
0283     if (ret < 0) {
0284         dev_err(dev, "failed to request handler for INTB IRQ\n");
0285         return ret;
0286     }
0287 
0288     /* Allocate extcon device */
0289     info->edev = devm_extcon_dev_allocate(info->dev, ptn5150_extcon_cable);
0290     if (IS_ERR(info->edev)) {
0291         dev_err(info->dev, "failed to allocate memory for extcon\n");
0292         return -ENOMEM;
0293     }
0294 
0295     /* Register extcon device */
0296     ret = devm_extcon_dev_register(info->dev, info->edev);
0297     if (ret) {
0298         dev_err(info->dev, "failed to register extcon device\n");
0299         return ret;
0300     }
0301 
0302     extcon_set_property_capability(info->edev, EXTCON_USB,
0303                     EXTCON_PROP_USB_VBUS);
0304     extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
0305                     EXTCON_PROP_USB_VBUS);
0306     extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
0307                     EXTCON_PROP_USB_TYPEC_POLARITY);
0308 
0309     /* Initialize PTN5150 device and print vendor id and version id */
0310     ret = ptn5150_init_dev_type(info);
0311     if (ret)
0312         return -EINVAL;
0313 
0314     info->role_sw = usb_role_switch_get(info->dev);
0315     if (IS_ERR(info->role_sw))
0316         return dev_err_probe(info->dev, PTR_ERR(info->role_sw),
0317                      "failed to get role switch\n");
0318 
0319     ret = devm_add_action_or_reset(dev, ptn5150_work_sync_and_put, info);
0320     if (ret)
0321         return ret;
0322 
0323     /*
0324      * Update current extcon state if for example OTG connection was there
0325      * before the probe
0326      */
0327     mutex_lock(&info->mutex);
0328     ptn5150_check_state(info);
0329     mutex_unlock(&info->mutex);
0330 
0331     return 0;
0332 }
0333 
0334 static const struct of_device_id ptn5150_dt_match[] = {
0335     { .compatible = "nxp,ptn5150" },
0336     { },
0337 };
0338 MODULE_DEVICE_TABLE(of, ptn5150_dt_match);
0339 
0340 static const struct i2c_device_id ptn5150_i2c_id[] = {
0341     { "ptn5150", 0 },
0342     { }
0343 };
0344 MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id);
0345 
0346 static struct i2c_driver ptn5150_i2c_driver = {
0347     .driver     = {
0348         .name   = "ptn5150",
0349         .of_match_table = ptn5150_dt_match,
0350     },
0351     .probe_new  = ptn5150_i2c_probe,
0352     .id_table = ptn5150_i2c_id,
0353 };
0354 module_i2c_driver(ptn5150_i2c_driver);
0355 
0356 MODULE_DESCRIPTION("NXP PTN5150 CC logic Extcon driver");
0357 MODULE_AUTHOR("Vijai Kumar K <vijaikumar.kanagarajan@gmail.com>");
0358 MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
0359 MODULE_LICENSE("GPL v2");