linux/drivers/extcon/extcon-usbc-tusb320.c
<<
>>
Prefs
   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
  23#define TUSB320_REGA                            0xa
  24#define TUSB320L_REGA_DISABLE_TERM              BIT(0)
  25#define TUSB320_REGA_I2C_SOFT_RESET             BIT(3)
  26#define TUSB320_REGA_MODE_SELECT_SHIFT          4
  27#define TUSB320_REGA_MODE_SELECT_MASK           0x3
  28
  29#define TUSB320L_REGA0_REVISION                 0xa0
  30
  31enum tusb320_attached_state {
  32        TUSB320_ATTACHED_STATE_NONE,
  33        TUSB320_ATTACHED_STATE_DFP,
  34        TUSB320_ATTACHED_STATE_UFP,
  35        TUSB320_ATTACHED_STATE_ACC,
  36};
  37
  38enum tusb320_mode {
  39        TUSB320_MODE_PORT,
  40        TUSB320_MODE_UFP,
  41        TUSB320_MODE_DFP,
  42        TUSB320_MODE_DRP,
  43};
  44
  45struct tusb320_priv;
  46
  47struct tusb320_ops {
  48        int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode);
  49        int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision);
  50};
  51
  52struct tusb320_priv {
  53        struct device *dev;
  54        struct regmap *regmap;
  55        struct extcon_dev *edev;
  56        struct tusb320_ops *ops;
  57        enum tusb320_attached_state state;
  58};
  59
  60static const char * const tusb_attached_states[] = {
  61        [TUSB320_ATTACHED_STATE_NONE] = "not attached",
  62        [TUSB320_ATTACHED_STATE_DFP]  = "downstream facing port",
  63        [TUSB320_ATTACHED_STATE_UFP]  = "upstream facing port",
  64        [TUSB320_ATTACHED_STATE_ACC]  = "accessory",
  65};
  66
  67static const unsigned int tusb320_extcon_cable[] = {
  68        EXTCON_USB,
  69        EXTCON_USB_HOST,
  70        EXTCON_NONE,
  71};
  72
  73static int tusb320_check_signature(struct tusb320_priv *priv)
  74{
  75        static const char sig[] = { '\0', 'T', 'U', 'S', 'B', '3', '2', '0' };
  76        unsigned val;
  77        int i, ret;
  78
  79        for (i = 0; i < sizeof(sig); i++) {
  80                ret = regmap_read(priv->regmap, sizeof(sig) - 1 - i, &val);
  81                if (ret < 0)
  82                        return ret;
  83                if (val != sig[i]) {
  84                        dev_err(priv->dev, "signature mismatch!\n");
  85                        return -ENODEV;
  86                }
  87        }
  88
  89        return 0;
  90}
  91
  92static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
  93{
  94        int ret;
  95
  96        /* Mode cannot be changed while cable is attached */
  97        if (priv->state != TUSB320_ATTACHED_STATE_NONE)
  98                return -EBUSY;
  99
 100        /* Write mode */
 101        ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
 102                TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
 103                mode << TUSB320_REGA_MODE_SELECT_SHIFT);
 104        if (ret) {
 105                dev_err(priv->dev, "failed to write mode: %d\n", ret);
 106                return ret;
 107        }
 108
 109        return 0;
 110}
 111
 112static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
 113{
 114        int ret;
 115
 116        /* Disable CC state machine */
 117        ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
 118                TUSB320L_REGA_DISABLE_TERM, 1);
 119        if (ret) {
 120                dev_err(priv->dev,
 121                        "failed to disable CC state machine: %d\n", ret);
 122                return ret;
 123        }
 124
 125        /* Write mode */
 126        ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
 127                TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
 128                mode << TUSB320_REGA_MODE_SELECT_SHIFT);
 129        if (ret) {
 130                dev_err(priv->dev, "failed to write mode: %d\n", ret);
 131                goto err;
 132        }
 133
 134        msleep(5);
 135err:
 136        /* Re-enable CC state machine */
 137        ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
 138                TUSB320L_REGA_DISABLE_TERM, 0);
 139        if (ret)
 140                dev_err(priv->dev,
 141                        "failed to re-enable CC state machine: %d\n", ret);
 142
 143        return ret;
 144}
 145
 146static int tusb320_reset(struct tusb320_priv *priv)
 147{
 148        int ret;
 149
 150        /* Set mode to default (follow PORT pin) */
 151        ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT);
 152        if (ret && ret != -EBUSY) {
 153                dev_err(priv->dev,
 154                        "failed to set mode to PORT: %d\n", ret);
 155                return ret;
 156        }
 157
 158        /* Perform soft reset */
 159        ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
 160                        TUSB320_REGA_I2C_SOFT_RESET, 1);
 161        if (ret) {
 162                dev_err(priv->dev,
 163                        "failed to write soft reset bit: %d\n", ret);
 164                return ret;
 165        }
 166
 167        /* Wait for chip to go through reset */
 168        msleep(95);
 169
 170        return 0;
 171}
 172
 173static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision)
 174{
 175        return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision);
 176}
 177
 178static struct tusb320_ops tusb320_ops = {
 179        .set_mode = tusb320_set_mode,
 180};
 181
 182static struct tusb320_ops tusb320l_ops = {
 183        .set_mode = tusb320l_set_mode,
 184        .get_revision = tusb320l_get_revision,
 185};
 186
 187static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
 188{
 189        struct tusb320_priv *priv = dev_id;
 190        int state, polarity;
 191        unsigned reg;
 192
 193        if (regmap_read(priv->regmap, TUSB320_REG9, &reg)) {
 194                dev_err(priv->dev, "error during i2c read!\n");
 195                return IRQ_NONE;
 196        }
 197
 198        if (!(reg & TUSB320_REG9_INTERRUPT_STATUS))
 199                return IRQ_NONE;
 200
 201        state = (reg >> TUSB320_REG9_ATTACHED_STATE_SHIFT) &
 202                TUSB320_REG9_ATTACHED_STATE_MASK;
 203        polarity = !!(reg & TUSB320_REG9_CABLE_DIRECTION);
 204
 205        dev_dbg(priv->dev, "attached state: %s, polarity: %d\n",
 206                tusb_attached_states[state], polarity);
 207
 208        extcon_set_state(priv->edev, EXTCON_USB,
 209                         state == TUSB320_ATTACHED_STATE_UFP);
 210        extcon_set_state(priv->edev, EXTCON_USB_HOST,
 211                         state == TUSB320_ATTACHED_STATE_DFP);
 212        extcon_set_property(priv->edev, EXTCON_USB,
 213                            EXTCON_PROP_USB_TYPEC_POLARITY,
 214                            (union extcon_property_value)polarity);
 215        extcon_set_property(priv->edev, EXTCON_USB_HOST,
 216                            EXTCON_PROP_USB_TYPEC_POLARITY,
 217                            (union extcon_property_value)polarity);
 218        extcon_sync(priv->edev, EXTCON_USB);
 219        extcon_sync(priv->edev, EXTCON_USB_HOST);
 220
 221        priv->state = state;
 222
 223        regmap_write(priv->regmap, TUSB320_REG9, reg);
 224
 225        return IRQ_HANDLED;
 226}
 227
 228static const struct regmap_config tusb320_regmap_config = {
 229        .reg_bits = 8,
 230        .val_bits = 8,
 231};
 232
 233static int tusb320_extcon_probe(struct i2c_client *client,
 234                                const struct i2c_device_id *id)
 235{
 236        struct tusb320_priv *priv;
 237        const void *match_data;
 238        unsigned int revision;
 239        int ret;
 240
 241        priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
 242        if (!priv)
 243                return -ENOMEM;
 244        priv->dev = &client->dev;
 245
 246        priv->regmap = devm_regmap_init_i2c(client, &tusb320_regmap_config);
 247        if (IS_ERR(priv->regmap))
 248                return PTR_ERR(priv->regmap);
 249
 250        ret = tusb320_check_signature(priv);
 251        if (ret)
 252                return ret;
 253
 254        match_data = device_get_match_data(&client->dev);
 255        if (!match_data)
 256                return -EINVAL;
 257
 258        priv->ops = (struct tusb320_ops*)match_data;
 259
 260        priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);
 261        if (IS_ERR(priv->edev)) {
 262                dev_err(priv->dev, "failed to allocate extcon device\n");
 263                return PTR_ERR(priv->edev);
 264        }
 265
 266        if (priv->ops->get_revision) {
 267                ret = priv->ops->get_revision(priv, &revision);
 268                if (ret)
 269                        dev_warn(priv->dev,
 270                                "failed to read revision register: %d\n", ret);
 271                else
 272                        dev_info(priv->dev, "chip revision %d\n", revision);
 273        }
 274
 275        ret = devm_extcon_dev_register(priv->dev, priv->edev);
 276        if (ret < 0) {
 277                dev_err(priv->dev, "failed to register extcon device\n");
 278                return ret;
 279        }
 280
 281        extcon_set_property_capability(priv->edev, EXTCON_USB,
 282                                       EXTCON_PROP_USB_TYPEC_POLARITY);
 283        extcon_set_property_capability(priv->edev, EXTCON_USB_HOST,
 284                                       EXTCON_PROP_USB_TYPEC_POLARITY);
 285
 286        /* update initial state */
 287        tusb320_irq_handler(client->irq, priv);
 288
 289        /* Reset chip to its default state */
 290        ret = tusb320_reset(priv);
 291        if (ret)
 292                dev_warn(priv->dev, "failed to reset chip: %d\n", ret);
 293        else
 294                /*
 295                 * State and polarity might change after a reset, so update
 296                 * them again and make sure the interrupt status bit is cleared.
 297                 */
 298                tusb320_irq_handler(client->irq, priv);
 299
 300        ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,
 301                                        tusb320_irq_handler,
 302                                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 303                                        client->name, priv);
 304
 305        return ret;
 306}
 307
 308static const struct of_device_id tusb320_extcon_dt_match[] = {
 309        { .compatible = "ti,tusb320", .data = &tusb320_ops, },
 310        { .compatible = "ti,tusb320l", .data = &tusb320l_ops, },
 311        { }
 312};
 313MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);
 314
 315static struct i2c_driver tusb320_extcon_driver = {
 316        .probe          = tusb320_extcon_probe,
 317        .driver         = {
 318                .name   = "extcon-tusb320",
 319                .of_match_table = tusb320_extcon_dt_match,
 320        },
 321};
 322
 323static int __init tusb320_init(void)
 324{
 325        return i2c_add_driver(&tusb320_extcon_driver);
 326}
 327subsys_initcall(tusb320_init);
 328
 329static void __exit tusb320_exit(void)
 330{
 331        i2c_del_driver(&tusb320_extcon_driver);
 332}
 333module_exit(tusb320_exit);
 334
 335MODULE_AUTHOR("Michael Auchter <michael.auchter@ni.com>");
 336MODULE_DESCRIPTION("TI TUSB320 extcon driver");
 337MODULE_LICENSE("GPL v2");
 338