]> git.ipfire.org Git - thirdparty/linux.git/blob - drivers/usb/typec/mux/pi3usb30532.c
Merge tag 'riscv-for-linus-5.7-rc4' of git://git.kernel.org/pub/scm/linux/kernel...
[thirdparty/linux.git] / drivers / usb / typec / mux / pi3usb30532.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
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
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
48 static 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
76 static int
77 pi3usb30532_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
114 static 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
163 static 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
172 static const struct i2c_device_id pi3usb30532_table[] = {
173 { "pi3usb30532" },
174 { }
175 };
176 MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
177
178 static 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
187 module_i2c_driver(pi3usb30532_driver);
188
189 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
190 MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
191 MODULE_LICENSE("GPL");