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