Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Pericom PI3USB30532 Type-C cross switch / mux driver
0004  *
0005  * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
0006  */
0007 
0008 #include <linux/i2c.h>
0009 #include <linux/kernel.h>
0010 #include <linux/module.h>
0011 #include <linux/mutex.h>
0012 #include <linux/usb/typec_dp.h>
0013 #include <linux/usb/typec_mux.h>
0014 
0015 #define PI3USB30532_CONF            0x00
0016 
0017 #define PI3USB30532_CONF_OPEN           0x00
0018 #define PI3USB30532_CONF_SWAP           0x01
0019 #define PI3USB30532_CONF_4LANE_DP       0x02
0020 #define PI3USB30532_CONF_USB3           0x04
0021 #define PI3USB30532_CONF_USB3_AND_2LANE_DP  0x06
0022 
0023 struct pi3usb30532 {
0024     struct i2c_client *client;
0025     struct mutex lock; /* protects the cached conf register */
0026     struct typec_switch_dev *sw;
0027     struct typec_mux_dev *mux;
0028     u8 conf;
0029 };
0030 
0031 static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
0032 {
0033     int ret = 0;
0034 
0035     if (pi->conf == new_conf)
0036         return 0;
0037 
0038     ret = i2c_smbus_write_byte_data(pi->client, PI3USB30532_CONF, new_conf);
0039     if (ret) {
0040         dev_err(&pi->client->dev, "Error writing conf: %d\n", ret);
0041         return ret;
0042     }
0043 
0044     pi->conf = new_conf;
0045     return 0;
0046 }
0047 
0048 static int pi3usb30532_sw_set(struct typec_switch_dev *sw,
0049                   enum typec_orientation orientation)
0050 {
0051     struct pi3usb30532 *pi = typec_switch_get_drvdata(sw);
0052     u8 new_conf;
0053     int ret;
0054 
0055     mutex_lock(&pi->lock);
0056     new_conf = pi->conf;
0057 
0058     switch (orientation) {
0059     case TYPEC_ORIENTATION_NONE:
0060         new_conf = PI3USB30532_CONF_OPEN;
0061         break;
0062     case TYPEC_ORIENTATION_NORMAL:
0063         new_conf &= ~PI3USB30532_CONF_SWAP;
0064         break;
0065     case TYPEC_ORIENTATION_REVERSE:
0066         new_conf |= PI3USB30532_CONF_SWAP;
0067         break;
0068     }
0069 
0070     ret = pi3usb30532_set_conf(pi, new_conf);
0071     mutex_unlock(&pi->lock);
0072 
0073     return ret;
0074 }
0075 
0076 static int
0077 pi3usb30532_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
0078 {
0079     struct pi3usb30532 *pi = typec_mux_get_drvdata(mux);
0080     u8 new_conf;
0081     int ret;
0082 
0083     mutex_lock(&pi->lock);
0084     new_conf = pi->conf;
0085 
0086     switch (state->mode) {
0087     case TYPEC_STATE_SAFE:
0088         new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
0089                PI3USB30532_CONF_OPEN;
0090         break;
0091     case TYPEC_STATE_USB:
0092         new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
0093                PI3USB30532_CONF_USB3;
0094         break;
0095     case TYPEC_DP_STATE_C:
0096     case TYPEC_DP_STATE_E:
0097         new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
0098                PI3USB30532_CONF_4LANE_DP;
0099         break;
0100     case TYPEC_DP_STATE_D:
0101         new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
0102                PI3USB30532_CONF_USB3_AND_2LANE_DP;
0103         break;
0104     default:
0105         break;
0106     }
0107 
0108     ret = pi3usb30532_set_conf(pi, new_conf);
0109     mutex_unlock(&pi->lock);
0110 
0111     return ret;
0112 }
0113 
0114 static int pi3usb30532_probe(struct i2c_client *client)
0115 {
0116     struct device *dev = &client->dev;
0117     struct typec_switch_desc sw_desc = { };
0118     struct typec_mux_desc mux_desc = { };
0119     struct pi3usb30532 *pi;
0120     int ret;
0121 
0122     pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL);
0123     if (!pi)
0124         return -ENOMEM;
0125 
0126     pi->client = client;
0127     mutex_init(&pi->lock);
0128 
0129     ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF);
0130     if (ret < 0) {
0131         dev_err(dev, "Error reading config register %d\n", ret);
0132         return ret;
0133     }
0134     pi->conf = ret;
0135 
0136     sw_desc.drvdata = pi;
0137     sw_desc.fwnode = dev->fwnode;
0138     sw_desc.set = pi3usb30532_sw_set;
0139 
0140     pi->sw = typec_switch_register(dev, &sw_desc);
0141     if (IS_ERR(pi->sw)) {
0142         dev_err(dev, "Error registering typec switch: %ld\n",
0143             PTR_ERR(pi->sw));
0144         return PTR_ERR(pi->sw);
0145     }
0146 
0147     mux_desc.drvdata = pi;
0148     mux_desc.fwnode = dev->fwnode;
0149     mux_desc.set = pi3usb30532_mux_set;
0150 
0151     pi->mux = typec_mux_register(dev, &mux_desc);
0152     if (IS_ERR(pi->mux)) {
0153         typec_switch_unregister(pi->sw);
0154         dev_err(dev, "Error registering typec mux: %ld\n",
0155             PTR_ERR(pi->mux));
0156         return PTR_ERR(pi->mux);
0157     }
0158 
0159     i2c_set_clientdata(client, pi);
0160     return 0;
0161 }
0162 
0163 static int pi3usb30532_remove(struct i2c_client *client)
0164 {
0165     struct pi3usb30532 *pi = i2c_get_clientdata(client);
0166 
0167     typec_mux_unregister(pi->mux);
0168     typec_switch_unregister(pi->sw);
0169     return 0;
0170 }
0171 
0172 static const struct i2c_device_id pi3usb30532_table[] = {
0173     { "pi3usb30532" },
0174     { }
0175 };
0176 MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
0177 
0178 static struct i2c_driver pi3usb30532_driver = {
0179     .driver = {
0180         .name = "pi3usb30532",
0181     },
0182     .probe_new  = pi3usb30532_probe,
0183     .remove     = pi3usb30532_remove,
0184     .id_table   = pi3usb30532_table,
0185 };
0186 
0187 module_i2c_driver(pi3usb30532_driver);
0188 
0189 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
0190 MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
0191 MODULE_LICENSE("GPL");