cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pi3usb30532.c (4315B)


      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_dev *sw;
     27	struct typec_mux_dev *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_dev *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_dev *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");