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