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 
23 struct 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 
pi3usb30532_set_conf(struct pi3usb30532 * pi,u8 new_conf)31 static 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 
pi3usb30532_sw_set(struct typec_switch * sw,enum typec_orientation orientation)48 static int pi3usb30532_sw_set(struct typec_switch *sw,
49 			      enum typec_orientation orientation)
50 {
51 	struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, 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 
pi3usb30532_mux_set(struct typec_mux * mux,int state)76 static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
77 {
78 	struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux);
79 	u8 new_conf;
80 	int ret;
81 
82 	mutex_lock(&pi->lock);
83 	new_conf = pi->conf;
84 
85 	switch (state) {
86 	case TYPEC_STATE_SAFE:
87 		new_conf = PI3USB30532_CONF_OPEN;
88 		break;
89 	case TYPEC_STATE_USB:
90 		new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
91 			   PI3USB30532_CONF_USB3;
92 		break;
93 	case TYPEC_DP_STATE_C:
94 	case TYPEC_DP_STATE_E:
95 		new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
96 			   PI3USB30532_CONF_4LANE_DP;
97 		break;
98 	case TYPEC_DP_STATE_D:
99 		new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
100 			   PI3USB30532_CONF_USB3_AND_2LANE_DP;
101 		break;
102 	default:
103 		break;
104 	}
105 
106 	ret = pi3usb30532_set_conf(pi, new_conf);
107 	mutex_unlock(&pi->lock);
108 
109 	return ret;
110 }
111 
pi3usb30532_probe(struct i2c_client * client)112 static int pi3usb30532_probe(struct i2c_client *client)
113 {
114 	struct device *dev = &client->dev;
115 	struct pi3usb30532 *pi;
116 	int ret;
117 
118 	pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL);
119 	if (!pi)
120 		return -ENOMEM;
121 
122 	pi->client = client;
123 	pi->sw.dev = dev;
124 	pi->sw.set = pi3usb30532_sw_set;
125 	pi->mux.dev = dev;
126 	pi->mux.set = pi3usb30532_mux_set;
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 	ret = typec_switch_register(&pi->sw);
137 	if (ret) {
138 		dev_err(dev, "Error registering typec switch: %d\n", ret);
139 		return ret;
140 	}
141 
142 	ret = typec_mux_register(&pi->mux);
143 	if (ret) {
144 		typec_switch_unregister(&pi->sw);
145 		dev_err(dev, "Error registering typec mux: %d\n", ret);
146 		return ret;
147 	}
148 
149 	i2c_set_clientdata(client, pi);
150 	return 0;
151 }
152 
pi3usb30532_remove(struct i2c_client * client)153 static int pi3usb30532_remove(struct i2c_client *client)
154 {
155 	struct pi3usb30532 *pi = i2c_get_clientdata(client);
156 
157 	typec_mux_unregister(&pi->mux);
158 	typec_switch_unregister(&pi->sw);
159 	return 0;
160 }
161 
162 static const struct i2c_device_id pi3usb30532_table[] = {
163 	{ "pi3usb30532" },
164 	{ }
165 };
166 MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
167 
168 static struct i2c_driver pi3usb30532_driver = {
169 	.driver = {
170 		.name = "pi3usb30532",
171 	},
172 	.probe_new	= pi3usb30532_probe,
173 	.remove		= pi3usb30532_remove,
174 	.id_table	= pi3usb30532_table,
175 };
176 
177 module_i2c_driver(pi3usb30532_driver);
178 
179 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
180 MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
181 MODULE_LICENSE("GPL");
182