xref: /linux/drivers/extcon/extcon-palmas.c (revision a4eb44a6435d6d8f9e642407a4a06f65eb90ca04)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Palmas USB transceiver driver
4  *
5  * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com
6  * Author: Graeme Gregory <gg@slimlogic.co.uk>
7  * Author: Kishon Vijay Abraham I <kishon@ti.com>
8  * Based on twl6030_usb.c
9  * Author: Hema HK <hemahk@ti.com>
10  */
11 
12 #include <linux/devm-helpers.h>
13 #include <linux/module.h>
14 #include <linux/interrupt.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17 #include <linux/err.h>
18 #include <linux/mfd/palmas.h>
19 #include <linux/of.h>
20 #include <linux/of_platform.h>
21 #include <linux/of_gpio.h>
22 #include <linux/gpio/consumer.h>
23 #include <linux/workqueue.h>
24 
25 #define USB_GPIO_DEBOUNCE_MS	20	/* ms */
26 
27 static const unsigned int palmas_extcon_cable[] = {
28 	EXTCON_USB,
29 	EXTCON_USB_HOST,
30 	EXTCON_NONE,
31 };
32 
33 static void palmas_usb_wakeup(struct palmas *palmas, int enable)
34 {
35 	if (enable)
36 		palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP,
37 			PALMAS_USB_WAKEUP_ID_WK_UP_COMP);
38 	else
39 		palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 0);
40 }
41 
42 static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
43 {
44 	struct palmas_usb *palmas_usb = _palmas_usb;
45 	struct extcon_dev *edev = palmas_usb->edev;
46 	unsigned int vbus_line_state;
47 
48 	palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE,
49 		PALMAS_INT3_LINE_STATE, &vbus_line_state);
50 
51 	if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
52 		if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
53 			palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
54 			extcon_set_state_sync(edev, EXTCON_USB, true);
55 			dev_dbg(palmas_usb->dev, "USB cable is attached\n");
56 		} else {
57 			dev_dbg(palmas_usb->dev,
58 				"Spurious connect event detected\n");
59 		}
60 	} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
61 		if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
62 			palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
63 			extcon_set_state_sync(edev, EXTCON_USB, false);
64 			dev_dbg(palmas_usb->dev, "USB cable is detached\n");
65 		} else {
66 			dev_dbg(palmas_usb->dev,
67 				"Spurious disconnect event detected\n");
68 		}
69 	}
70 
71 	return IRQ_HANDLED;
72 }
73 
74 static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
75 {
76 	unsigned int set, id_src;
77 	struct palmas_usb *palmas_usb = _palmas_usb;
78 	struct extcon_dev *edev = palmas_usb->edev;
79 
80 	palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
81 		PALMAS_USB_ID_INT_LATCH_SET, &set);
82 	palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
83 		PALMAS_USB_ID_INT_SRC, &id_src);
84 
85 	if ((set & PALMAS_USB_ID_INT_SRC_ID_GND) &&
86 				(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
87 		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
88 			PALMAS_USB_ID_INT_LATCH_CLR,
89 			PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
90 		palmas_usb->linkstat = PALMAS_USB_STATE_ID;
91 		extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
92 		dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
93 	} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
94 				(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
95 		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
96 			PALMAS_USB_ID_INT_LATCH_CLR,
97 			PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
98 		palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
99 		extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
100 		dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
101 	} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
102 				(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
103 		palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
104 		extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
105 		dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
106 	} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
107 				(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
108 		palmas_usb->linkstat = PALMAS_USB_STATE_ID;
109 		extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
110 		dev_dbg(palmas_usb->dev, " USB-HOST cable is attached\n");
111 	}
112 
113 	return IRQ_HANDLED;
114 }
115 
116 static void palmas_gpio_id_detect(struct work_struct *work)
117 {
118 	int id;
119 	struct palmas_usb *palmas_usb = container_of(to_delayed_work(work),
120 						     struct palmas_usb,
121 						     wq_detectid);
122 	struct extcon_dev *edev = palmas_usb->edev;
123 
124 	if (!palmas_usb->id_gpiod)
125 		return;
126 
127 	id = gpiod_get_value_cansleep(palmas_usb->id_gpiod);
128 
129 	if (id) {
130 		extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
131 		dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
132 	} else {
133 		extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
134 		dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
135 	}
136 }
137 
138 static irqreturn_t palmas_gpio_id_irq_handler(int irq, void *_palmas_usb)
139 {
140 	struct palmas_usb *palmas_usb = _palmas_usb;
141 
142 	queue_delayed_work(system_power_efficient_wq, &palmas_usb->wq_detectid,
143 			   palmas_usb->sw_debounce_jiffies);
144 
145 	return IRQ_HANDLED;
146 }
147 
148 static void palmas_enable_irq(struct palmas_usb *palmas_usb)
149 {
150 	palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
151 		PALMAS_USB_VBUS_CTRL_SET,
152 		PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP);
153 
154 	if (palmas_usb->enable_id_detection) {
155 		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
156 			     PALMAS_USB_ID_CTRL_SET,
157 			     PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP);
158 
159 		palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
160 			     PALMAS_USB_ID_INT_EN_HI_SET,
161 			     PALMAS_USB_ID_INT_EN_HI_SET_ID_GND |
162 			     PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT);
163 	}
164 
165 	if (palmas_usb->enable_vbus_detection)
166 		palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb);
167 
168 	/* cold plug for host mode needs this delay */
169 	if (palmas_usb->enable_id_detection) {
170 		msleep(30);
171 		palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb);
172 	}
173 }
174 
175 static int palmas_usb_probe(struct platform_device *pdev)
176 {
177 	struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
178 	struct palmas_usb_platform_data	*pdata = dev_get_platdata(&pdev->dev);
179 	struct device_node *node = pdev->dev.of_node;
180 	struct palmas_usb *palmas_usb;
181 	int status;
182 
183 	if (!palmas) {
184 		dev_err(&pdev->dev, "failed to get valid parent\n");
185 		return -EINVAL;
186 	}
187 
188 	palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL);
189 	if (!palmas_usb)
190 		return -ENOMEM;
191 
192 	if (node && !pdata) {
193 		palmas_usb->wakeup = of_property_read_bool(node, "ti,wakeup");
194 		palmas_usb->enable_id_detection = of_property_read_bool(node,
195 						"ti,enable-id-detection");
196 		palmas_usb->enable_vbus_detection = of_property_read_bool(node,
197 						"ti,enable-vbus-detection");
198 	} else {
199 		palmas_usb->wakeup = true;
200 		palmas_usb->enable_id_detection = true;
201 		palmas_usb->enable_vbus_detection = true;
202 
203 		if (pdata)
204 			palmas_usb->wakeup = pdata->wakeup;
205 	}
206 
207 	palmas_usb->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id",
208 							GPIOD_IN);
209 	if (IS_ERR(palmas_usb->id_gpiod))
210 		return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->id_gpiod),
211 				     "failed to get id gpio\n");
212 
213 	palmas_usb->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus",
214 							GPIOD_IN);
215 	if (IS_ERR(palmas_usb->vbus_gpiod))
216 		return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->vbus_gpiod),
217 				     "failed to get id gpio\n");
218 
219 	if (palmas_usb->enable_id_detection && palmas_usb->id_gpiod) {
220 		palmas_usb->enable_id_detection = false;
221 		palmas_usb->enable_gpio_id_detection = true;
222 	}
223 
224 	if (palmas_usb->enable_vbus_detection && palmas_usb->vbus_gpiod) {
225 		palmas_usb->enable_vbus_detection = false;
226 		palmas_usb->enable_gpio_vbus_detection = true;
227 	}
228 
229 	if (palmas_usb->enable_gpio_id_detection) {
230 		u32 debounce;
231 
232 		if (of_property_read_u32(node, "debounce-delay-ms", &debounce))
233 			debounce = USB_GPIO_DEBOUNCE_MS;
234 
235 		status = gpiod_set_debounce(palmas_usb->id_gpiod,
236 					    debounce * 1000);
237 		if (status < 0)
238 			palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce);
239 	}
240 
241 	status = devm_delayed_work_autocancel(&pdev->dev,
242 					      &palmas_usb->wq_detectid,
243 					      palmas_gpio_id_detect);
244 	if (status)
245 		return status;
246 
247 	palmas->usb = palmas_usb;
248 	palmas_usb->palmas = palmas;
249 
250 	palmas_usb->dev	 = &pdev->dev;
251 
252 	palmas_usb_wakeup(palmas, palmas_usb->wakeup);
253 
254 	platform_set_drvdata(pdev, palmas_usb);
255 
256 	palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev,
257 						    palmas_extcon_cable);
258 	if (IS_ERR(palmas_usb->edev)) {
259 		dev_err(&pdev->dev, "failed to allocate extcon device\n");
260 		return -ENOMEM;
261 	}
262 
263 	status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
264 	if (status) {
265 		dev_err(&pdev->dev, "failed to register extcon device\n");
266 		return status;
267 	}
268 
269 	if (palmas_usb->enable_id_detection) {
270 		palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data,
271 							     PALMAS_ID_OTG_IRQ);
272 		palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data,
273 							 PALMAS_ID_IRQ);
274 		status = devm_request_threaded_irq(palmas_usb->dev,
275 				palmas_usb->id_irq,
276 				NULL, palmas_id_irq_handler,
277 				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
278 				IRQF_ONESHOT,
279 				"palmas_usb_id", palmas_usb);
280 		if (status < 0) {
281 			dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
282 					palmas_usb->id_irq, status);
283 			return status;
284 		}
285 	} else if (palmas_usb->enable_gpio_id_detection) {
286 		palmas_usb->gpio_id_irq = gpiod_to_irq(palmas_usb->id_gpiod);
287 		if (palmas_usb->gpio_id_irq < 0) {
288 			dev_err(&pdev->dev, "failed to get id irq\n");
289 			return palmas_usb->gpio_id_irq;
290 		}
291 		status = devm_request_threaded_irq(&pdev->dev,
292 						   palmas_usb->gpio_id_irq,
293 						   NULL,
294 						   palmas_gpio_id_irq_handler,
295 						   IRQF_TRIGGER_RISING |
296 						   IRQF_TRIGGER_FALLING |
297 						   IRQF_ONESHOT,
298 						   "palmas_usb_id",
299 						   palmas_usb);
300 		if (status < 0) {
301 			dev_err(&pdev->dev,
302 				"failed to request handler for id irq\n");
303 			return status;
304 		}
305 	}
306 
307 	if (palmas_usb->enable_vbus_detection) {
308 		palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
309 						       PALMAS_VBUS_OTG_IRQ);
310 		palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data,
311 							   PALMAS_VBUS_IRQ);
312 		status = devm_request_threaded_irq(palmas_usb->dev,
313 				palmas_usb->vbus_irq, NULL,
314 				palmas_vbus_irq_handler,
315 				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
316 				IRQF_ONESHOT,
317 				"palmas_usb_vbus", palmas_usb);
318 		if (status < 0) {
319 			dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
320 					palmas_usb->vbus_irq, status);
321 			return status;
322 		}
323 	} else if (palmas_usb->enable_gpio_vbus_detection) {
324 		/* remux GPIO_1 as VBUSDET */
325 		status = palmas_update_bits(palmas,
326 			PALMAS_PU_PD_OD_BASE,
327 			PALMAS_PRIMARY_SECONDARY_PAD1,
328 			PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK,
329 			(1 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT));
330 		if (status < 0) {
331 			dev_err(&pdev->dev, "can't remux GPIO1\n");
332 			return status;
333 		}
334 
335 		palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
336 						       PALMAS_VBUS_OTG_IRQ);
337 		palmas_usb->gpio_vbus_irq = gpiod_to_irq(palmas_usb->vbus_gpiod);
338 		if (palmas_usb->gpio_vbus_irq < 0) {
339 			dev_err(&pdev->dev, "failed to get vbus irq\n");
340 			return palmas_usb->gpio_vbus_irq;
341 		}
342 		status = devm_request_threaded_irq(&pdev->dev,
343 						palmas_usb->gpio_vbus_irq,
344 						NULL,
345 						palmas_vbus_irq_handler,
346 						IRQF_TRIGGER_FALLING |
347 						IRQF_TRIGGER_RISING |
348 						IRQF_ONESHOT,
349 						"palmas_usb_vbus",
350 						palmas_usb);
351 		if (status < 0) {
352 			dev_err(&pdev->dev,
353 				"failed to request handler for vbus irq\n");
354 			return status;
355 		}
356 	}
357 
358 	palmas_enable_irq(palmas_usb);
359 	/* perform initial detection */
360 	if (palmas_usb->enable_gpio_vbus_detection)
361 		palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb);
362 	palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
363 	device_set_wakeup_capable(&pdev->dev, true);
364 	return 0;
365 }
366 
367 #ifdef CONFIG_PM_SLEEP
368 static int palmas_usb_suspend(struct device *dev)
369 {
370 	struct palmas_usb *palmas_usb = dev_get_drvdata(dev);
371 
372 	if (device_may_wakeup(dev)) {
373 		if (palmas_usb->enable_vbus_detection)
374 			enable_irq_wake(palmas_usb->vbus_irq);
375 		if (palmas_usb->enable_gpio_vbus_detection)
376 			enable_irq_wake(palmas_usb->gpio_vbus_irq);
377 		if (palmas_usb->enable_id_detection)
378 			enable_irq_wake(palmas_usb->id_irq);
379 		if (palmas_usb->enable_gpio_id_detection)
380 			enable_irq_wake(palmas_usb->gpio_id_irq);
381 	}
382 	return 0;
383 }
384 
385 static int palmas_usb_resume(struct device *dev)
386 {
387 	struct palmas_usb *palmas_usb = dev_get_drvdata(dev);
388 
389 	if (device_may_wakeup(dev)) {
390 		if (palmas_usb->enable_vbus_detection)
391 			disable_irq_wake(palmas_usb->vbus_irq);
392 		if (palmas_usb->enable_gpio_vbus_detection)
393 			disable_irq_wake(palmas_usb->gpio_vbus_irq);
394 		if (palmas_usb->enable_id_detection)
395 			disable_irq_wake(palmas_usb->id_irq);
396 		if (palmas_usb->enable_gpio_id_detection)
397 			disable_irq_wake(palmas_usb->gpio_id_irq);
398 	}
399 
400 	/* check if GPIO states changed while suspend/resume */
401 	if (palmas_usb->enable_gpio_vbus_detection)
402 		palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb);
403 	palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
404 
405 	return 0;
406 };
407 #endif
408 
409 static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume);
410 
411 static const struct of_device_id of_palmas_match_tbl[] = {
412 	{ .compatible = "ti,palmas-usb", },
413 	{ .compatible = "ti,palmas-usb-vid", },
414 	{ .compatible = "ti,twl6035-usb", },
415 	{ .compatible = "ti,twl6035-usb-vid", },
416 	{ /* end */ }
417 };
418 
419 static struct platform_driver palmas_usb_driver = {
420 	.probe = palmas_usb_probe,
421 	.driver = {
422 		.name = "palmas-usb",
423 		.of_match_table = of_palmas_match_tbl,
424 		.pm = &palmas_pm_ops,
425 	},
426 };
427 
428 module_platform_driver(palmas_usb_driver);
429 
430 MODULE_ALIAS("platform:palmas-usb");
431 MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
432 MODULE_DESCRIPTION("Palmas USB transceiver driver");
433 MODULE_LICENSE("GPL");
434 MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
435