1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * TI HD3SS3220 Type-C DRP Port Controller Driver
4 *
5 * Copyright (C) 2019 Renesas Electronics Corp.
6 */
7
8#include <linux/module.h>
9#include <linux/i2c.h>
10#include <linux/usb/role.h>
11#include <linux/irqreturn.h>
12#include <linux/interrupt.h>
13#include <linux/regmap.h>
14#include <linux/slab.h>
15#include <linux/usb/typec.h>
16#include <linux/delay.h>
17#include <linux/workqueue.h>
18
19#define HD3SS3220_REG_CN_STAT 0x08
20#define HD3SS3220_REG_CN_STAT_CTRL 0x09
21#define HD3SS3220_REG_GEN_CTRL 0x0A
22#define HD3SS3220_REG_DEV_REV 0xA0
23
24/* Register HD3SS3220_REG_CN_STAT */
25#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_MASK (BIT(7) | BIT(6))
26#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_DEFAULT 0x00
27#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_MID BIT(6)
28#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_HIGH BIT(7)
29
30/* Register HD3SS3220_REG_CN_STAT_CTRL*/
31#define HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK (BIT(7) | BIT(6))
32#define HD3SS3220_REG_CN_STAT_CTRL_AS_DFP BIT(6)
33#define HD3SS3220_REG_CN_STAT_CTRL_AS_UFP BIT(7)
34#define HD3SS3220_REG_CN_STAT_CTRL_TO_ACCESSORY (BIT(7) | BIT(6))
35#define HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS BIT(4)
36
37/* Register HD3SS3220_REG_GEN_CTRL*/
38#define HD3SS3220_REG_GEN_CTRL_DISABLE_TERM BIT(0)
39#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK (BIT(2) | BIT(1))
40#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT 0x00
41#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK BIT(1)
42#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC (BIT(2) | BIT(1))
43#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_MASK (BIT(5) | BIT(4))
44#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DEFAULT 0x00
45#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DFP BIT(5)
46#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_UFP BIT(4)
47#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DRP (BIT(5) | BIT(4))
48
49struct hd3ss3220 {
50 struct device *dev;
51 struct regmap *regmap;
52 struct usb_role_switch *role_sw;
53 struct typec_port *port;
54 struct delayed_work output_poll_work;
55 enum usb_role role_state;
56 bool poll;
57};
58
59static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode)
60{
61 int current_mode;
62
63 switch (power_opmode) {
64 case TYPEC_PWR_MODE_USB:
65 current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_DEFAULT;
66 break;
67 case TYPEC_PWR_MODE_1_5A:
68 current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_MID;
69 break;
70 case TYPEC_PWR_MODE_3_0A:
71 current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_HIGH;
72 break;
73 case TYPEC_PWR_MODE_PD: /* Power delivery not supported */
74 default:
75 dev_err(hd3ss3220->dev, "bad power operation mode: %d\n", power_opmode);
76 return -EINVAL;
77 }
78
79 return regmap_update_bits(map: hd3ss3220->regmap, HD3SS3220_REG_CN_STAT,
80 HD3SS3220_REG_CN_STAT_CURRENT_MODE_MASK,
81 val: current_mode);
82}
83
84static int hd3ss3220_set_port_type(struct hd3ss3220 *hd3ss3220, int type)
85{
86 int mode_select, err;
87
88 switch (type) {
89 case TYPEC_PORT_SRC:
90 mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DFP;
91 break;
92 case TYPEC_PORT_SNK:
93 mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_UFP;
94 break;
95 case TYPEC_PORT_DRP:
96 mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DRP;
97 break;
98 default:
99 dev_err(hd3ss3220->dev, "bad port type: %d\n", type);
100 return -EINVAL;
101 }
102
103 /* Disable termination before changing MODE_SELECT as required by datasheet */
104 err = regmap_update_bits(map: hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
105 HD3SS3220_REG_GEN_CTRL_DISABLE_TERM,
106 HD3SS3220_REG_GEN_CTRL_DISABLE_TERM);
107 if (err < 0) {
108 dev_err(hd3ss3220->dev, "Failed to disable port for mode change: %d\n", err);
109 return err;
110 }
111
112 err = regmap_update_bits(map: hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
113 HD3SS3220_REG_GEN_CTRL_MODE_SELECT_MASK,
114 val: mode_select);
115 if (err < 0) {
116 dev_err(hd3ss3220->dev, "Failed to change mode: %d\n", err);
117 regmap_update_bits(map: hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
118 HD3SS3220_REG_GEN_CTRL_DISABLE_TERM, val: 0);
119 return err;
120 }
121
122 err = regmap_update_bits(map: hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
123 HD3SS3220_REG_GEN_CTRL_DISABLE_TERM, val: 0);
124 if (err < 0)
125 dev_err(hd3ss3220->dev, "Failed to re-enable port after mode change: %d\n", err);
126
127 return err;
128}
129
130static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int prefer_role)
131{
132 int src_pref;
133
134 switch (prefer_role) {
135 case TYPEC_NO_PREFERRED_ROLE:
136 src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT;
137 break;
138 case TYPEC_SINK:
139 src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK;
140 break;
141 case TYPEC_SOURCE:
142 src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC;
143 break;
144 default:
145 dev_err(hd3ss3220->dev, "bad role preference: %d\n", prefer_role);
146 return -EINVAL;
147 }
148
149 return regmap_update_bits(map: hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
150 HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK,
151 val: src_pref);
152}
153
154static enum usb_role hd3ss3220_get_attached_state(struct hd3ss3220 *hd3ss3220)
155{
156 unsigned int reg_val;
157 enum usb_role attached_state;
158 int ret;
159
160 ret = regmap_read(map: hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL,
161 val: &reg_val);
162 if (ret < 0)
163 return ret;
164
165 switch (reg_val & HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK) {
166 case HD3SS3220_REG_CN_STAT_CTRL_AS_DFP:
167 attached_state = USB_ROLE_HOST;
168 break;
169 case HD3SS3220_REG_CN_STAT_CTRL_AS_UFP:
170 attached_state = USB_ROLE_DEVICE;
171 break;
172 default:
173 attached_state = USB_ROLE_NONE;
174 break;
175 }
176
177 return attached_state;
178}
179
180static int hd3ss3220_try_role(struct typec_port *port, int role)
181{
182 struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port);
183
184 return hd3ss3220_set_source_pref(hd3ss3220, prefer_role: role);
185}
186
187static int hd3ss3220_port_type_set(struct typec_port *port, enum typec_port_type type)
188{
189 struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port);
190
191 return hd3ss3220_set_port_type(hd3ss3220, type);
192}
193
194static const struct typec_operations hd3ss3220_ops = {
195 .try_role = hd3ss3220_try_role,
196 .port_type_set = hd3ss3220_port_type_set,
197};
198
199static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
200{
201 enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
202
203 usb_role_switch_set_role(sw: hd3ss3220->role_sw, role: role_state);
204
205 switch (role_state) {
206 case USB_ROLE_HOST:
207 typec_set_data_role(port: hd3ss3220->port, role: TYPEC_HOST);
208 break;
209 case USB_ROLE_DEVICE:
210 typec_set_data_role(port: hd3ss3220->port, role: TYPEC_DEVICE);
211 break;
212 default:
213 break;
214 }
215
216 hd3ss3220->role_state = role_state;
217}
218
219static void output_poll_execute(struct work_struct *work)
220{
221 struct delayed_work *delayed_work = to_delayed_work(work);
222 struct hd3ss3220 *hd3ss3220 = container_of(delayed_work,
223 struct hd3ss3220,
224 output_poll_work);
225 enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
226
227 if (hd3ss3220->role_state != role_state)
228 hd3ss3220_set_role(hd3ss3220);
229
230 schedule_delayed_work(dwork: &hd3ss3220->output_poll_work, HZ);
231}
232
233static irqreturn_t hd3ss3220_irq(struct hd3ss3220 *hd3ss3220)
234{
235 int err;
236
237 hd3ss3220_set_role(hd3ss3220);
238 err = regmap_write_bits(map: hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL,
239 HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS,
240 HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS);
241 if (err < 0)
242 return IRQ_NONE;
243
244 return IRQ_HANDLED;
245}
246
247static irqreturn_t hd3ss3220_irq_handler(int irq, void *data)
248{
249 struct i2c_client *client = to_i2c_client(data);
250 struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client);
251
252 return hd3ss3220_irq(hd3ss3220);
253}
254
255static int hd3ss3220_configure_power_opmode(struct hd3ss3220 *hd3ss3220,
256 struct fwnode_handle *connector)
257{
258 /*
259 * Supported power operation mode can be configured through device tree
260 */
261 const char *cap_str;
262 int ret, power_opmode;
263
264 ret = fwnode_property_read_string(fwnode: connector, propname: "typec-power-opmode", val: &cap_str);
265 if (ret)
266 return 0;
267
268 power_opmode = typec_find_pwr_opmode(name: cap_str);
269 return hd3ss3220_set_power_opmode(hd3ss3220, power_opmode);
270}
271
272static int hd3ss3220_configure_port_type(struct hd3ss3220 *hd3ss3220,
273 struct fwnode_handle *connector,
274 struct typec_capability *cap)
275{
276 /*
277 * Port type can be configured through device tree
278 */
279 const char *cap_str;
280 int ret;
281
282 ret = fwnode_property_read_string(fwnode: connector, propname: "power-role", val: &cap_str);
283 if (ret)
284 return 0;
285
286 ret = typec_find_port_power_role(name: cap_str);
287 if (ret < 0)
288 return ret;
289
290 cap->type = ret;
291 return hd3ss3220_set_port_type(hd3ss3220, type: cap->type);
292}
293
294static int hd3ss3220_configure_source_pref(struct hd3ss3220 *hd3ss3220,
295 struct fwnode_handle *connector,
296 struct typec_capability *cap)
297{
298 /*
299 * Preferred role can be configured through device tree
300 */
301 const char *cap_str;
302 int ret;
303
304 ret = fwnode_property_read_string(fwnode: connector, propname: "try-power-role", val: &cap_str);
305 if (ret)
306 return 0;
307
308 ret = typec_find_power_role(name: cap_str);
309 if (ret < 0)
310 return ret;
311
312 cap->prefer_role = ret;
313 return hd3ss3220_set_source_pref(hd3ss3220, prefer_role: cap->prefer_role);
314}
315
316static const struct regmap_config config = {
317 .reg_bits = 8,
318 .val_bits = 8,
319 .max_register = 0x0A,
320};
321
322static int hd3ss3220_probe(struct i2c_client *client)
323{
324 struct typec_capability typec_cap = { };
325 struct hd3ss3220 *hd3ss3220;
326 struct fwnode_handle *connector, *ep;
327 int ret;
328 unsigned int data;
329
330 hd3ss3220 = devm_kzalloc(dev: &client->dev, size: sizeof(struct hd3ss3220),
331 GFP_KERNEL);
332 if (!hd3ss3220)
333 return -ENOMEM;
334
335 i2c_set_clientdata(client, data: hd3ss3220);
336
337 hd3ss3220->dev = &client->dev;
338 hd3ss3220->regmap = devm_regmap_init_i2c(client, &config);
339 if (IS_ERR(ptr: hd3ss3220->regmap))
340 return PTR_ERR(ptr: hd3ss3220->regmap);
341
342 /* For backward compatibility check the connector child node first */
343 connector = device_get_named_child_node(dev: hd3ss3220->dev, childname: "connector");
344 if (connector) {
345 hd3ss3220->role_sw = fwnode_usb_role_switch_get(node: connector);
346 } else {
347 ep = fwnode_graph_get_next_endpoint(dev_fwnode(hd3ss3220->dev), NULL);
348 if (!ep)
349 return -ENODEV;
350 connector = fwnode_graph_get_remote_port_parent(fwnode: ep);
351 fwnode_handle_put(fwnode: ep);
352 if (!connector)
353 return -ENODEV;
354 hd3ss3220->role_sw = usb_role_switch_get(dev: hd3ss3220->dev);
355 }
356
357 if (IS_ERR(ptr: hd3ss3220->role_sw)) {
358 ret = PTR_ERR(ptr: hd3ss3220->role_sw);
359 goto err_put_fwnode;
360 }
361
362 typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
363 typec_cap.driver_data = hd3ss3220;
364 typec_cap.type = TYPEC_PORT_DRP;
365 typec_cap.data = TYPEC_PORT_DRD;
366 typec_cap.ops = &hd3ss3220_ops;
367 typec_cap.fwnode = connector;
368
369 ret = hd3ss3220_configure_source_pref(hd3ss3220, connector, cap: &typec_cap);
370 if (ret < 0)
371 goto err_put_role;
372
373 ret = hd3ss3220_configure_port_type(hd3ss3220, connector, cap: &typec_cap);
374 if (ret < 0)
375 goto err_put_role;
376
377 hd3ss3220->port = typec_register_port(parent: &client->dev, cap: &typec_cap);
378 if (IS_ERR(ptr: hd3ss3220->port)) {
379 ret = PTR_ERR(ptr: hd3ss3220->port);
380 goto err_put_role;
381 }
382
383 ret = hd3ss3220_configure_power_opmode(hd3ss3220, connector);
384 if (ret < 0)
385 goto err_unreg_port;
386
387 hd3ss3220_set_role(hd3ss3220);
388 ret = regmap_read(map: hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, val: &data);
389 if (ret < 0)
390 goto err_unreg_port;
391
392 if (data & HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS) {
393 ret = regmap_write(map: hd3ss3220->regmap,
394 HD3SS3220_REG_CN_STAT_CTRL,
395 val: data | HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS);
396 if (ret < 0)
397 goto err_unreg_port;
398 }
399
400 if (client->irq > 0) {
401 ret = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, NULL,
402 thread_fn: hd3ss3220_irq_handler,
403 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
404 devname: "hd3ss3220", dev_id: &client->dev);
405 if (ret)
406 goto err_unreg_port;
407 } else {
408 INIT_DELAYED_WORK(&hd3ss3220->output_poll_work, output_poll_execute);
409 hd3ss3220->poll = true;
410 }
411
412 ret = i2c_smbus_read_byte_data(client, HD3SS3220_REG_DEV_REV);
413 if (ret < 0)
414 goto err_unreg_port;
415
416 fwnode_handle_put(fwnode: connector);
417
418 if (hd3ss3220->poll)
419 schedule_delayed_work(dwork: &hd3ss3220->output_poll_work, HZ);
420
421 dev_info(&client->dev, "probed revision=0x%x\n", ret);
422
423 return 0;
424err_unreg_port:
425 typec_unregister_port(port: hd3ss3220->port);
426err_put_role:
427 usb_role_switch_put(sw: hd3ss3220->role_sw);
428err_put_fwnode:
429 fwnode_handle_put(fwnode: connector);
430
431 return ret;
432}
433
434static void hd3ss3220_remove(struct i2c_client *client)
435{
436 struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client);
437
438 if (hd3ss3220->poll)
439 cancel_delayed_work_sync(dwork: &hd3ss3220->output_poll_work);
440
441 typec_unregister_port(port: hd3ss3220->port);
442 usb_role_switch_put(sw: hd3ss3220->role_sw);
443}
444
445static const struct of_device_id dev_ids[] = {
446 { .compatible = "ti,hd3ss3220"},
447 {}
448};
449MODULE_DEVICE_TABLE(of, dev_ids);
450
451static struct i2c_driver hd3ss3220_driver = {
452 .driver = {
453 .name = "hd3ss3220",
454 .of_match_table = dev_ids,
455 },
456 .probe = hd3ss3220_probe,
457 .remove = hd3ss3220_remove,
458};
459
460module_i2c_driver(hd3ss3220_driver);
461
462MODULE_AUTHOR("Biju Das <biju.das@bp.renesas.com>");
463MODULE_DESCRIPTION("TI HD3SS3220 DRP Port Controller Driver");
464MODULE_LICENSE("GPL");
465

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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