xref: /linux/drivers/usb/phy/phy-omap-otg.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2449d2ba6SAaro Koskinen /*
3449d2ba6SAaro Koskinen  * OMAP OTG controller driver
4449d2ba6SAaro Koskinen  *
5449d2ba6SAaro Koskinen  * Based on code from tahvo-usb.c and isp1301_omap.c drivers.
6449d2ba6SAaro Koskinen  *
7449d2ba6SAaro Koskinen  * Copyright (C) 2005-2006 Nokia Corporation
8449d2ba6SAaro Koskinen  * Copyright (C) 2004 Texas Instruments
9449d2ba6SAaro Koskinen  * Copyright (C) 2004 David Brownell
10449d2ba6SAaro Koskinen  */
11449d2ba6SAaro Koskinen 
12449d2ba6SAaro Koskinen #include <linux/io.h>
13449d2ba6SAaro Koskinen #include <linux/err.h>
14449d2ba6SAaro Koskinen #include <linux/extcon.h>
15449d2ba6SAaro Koskinen #include <linux/kernel.h>
16449d2ba6SAaro Koskinen #include <linux/module.h>
17449d2ba6SAaro Koskinen #include <linux/interrupt.h>
18449d2ba6SAaro Koskinen #include <linux/platform_device.h>
19449d2ba6SAaro Koskinen #include <linux/platform_data/usb-omap1.h>
20449d2ba6SAaro Koskinen 
21449d2ba6SAaro Koskinen struct otg_device {
22449d2ba6SAaro Koskinen 	void __iomem			*base;
23449d2ba6SAaro Koskinen 	bool				id;
24449d2ba6SAaro Koskinen 	bool				vbus;
25a2fd2423SChanwoo Choi 	struct extcon_dev		*extcon;
26449d2ba6SAaro Koskinen 	struct notifier_block		vbus_nb;
27449d2ba6SAaro Koskinen 	struct notifier_block		id_nb;
28449d2ba6SAaro Koskinen };
29449d2ba6SAaro Koskinen 
30449d2ba6SAaro Koskinen #define OMAP_OTG_CTRL		0x0c
31449d2ba6SAaro Koskinen #define OMAP_OTG_ASESSVLD	(1 << 20)
32449d2ba6SAaro Koskinen #define OMAP_OTG_BSESSEND	(1 << 19)
33449d2ba6SAaro Koskinen #define OMAP_OTG_BSESSVLD	(1 << 18)
34449d2ba6SAaro Koskinen #define OMAP_OTG_VBUSVLD	(1 << 17)
35449d2ba6SAaro Koskinen #define OMAP_OTG_ID		(1 << 16)
36449d2ba6SAaro Koskinen #define OMAP_OTG_XCEIV_OUTPUTS \
37449d2ba6SAaro Koskinen 	(OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \
38449d2ba6SAaro Koskinen 	 OMAP_OTG_VBUSVLD  | OMAP_OTG_ID)
39449d2ba6SAaro Koskinen 
omap_otg_ctrl(struct otg_device * otg_dev,u32 outputs)40449d2ba6SAaro Koskinen static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs)
41449d2ba6SAaro Koskinen {
42449d2ba6SAaro Koskinen 	u32 l;
43449d2ba6SAaro Koskinen 
44449d2ba6SAaro Koskinen 	l = readl(otg_dev->base + OMAP_OTG_CTRL);
45449d2ba6SAaro Koskinen 	l &= ~OMAP_OTG_XCEIV_OUTPUTS;
46449d2ba6SAaro Koskinen 	l |= outputs;
47449d2ba6SAaro Koskinen 	writel(l, otg_dev->base + OMAP_OTG_CTRL);
48449d2ba6SAaro Koskinen }
49449d2ba6SAaro Koskinen 
omap_otg_set_mode(struct otg_device * otg_dev)50449d2ba6SAaro Koskinen static void omap_otg_set_mode(struct otg_device *otg_dev)
51449d2ba6SAaro Koskinen {
52449d2ba6SAaro Koskinen 	if (!otg_dev->id && otg_dev->vbus)
53449d2ba6SAaro Koskinen 		/* Set B-session valid. */
54449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD);
55449d2ba6SAaro Koskinen 	else if (otg_dev->vbus)
56449d2ba6SAaro Koskinen 		/* Set A-session valid. */
57449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD);
58449d2ba6SAaro Koskinen 	else if (!otg_dev->id)
59449d2ba6SAaro Koskinen 		/* Set B-session end to indicate no VBUS. */
60449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND);
61449d2ba6SAaro Koskinen }
62449d2ba6SAaro Koskinen 
omap_otg_id_notifier(struct notifier_block * nb,unsigned long event,void * ptr)63449d2ba6SAaro Koskinen static int omap_otg_id_notifier(struct notifier_block *nb,
64449d2ba6SAaro Koskinen 				unsigned long event, void *ptr)
65449d2ba6SAaro Koskinen {
66449d2ba6SAaro Koskinen 	struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb);
67449d2ba6SAaro Koskinen 
68449d2ba6SAaro Koskinen 	otg_dev->id = event;
69449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
70449d2ba6SAaro Koskinen 
71449d2ba6SAaro Koskinen 	return NOTIFY_DONE;
72449d2ba6SAaro Koskinen }
73449d2ba6SAaro Koskinen 
omap_otg_vbus_notifier(struct notifier_block * nb,unsigned long event,void * ptr)74449d2ba6SAaro Koskinen static int omap_otg_vbus_notifier(struct notifier_block *nb,
75449d2ba6SAaro Koskinen 				  unsigned long event, void *ptr)
76449d2ba6SAaro Koskinen {
77449d2ba6SAaro Koskinen 	struct otg_device *otg_dev = container_of(nb, struct otg_device,
78449d2ba6SAaro Koskinen 						  vbus_nb);
79449d2ba6SAaro Koskinen 
80449d2ba6SAaro Koskinen 	otg_dev->vbus = event;
81449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
82449d2ba6SAaro Koskinen 
83449d2ba6SAaro Koskinen 	return NOTIFY_DONE;
84449d2ba6SAaro Koskinen }
85449d2ba6SAaro Koskinen 
omap_otg_probe(struct platform_device * pdev)86449d2ba6SAaro Koskinen static int omap_otg_probe(struct platform_device *pdev)
87449d2ba6SAaro Koskinen {
88449d2ba6SAaro Koskinen 	const struct omap_usb_config *config = pdev->dev.platform_data;
89449d2ba6SAaro Koskinen 	struct otg_device *otg_dev;
90449d2ba6SAaro Koskinen 	struct extcon_dev *extcon;
91449d2ba6SAaro Koskinen 	int ret;
92449d2ba6SAaro Koskinen 	u32 rev;
93449d2ba6SAaro Koskinen 
94449d2ba6SAaro Koskinen 	if (!config || !config->extcon)
95449d2ba6SAaro Koskinen 		return -ENODEV;
96449d2ba6SAaro Koskinen 
97449d2ba6SAaro Koskinen 	extcon = extcon_get_extcon_dev(config->extcon);
98*58e4a2d2SDan Carpenter 	if (IS_ERR(extcon))
99*58e4a2d2SDan Carpenter 		return PTR_ERR(extcon);
100449d2ba6SAaro Koskinen 
101449d2ba6SAaro Koskinen 	otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
102449d2ba6SAaro Koskinen 	if (!otg_dev)
103449d2ba6SAaro Koskinen 		return -ENOMEM;
104449d2ba6SAaro Koskinen 
105449d2ba6SAaro Koskinen 	otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
106449d2ba6SAaro Koskinen 	if (IS_ERR(otg_dev->base))
107449d2ba6SAaro Koskinen 		return PTR_ERR(otg_dev->base);
108449d2ba6SAaro Koskinen 
1092c2025b4SAaro Koskinen 	otg_dev->extcon = extcon;
110449d2ba6SAaro Koskinen 	otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
111449d2ba6SAaro Koskinen 	otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
112449d2ba6SAaro Koskinen 
1137df33789SChanwoo Choi 	ret = devm_extcon_register_notifier(&pdev->dev, extcon,
1147df33789SChanwoo Choi 					EXTCON_USB_HOST, &otg_dev->id_nb);
115449d2ba6SAaro Koskinen 	if (ret)
116449d2ba6SAaro Koskinen 		return ret;
117449d2ba6SAaro Koskinen 
1187df33789SChanwoo Choi 	ret = devm_extcon_register_notifier(&pdev->dev, extcon,
1197df33789SChanwoo Choi 					EXTCON_USB, &otg_dev->vbus_nb);
120449d2ba6SAaro Koskinen 	if (ret) {
121449d2ba6SAaro Koskinen 		return ret;
122449d2ba6SAaro Koskinen 	}
123449d2ba6SAaro Koskinen 
1247df33789SChanwoo Choi 	otg_dev->id = extcon_get_state(extcon, EXTCON_USB_HOST);
1257df33789SChanwoo Choi 	otg_dev->vbus = extcon_get_state(extcon, EXTCON_USB);
126449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
127449d2ba6SAaro Koskinen 
128449d2ba6SAaro Koskinen 	rev = readl(otg_dev->base);
129449d2ba6SAaro Koskinen 
130449d2ba6SAaro Koskinen 	dev_info(&pdev->dev,
131449d2ba6SAaro Koskinen 		 "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n",
132449d2ba6SAaro Koskinen 		 (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id,
133449d2ba6SAaro Koskinen 		 otg_dev->vbus);
134449d2ba6SAaro Koskinen 
135ec57fcd0SWei Yongjun 	platform_set_drvdata(pdev, otg_dev);
136ec57fcd0SWei Yongjun 
137449d2ba6SAaro Koskinen 	return 0;
138449d2ba6SAaro Koskinen }
139449d2ba6SAaro Koskinen 
140449d2ba6SAaro Koskinen static struct platform_driver omap_otg_driver = {
141449d2ba6SAaro Koskinen 	.probe		= omap_otg_probe,
142449d2ba6SAaro Koskinen 	.driver		= {
143449d2ba6SAaro Koskinen 		.name	= "omap_otg",
144449d2ba6SAaro Koskinen 	},
145449d2ba6SAaro Koskinen };
146449d2ba6SAaro Koskinen module_platform_driver(omap_otg_driver);
147449d2ba6SAaro Koskinen 
148449d2ba6SAaro Koskinen MODULE_DESCRIPTION("OMAP USB OTG controller driver");
149449d2ba6SAaro Koskinen MODULE_LICENSE("GPL");
150449d2ba6SAaro Koskinen MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
151