xref: /linux/drivers/usb/phy/phy-omap-otg.c (revision 2c2025b41aeff57963f9ae2dd909fea704c625ab)
1449d2ba6SAaro Koskinen /*
2449d2ba6SAaro Koskinen  * OMAP OTG controller driver
3449d2ba6SAaro Koskinen  *
4449d2ba6SAaro Koskinen  * Based on code from tahvo-usb.c and isp1301_omap.c drivers.
5449d2ba6SAaro Koskinen  *
6449d2ba6SAaro Koskinen  * Copyright (C) 2005-2006 Nokia Corporation
7449d2ba6SAaro Koskinen  * Copyright (C) 2004 Texas Instruments
8449d2ba6SAaro Koskinen  * Copyright (C) 2004 David Brownell
9449d2ba6SAaro Koskinen  *
10449d2ba6SAaro Koskinen  * This file is subject to the terms and conditions of the GNU General
11449d2ba6SAaro Koskinen  * Public License. See the file "COPYING" in the main directory of this
12449d2ba6SAaro Koskinen  * archive for more details.
13449d2ba6SAaro Koskinen  *
14449d2ba6SAaro Koskinen  * This program is distributed in the hope that it will be useful,
15449d2ba6SAaro Koskinen  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16449d2ba6SAaro Koskinen  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17449d2ba6SAaro Koskinen  * GNU General Public License for more details.
18449d2ba6SAaro Koskinen  */
19449d2ba6SAaro Koskinen 
20449d2ba6SAaro Koskinen #include <linux/io.h>
21449d2ba6SAaro Koskinen #include <linux/err.h>
22449d2ba6SAaro Koskinen #include <linux/extcon.h>
23449d2ba6SAaro Koskinen #include <linux/kernel.h>
24449d2ba6SAaro Koskinen #include <linux/module.h>
25449d2ba6SAaro Koskinen #include <linux/interrupt.h>
26449d2ba6SAaro Koskinen #include <linux/platform_device.h>
27449d2ba6SAaro Koskinen #include <linux/platform_data/usb-omap1.h>
28449d2ba6SAaro Koskinen 
29449d2ba6SAaro Koskinen struct otg_device {
30449d2ba6SAaro Koskinen 	void __iomem			*base;
31449d2ba6SAaro Koskinen 	bool				id;
32449d2ba6SAaro Koskinen 	bool				vbus;
33a2fd2423SChanwoo Choi 	struct extcon_dev		*extcon;
34449d2ba6SAaro Koskinen 	struct notifier_block		vbus_nb;
35449d2ba6SAaro Koskinen 	struct notifier_block		id_nb;
36449d2ba6SAaro Koskinen };
37449d2ba6SAaro Koskinen 
38449d2ba6SAaro Koskinen #define OMAP_OTG_CTRL		0x0c
39449d2ba6SAaro Koskinen #define OMAP_OTG_ASESSVLD	(1 << 20)
40449d2ba6SAaro Koskinen #define OMAP_OTG_BSESSEND	(1 << 19)
41449d2ba6SAaro Koskinen #define OMAP_OTG_BSESSVLD	(1 << 18)
42449d2ba6SAaro Koskinen #define OMAP_OTG_VBUSVLD	(1 << 17)
43449d2ba6SAaro Koskinen #define OMAP_OTG_ID		(1 << 16)
44449d2ba6SAaro Koskinen #define OMAP_OTG_XCEIV_OUTPUTS \
45449d2ba6SAaro Koskinen 	(OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \
46449d2ba6SAaro Koskinen 	 OMAP_OTG_VBUSVLD  | OMAP_OTG_ID)
47449d2ba6SAaro Koskinen 
48449d2ba6SAaro Koskinen static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs)
49449d2ba6SAaro Koskinen {
50449d2ba6SAaro Koskinen 	u32 l;
51449d2ba6SAaro Koskinen 
52449d2ba6SAaro Koskinen 	l = readl(otg_dev->base + OMAP_OTG_CTRL);
53449d2ba6SAaro Koskinen 	l &= ~OMAP_OTG_XCEIV_OUTPUTS;
54449d2ba6SAaro Koskinen 	l |= outputs;
55449d2ba6SAaro Koskinen 	writel(l, otg_dev->base + OMAP_OTG_CTRL);
56449d2ba6SAaro Koskinen }
57449d2ba6SAaro Koskinen 
58449d2ba6SAaro Koskinen static void omap_otg_set_mode(struct otg_device *otg_dev)
59449d2ba6SAaro Koskinen {
60449d2ba6SAaro Koskinen 	if (!otg_dev->id && otg_dev->vbus)
61449d2ba6SAaro Koskinen 		/* Set B-session valid. */
62449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD);
63449d2ba6SAaro Koskinen 	else if (otg_dev->vbus)
64449d2ba6SAaro Koskinen 		/* Set A-session valid. */
65449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD);
66449d2ba6SAaro Koskinen 	else if (!otg_dev->id)
67449d2ba6SAaro Koskinen 		/* Set B-session end to indicate no VBUS. */
68449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND);
69449d2ba6SAaro Koskinen }
70449d2ba6SAaro Koskinen 
71449d2ba6SAaro Koskinen static int omap_otg_id_notifier(struct notifier_block *nb,
72449d2ba6SAaro Koskinen 				unsigned long event, void *ptr)
73449d2ba6SAaro Koskinen {
74449d2ba6SAaro Koskinen 	struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb);
75449d2ba6SAaro Koskinen 
76449d2ba6SAaro Koskinen 	otg_dev->id = event;
77449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
78449d2ba6SAaro Koskinen 
79449d2ba6SAaro Koskinen 	return NOTIFY_DONE;
80449d2ba6SAaro Koskinen }
81449d2ba6SAaro Koskinen 
82449d2ba6SAaro Koskinen static int omap_otg_vbus_notifier(struct notifier_block *nb,
83449d2ba6SAaro Koskinen 				  unsigned long event, void *ptr)
84449d2ba6SAaro Koskinen {
85449d2ba6SAaro Koskinen 	struct otg_device *otg_dev = container_of(nb, struct otg_device,
86449d2ba6SAaro Koskinen 						  vbus_nb);
87449d2ba6SAaro Koskinen 
88449d2ba6SAaro Koskinen 	otg_dev->vbus = event;
89449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
90449d2ba6SAaro Koskinen 
91449d2ba6SAaro Koskinen 	return NOTIFY_DONE;
92449d2ba6SAaro Koskinen }
93449d2ba6SAaro Koskinen 
94449d2ba6SAaro Koskinen static int omap_otg_probe(struct platform_device *pdev)
95449d2ba6SAaro Koskinen {
96449d2ba6SAaro Koskinen 	const struct omap_usb_config *config = pdev->dev.platform_data;
97449d2ba6SAaro Koskinen 	struct otg_device *otg_dev;
98449d2ba6SAaro Koskinen 	struct extcon_dev *extcon;
99449d2ba6SAaro Koskinen 	int ret;
100449d2ba6SAaro Koskinen 	u32 rev;
101449d2ba6SAaro Koskinen 
102449d2ba6SAaro Koskinen 	if (!config || !config->extcon)
103449d2ba6SAaro Koskinen 		return -ENODEV;
104449d2ba6SAaro Koskinen 
105449d2ba6SAaro Koskinen 	extcon = extcon_get_extcon_dev(config->extcon);
106449d2ba6SAaro Koskinen 	if (!extcon)
107449d2ba6SAaro Koskinen 		return -EPROBE_DEFER;
108449d2ba6SAaro Koskinen 
109449d2ba6SAaro Koskinen 	otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
110449d2ba6SAaro Koskinen 	if (!otg_dev)
111449d2ba6SAaro Koskinen 		return -ENOMEM;
112449d2ba6SAaro Koskinen 
113449d2ba6SAaro Koskinen 	otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
114449d2ba6SAaro Koskinen 	if (IS_ERR(otg_dev->base))
115449d2ba6SAaro Koskinen 		return PTR_ERR(otg_dev->base);
116449d2ba6SAaro Koskinen 
117*2c2025b4SAaro Koskinen 	otg_dev->extcon = extcon;
118449d2ba6SAaro Koskinen 	otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
119449d2ba6SAaro Koskinen 	otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
120449d2ba6SAaro Koskinen 
121a2fd2423SChanwoo Choi 	ret = extcon_register_notifier(extcon, EXTCON_USB_HOST, &otg_dev->id_nb);
122449d2ba6SAaro Koskinen 	if (ret)
123449d2ba6SAaro Koskinen 		return ret;
124449d2ba6SAaro Koskinen 
125a2fd2423SChanwoo Choi 	ret = extcon_register_notifier(extcon, EXTCON_USB, &otg_dev->vbus_nb);
126449d2ba6SAaro Koskinen 	if (ret) {
127a2fd2423SChanwoo Choi 		extcon_unregister_notifier(extcon, EXTCON_USB_HOST,
128a2fd2423SChanwoo Choi 					&otg_dev->id_nb);
129449d2ba6SAaro Koskinen 		return ret;
130449d2ba6SAaro Koskinen 	}
131449d2ba6SAaro Koskinen 
132a2fd2423SChanwoo Choi 	otg_dev->id = extcon_get_cable_state_(extcon, EXTCON_USB_HOST);
133a2fd2423SChanwoo Choi 	otg_dev->vbus = extcon_get_cable_state_(extcon, EXTCON_USB);
134449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
135449d2ba6SAaro Koskinen 
136449d2ba6SAaro Koskinen 	rev = readl(otg_dev->base);
137449d2ba6SAaro Koskinen 
138449d2ba6SAaro Koskinen 	dev_info(&pdev->dev,
139449d2ba6SAaro Koskinen 		 "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n",
140449d2ba6SAaro Koskinen 		 (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id,
141449d2ba6SAaro Koskinen 		 otg_dev->vbus);
142449d2ba6SAaro Koskinen 
143449d2ba6SAaro Koskinen 	return 0;
144449d2ba6SAaro Koskinen }
145449d2ba6SAaro Koskinen 
146449d2ba6SAaro Koskinen static int omap_otg_remove(struct platform_device *pdev)
147449d2ba6SAaro Koskinen {
148449d2ba6SAaro Koskinen 	struct otg_device *otg_dev = platform_get_drvdata(pdev);
149a2fd2423SChanwoo Choi 	struct extcon_dev *edev = otg_dev->extcon;
150449d2ba6SAaro Koskinen 
151a2fd2423SChanwoo Choi 	extcon_unregister_notifier(edev, EXTCON_USB_HOST,&otg_dev->id_nb);
152a2fd2423SChanwoo Choi 	extcon_unregister_notifier(edev, EXTCON_USB, &otg_dev->vbus_nb);
153449d2ba6SAaro Koskinen 
154449d2ba6SAaro Koskinen 	return 0;
155449d2ba6SAaro Koskinen }
156449d2ba6SAaro Koskinen 
157449d2ba6SAaro Koskinen static struct platform_driver omap_otg_driver = {
158449d2ba6SAaro Koskinen 	.probe		= omap_otg_probe,
159449d2ba6SAaro Koskinen 	.remove		= omap_otg_remove,
160449d2ba6SAaro Koskinen 	.driver		= {
161449d2ba6SAaro Koskinen 		.name	= "omap_otg",
162449d2ba6SAaro Koskinen 	},
163449d2ba6SAaro Koskinen };
164449d2ba6SAaro Koskinen module_platform_driver(omap_otg_driver);
165449d2ba6SAaro Koskinen 
166449d2ba6SAaro Koskinen MODULE_DESCRIPTION("OMAP USB OTG controller driver");
167449d2ba6SAaro Koskinen MODULE_LICENSE("GPL");
168449d2ba6SAaro Koskinen MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
169