Commit | Line | Data |
---|---|---|
4ed754de VK |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // | |
3 | // extcon-ptn5150.c - PTN5150 CC logic extcon driver to support USB detection | |
4 | // | |
5 | // Based on extcon-sm5502.c driver | |
6 | // Copyright (c) 2018-2019 by Vijai Kumar K | |
7 | // Author: Vijai Kumar K <vijaikumar.kanagarajan@gmail.com> | |
85256f61 | 8 | // Copyright (c) 2020 Krzysztof Kozlowski <krzk@kernel.org> |
4ed754de | 9 | |
7e3b1caf | 10 | #include <linux/bitfield.h> |
4ed754de VK |
11 | #include <linux/err.h> |
12 | #include <linux/i2c.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/regmap.h> | |
17 | #include <linux/slab.h> | |
18 | #include <linux/extcon-provider.h> | |
19 | #include <linux/gpio/consumer.h> | |
1bfcaa57 | 20 | #include <linux/usb/role.h> |
4ed754de VK |
21 | |
22 | /* PTN5150 registers */ | |
b9a32f62 KK |
23 | #define PTN5150_REG_DEVICE_ID 0x01 |
24 | #define PTN5150_REG_CONTROL 0x02 | |
25 | #define PTN5150_REG_INT_STATUS 0x03 | |
26 | #define PTN5150_REG_CC_STATUS 0x04 | |
27 | #define PTN5150_REG_CON_DET 0x09 | |
28 | #define PTN5150_REG_VCONN_STATUS 0x0a | |
29 | #define PTN5150_REG_RESET 0x0b | |
30 | #define PTN5150_REG_INT_MASK 0x18 | |
31 | #define PTN5150_REG_INT_REG_STATUS 0x19 | |
32 | #define PTN5150_REG_END PTN5150_REG_INT_REG_STATUS | |
4ed754de VK |
33 | |
34 | #define PTN5150_DFP_ATTACHED 0x1 | |
35 | #define PTN5150_UFP_ATTACHED 0x2 | |
36 | ||
37 | /* Define PTN5150 MASK/SHIFT constant */ | |
7e3b1caf RVM |
38 | #define PTN5150_REG_DEVICE_ID_VERSION GENMASK(7, 3) |
39 | #define PTN5150_REG_DEVICE_ID_VENDOR GENMASK(2, 0) | |
4ed754de | 40 | |
7e3b1caf RVM |
41 | #define PTN5150_REG_CC_PORT_ATTACHMENT GENMASK(4, 2) |
42 | #define PTN5150_REG_CC_VBUS_DETECTION BIT(7) | |
43 | #define PTN5150_REG_INT_CABLE_ATTACH_MASK BIT(0) | |
44 | #define PTN5150_REG_INT_CABLE_DETACH_MASK BIT(1) | |
4ed754de VK |
45 | |
46 | struct ptn5150_info { | |
47 | struct device *dev; | |
48 | struct extcon_dev *edev; | |
49 | struct i2c_client *i2c; | |
50 | struct regmap *regmap; | |
51 | struct gpio_desc *int_gpiod; | |
52 | struct gpio_desc *vbus_gpiod; | |
53 | int irq; | |
54 | struct work_struct irq_work; | |
55 | struct mutex mutex; | |
1bfcaa57 | 56 | struct usb_role_switch *role_sw; |
4ed754de VK |
57 | }; |
58 | ||
59 | /* List of detectable cables */ | |
60 | static const unsigned int ptn5150_extcon_cable[] = { | |
61 | EXTCON_USB, | |
62 | EXTCON_USB_HOST, | |
63 | EXTCON_NONE, | |
64 | }; | |
65 | ||
66 | static const struct regmap_config ptn5150_regmap_config = { | |
67 | .reg_bits = 8, | |
68 | .val_bits = 8, | |
69 | .max_register = PTN5150_REG_END, | |
70 | }; | |
71 | ||
85256f61 KK |
72 | static void ptn5150_check_state(struct ptn5150_info *info) |
73 | { | |
74 | unsigned int port_status, reg_data, vbus; | |
1bfcaa57 | 75 | enum usb_role usb_role = USB_ROLE_NONE; |
85256f61 KK |
76 | int ret; |
77 | ||
78 | ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, ®_data); | |
79 | if (ret) { | |
80 | dev_err(info->dev, "failed to read CC STATUS %d\n", ret); | |
81 | return; | |
82 | } | |
83 | ||
7e3b1caf | 84 | port_status = FIELD_GET(PTN5150_REG_CC_PORT_ATTACHMENT, reg_data); |
85256f61 KK |
85 | |
86 | switch (port_status) { | |
87 | case PTN5150_DFP_ATTACHED: | |
88 | extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false); | |
89 | gpiod_set_value_cansleep(info->vbus_gpiod, 0); | |
90 | extcon_set_state_sync(info->edev, EXTCON_USB, true); | |
1bfcaa57 | 91 | usb_role = USB_ROLE_DEVICE; |
85256f61 KK |
92 | break; |
93 | case PTN5150_UFP_ATTACHED: | |
94 | extcon_set_state_sync(info->edev, EXTCON_USB, false); | |
7e3b1caf | 95 | vbus = FIELD_GET(PTN5150_REG_CC_VBUS_DETECTION, reg_data); |
85256f61 KK |
96 | if (vbus) |
97 | gpiod_set_value_cansleep(info->vbus_gpiod, 0); | |
98 | else | |
99 | gpiod_set_value_cansleep(info->vbus_gpiod, 1); | |
100 | ||
101 | extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true); | |
1bfcaa57 | 102 | usb_role = USB_ROLE_HOST; |
85256f61 KK |
103 | break; |
104 | default: | |
85256f61 KK |
105 | break; |
106 | } | |
1bfcaa57 LJ |
107 | |
108 | if (usb_role) { | |
109 | ret = usb_role_switch_set_role(info->role_sw, usb_role); | |
110 | if (ret) | |
111 | dev_err(info->dev, "failed to set %s role: %d\n", | |
112 | usb_role_string(usb_role), ret); | |
113 | } | |
85256f61 KK |
114 | } |
115 | ||
4ed754de VK |
116 | static void ptn5150_irq_work(struct work_struct *work) |
117 | { | |
118 | struct ptn5150_info *info = container_of(work, | |
119 | struct ptn5150_info, irq_work); | |
120 | int ret = 0; | |
4ed754de VK |
121 | unsigned int int_status; |
122 | ||
123 | if (!info->edev) | |
124 | return; | |
125 | ||
126 | mutex_lock(&info->mutex); | |
127 | ||
4ed754de VK |
128 | /* Clear interrupt. Read would clear the register */ |
129 | ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &int_status); | |
130 | if (ret) { | |
131 | dev_err(info->dev, "failed to read INT STATUS %d\n", ret); | |
132 | mutex_unlock(&info->mutex); | |
133 | return; | |
134 | } | |
135 | ||
136 | if (int_status) { | |
137 | unsigned int cable_attach; | |
138 | ||
139 | cable_attach = int_status & PTN5150_REG_INT_CABLE_ATTACH_MASK; | |
140 | if (cable_attach) { | |
85256f61 | 141 | ptn5150_check_state(info); |
4ed754de VK |
142 | } else { |
143 | extcon_set_state_sync(info->edev, | |
144 | EXTCON_USB_HOST, false); | |
145 | extcon_set_state_sync(info->edev, | |
146 | EXTCON_USB, false); | |
6aaad58c | 147 | gpiod_set_value_cansleep(info->vbus_gpiod, 0); |
1bfcaa57 LJ |
148 | |
149 | ret = usb_role_switch_set_role(info->role_sw, | |
150 | USB_ROLE_NONE); | |
151 | if (ret) | |
152 | dev_err(info->dev, | |
153 | "failed to set none role: %d\n", | |
154 | ret); | |
4ed754de VK |
155 | } |
156 | } | |
157 | ||
158 | /* Clear interrupt. Read would clear the register */ | |
159 | ret = regmap_read(info->regmap, PTN5150_REG_INT_REG_STATUS, | |
160 | &int_status); | |
161 | if (ret) { | |
162 | dev_err(info->dev, | |
163 | "failed to read INT REG STATUS %d\n", ret); | |
164 | mutex_unlock(&info->mutex); | |
165 | return; | |
166 | } | |
167 | ||
168 | mutex_unlock(&info->mutex); | |
169 | } | |
170 | ||
171 | ||
172 | static irqreturn_t ptn5150_irq_handler(int irq, void *data) | |
173 | { | |
174 | struct ptn5150_info *info = data; | |
175 | ||
176 | schedule_work(&info->irq_work); | |
177 | ||
178 | return IRQ_HANDLED; | |
179 | } | |
180 | ||
181 | static int ptn5150_init_dev_type(struct ptn5150_info *info) | |
182 | { | |
183 | unsigned int reg_data, vendor_id, version_id; | |
184 | int ret; | |
185 | ||
186 | ret = regmap_read(info->regmap, PTN5150_REG_DEVICE_ID, ®_data); | |
187 | if (ret) { | |
188 | dev_err(info->dev, "failed to read DEVICE_ID %d\n", ret); | |
189 | return -EINVAL; | |
190 | } | |
191 | ||
7e3b1caf RVM |
192 | vendor_id = FIELD_GET(PTN5150_REG_DEVICE_ID_VENDOR, reg_data); |
193 | version_id = FIELD_GET(PTN5150_REG_DEVICE_ID_VERSION, reg_data); | |
fa31f587 KK |
194 | dev_dbg(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n", |
195 | version_id, vendor_id); | |
4ed754de VK |
196 | |
197 | /* Clear any existing interrupts */ | |
198 | ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, ®_data); | |
199 | if (ret) { | |
200 | dev_err(info->dev, | |
201 | "failed to read PTN5150_REG_INT_STATUS %d\n", | |
202 | ret); | |
203 | return -EINVAL; | |
204 | } | |
205 | ||
206 | ret = regmap_read(info->regmap, PTN5150_REG_INT_REG_STATUS, ®_data); | |
207 | if (ret) { | |
208 | dev_err(info->dev, | |
209 | "failed to read PTN5150_REG_INT_REG_STATUS %d\n", ret); | |
210 | return -EINVAL; | |
211 | } | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
782cd939 LJ |
216 | static void ptn5150_work_sync_and_put(void *data) |
217 | { | |
218 | struct ptn5150_info *info = data; | |
219 | ||
220 | cancel_work_sync(&info->irq_work); | |
1bfcaa57 | 221 | usb_role_switch_put(info->role_sw); |
782cd939 LJ |
222 | } |
223 | ||
0b0549b6 | 224 | static int ptn5150_i2c_probe(struct i2c_client *i2c) |
4ed754de VK |
225 | { |
226 | struct device *dev = &i2c->dev; | |
227 | struct device_node *np = i2c->dev.of_node; | |
228 | struct ptn5150_info *info; | |
229 | int ret; | |
230 | ||
231 | if (!np) | |
232 | return -EINVAL; | |
233 | ||
234 | info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); | |
235 | if (!info) | |
236 | return -ENOMEM; | |
237 | i2c_set_clientdata(i2c, info); | |
238 | ||
239 | info->dev = &i2c->dev; | |
240 | info->i2c = i2c; | |
e095882e | 241 | info->vbus_gpiod = devm_gpiod_get(&i2c->dev, "vbus", GPIOD_OUT_LOW); |
3dfed895 | 242 | if (IS_ERR(info->vbus_gpiod)) { |
fbaf3b67 KK |
243 | ret = PTR_ERR(info->vbus_gpiod); |
244 | if (ret == -ENOENT) { | |
245 | dev_info(dev, "No VBUS GPIO, ignoring VBUS control\n"); | |
246 | info->vbus_gpiod = NULL; | |
247 | } else { | |
611e92a0 | 248 | return dev_err_probe(dev, ret, "failed to get VBUS GPIO\n"); |
fbaf3b67 | 249 | } |
4ed754de | 250 | } |
4ed754de VK |
251 | |
252 | mutex_init(&info->mutex); | |
253 | ||
254 | INIT_WORK(&info->irq_work, ptn5150_irq_work); | |
255 | ||
256 | info->regmap = devm_regmap_init_i2c(i2c, &ptn5150_regmap_config); | |
257 | if (IS_ERR(info->regmap)) { | |
611e92a0 AS |
258 | return dev_err_probe(info->dev, PTR_ERR(info->regmap), |
259 | "failed to allocate register map\n"); | |
4ed754de VK |
260 | } |
261 | ||
45ce36f5 KK |
262 | if (i2c->irq > 0) { |
263 | info->irq = i2c->irq; | |
264 | } else { | |
265 | info->int_gpiod = devm_gpiod_get(&i2c->dev, "int", GPIOD_IN); | |
266 | if (IS_ERR(info->int_gpiod)) { | |
611e92a0 AS |
267 | return dev_err_probe(dev, PTR_ERR(info->int_gpiod), |
268 | "failed to get INT GPIO\n"); | |
45ce36f5 KK |
269 | } |
270 | ||
4ed754de VK |
271 | info->irq = gpiod_to_irq(info->int_gpiod); |
272 | if (info->irq < 0) { | |
273 | dev_err(dev, "failed to get INTB IRQ\n"); | |
274 | return info->irq; | |
275 | } | |
45ce36f5 | 276 | } |
4ed754de | 277 | |
45ce36f5 KK |
278 | ret = devm_request_threaded_irq(dev, info->irq, NULL, |
279 | ptn5150_irq_handler, | |
280 | IRQF_TRIGGER_FALLING | | |
281 | IRQF_ONESHOT, | |
282 | i2c->name, info); | |
283 | if (ret < 0) { | |
284 | dev_err(dev, "failed to request handler for INTB IRQ\n"); | |
285 | return ret; | |
4ed754de VK |
286 | } |
287 | ||
288 | /* Allocate extcon device */ | |
289 | info->edev = devm_extcon_dev_allocate(info->dev, ptn5150_extcon_cable); | |
290 | if (IS_ERR(info->edev)) { | |
291 | dev_err(info->dev, "failed to allocate memory for extcon\n"); | |
292 | return -ENOMEM; | |
293 | } | |
294 | ||
295 | /* Register extcon device */ | |
296 | ret = devm_extcon_dev_register(info->dev, info->edev); | |
297 | if (ret) { | |
298 | dev_err(info->dev, "failed to register extcon device\n"); | |
299 | return ret; | |
300 | } | |
301 | ||
ea6a95d0 RVM |
302 | extcon_set_property_capability(info->edev, EXTCON_USB, |
303 | EXTCON_PROP_USB_VBUS); | |
304 | extcon_set_property_capability(info->edev, EXTCON_USB_HOST, | |
305 | EXTCON_PROP_USB_VBUS); | |
306 | extcon_set_property_capability(info->edev, EXTCON_USB_HOST, | |
307 | EXTCON_PROP_USB_TYPEC_POLARITY); | |
308 | ||
4ed754de VK |
309 | /* Initialize PTN5150 device and print vendor id and version id */ |
310 | ret = ptn5150_init_dev_type(info); | |
311 | if (ret) | |
312 | return -EINVAL; | |
313 | ||
1bfcaa57 LJ |
314 | info->role_sw = usb_role_switch_get(info->dev); |
315 | if (IS_ERR(info->role_sw)) | |
316 | return dev_err_probe(info->dev, PTR_ERR(info->role_sw), | |
317 | "failed to get role switch\n"); | |
318 | ||
782cd939 LJ |
319 | ret = devm_add_action_or_reset(dev, ptn5150_work_sync_and_put, info); |
320 | if (ret) | |
321 | return ret; | |
322 | ||
85256f61 KK |
323 | /* |
324 | * Update current extcon state if for example OTG connection was there | |
325 | * before the probe | |
326 | */ | |
327 | mutex_lock(&info->mutex); | |
328 | ptn5150_check_state(info); | |
329 | mutex_unlock(&info->mutex); | |
330 | ||
4ed754de VK |
331 | return 0; |
332 | } | |
333 | ||
334 | static const struct of_device_id ptn5150_dt_match[] = { | |
335 | { .compatible = "nxp,ptn5150" }, | |
336 | { }, | |
337 | }; | |
338 | MODULE_DEVICE_TABLE(of, ptn5150_dt_match); | |
339 | ||
340 | static const struct i2c_device_id ptn5150_i2c_id[] = { | |
341 | { "ptn5150", 0 }, | |
342 | { } | |
343 | }; | |
344 | MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id); | |
345 | ||
346 | static struct i2c_driver ptn5150_i2c_driver = { | |
347 | .driver = { | |
348 | .name = "ptn5150", | |
349 | .of_match_table = ptn5150_dt_match, | |
350 | }, | |
bcfa8e33 | 351 | .probe = ptn5150_i2c_probe, |
4ed754de VK |
352 | .id_table = ptn5150_i2c_id, |
353 | }; | |
35f1f8f2 | 354 | module_i2c_driver(ptn5150_i2c_driver); |
4ed754de VK |
355 | |
356 | MODULE_DESCRIPTION("NXP PTN5150 CC logic Extcon driver"); | |
357 | MODULE_AUTHOR("Vijai Kumar K <vijaikumar.kanagarajan@gmail.com>"); | |
85256f61 | 358 | MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); |
4ed754de | 359 | MODULE_LICENSE("GPL v2"); |