File Index
Files and directories
Sign in
FILE

usb-conn-gpio.c

File preview with syntax highlighting.

linux-6.19.14/drivers/usb/common/usb-conn-gpio.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * USB GPIO Based Connection Detection Driver
  4 *
  5 * Copyright (C) 2019 MediaTek Inc.
  6 *
  7 * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
  8 *
  9 * Some code borrowed from drivers/extcon/extcon-usb-gpio.c
 10 */
 11
 12#include <linux/device.h>
 13#include <linux/gpio/consumer.h>
 14#include <linux/interrupt.h>
 15#include <linux/irq.h>
 16#include <linux/module.h>
 17#include <linux/of.h>
 18#include <linux/pinctrl/consumer.h>
 19#include <linux/platform_device.h>
 20#include <linux/power_supply.h>
 21#include <linux/regulator/consumer.h>
 22#include <linux/string_choices.h>
 23#include <linux/usb/role.h>
 24#include <linux/idr.h>
 25
 26static DEFINE_IDA(usb_conn_ida);
 27
 28#define USB_GPIO_DEB_MS		20	/* ms */
 29#define USB_GPIO_DEB_US		((USB_GPIO_DEB_MS) * 1000)	/* us */
 30
 31#define USB_CONN_IRQF	\
 32	(IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT)
 33
 34struct usb_conn_info {
 35	struct device *dev;
 36	int conn_id; /* store the IDA-allocated ID */
 37	struct usb_role_switch *role_sw;
 38	enum usb_role last_role;
 39	struct regulator *vbus;
 40	struct delayed_work dw_det;
 41	unsigned long debounce_jiffies;
 42
 43	struct gpio_desc *id_gpiod;
 44	struct gpio_desc *vbus_gpiod;
 45	int id_irq;
 46	int vbus_irq;
 47
 48	struct power_supply_desc desc;
 49	struct power_supply *charger;
 50	bool initial_detection;
 51};
 52
 53/*
 54 * "DEVICE" = VBUS and "HOST" = !ID, so we have:
 55 * Both "DEVICE" and "HOST" can't be set as active at the same time
 56 * so if "HOST" is active (i.e. ID is 0)  we keep "DEVICE" inactive
 57 * even if VBUS is on.
 58 *
 59 *  Role          |   ID  |  VBUS
 60 * ------------------------------------
 61 *  [1] DEVICE    |   H   |   H
 62 *  [2] NONE      |   H   |   L
 63 *  [3] HOST      |   L   |   H
 64 *  [4] HOST      |   L   |   L
 65 *
 66 * In case we have only one of these signals:
 67 * - VBUS only - we want to distinguish between [1] and [2], so ID is always 1
 68 * - ID only - we want to distinguish between [1] and [4], so VBUS = ID
 69 */
 70static void usb_conn_detect_cable(struct work_struct *work)
 71{
 72	struct usb_conn_info *info;
 73	enum usb_role role;
 74	int id, vbus, ret;
 75
 76	info = container_of(to_delayed_work(work),
 77			    struct usb_conn_info, dw_det);
 78
 79	/* check ID and VBUS */
 80	id = info->id_gpiod ?
 81		gpiod_get_value_cansleep(info->id_gpiod) : 1;
 82	vbus = info->vbus_gpiod ?
 83		gpiod_get_value_cansleep(info->vbus_gpiod) : id;
 84
 85	if (!id)
 86		role = USB_ROLE_HOST;
 87	else if (vbus)
 88		role = USB_ROLE_DEVICE;
 89	else
 90		role = USB_ROLE_NONE;
 91
 92	dev_dbg(info->dev, "role %s -> %s, gpios: id %d, vbus %d\n",
 93		usb_role_string(info->last_role), usb_role_string(role), id, vbus);
 94
 95	if (!info->initial_detection && info->last_role == role) {
 96		dev_warn(info->dev, "repeated role: %s\n", usb_role_string(role));
 97		return;
 98	}
 99
100	info->initial_detection = false;
101
102	if (info->last_role == USB_ROLE_HOST && info->vbus)
103		regulator_disable(info->vbus);
104
105	ret = usb_role_switch_set_role(info->role_sw, role);
106	if (ret)
107		dev_err(info->dev, "failed to set role: %d\n", ret);
108
109	if (role == USB_ROLE_HOST && info->vbus) {
110		ret = regulator_enable(info->vbus);
111		if (ret)
112			dev_err(info->dev, "enable vbus regulator failed\n");
113	}
114
115	info->last_role = role;
116
117	if (info->vbus)
118		dev_dbg(info->dev, "vbus regulator is %s\n",
119			str_enabled_disabled(regulator_is_enabled(info->vbus)));
120
121	power_supply_changed(info->charger);
122}
123
124static void usb_conn_queue_dwork(struct usb_conn_info *info,
125				 unsigned long delay)
126{
127	queue_delayed_work(system_power_efficient_wq, &info->dw_det, delay);
128}
129
130static irqreturn_t usb_conn_isr(int irq, void *dev_id)
131{
132	struct usb_conn_info *info = dev_id;
133
134	usb_conn_queue_dwork(info, info->debounce_jiffies);
135
136	return IRQ_HANDLED;
137}
138
139static enum power_supply_property usb_charger_properties[] = {
140	POWER_SUPPLY_PROP_ONLINE,
141};
142
143static int usb_charger_get_property(struct power_supply *psy,
144				    enum power_supply_property psp,
145				    union power_supply_propval *val)
146{
147	struct usb_conn_info *info = power_supply_get_drvdata(psy);
148
149	switch (psp) {
150	case POWER_SUPPLY_PROP_ONLINE:
151		val->intval = info->last_role == USB_ROLE_DEVICE;
152		break;
153	default:
154		return -EINVAL;
155	}
156
157	return 0;
158}
159
160static int usb_conn_psy_register(struct usb_conn_info *info)
161{
162	struct device *dev = info->dev;
163	struct power_supply_desc *desc = &info->desc;
164	struct power_supply_config cfg = {
165		.fwnode = dev_fwnode(dev),
166	};
167
168	info->conn_id = ida_alloc(&usb_conn_ida, GFP_KERNEL);
169	if (info->conn_id < 0)
170		return info->conn_id;
171
172	desc->name = devm_kasprintf(dev, GFP_KERNEL, "usb-charger-%d",
173				    info->conn_id);
174	if (!desc->name) {
175		ida_free(&usb_conn_ida, info->conn_id);
176		return -ENOMEM;
177	}
178
179	desc->properties = usb_charger_properties;
180	desc->num_properties = ARRAY_SIZE(usb_charger_properties);
181	desc->get_property = usb_charger_get_property;
182	desc->type = POWER_SUPPLY_TYPE_USB;
183	cfg.drv_data = info;
184
185	info->charger = devm_power_supply_register(dev, desc, &cfg);
186	if (IS_ERR(info->charger)) {
187		dev_err(dev, "Unable to register charger %d\n", info->conn_id);
188		ida_free(&usb_conn_ida, info->conn_id);
189	}
190
191	return PTR_ERR_OR_ZERO(info->charger);
192}
193
194static int usb_conn_probe(struct platform_device *pdev)
195{
196	struct device *dev = &pdev->dev;
197	struct usb_conn_info *info;
198	int ret = 0;
199
200	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
201	if (!info)
202		return -ENOMEM;
203
204	info->dev = dev;
205	info->id_gpiod = devm_gpiod_get_optional(dev, "id", GPIOD_IN);
206	if (IS_ERR(info->id_gpiod))
207		return PTR_ERR(info->id_gpiod);
208
209	info->vbus_gpiod = devm_gpiod_get_optional(dev, "vbus", GPIOD_IN);
210	if (IS_ERR(info->vbus_gpiod))
211		return PTR_ERR(info->vbus_gpiod);
212
213	if (!info->id_gpiod && !info->vbus_gpiod) {
214		dev_err(dev, "failed to get gpios\n");
215		return -ENODEV;
216	}
217
218	if (info->id_gpiod)
219		ret = gpiod_set_debounce(info->id_gpiod, USB_GPIO_DEB_US);
220	if (!ret && info->vbus_gpiod)
221		ret = gpiod_set_debounce(info->vbus_gpiod, USB_GPIO_DEB_US);
222	if (ret < 0)
223		info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEB_MS);
224
225	INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
226
227	info->vbus = devm_regulator_get_optional(dev, "vbus");
228	if (PTR_ERR(info->vbus) == -ENODEV)
229		info->vbus = NULL;
230
231	if (IS_ERR(info->vbus))
232		return dev_err_probe(dev, PTR_ERR(info->vbus), "failed to get vbus\n");
233
234	info->role_sw = usb_role_switch_get(dev);
235	if (IS_ERR(info->role_sw))
236		return dev_err_probe(dev, PTR_ERR(info->role_sw),
237				     "failed to get role switch\n");
238
239	ret = usb_conn_psy_register(info);
240	if (ret)
241		goto put_role_sw;
242
243	if (info->id_gpiod) {
244		info->id_irq = gpiod_to_irq(info->id_gpiod);
245		if (info->id_irq < 0) {
246			dev_err(dev, "failed to get ID IRQ\n");
247			ret = info->id_irq;
248			goto put_role_sw;
249		}
250
251		ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
252						usb_conn_isr, USB_CONN_IRQF,
253						pdev->name, info);
254		if (ret < 0) {
255			dev_err(dev, "failed to request ID IRQ\n");
256			goto put_role_sw;
257		}
258	}
259
260	if (info->vbus_gpiod) {
261		info->vbus_irq = gpiod_to_irq(info->vbus_gpiod);
262		if (info->vbus_irq < 0) {
263			dev_err(dev, "failed to get VBUS IRQ\n");
264			ret = info->vbus_irq;
265			goto put_role_sw;
266		}
267
268		ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
269						usb_conn_isr, USB_CONN_IRQF,
270						pdev->name, info);
271		if (ret < 0) {
272			dev_err(dev, "failed to request VBUS IRQ\n");
273			goto put_role_sw;
274		}
275	}
276
277	platform_set_drvdata(pdev, info);
278	device_set_wakeup_capable(&pdev->dev, true);
279
280	/* Perform initial detection */
281	info->initial_detection = true;
282	usb_conn_queue_dwork(info, 0);
283
284	return 0;
285
286put_role_sw:
287	usb_role_switch_put(info->role_sw);
288	return ret;
289}
290
291static void usb_conn_remove(struct platform_device *pdev)
292{
293	struct usb_conn_info *info = platform_get_drvdata(pdev);
294
295	cancel_delayed_work_sync(&info->dw_det);
296
297	if (info->charger)
298		ida_free(&usb_conn_ida, info->conn_id);
299
300	if (info->last_role == USB_ROLE_HOST && info->vbus)
301		regulator_disable(info->vbus);
302
303	usb_role_switch_put(info->role_sw);
304}
305
306static int __maybe_unused usb_conn_suspend(struct device *dev)
307{
308	struct usb_conn_info *info = dev_get_drvdata(dev);
309
310	if (device_may_wakeup(dev)) {
311		if (info->id_gpiod)
312			enable_irq_wake(info->id_irq);
313		if (info->vbus_gpiod)
314			enable_irq_wake(info->vbus_irq);
315		return 0;
316	}
317
318	if (info->id_gpiod)
319		disable_irq(info->id_irq);
320	if (info->vbus_gpiod)
321		disable_irq(info->vbus_irq);
322
323	pinctrl_pm_select_sleep_state(dev);
324
325	return 0;
326}
327
328static int __maybe_unused usb_conn_resume(struct device *dev)
329{
330	struct usb_conn_info *info = dev_get_drvdata(dev);
331
332	if (device_may_wakeup(dev)) {
333		if (info->id_gpiod)
334			disable_irq_wake(info->id_irq);
335		if (info->vbus_gpiod)
336			disable_irq_wake(info->vbus_irq);
337		return 0;
338	}
339
340	pinctrl_pm_select_default_state(dev);
341
342	if (info->id_gpiod)
343		enable_irq(info->id_irq);
344	if (info->vbus_gpiod)
345		enable_irq(info->vbus_irq);
346
347	usb_conn_queue_dwork(info, 0);
348
349	return 0;
350}
351
352static SIMPLE_DEV_PM_OPS(usb_conn_pm_ops,
353			 usb_conn_suspend, usb_conn_resume);
354
355static const struct of_device_id usb_conn_dt_match[] = {
356	{ .compatible = "gpio-usb-b-connector", },
357	{ }
358};
359MODULE_DEVICE_TABLE(of, usb_conn_dt_match);
360
361static struct platform_driver usb_conn_driver = {
362	.probe		= usb_conn_probe,
363	.remove		= usb_conn_remove,
364	.driver		= {
365		.name	= "usb-conn-gpio",
366		.pm	= &usb_conn_pm_ops,
367		.of_match_table = usb_conn_dt_match,
368	},
369};
370
371module_platform_driver(usb_conn_driver);
372
373MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
374MODULE_DESCRIPTION("USB GPIO based connection detection driver");
375MODULE_LICENSE("GPL v2");
~/ Read-only file preview.