1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Willsemi WUSB3801 Type-C port controller driver
4 *
5 * Copyright (C) 2022 Samuel Holland <samuel@sholland.org>
6 */
7
8#include <linux/i2c.h>
9#include <linux/module.h>
10#include <linux/regmap.h>
11#include <linux/regulator/consumer.h>
12#include <linux/usb/typec.h>
13
14#define WUSB3801_REG_DEVICE_ID 0x01
15#define WUSB3801_REG_CTRL0 0x02
16#define WUSB3801_REG_INT 0x03
17#define WUSB3801_REG_STAT 0x04
18#define WUSB3801_REG_CTRL1 0x05
19#define WUSB3801_REG_TEST00 0x06
20#define WUSB3801_REG_TEST01 0x07
21#define WUSB3801_REG_TEST02 0x08
22#define WUSB3801_REG_TEST03 0x09
23#define WUSB3801_REG_TEST04 0x0a
24#define WUSB3801_REG_TEST05 0x0b
25#define WUSB3801_REG_TEST06 0x0c
26#define WUSB3801_REG_TEST07 0x0d
27#define WUSB3801_REG_TEST08 0x0e
28#define WUSB3801_REG_TEST09 0x0f
29#define WUSB3801_REG_TEST0A 0x10
30#define WUSB3801_REG_TEST0B 0x11
31#define WUSB3801_REG_TEST0C 0x12
32#define WUSB3801_REG_TEST0D 0x13
33#define WUSB3801_REG_TEST0E 0x14
34#define WUSB3801_REG_TEST0F 0x15
35#define WUSB3801_REG_TEST10 0x16
36#define WUSB3801_REG_TEST11 0x17
37#define WUSB3801_REG_TEST12 0x18
38
39#define WUSB3801_DEVICE_ID_VERSION_ID GENMASK(7, 3)
40#define WUSB3801_DEVICE_ID_VENDOR_ID GENMASK(2, 0)
41
42#define WUSB3801_CTRL0_DIS_ACC_SUPPORT BIT(7)
43#define WUSB3801_CTRL0_TRY GENMASK(6, 5)
44#define WUSB3801_CTRL0_TRY_NONE (0x0 << 5)
45#define WUSB3801_CTRL0_TRY_SNK (0x1 << 5)
46#define WUSB3801_CTRL0_TRY_SRC (0x2 << 5)
47#define WUSB3801_CTRL0_CURRENT GENMASK(4, 3) /* SRC */
48#define WUSB3801_CTRL0_CURRENT_DEFAULT (0x0 << 3)
49#define WUSB3801_CTRL0_CURRENT_1_5A (0x1 << 3)
50#define WUSB3801_CTRL0_CURRENT_3_0A (0x2 << 3)
51#define WUSB3801_CTRL0_ROLE GENMASK(2, 1)
52#define WUSB3801_CTRL0_ROLE_SNK (0x0 << 1)
53#define WUSB3801_CTRL0_ROLE_SRC (0x1 << 1)
54#define WUSB3801_CTRL0_ROLE_DRP (0x2 << 1)
55#define WUSB3801_CTRL0_INT_MASK BIT(0)
56
57#define WUSB3801_INT_ATTACHED BIT(0)
58#define WUSB3801_INT_DETACHED BIT(1)
59
60#define WUSB3801_STAT_VBUS_DETECTED BIT(7)
61#define WUSB3801_STAT_CURRENT GENMASK(6, 5) /* SNK */
62#define WUSB3801_STAT_CURRENT_STANDBY (0x0 << 5)
63#define WUSB3801_STAT_CURRENT_DEFAULT (0x1 << 5)
64#define WUSB3801_STAT_CURRENT_1_5A (0x2 << 5)
65#define WUSB3801_STAT_CURRENT_3_0A (0x3 << 5)
66#define WUSB3801_STAT_PARTNER GENMASK(4, 2)
67#define WUSB3801_STAT_PARTNER_STANDBY (0x0 << 2)
68#define WUSB3801_STAT_PARTNER_SNK (0x1 << 2)
69#define WUSB3801_STAT_PARTNER_SRC (0x2 << 2)
70#define WUSB3801_STAT_PARTNER_AUDIO (0x3 << 2)
71#define WUSB3801_STAT_PARTNER_DEBUG (0x4 << 2)
72#define WUSB3801_STAT_ORIENTATION GENMASK(1, 0)
73#define WUSB3801_STAT_ORIENTATION_NONE (0x0 << 0)
74#define WUSB3801_STAT_ORIENTATION_CC1 (0x1 << 0)
75#define WUSB3801_STAT_ORIENTATION_CC2 (0x2 << 0)
76#define WUSB3801_STAT_ORIENTATION_BOTH (0x3 << 0)
77
78#define WUSB3801_CTRL1_SM_RESET BIT(0)
79
80#define WUSB3801_TEST01_VENDOR_SUB_ID (BIT(8) | BIT(6))
81
82#define WUSB3801_TEST02_FORCE_ERR_RCY BIT(8)
83
84#define WUSB3801_TEST0A_WAIT_VBUS BIT(5)
85
86struct wusb3801 {
87 struct typec_capability cap;
88 struct device *dev;
89 struct typec_partner *partner;
90 struct typec_port *port;
91 struct regmap *regmap;
92 struct regulator *vbus_supply;
93 unsigned int partner_type;
94 enum typec_port_type port_type;
95 enum typec_pwr_opmode pwr_opmode;
96 bool vbus_on;
97};
98
99static enum typec_role wusb3801_get_default_role(struct wusb3801 *wusb3801)
100{
101 switch (wusb3801->port_type) {
102 case TYPEC_PORT_SRC:
103 return TYPEC_SOURCE;
104 case TYPEC_PORT_SNK:
105 return TYPEC_SINK;
106 case TYPEC_PORT_DRP:
107 default:
108 if (wusb3801->cap.prefer_role == TYPEC_SOURCE)
109 return TYPEC_SOURCE;
110 return TYPEC_SINK;
111 }
112}
113
114static int wusb3801_map_port_type(enum typec_port_type type)
115{
116 switch (type) {
117 case TYPEC_PORT_SRC:
118 return WUSB3801_CTRL0_ROLE_SRC;
119 case TYPEC_PORT_SNK:
120 return WUSB3801_CTRL0_ROLE_SNK;
121 case TYPEC_PORT_DRP:
122 default:
123 return WUSB3801_CTRL0_ROLE_DRP;
124 }
125}
126
127static int wusb3801_map_pwr_opmode(enum typec_pwr_opmode mode)
128{
129 switch (mode) {
130 case TYPEC_PWR_MODE_USB:
131 default:
132 return WUSB3801_CTRL0_CURRENT_DEFAULT;
133 case TYPEC_PWR_MODE_1_5A:
134 return WUSB3801_CTRL0_CURRENT_1_5A;
135 case TYPEC_PWR_MODE_3_0A:
136 return WUSB3801_CTRL0_CURRENT_3_0A;
137 }
138}
139
140static unsigned int wusb3801_map_try_role(int role)
141{
142 switch (role) {
143 case TYPEC_NO_PREFERRED_ROLE:
144 default:
145 return WUSB3801_CTRL0_TRY_NONE;
146 case TYPEC_SINK:
147 return WUSB3801_CTRL0_TRY_SNK;
148 case TYPEC_SOURCE:
149 return WUSB3801_CTRL0_TRY_SRC;
150 }
151}
152
153static enum typec_orientation wusb3801_unmap_orientation(unsigned int status)
154{
155 switch (status & WUSB3801_STAT_ORIENTATION) {
156 case WUSB3801_STAT_ORIENTATION_NONE:
157 case WUSB3801_STAT_ORIENTATION_BOTH:
158 default:
159 return TYPEC_ORIENTATION_NONE;
160 case WUSB3801_STAT_ORIENTATION_CC1:
161 return TYPEC_ORIENTATION_NORMAL;
162 case WUSB3801_STAT_ORIENTATION_CC2:
163 return TYPEC_ORIENTATION_REVERSE;
164 }
165}
166
167static enum typec_pwr_opmode wusb3801_unmap_pwr_opmode(unsigned int status)
168{
169 switch (status & WUSB3801_STAT_CURRENT) {
170 case WUSB3801_STAT_CURRENT_STANDBY:
171 case WUSB3801_STAT_CURRENT_DEFAULT:
172 default:
173 return TYPEC_PWR_MODE_USB;
174 case WUSB3801_STAT_CURRENT_1_5A:
175 return TYPEC_PWR_MODE_1_5A;
176 case WUSB3801_STAT_CURRENT_3_0A:
177 return TYPEC_PWR_MODE_3_0A;
178 }
179}
180
181static int wusb3801_try_role(struct typec_port *port, int role)
182{
183 struct wusb3801 *wusb3801 = typec_get_drvdata(port);
184
185 return regmap_update_bits(map: wusb3801->regmap, WUSB3801_REG_CTRL0,
186 WUSB3801_CTRL0_TRY,
187 val: wusb3801_map_try_role(role));
188}
189
190static int wusb3801_port_type_set(struct typec_port *port,
191 enum typec_port_type type)
192{
193 struct wusb3801 *wusb3801 = typec_get_drvdata(port);
194 int ret;
195
196 ret = regmap_update_bits(map: wusb3801->regmap, WUSB3801_REG_CTRL0,
197 WUSB3801_CTRL0_ROLE,
198 val: wusb3801_map_port_type(type));
199 if (ret)
200 return ret;
201
202 wusb3801->port_type = type;
203
204 return 0;
205}
206
207static const struct typec_operations wusb3801_typec_ops = {
208 .try_role = wusb3801_try_role,
209 .port_type_set = wusb3801_port_type_set,
210};
211
212static int wusb3801_hw_init(struct wusb3801 *wusb3801)
213{
214 return regmap_write(map: wusb3801->regmap, WUSB3801_REG_CTRL0,
215 val: wusb3801_map_try_role(role: wusb3801->cap.prefer_role) |
216 wusb3801_map_pwr_opmode(mode: wusb3801->pwr_opmode) |
217 wusb3801_map_port_type(type: wusb3801->port_type));
218}
219
220static void wusb3801_hw_update(struct wusb3801 *wusb3801)
221{
222 struct typec_port *port = wusb3801->port;
223 struct device *dev = wusb3801->dev;
224 unsigned int partner_type, status;
225 int ret;
226
227 ret = regmap_read(map: wusb3801->regmap, WUSB3801_REG_STAT, val: &status);
228 if (ret) {
229 dev_warn(dev, "Failed to read port status: %d\n", ret);
230 status = 0;
231 }
232 dev_dbg(dev, "status = 0x%02x\n", status);
233
234 partner_type = status & WUSB3801_STAT_PARTNER;
235
236 if (partner_type == WUSB3801_STAT_PARTNER_SNK) {
237 if (!wusb3801->vbus_on) {
238 ret = regulator_enable(regulator: wusb3801->vbus_supply);
239 if (ret)
240 dev_warn(dev, "Failed to enable VBUS: %d\n", ret);
241 wusb3801->vbus_on = true;
242 }
243 } else {
244 if (wusb3801->vbus_on) {
245 regulator_disable(regulator: wusb3801->vbus_supply);
246 wusb3801->vbus_on = false;
247 }
248 }
249
250 if (partner_type != wusb3801->partner_type) {
251 struct typec_partner_desc desc = {};
252 enum typec_data_role data_role;
253 enum typec_role pwr_role = wusb3801_get_default_role(wusb3801);
254
255 switch (partner_type) {
256 case WUSB3801_STAT_PARTNER_STANDBY:
257 break;
258 case WUSB3801_STAT_PARTNER_SNK:
259 pwr_role = TYPEC_SOURCE;
260 break;
261 case WUSB3801_STAT_PARTNER_SRC:
262 pwr_role = TYPEC_SINK;
263 break;
264 case WUSB3801_STAT_PARTNER_AUDIO:
265 desc.accessory = TYPEC_ACCESSORY_AUDIO;
266 break;
267 case WUSB3801_STAT_PARTNER_DEBUG:
268 desc.accessory = TYPEC_ACCESSORY_DEBUG;
269 break;
270 }
271
272 if (wusb3801->partner) {
273 typec_unregister_partner(partner: wusb3801->partner);
274 wusb3801->partner = NULL;
275 }
276
277 if (partner_type != WUSB3801_STAT_PARTNER_STANDBY) {
278 wusb3801->partner = typec_register_partner(port, desc: &desc);
279 if (IS_ERR(ptr: wusb3801->partner))
280 dev_err(dev, "Failed to register partner: %ld\n",
281 PTR_ERR(wusb3801->partner));
282 }
283
284 data_role = pwr_role == TYPEC_SOURCE ? TYPEC_HOST : TYPEC_DEVICE;
285 typec_set_data_role(port, role: data_role);
286 typec_set_pwr_role(port, role: pwr_role);
287 typec_set_vconn_role(port, role: pwr_role);
288 }
289
290 typec_set_pwr_opmode(port: wusb3801->port,
291 mode: partner_type == WUSB3801_STAT_PARTNER_SRC
292 ? wusb3801_unmap_pwr_opmode(status)
293 : wusb3801->pwr_opmode);
294 typec_set_orientation(port: wusb3801->port,
295 orientation: wusb3801_unmap_orientation(status));
296
297 wusb3801->partner_type = partner_type;
298}
299
300static irqreturn_t wusb3801_irq(int irq, void *data)
301{
302 struct wusb3801 *wusb3801 = data;
303 unsigned int dummy;
304
305 /*
306 * The interrupt register must be read in order to clear the IRQ,
307 * but all of the useful information is in the status register.
308 */
309 regmap_read(map: wusb3801->regmap, WUSB3801_REG_INT, val: &dummy);
310
311 wusb3801_hw_update(wusb3801);
312
313 return IRQ_HANDLED;
314}
315
316static const struct regmap_config config = {
317 .reg_bits = 8,
318 .val_bits = 8,
319 .max_register = WUSB3801_REG_TEST12,
320};
321
322static int wusb3801_probe(struct i2c_client *client)
323{
324 struct device *dev = &client->dev;
325 struct fwnode_handle *connector;
326 struct wusb3801 *wusb3801;
327 const char *cap_str;
328 int ret;
329
330 wusb3801 = devm_kzalloc(dev, size: sizeof(*wusb3801), GFP_KERNEL);
331 if (!wusb3801)
332 return -ENOMEM;
333
334 i2c_set_clientdata(client, data: wusb3801);
335
336 wusb3801->dev = dev;
337
338 wusb3801->regmap = devm_regmap_init_i2c(client, &config);
339 if (IS_ERR(ptr: wusb3801->regmap))
340 return PTR_ERR(ptr: wusb3801->regmap);
341
342 wusb3801->vbus_supply = devm_regulator_get(dev, id: "vbus");
343 if (IS_ERR(ptr: wusb3801->vbus_supply))
344 return PTR_ERR(ptr: wusb3801->vbus_supply);
345
346 connector = device_get_named_child_node(dev, childname: "connector");
347 if (!connector)
348 return -ENODEV;
349
350 ret = typec_get_fw_cap(cap: &wusb3801->cap, fwnode: connector);
351 if (ret)
352 goto err_put_connector;
353 wusb3801->port_type = wusb3801->cap.type;
354
355 ret = fwnode_property_read_string(fwnode: connector, propname: "typec-power-opmode", val: &cap_str);
356 if (ret)
357 goto err_put_connector;
358
359 ret = typec_find_pwr_opmode(name: cap_str);
360 if (ret < 0 || ret == TYPEC_PWR_MODE_PD)
361 goto err_put_connector;
362 wusb3801->pwr_opmode = ret;
363
364 /* Initialize the hardware with the devicetree settings. */
365 ret = wusb3801_hw_init(wusb3801);
366 if (ret)
367 goto err_put_connector;
368
369 wusb3801->cap.revision = USB_TYPEC_REV_1_2;
370 wusb3801->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO;
371 wusb3801->cap.accessory[1] = TYPEC_ACCESSORY_DEBUG;
372 wusb3801->cap.orientation_aware = true;
373 wusb3801->cap.driver_data = wusb3801;
374 wusb3801->cap.ops = &wusb3801_typec_ops;
375
376 wusb3801->port = typec_register_port(parent: dev, cap: &wusb3801->cap);
377 if (IS_ERR(ptr: wusb3801->port)) {
378 ret = PTR_ERR(ptr: wusb3801->port);
379 goto err_put_connector;
380 }
381
382 /* Initialize the port attributes from the hardware state. */
383 wusb3801_hw_update(wusb3801);
384
385 ret = request_threaded_irq(irq: client->irq, NULL, thread_fn: wusb3801_irq,
386 IRQF_ONESHOT, name: dev_name(dev), dev: wusb3801);
387 if (ret)
388 goto err_unregister_port;
389
390 fwnode_handle_put(fwnode: connector);
391
392 return 0;
393
394err_unregister_port:
395 typec_unregister_port(port: wusb3801->port);
396err_put_connector:
397 fwnode_handle_put(fwnode: connector);
398
399 return ret;
400}
401
402static void wusb3801_remove(struct i2c_client *client)
403{
404 struct wusb3801 *wusb3801 = i2c_get_clientdata(client);
405
406 free_irq(client->irq, wusb3801);
407
408 if (wusb3801->partner)
409 typec_unregister_partner(partner: wusb3801->partner);
410 typec_unregister_port(port: wusb3801->port);
411
412 if (wusb3801->vbus_on)
413 regulator_disable(regulator: wusb3801->vbus_supply);
414}
415
416static const struct of_device_id wusb3801_of_match[] = {
417 { .compatible = "willsemi,wusb3801" },
418 {}
419};
420MODULE_DEVICE_TABLE(of, wusb3801_of_match);
421
422static struct i2c_driver wusb3801_driver = {
423 .probe = wusb3801_probe,
424 .remove = wusb3801_remove,
425 .driver = {
426 .name = "wusb3801",
427 .of_match_table = wusb3801_of_match,
428 },
429};
430
431module_i2c_driver(wusb3801_driver);
432
433MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
434MODULE_DESCRIPTION("Willsemi WUSB3801 Type-C port controller driver");
435MODULE_LICENSE("GPL");
436

source code of linux/drivers/usb/typec/wusb3801.c