linux/drivers/usb/typec/mux/pi3usb30532.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Pericom PI3USB30532 Type-C cross switch / mux driver
   4 *
   5 * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
   6 */
   7
   8#include <linux/i2c.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/mutex.h>
  12#include <linux/usb/typec_dp.h>
  13#include <linux/usb/typec_mux.h>
  14
  15#define PI3USB30532_CONF                        0x00
  16
  17#define PI3USB30532_CONF_OPEN                   0x00
  18#define PI3USB30532_CONF_SWAP                   0x01
  19#define PI3USB30532_CONF_4LANE_DP               0x02
  20#define PI3USB30532_CONF_USB3                   0x04
  21#define PI3USB30532_CONF_USB3_AND_2LANE_DP      0x06
  22
  23struct pi3usb30532 {
  24        struct i2c_client *client;
  25        struct mutex lock; /* protects the cached conf register */
  26        struct typec_switch *sw;
  27        struct typec_mux *mux;
  28        u8 conf;
  29};
  30
  31static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
  32{
  33        int ret = 0;
  34
  35        if (pi->conf == new_conf)
  36                return 0;
  37
  38        ret = i2c_smbus_write_byte_data(pi->client, PI3USB30532_CONF, new_conf);
  39        if (ret) {
  40                dev_err(&pi->client->dev, "Error writing conf: %d\n", ret);
  41                return ret;
  42        }
  43
  44        pi->conf = new_conf;
  45        return 0;
  46}
  47
  48static int pi3usb30532_sw_set(struct typec_switch *sw,
  49                              enum typec_orientation orientation)
  50{
  51        struct pi3usb30532 *pi = typec_switch_get_drvdata(sw);
  52        u8 new_conf;
  53        int ret;
  54
  55        mutex_lock(&pi->lock);
  56        new_conf = pi->conf;
  57
  58        switch (orientation) {
  59        case TYPEC_ORIENTATION_NONE:
  60                new_conf = PI3USB30532_CONF_OPEN;
  61                break;
  62        case TYPEC_ORIENTATION_NORMAL:
  63                new_conf &= ~PI3USB30532_CONF_SWAP;
  64                break;
  65        case TYPEC_ORIENTATION_REVERSE:
  66                new_conf |= PI3USB30532_CONF_SWAP;
  67                break;
  68        }
  69
  70        ret = pi3usb30532_set_conf(pi, new_conf);
  71        mutex_unlock(&pi->lock);
  72
  73        return ret;
  74}
  75
  76static int
  77pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
  78{
  79        struct pi3usb30532 *pi = typec_mux_get_drvdata(mux);
  80        u8 new_conf;
  81        int ret;
  82
  83        mutex_lock(&pi->lock);
  84        new_conf = pi->conf;
  85
  86        switch (state->mode) {
  87        case TYPEC_STATE_SAFE:
  88                new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
  89                           PI3USB30532_CONF_OPEN;
  90                break;
  91        case TYPEC_STATE_USB:
  92                new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
  93                           PI3USB30532_CONF_USB3;
  94                break;
  95        case TYPEC_DP_STATE_C:
  96        case TYPEC_DP_STATE_E:
  97                new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
  98                           PI3USB30532_CONF_4LANE_DP;
  99                break;
 100        case TYPEC_DP_STATE_D:
 101                new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
 102                           PI3USB30532_CONF_USB3_AND_2LANE_DP;
 103                break;
 104        default:
 105                break;
 106        }
 107
 108        ret = pi3usb30532_set_conf(pi, new_conf);
 109        mutex_unlock(&pi->lock);
 110
 111        return ret;
 112}
 113
 114static int pi3usb30532_probe(struct i2c_client *client)
 115{
 116        struct device *dev = &client->dev;
 117        struct typec_switch_desc sw_desc = { };
 118        struct typec_mux_desc mux_desc = { };
 119        struct pi3usb30532 *pi;
 120        int ret;
 121
 122        pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL);
 123        if (!pi)
 124                return -ENOMEM;
 125
 126        pi->client = client;
 127        mutex_init(&pi->lock);
 128
 129        ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF);
 130        if (ret < 0) {
 131                dev_err(dev, "Error reading config register %d\n", ret);
 132                return ret;
 133        }
 134        pi->conf = ret;
 135
 136        sw_desc.drvdata = pi;
 137        sw_desc.fwnode = dev->fwnode;
 138        sw_desc.set = pi3usb30532_sw_set;
 139
 140        pi->sw = typec_switch_register(dev, &sw_desc);
 141        if (IS_ERR(pi->sw)) {
 142                dev_err(dev, "Error registering typec switch: %ld\n",
 143                        PTR_ERR(pi->sw));
 144                return PTR_ERR(pi->sw);
 145        }
 146
 147        mux_desc.drvdata = pi;
 148        mux_desc.fwnode = dev->fwnode;
 149        mux_desc.set = pi3usb30532_mux_set;
 150
 151        pi->mux = typec_mux_register(dev, &mux_desc);
 152        if (IS_ERR(pi->mux)) {
 153                typec_switch_unregister(pi->sw);
 154                dev_err(dev, "Error registering typec mux: %ld\n",
 155                        PTR_ERR(pi->mux));
 156                return PTR_ERR(pi->mux);
 157        }
 158
 159        i2c_set_clientdata(client, pi);
 160        return 0;
 161}
 162
 163static int pi3usb30532_remove(struct i2c_client *client)
 164{
 165        struct pi3usb30532 *pi = i2c_get_clientdata(client);
 166
 167        typec_mux_unregister(pi->mux);
 168        typec_switch_unregister(pi->sw);
 169        return 0;
 170}
 171
 172static const struct i2c_device_id pi3usb30532_table[] = {
 173        { "pi3usb30532" },
 174        { }
 175};
 176MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
 177
 178static struct i2c_driver pi3usb30532_driver = {
 179        .driver = {
 180                .name = "pi3usb30532",
 181        },
 182        .probe_new      = pi3usb30532_probe,
 183        .remove         = pi3usb30532_remove,
 184        .id_table       = pi3usb30532_table,
 185};
 186
 187module_i2c_driver(pi3usb30532_driver);
 188
 189MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 190MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
 191MODULE_LICENSE("GPL");
 192