xref: /linux/drivers/usb/phy/phy-omap-otg.c (revision 5fd54ace4721fc5ce2bb5aef6318fcf17f421460)
1*5fd54aceSGreg 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  * This file is subject to the terms and conditions of the GNU General
12449d2ba6SAaro Koskinen  * Public License. See the file "COPYING" in the main directory of this
13449d2ba6SAaro Koskinen  * archive for more details.
14449d2ba6SAaro Koskinen  *
15449d2ba6SAaro Koskinen  * This program is distributed in the hope that it will be useful,
16449d2ba6SAaro Koskinen  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17449d2ba6SAaro Koskinen  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18449d2ba6SAaro Koskinen  * GNU General Public License for more details.
19449d2ba6SAaro Koskinen  */
20449d2ba6SAaro Koskinen 
21449d2ba6SAaro Koskinen #include <linux/io.h>
22449d2ba6SAaro Koskinen #include <linux/err.h>
23449d2ba6SAaro Koskinen #include <linux/extcon.h>
24449d2ba6SAaro Koskinen #include <linux/kernel.h>
25449d2ba6SAaro Koskinen #include <linux/module.h>
26449d2ba6SAaro Koskinen #include <linux/interrupt.h>
27449d2ba6SAaro Koskinen #include <linux/platform_device.h>
28449d2ba6SAaro Koskinen #include <linux/platform_data/usb-omap1.h>
29449d2ba6SAaro Koskinen 
30449d2ba6SAaro Koskinen struct otg_device {
31449d2ba6SAaro Koskinen 	void __iomem			*base;
32449d2ba6SAaro Koskinen 	bool				id;
33449d2ba6SAaro Koskinen 	bool				vbus;
34a2fd2423SChanwoo Choi 	struct extcon_dev		*extcon;
35449d2ba6SAaro Koskinen 	struct notifier_block		vbus_nb;
36449d2ba6SAaro Koskinen 	struct notifier_block		id_nb;
37449d2ba6SAaro Koskinen };
38449d2ba6SAaro Koskinen 
39449d2ba6SAaro Koskinen #define OMAP_OTG_CTRL		0x0c
40449d2ba6SAaro Koskinen #define OMAP_OTG_ASESSVLD	(1 << 20)
41449d2ba6SAaro Koskinen #define OMAP_OTG_BSESSEND	(1 << 19)
42449d2ba6SAaro Koskinen #define OMAP_OTG_BSESSVLD	(1 << 18)
43449d2ba6SAaro Koskinen #define OMAP_OTG_VBUSVLD	(1 << 17)
44449d2ba6SAaro Koskinen #define OMAP_OTG_ID		(1 << 16)
45449d2ba6SAaro Koskinen #define OMAP_OTG_XCEIV_OUTPUTS \
46449d2ba6SAaro Koskinen 	(OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \
47449d2ba6SAaro Koskinen 	 OMAP_OTG_VBUSVLD  | OMAP_OTG_ID)
48449d2ba6SAaro Koskinen 
49449d2ba6SAaro Koskinen static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs)
50449d2ba6SAaro Koskinen {
51449d2ba6SAaro Koskinen 	u32 l;
52449d2ba6SAaro Koskinen 
53449d2ba6SAaro Koskinen 	l = readl(otg_dev->base + OMAP_OTG_CTRL);
54449d2ba6SAaro Koskinen 	l &= ~OMAP_OTG_XCEIV_OUTPUTS;
55449d2ba6SAaro Koskinen 	l |= outputs;
56449d2ba6SAaro Koskinen 	writel(l, otg_dev->base + OMAP_OTG_CTRL);
57449d2ba6SAaro Koskinen }
58449d2ba6SAaro Koskinen 
59449d2ba6SAaro Koskinen static void omap_otg_set_mode(struct otg_device *otg_dev)
60449d2ba6SAaro Koskinen {
61449d2ba6SAaro Koskinen 	if (!otg_dev->id && otg_dev->vbus)
62449d2ba6SAaro Koskinen 		/* Set B-session valid. */
63449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD);
64449d2ba6SAaro Koskinen 	else if (otg_dev->vbus)
65449d2ba6SAaro Koskinen 		/* Set A-session valid. */
66449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD);
67449d2ba6SAaro Koskinen 	else if (!otg_dev->id)
68449d2ba6SAaro Koskinen 		/* Set B-session end to indicate no VBUS. */
69449d2ba6SAaro Koskinen 		omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND);
70449d2ba6SAaro Koskinen }
71449d2ba6SAaro Koskinen 
72449d2ba6SAaro Koskinen static int omap_otg_id_notifier(struct notifier_block *nb,
73449d2ba6SAaro Koskinen 				unsigned long event, void *ptr)
74449d2ba6SAaro Koskinen {
75449d2ba6SAaro Koskinen 	struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb);
76449d2ba6SAaro Koskinen 
77449d2ba6SAaro Koskinen 	otg_dev->id = event;
78449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
79449d2ba6SAaro Koskinen 
80449d2ba6SAaro Koskinen 	return NOTIFY_DONE;
81449d2ba6SAaro Koskinen }
82449d2ba6SAaro Koskinen 
83449d2ba6SAaro Koskinen static int omap_otg_vbus_notifier(struct notifier_block *nb,
84449d2ba6SAaro Koskinen 				  unsigned long event, void *ptr)
85449d2ba6SAaro Koskinen {
86449d2ba6SAaro Koskinen 	struct otg_device *otg_dev = container_of(nb, struct otg_device,
87449d2ba6SAaro Koskinen 						  vbus_nb);
88449d2ba6SAaro Koskinen 
89449d2ba6SAaro Koskinen 	otg_dev->vbus = event;
90449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
91449d2ba6SAaro Koskinen 
92449d2ba6SAaro Koskinen 	return NOTIFY_DONE;
93449d2ba6SAaro Koskinen }
94449d2ba6SAaro Koskinen 
95449d2ba6SAaro Koskinen static int omap_otg_probe(struct platform_device *pdev)
96449d2ba6SAaro Koskinen {
97449d2ba6SAaro Koskinen 	const struct omap_usb_config *config = pdev->dev.platform_data;
98449d2ba6SAaro Koskinen 	struct otg_device *otg_dev;
99449d2ba6SAaro Koskinen 	struct extcon_dev *extcon;
100449d2ba6SAaro Koskinen 	int ret;
101449d2ba6SAaro Koskinen 	u32 rev;
102449d2ba6SAaro Koskinen 
103449d2ba6SAaro Koskinen 	if (!config || !config->extcon)
104449d2ba6SAaro Koskinen 		return -ENODEV;
105449d2ba6SAaro Koskinen 
106449d2ba6SAaro Koskinen 	extcon = extcon_get_extcon_dev(config->extcon);
107449d2ba6SAaro Koskinen 	if (!extcon)
108449d2ba6SAaro Koskinen 		return -EPROBE_DEFER;
109449d2ba6SAaro Koskinen 
110449d2ba6SAaro Koskinen 	otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
111449d2ba6SAaro Koskinen 	if (!otg_dev)
112449d2ba6SAaro Koskinen 		return -ENOMEM;
113449d2ba6SAaro Koskinen 
114449d2ba6SAaro Koskinen 	otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
115449d2ba6SAaro Koskinen 	if (IS_ERR(otg_dev->base))
116449d2ba6SAaro Koskinen 		return PTR_ERR(otg_dev->base);
117449d2ba6SAaro Koskinen 
1182c2025b4SAaro Koskinen 	otg_dev->extcon = extcon;
119449d2ba6SAaro Koskinen 	otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
120449d2ba6SAaro Koskinen 	otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
121449d2ba6SAaro Koskinen 
1227df33789SChanwoo Choi 	ret = devm_extcon_register_notifier(&pdev->dev, extcon,
1237df33789SChanwoo Choi 					EXTCON_USB_HOST, &otg_dev->id_nb);
124449d2ba6SAaro Koskinen 	if (ret)
125449d2ba6SAaro Koskinen 		return ret;
126449d2ba6SAaro Koskinen 
1277df33789SChanwoo Choi 	ret = devm_extcon_register_notifier(&pdev->dev, extcon,
1287df33789SChanwoo Choi 					EXTCON_USB, &otg_dev->vbus_nb);
129449d2ba6SAaro Koskinen 	if (ret) {
130449d2ba6SAaro Koskinen 		return ret;
131449d2ba6SAaro Koskinen 	}
132449d2ba6SAaro Koskinen 
1337df33789SChanwoo Choi 	otg_dev->id = extcon_get_state(extcon, EXTCON_USB_HOST);
1347df33789SChanwoo Choi 	otg_dev->vbus = extcon_get_state(extcon, EXTCON_USB);
135449d2ba6SAaro Koskinen 	omap_otg_set_mode(otg_dev);
136449d2ba6SAaro Koskinen 
137449d2ba6SAaro Koskinen 	rev = readl(otg_dev->base);
138449d2ba6SAaro Koskinen 
139449d2ba6SAaro Koskinen 	dev_info(&pdev->dev,
140449d2ba6SAaro Koskinen 		 "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n",
141449d2ba6SAaro Koskinen 		 (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id,
142449d2ba6SAaro Koskinen 		 otg_dev->vbus);
143449d2ba6SAaro Koskinen 
144ec57fcd0SWei Yongjun 	platform_set_drvdata(pdev, otg_dev);
145ec57fcd0SWei Yongjun 
146449d2ba6SAaro Koskinen 	return 0;
147449d2ba6SAaro Koskinen }
148449d2ba6SAaro Koskinen 
149449d2ba6SAaro Koskinen static struct platform_driver omap_otg_driver = {
150449d2ba6SAaro Koskinen 	.probe		= omap_otg_probe,
151449d2ba6SAaro Koskinen 	.driver		= {
152449d2ba6SAaro Koskinen 		.name	= "omap_otg",
153449d2ba6SAaro Koskinen 	},
154449d2ba6SAaro Koskinen };
155449d2ba6SAaro Koskinen module_platform_driver(omap_otg_driver);
156449d2ba6SAaro Koskinen 
157449d2ba6SAaro Koskinen MODULE_DESCRIPTION("OMAP USB OTG controller driver");
158449d2ba6SAaro Koskinen MODULE_LICENSE("GPL");
159449d2ba6SAaro Koskinen MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
160