File Index
Files and directories
Sign in
FILE

pi3usb30532.c

File preview with syntax highlighting.

linux-6.19.14/drivers/usb/typec/mux/pi3usb30532.c C
  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 void 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}
170
171static const struct i2c_device_id pi3usb30532_table[] = {
172	{ "pi3usb30532" },
173	{ }
174};
175MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
176
177static struct i2c_driver pi3usb30532_driver = {
178	.driver = {
179		.name = "pi3usb30532",
180	},
181	.probe		= pi3usb30532_probe,
182	.remove		= pi3usb30532_remove,
183	.id_table	= pi3usb30532_table,
184};
185
186module_i2c_driver(pi3usb30532_driver);
187
188MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
189MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
190MODULE_LICENSE("GPL");
~/ Read-only file preview.