xref: /linux/drivers/usb/typec/hd3ss3220.c (revision c6fbb759)
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 
18 #define HD3SS3220_REG_CN_STAT_CTRL	0x09
19 #define HD3SS3220_REG_GEN_CTRL		0x0A
20 #define HD3SS3220_REG_DEV_REV		0xA0
21 
22 /* Register HD3SS3220_REG_CN_STAT_CTRL*/
23 #define HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK	(BIT(7) | BIT(6))
24 #define HD3SS3220_REG_CN_STAT_CTRL_AS_DFP		BIT(6)
25 #define HD3SS3220_REG_CN_STAT_CTRL_AS_UFP		BIT(7)
26 #define HD3SS3220_REG_CN_STAT_CTRL_TO_ACCESSORY		(BIT(7) | BIT(6))
27 #define HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS		BIT(4)
28 
29 /* Register HD3SS3220_REG_GEN_CTRL*/
30 #define HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK		(BIT(2) | BIT(1))
31 #define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT	0x00
32 #define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK	BIT(1)
33 #define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC	(BIT(2) | BIT(1))
34 
35 struct hd3ss3220 {
36 	struct device *dev;
37 	struct regmap *regmap;
38 	struct usb_role_switch	*role_sw;
39 	struct typec_port *port;
40 };
41 
42 static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int src_pref)
43 {
44 	return regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
45 				  HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK,
46 				  src_pref);
47 }
48 
49 static enum usb_role hd3ss3220_get_attached_state(struct hd3ss3220 *hd3ss3220)
50 {
51 	unsigned int reg_val;
52 	enum usb_role attached_state;
53 	int ret;
54 
55 	ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL,
56 			  &reg_val);
57 	if (ret < 0)
58 		return ret;
59 
60 	switch (reg_val & HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK) {
61 	case HD3SS3220_REG_CN_STAT_CTRL_AS_DFP:
62 		attached_state = USB_ROLE_HOST;
63 		break;
64 	case HD3SS3220_REG_CN_STAT_CTRL_AS_UFP:
65 		attached_state = USB_ROLE_DEVICE;
66 		break;
67 	default:
68 		attached_state = USB_ROLE_NONE;
69 		break;
70 	}
71 
72 	return attached_state;
73 }
74 
75 static int hd3ss3220_dr_set(struct typec_port *port, enum typec_data_role role)
76 {
77 	struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port);
78 	enum usb_role role_val;
79 	int pref, ret = 0;
80 
81 	if (role == TYPEC_HOST) {
82 		role_val = USB_ROLE_HOST;
83 		pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC;
84 	} else {
85 		role_val = USB_ROLE_DEVICE;
86 		pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK;
87 	}
88 
89 	ret = hd3ss3220_set_source_pref(hd3ss3220, pref);
90 	usleep_range(10, 100);
91 
92 	usb_role_switch_set_role(hd3ss3220->role_sw, role_val);
93 	typec_set_data_role(hd3ss3220->port, role);
94 
95 	return ret;
96 }
97 
98 static const struct typec_operations hd3ss3220_ops = {
99 	.dr_set = hd3ss3220_dr_set
100 };
101 
102 static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
103 {
104 	enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
105 
106 	usb_role_switch_set_role(hd3ss3220->role_sw, role_state);
107 	if (role_state == USB_ROLE_NONE)
108 		hd3ss3220_set_source_pref(hd3ss3220,
109 				HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT);
110 
111 	switch (role_state) {
112 	case USB_ROLE_HOST:
113 		typec_set_data_role(hd3ss3220->port, TYPEC_HOST);
114 		break;
115 	case USB_ROLE_DEVICE:
116 		typec_set_data_role(hd3ss3220->port, TYPEC_DEVICE);
117 		break;
118 	default:
119 		break;
120 	}
121 }
122 
123 static irqreturn_t hd3ss3220_irq(struct hd3ss3220 *hd3ss3220)
124 {
125 	int err;
126 
127 	hd3ss3220_set_role(hd3ss3220);
128 	err = regmap_write_bits(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL,
129 				HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS,
130 				HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS);
131 	if (err < 0)
132 		return IRQ_NONE;
133 
134 	return IRQ_HANDLED;
135 }
136 
137 static irqreturn_t hd3ss3220_irq_handler(int irq, void *data)
138 {
139 	struct i2c_client *client = to_i2c_client(data);
140 	struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client);
141 
142 	return hd3ss3220_irq(hd3ss3220);
143 }
144 
145 static const struct regmap_config config = {
146 	.reg_bits = 8,
147 	.val_bits = 8,
148 	.max_register = 0x0A,
149 };
150 
151 static int hd3ss3220_probe(struct i2c_client *client,
152 		const struct i2c_device_id *id)
153 {
154 	struct typec_capability typec_cap = { };
155 	struct hd3ss3220 *hd3ss3220;
156 	struct fwnode_handle *connector, *ep;
157 	int ret;
158 	unsigned int data;
159 
160 	hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220),
161 				 GFP_KERNEL);
162 	if (!hd3ss3220)
163 		return -ENOMEM;
164 
165 	i2c_set_clientdata(client, hd3ss3220);
166 
167 	hd3ss3220->dev = &client->dev;
168 	hd3ss3220->regmap = devm_regmap_init_i2c(client, &config);
169 	if (IS_ERR(hd3ss3220->regmap))
170 		return PTR_ERR(hd3ss3220->regmap);
171 
172 	hd3ss3220_set_source_pref(hd3ss3220,
173 				  HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT);
174 	/* For backward compatibility check the connector child node first */
175 	connector = device_get_named_child_node(hd3ss3220->dev, "connector");
176 	if (connector) {
177 		hd3ss3220->role_sw = fwnode_usb_role_switch_get(connector);
178 	} else {
179 		ep = fwnode_graph_get_next_endpoint(dev_fwnode(hd3ss3220->dev), NULL);
180 		if (!ep)
181 			return -ENODEV;
182 		connector = fwnode_graph_get_remote_port_parent(ep);
183 		fwnode_handle_put(ep);
184 		if (!connector)
185 			return -ENODEV;
186 		hd3ss3220->role_sw = usb_role_switch_get(hd3ss3220->dev);
187 	}
188 
189 	if (IS_ERR(hd3ss3220->role_sw)) {
190 		ret = PTR_ERR(hd3ss3220->role_sw);
191 		goto err_put_fwnode;
192 	}
193 
194 	typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
195 	typec_cap.driver_data = hd3ss3220;
196 	typec_cap.type = TYPEC_PORT_DRP;
197 	typec_cap.data = TYPEC_PORT_DRD;
198 	typec_cap.ops = &hd3ss3220_ops;
199 	typec_cap.fwnode = connector;
200 
201 	hd3ss3220->port = typec_register_port(&client->dev, &typec_cap);
202 	if (IS_ERR(hd3ss3220->port)) {
203 		ret = PTR_ERR(hd3ss3220->port);
204 		goto err_put_role;
205 	}
206 
207 	hd3ss3220_set_role(hd3ss3220);
208 	ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data);
209 	if (ret < 0)
210 		goto err_unreg_port;
211 
212 	if (data & HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS) {
213 		ret = regmap_write(hd3ss3220->regmap,
214 				HD3SS3220_REG_CN_STAT_CTRL,
215 				data | HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS);
216 		if (ret < 0)
217 			goto err_unreg_port;
218 	}
219 
220 	if (client->irq > 0) {
221 		ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
222 					hd3ss3220_irq_handler,
223 					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
224 					"hd3ss3220", &client->dev);
225 		if (ret)
226 			goto err_unreg_port;
227 	}
228 
229 	ret = i2c_smbus_read_byte_data(client, HD3SS3220_REG_DEV_REV);
230 	if (ret < 0)
231 		goto err_unreg_port;
232 
233 	fwnode_handle_put(connector);
234 
235 	dev_info(&client->dev, "probed revision=0x%x\n", ret);
236 
237 	return 0;
238 err_unreg_port:
239 	typec_unregister_port(hd3ss3220->port);
240 err_put_role:
241 	usb_role_switch_put(hd3ss3220->role_sw);
242 err_put_fwnode:
243 	fwnode_handle_put(connector);
244 
245 	return ret;
246 }
247 
248 static void hd3ss3220_remove(struct i2c_client *client)
249 {
250 	struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client);
251 
252 	typec_unregister_port(hd3ss3220->port);
253 	usb_role_switch_put(hd3ss3220->role_sw);
254 }
255 
256 static const struct of_device_id dev_ids[] = {
257 	{ .compatible = "ti,hd3ss3220"},
258 	{}
259 };
260 MODULE_DEVICE_TABLE(of, dev_ids);
261 
262 static struct i2c_driver hd3ss3220_driver = {
263 	.driver = {
264 		.name = "hd3ss3220",
265 		.of_match_table = of_match_ptr(dev_ids),
266 	},
267 	.probe = hd3ss3220_probe,
268 	.remove =  hd3ss3220_remove,
269 };
270 
271 module_i2c_driver(hd3ss3220_driver);
272 
273 MODULE_AUTHOR("Biju Das <biju.das@bp.renesas.com>");
274 MODULE_DESCRIPTION("TI HD3SS3220 DRP Port Controller Driver");
275 MODULE_LICENSE("GPL");
276