xref: /linux/drivers/usb/host/ohci-exynos.c (revision e1fd7341837238c6c5380c5073887d238f706cf0)
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 = dev_get_platdata(&pdev->dev);
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 	err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
118 	if (err)
119 		return err;
120 
121 	exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd),
122 					GFP_KERNEL);
123 	if (!exynos_ohci)
124 		return -ENOMEM;
125 
126 	if (of_device_is_compatible(pdev->dev.of_node,
127 					"samsung,exynos5440-ohci"))
128 		goto skip_phy;
129 
130 	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
131 	if (IS_ERR(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 skip_phy:
145 
146 	exynos_ohci->dev = &pdev->dev;
147 
148 	hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev,
149 					dev_name(&pdev->dev));
150 	if (!hcd) {
151 		dev_err(&pdev->dev, "Unable to create HCD\n");
152 		return -ENOMEM;
153 	}
154 
155 	exynos_ohci->hcd = hcd;
156 	exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
157 
158 	if (IS_ERR(exynos_ohci->clk)) {
159 		dev_err(&pdev->dev, "Failed to get usbhost clock\n");
160 		err = PTR_ERR(exynos_ohci->clk);
161 		goto fail_clk;
162 	}
163 
164 	err = clk_prepare_enable(exynos_ohci->clk);
165 	if (err)
166 		goto fail_clk;
167 
168 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
169 	if (!res) {
170 		dev_err(&pdev->dev, "Failed to get I/O memory\n");
171 		err = -ENXIO;
172 		goto fail_io;
173 	}
174 
175 	hcd->rsrc_start = res->start;
176 	hcd->rsrc_len = resource_size(res);
177 	hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
178 	if (!hcd->regs) {
179 		dev_err(&pdev->dev, "Failed to remap I/O memory\n");
180 		err = -ENOMEM;
181 		goto fail_io;
182 	}
183 
184 	irq = platform_get_irq(pdev, 0);
185 	if (!irq) {
186 		dev_err(&pdev->dev, "Failed to get IRQ\n");
187 		err = -ENODEV;
188 		goto fail_io;
189 	}
190 
191 	if (exynos_ohci->otg)
192 		exynos_ohci->otg->set_host(exynos_ohci->otg,
193 					&exynos_ohci->hcd->self);
194 
195 	exynos_ohci_phy_enable(exynos_ohci);
196 
197 	ohci = hcd_to_ohci(hcd);
198 	ohci_hcd_init(ohci);
199 
200 	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
201 	if (err) {
202 		dev_err(&pdev->dev, "Failed to add USB HCD\n");
203 		goto fail_add_hcd;
204 	}
205 
206 	platform_set_drvdata(pdev, exynos_ohci);
207 
208 	return 0;
209 
210 fail_add_hcd:
211 	exynos_ohci_phy_disable(exynos_ohci);
212 fail_io:
213 	clk_disable_unprepare(exynos_ohci->clk);
214 fail_clk:
215 	usb_put_hcd(hcd);
216 	return err;
217 }
218 
219 static int exynos_ohci_remove(struct platform_device *pdev)
220 {
221 	struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
222 	struct usb_hcd *hcd = exynos_ohci->hcd;
223 
224 	usb_remove_hcd(hcd);
225 
226 	if (exynos_ohci->otg)
227 		exynos_ohci->otg->set_host(exynos_ohci->otg,
228 					&exynos_ohci->hcd->self);
229 
230 	exynos_ohci_phy_disable(exynos_ohci);
231 
232 	clk_disable_unprepare(exynos_ohci->clk);
233 
234 	usb_put_hcd(hcd);
235 
236 	return 0;
237 }
238 
239 static void exynos_ohci_shutdown(struct platform_device *pdev)
240 {
241 	struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
242 	struct usb_hcd *hcd = exynos_ohci->hcd;
243 
244 	if (hcd->driver->shutdown)
245 		hcd->driver->shutdown(hcd);
246 }
247 
248 #ifdef CONFIG_PM
249 static int exynos_ohci_suspend(struct device *dev)
250 {
251 	struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
252 	struct usb_hcd *hcd = exynos_ohci->hcd;
253 	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
254 	unsigned long flags;
255 	int rc = 0;
256 
257 	/*
258 	 * Root hub was already suspended. Disable irq emission and
259 	 * mark HW unaccessible, bail out if RH has been resumed. Use
260 	 * the spinlock to properly synchronize with possible pending
261 	 * RH suspend or resume activity.
262 	 */
263 	spin_lock_irqsave(&ohci->lock, flags);
264 	if (ohci->rh_state != OHCI_RH_SUSPENDED &&
265 			ohci->rh_state != OHCI_RH_HALTED) {
266 		rc = -EINVAL;
267 		goto fail;
268 	}
269 
270 	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
271 
272 	if (exynos_ohci->otg)
273 		exynos_ohci->otg->set_host(exynos_ohci->otg,
274 					&exynos_ohci->hcd->self);
275 
276 	exynos_ohci_phy_disable(exynos_ohci);
277 
278 	clk_disable_unprepare(exynos_ohci->clk);
279 
280 fail:
281 	spin_unlock_irqrestore(&ohci->lock, flags);
282 
283 	return rc;
284 }
285 
286 static int exynos_ohci_resume(struct device *dev)
287 {
288 	struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
289 	struct usb_hcd *hcd = exynos_ohci->hcd;
290 
291 	clk_prepare_enable(exynos_ohci->clk);
292 
293 	if (exynos_ohci->otg)
294 		exynos_ohci->otg->set_host(exynos_ohci->otg,
295 					&exynos_ohci->hcd->self);
296 
297 	exynos_ohci_phy_enable(exynos_ohci);
298 
299 	ohci_resume(hcd, false);
300 
301 	return 0;
302 }
303 #else
304 #define exynos_ohci_suspend	NULL
305 #define exynos_ohci_resume	NULL
306 #endif
307 
308 static const struct dev_pm_ops exynos_ohci_pm_ops = {
309 	.suspend	= exynos_ohci_suspend,
310 	.resume		= exynos_ohci_resume,
311 };
312 
313 #ifdef CONFIG_OF
314 static const struct of_device_id exynos_ohci_match[] = {
315 	{ .compatible = "samsung,exynos4210-ohci" },
316 	{ .compatible = "samsung,exynos5440-ohci" },
317 	{},
318 };
319 MODULE_DEVICE_TABLE(of, exynos_ohci_match);
320 #endif
321 
322 static struct platform_driver exynos_ohci_driver = {
323 	.probe		= exynos_ohci_probe,
324 	.remove		= exynos_ohci_remove,
325 	.shutdown	= exynos_ohci_shutdown,
326 	.driver = {
327 		.name	= "exynos-ohci",
328 		.owner	= THIS_MODULE,
329 		.pm	= &exynos_ohci_pm_ops,
330 		.of_match_table	= of_match_ptr(exynos_ohci_match),
331 	}
332 };
333 
334 MODULE_ALIAS("platform:exynos-ohci");
335 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
336