xref: /openbmc/linux/drivers/usb/typec/hd3ss3220.c (revision 8dda2eac)
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_update_bits_base(hd3ss3220->regmap,
129 				      HD3SS3220_REG_CN_STAT_CTRL,
130 				      HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS,
131 				      HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS,
132 				      NULL, false, true);
133 	if (err < 0)
134 		return IRQ_NONE;
135 
136 	return IRQ_HANDLED;
137 }
138 
139 static irqreturn_t hd3ss3220_irq_handler(int irq, void *data)
140 {
141 	struct i2c_client *client = to_i2c_client(data);
142 	struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client);
143 
144 	return hd3ss3220_irq(hd3ss3220);
145 }
146 
147 static const struct regmap_config config = {
148 	.reg_bits = 8,
149 	.val_bits = 8,
150 	.max_register = 0x0A,
151 };
152 
153 static int hd3ss3220_probe(struct i2c_client *client,
154 		const struct i2c_device_id *id)
155 {
156 	struct typec_capability typec_cap = { };
157 	struct hd3ss3220 *hd3ss3220;
158 	struct fwnode_handle *connector, *ep;
159 	int ret;
160 	unsigned int data;
161 
162 	hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220),
163 				 GFP_KERNEL);
164 	if (!hd3ss3220)
165 		return -ENOMEM;
166 
167 	i2c_set_clientdata(client, hd3ss3220);
168 
169 	hd3ss3220->dev = &client->dev;
170 	hd3ss3220->regmap = devm_regmap_init_i2c(client, &config);
171 	if (IS_ERR(hd3ss3220->regmap))
172 		return PTR_ERR(hd3ss3220->regmap);
173 
174 	hd3ss3220_set_source_pref(hd3ss3220,
175 				  HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT);
176 	/* For backward compatibility check the connector child node first */
177 	connector = device_get_named_child_node(hd3ss3220->dev, "connector");
178 	if (connector) {
179 		hd3ss3220->role_sw = fwnode_usb_role_switch_get(connector);
180 	} else {
181 		ep = fwnode_graph_get_next_endpoint(dev_fwnode(hd3ss3220->dev), NULL);
182 		if (!ep)
183 			return -ENODEV;
184 		connector = fwnode_graph_get_remote_port_parent(ep);
185 		fwnode_handle_put(ep);
186 		if (!connector)
187 			return -ENODEV;
188 		hd3ss3220->role_sw = usb_role_switch_get(hd3ss3220->dev);
189 	}
190 
191 	if (IS_ERR(hd3ss3220->role_sw)) {
192 		ret = PTR_ERR(hd3ss3220->role_sw);
193 		goto err_put_fwnode;
194 	}
195 
196 	typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
197 	typec_cap.driver_data = hd3ss3220;
198 	typec_cap.type = TYPEC_PORT_DRP;
199 	typec_cap.data = TYPEC_PORT_DRD;
200 	typec_cap.ops = &hd3ss3220_ops;
201 	typec_cap.fwnode = connector;
202 
203 	hd3ss3220->port = typec_register_port(&client->dev, &typec_cap);
204 	if (IS_ERR(hd3ss3220->port)) {
205 		ret = PTR_ERR(hd3ss3220->port);
206 		goto err_put_role;
207 	}
208 
209 	hd3ss3220_set_role(hd3ss3220);
210 	ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data);
211 	if (ret < 0)
212 		goto err_unreg_port;
213 
214 	if (data & HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS) {
215 		ret = regmap_write(hd3ss3220->regmap,
216 				HD3SS3220_REG_CN_STAT_CTRL,
217 				data | HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS);
218 		if (ret < 0)
219 			goto err_unreg_port;
220 	}
221 
222 	if (client->irq > 0) {
223 		ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
224 					hd3ss3220_irq_handler,
225 					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
226 					"hd3ss3220", &client->dev);
227 		if (ret)
228 			goto err_unreg_port;
229 	}
230 
231 	ret = i2c_smbus_read_byte_data(client, HD3SS3220_REG_DEV_REV);
232 	if (ret < 0)
233 		goto err_unreg_port;
234 
235 	fwnode_handle_put(connector);
236 
237 	dev_info(&client->dev, "probed revision=0x%x\n", ret);
238 
239 	return 0;
240 err_unreg_port:
241 	typec_unregister_port(hd3ss3220->port);
242 err_put_role:
243 	usb_role_switch_put(hd3ss3220->role_sw);
244 err_put_fwnode:
245 	fwnode_handle_put(connector);
246 
247 	return ret;
248 }
249 
250 static int hd3ss3220_remove(struct i2c_client *client)
251 {
252 	struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client);
253 
254 	typec_unregister_port(hd3ss3220->port);
255 	usb_role_switch_put(hd3ss3220->role_sw);
256 
257 	return 0;
258 }
259 
260 static const struct of_device_id dev_ids[] = {
261 	{ .compatible = "ti,hd3ss3220"},
262 	{}
263 };
264 MODULE_DEVICE_TABLE(of, dev_ids);
265 
266 static struct i2c_driver hd3ss3220_driver = {
267 	.driver = {
268 		.name = "hd3ss3220",
269 		.of_match_table = of_match_ptr(dev_ids),
270 	},
271 	.probe = hd3ss3220_probe,
272 	.remove =  hd3ss3220_remove,
273 };
274 
275 module_i2c_driver(hd3ss3220_driver);
276 
277 MODULE_AUTHOR("Biju Das <biju.das@bp.renesas.com>");
278 MODULE_DESCRIPTION("TI HD3SS3220 DRP Port Controller Driver");
279 MODULE_LICENSE("GPL");
280