xref: /openbmc/linux/drivers/usb/host/ohci-exynos.c (revision 840ef8b7cc584a23c4f9d05352f4dbaf8e56e5ab)
1 /*
2  * SAMSUNG EXYNOS USB HOST OHCI Controller
3  *
4  * Copyright (C) 2011 Samsung Electronics Co.Ltd
5  * Author: Jingoo Han <jg1.han@samsung.com>
6  *
7  * This program is free software; you can redistribute  it and/or modify it
8  * under  the terms of  the GNU General  Public License as published by the
9  * Free Software Foundation;  either version 2 of the  License, or (at your
10  * option) any later version.
11  *
12  */
13 
14 #include <linux/clk.h>
15 #include <linux/of.h>
16 #include <linux/platform_device.h>
17 #include <linux/platform_data/usb-exynos.h>
18 #include <linux/usb/phy.h>
19 #include <linux/usb/samsung_usb_phy.h>
20 #include <plat/usb-phy.h>
21 
22 struct exynos_ohci_hcd {
23 	struct device *dev;
24 	struct usb_hcd *hcd;
25 	struct clk *clk;
26 	struct usb_phy *phy;
27 	struct usb_otg *otg;
28 	struct exynos4_ohci_platdata *pdata;
29 };
30 
31 static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci)
32 {
33 	struct platform_device *pdev = to_platform_device(exynos_ohci->dev);
34 
35 	if (exynos_ohci->phy)
36 		usb_phy_init(exynos_ohci->phy);
37 	else if (exynos_ohci->pdata->phy_init)
38 		exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
39 }
40 
41 static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci)
42 {
43 	struct platform_device *pdev = to_platform_device(exynos_ohci->dev);
44 
45 	if (exynos_ohci->phy)
46 		usb_phy_shutdown(exynos_ohci->phy);
47 	else if (exynos_ohci->pdata->phy_exit)
48 		exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
49 }
50 
51 static int ohci_exynos_reset(struct usb_hcd *hcd)
52 {
53 	return ohci_init(hcd_to_ohci(hcd));
54 }
55 
56 static int ohci_exynos_start(struct usb_hcd *hcd)
57 {
58 	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
59 	int ret;
60 
61 	ohci_dbg(ohci, "ohci_exynos_start, ohci:%p", ohci);
62 
63 	ret = ohci_run(ohci);
64 	if (ret < 0) {
65 		dev_err(hcd->self.controller, "can't start %s\n",
66 			hcd->self.bus_name);
67 		ohci_stop(hcd);
68 		return ret;
69 	}
70 
71 	return 0;
72 }
73 
74 static const struct hc_driver exynos_ohci_hc_driver = {
75 	.description		= hcd_name,
76 	.product_desc		= "EXYNOS OHCI Host Controller",
77 	.hcd_priv_size		= sizeof(struct ohci_hcd),
78 
79 	.irq			= ohci_irq,
80 	.flags			= HCD_MEMORY|HCD_USB11,
81 
82 	.reset			= ohci_exynos_reset,
83 	.start			= ohci_exynos_start,
84 	.stop			= ohci_stop,
85 	.shutdown		= ohci_shutdown,
86 
87 	.get_frame_number	= ohci_get_frame,
88 
89 	.urb_enqueue		= ohci_urb_enqueue,
90 	.urb_dequeue		= ohci_urb_dequeue,
91 	.endpoint_disable	= ohci_endpoint_disable,
92 
93 	.hub_status_data	= ohci_hub_status_data,
94 	.hub_control		= ohci_hub_control,
95 #ifdef	CONFIG_PM
96 	.bus_suspend		= ohci_bus_suspend,
97 	.bus_resume		= ohci_bus_resume,
98 #endif
99 	.start_port_reset	= ohci_start_port_reset,
100 };
101 
102 static u64 ohci_exynos_dma_mask = DMA_BIT_MASK(32);
103 
104 static int exynos_ohci_probe(struct platform_device *pdev)
105 {
106 	struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data;
107 	struct exynos_ohci_hcd *exynos_ohci;
108 	struct usb_hcd *hcd;
109 	struct ohci_hcd *ohci;
110 	struct resource *res;
111 	struct usb_phy *phy;
112 	int irq;
113 	int err;
114 
115 	/*
116 	 * Right now device-tree probed devices don't get dma_mask set.
117 	 * Since shared usb code relies on it, set it here for now.
118 	 * Once we move to full device tree support this will vanish off.
119 	 */
120 	if (!pdev->dev.dma_mask)
121 		pdev->dev.dma_mask = &ohci_exynos_dma_mask;
122 	if (!pdev->dev.coherent_dma_mask)
123 		pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
124 
125 	exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd),
126 					GFP_KERNEL);
127 	if (!exynos_ohci)
128 		return -ENOMEM;
129 
130 	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
131 	if (IS_ERR_OR_NULL(phy)) {
132 		/* Fallback to pdata */
133 		if (!pdata) {
134 			dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
135 			return -EPROBE_DEFER;
136 		} else {
137 			exynos_ohci->pdata = pdata;
138 		}
139 	} else {
140 		exynos_ohci->phy = phy;
141 		exynos_ohci->otg = phy->otg;
142 	}
143 
144 	exynos_ohci->dev = &pdev->dev;
145 
146 	hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev,
147 					dev_name(&pdev->dev));
148 	if (!hcd) {
149 		dev_err(&pdev->dev, "Unable to create HCD\n");
150 		return -ENOMEM;
151 	}
152 
153 	exynos_ohci->hcd = hcd;
154 	exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
155 
156 	if (IS_ERR(exynos_ohci->clk)) {
157 		dev_err(&pdev->dev, "Failed to get usbhost clock\n");
158 		err = PTR_ERR(exynos_ohci->clk);
159 		goto fail_clk;
160 	}
161 
162 	err = clk_prepare_enable(exynos_ohci->clk);
163 	if (err)
164 		goto fail_clk;
165 
166 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
167 	if (!res) {
168 		dev_err(&pdev->dev, "Failed to get I/O memory\n");
169 		err = -ENXIO;
170 		goto fail_io;
171 	}
172 
173 	hcd->rsrc_start = res->start;
174 	hcd->rsrc_len = resource_size(res);
175 	hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
176 	if (!hcd->regs) {
177 		dev_err(&pdev->dev, "Failed to remap I/O memory\n");
178 		err = -ENOMEM;
179 		goto fail_io;
180 	}
181 
182 	irq = platform_get_irq(pdev, 0);
183 	if (!irq) {
184 		dev_err(&pdev->dev, "Failed to get IRQ\n");
185 		err = -ENODEV;
186 		goto fail_io;
187 	}
188 
189 	if (exynos_ohci->otg)
190 		exynos_ohci->otg->set_host(exynos_ohci->otg,
191 					&exynos_ohci->hcd->self);
192 
193 	exynos_ohci_phy_enable(exynos_ohci);
194 
195 	ohci = hcd_to_ohci(hcd);
196 	ohci_hcd_init(ohci);
197 
198 	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
199 	if (err) {
200 		dev_err(&pdev->dev, "Failed to add USB HCD\n");
201 		goto fail_add_hcd;
202 	}
203 
204 	platform_set_drvdata(pdev, exynos_ohci);
205 
206 	return 0;
207 
208 fail_add_hcd:
209 	exynos_ohci_phy_disable(exynos_ohci);
210 fail_io:
211 	clk_disable_unprepare(exynos_ohci->clk);
212 fail_clk:
213 	usb_put_hcd(hcd);
214 	return err;
215 }
216 
217 static int exynos_ohci_remove(struct platform_device *pdev)
218 {
219 	struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
220 	struct usb_hcd *hcd = exynos_ohci->hcd;
221 
222 	usb_remove_hcd(hcd);
223 
224 	if (exynos_ohci->otg)
225 		exynos_ohci->otg->set_host(exynos_ohci->otg,
226 					&exynos_ohci->hcd->self);
227 
228 	exynos_ohci_phy_disable(exynos_ohci);
229 
230 	clk_disable_unprepare(exynos_ohci->clk);
231 
232 	usb_put_hcd(hcd);
233 
234 	return 0;
235 }
236 
237 static void exynos_ohci_shutdown(struct platform_device *pdev)
238 {
239 	struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
240 	struct usb_hcd *hcd = exynos_ohci->hcd;
241 
242 	if (hcd->driver->shutdown)
243 		hcd->driver->shutdown(hcd);
244 }
245 
246 #ifdef CONFIG_PM
247 static int exynos_ohci_suspend(struct device *dev)
248 {
249 	struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
250 	struct usb_hcd *hcd = exynos_ohci->hcd;
251 	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
252 	unsigned long flags;
253 	int rc = 0;
254 
255 	/*
256 	 * Root hub was already suspended. Disable irq emission and
257 	 * mark HW unaccessible, bail out if RH has been resumed. Use
258 	 * the spinlock to properly synchronize with possible pending
259 	 * RH suspend or resume activity.
260 	 */
261 	spin_lock_irqsave(&ohci->lock, flags);
262 	if (ohci->rh_state != OHCI_RH_SUSPENDED &&
263 			ohci->rh_state != OHCI_RH_HALTED) {
264 		rc = -EINVAL;
265 		goto fail;
266 	}
267 
268 	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
269 
270 	if (exynos_ohci->otg)
271 		exynos_ohci->otg->set_host(exynos_ohci->otg,
272 					&exynos_ohci->hcd->self);
273 
274 	exynos_ohci_phy_disable(exynos_ohci);
275 
276 	clk_disable_unprepare(exynos_ohci->clk);
277 
278 fail:
279 	spin_unlock_irqrestore(&ohci->lock, flags);
280 
281 	return rc;
282 }
283 
284 static int exynos_ohci_resume(struct device *dev)
285 {
286 	struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
287 	struct usb_hcd *hcd = exynos_ohci->hcd;
288 
289 	clk_prepare_enable(exynos_ohci->clk);
290 
291 	if (exynos_ohci->otg)
292 		exynos_ohci->otg->set_host(exynos_ohci->otg,
293 					&exynos_ohci->hcd->self);
294 
295 	exynos_ohci_phy_enable(exynos_ohci);
296 
297 	ohci_resume(hcd, false);
298 
299 	return 0;
300 }
301 #else
302 #define exynos_ohci_suspend	NULL
303 #define exynos_ohci_resume	NULL
304 #endif
305 
306 static const struct dev_pm_ops exynos_ohci_pm_ops = {
307 	.suspend	= exynos_ohci_suspend,
308 	.resume		= exynos_ohci_resume,
309 };
310 
311 #ifdef CONFIG_OF
312 static const struct of_device_id exynos_ohci_match[] = {
313 	{ .compatible = "samsung,exynos4210-ohci" },
314 	{},
315 };
316 MODULE_DEVICE_TABLE(of, exynos_ohci_match);
317 #endif
318 
319 static struct platform_driver exynos_ohci_driver = {
320 	.probe		= exynos_ohci_probe,
321 	.remove		= exynos_ohci_remove,
322 	.shutdown	= exynos_ohci_shutdown,
323 	.driver = {
324 		.name	= "exynos-ohci",
325 		.owner	= THIS_MODULE,
326 		.pm	= &exynos_ohci_pm_ops,
327 		.of_match_table	= of_match_ptr(exynos_ohci_match),
328 	}
329 };
330 
331 MODULE_ALIAS("platform:exynos-ohci");
332 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
333