1 /*
2  * SuperH EHCI host controller driver
3  *
4  * Copyright (C) 2010  Paul Mundt
5  *
6  * Based on ohci-sh.c and ehci-atmel.c.
7  *
8  * This file is subject to the terms and conditions of the GNU General Public
9  * License.  See the file "COPYING" in the main directory of this archive
10  * for more details.
11  */
12 #include <linux/platform_device.h>
13 #include <linux/clk.h>
14 
15 struct ehci_sh_priv {
16 	struct clk *iclk, *fclk;
17 	struct usb_hcd *hcd;
18 };
19 
ehci_sh_reset(struct usb_hcd * hcd)20 static int ehci_sh_reset(struct usb_hcd *hcd)
21 {
22 	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
23 	int ret;
24 
25 	ehci->caps = hcd->regs;
26 	ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci,
27 		&ehci->caps->hc_capbase));
28 
29 	dbg_hcs_params(ehci, "reset");
30 	dbg_hcc_params(ehci, "reset");
31 
32 	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
33 
34 	ret = ehci_halt(ehci);
35 	if (unlikely(ret))
36 		return ret;
37 
38 	ret = ehci_init(hcd);
39 	if (unlikely(ret))
40 		return ret;
41 
42 	ehci->sbrn = 0x20;
43 
44 	ehci_reset(ehci);
45 	ehci_port_power(ehci, 0);
46 
47 	return ret;
48 }
49 
50 static const struct hc_driver ehci_sh_hc_driver = {
51 	.description			= hcd_name,
52 	.product_desc			= "SuperH EHCI",
53 	.hcd_priv_size			= sizeof(struct ehci_hcd),
54 
55 	/*
56 	 * generic hardware linkage
57 	 */
58 	.irq				= ehci_irq,
59 	.flags				= HCD_USB2 | HCD_MEMORY,
60 
61 	/*
62 	 * basic lifecycle operations
63 	 */
64 	.reset				= ehci_sh_reset,
65 	.start				= ehci_run,
66 	.stop				= ehci_stop,
67 	.shutdown			= ehci_shutdown,
68 
69 	/*
70 	 * managing i/o requests and associated device resources
71 	 */
72 	.urb_enqueue			= ehci_urb_enqueue,
73 	.urb_dequeue			= ehci_urb_dequeue,
74 	.endpoint_disable		= ehci_endpoint_disable,
75 	.endpoint_reset			= ehci_endpoint_reset,
76 
77 	/*
78 	 * scheduling support
79 	 */
80 	.get_frame_number		= ehci_get_frame,
81 
82 	/*
83 	 * root hub support
84 	 */
85 	.hub_status_data		= ehci_hub_status_data,
86 	.hub_control			= ehci_hub_control,
87 
88 #ifdef CONFIG_PM
89 	.bus_suspend			= ehci_bus_suspend,
90 	.bus_resume			= ehci_bus_resume,
91 #endif
92 
93 	.relinquish_port		= ehci_relinquish_port,
94 	.port_handed_over		= ehci_port_handed_over,
95 	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
96 };
97 
ehci_hcd_sh_probe(struct platform_device * pdev)98 static int ehci_hcd_sh_probe(struct platform_device *pdev)
99 {
100 	const struct hc_driver *driver = &ehci_sh_hc_driver;
101 	struct resource *res;
102 	struct ehci_sh_priv *priv;
103 	struct usb_hcd *hcd;
104 	int irq, ret;
105 
106 	if (usb_disabled())
107 		return -ENODEV;
108 
109 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
110 	if (!res) {
111 		dev_err(&pdev->dev,
112 			"Found HC with no register addr. Check %s setup!\n",
113 			dev_name(&pdev->dev));
114 		ret = -ENODEV;
115 		goto fail_create_hcd;
116 	}
117 
118 	irq = platform_get_irq(pdev, 0);
119 	if (irq <= 0) {
120 		dev_err(&pdev->dev,
121 			"Found HC with no IRQ. Check %s setup!\n",
122 			dev_name(&pdev->dev));
123 		ret = -ENODEV;
124 		goto fail_create_hcd;
125 	}
126 
127 	/* initialize hcd */
128 	hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev,
129 			     dev_name(&pdev->dev));
130 	if (!hcd) {
131 		ret = -ENOMEM;
132 		goto fail_create_hcd;
133 	}
134 
135 	hcd->rsrc_start = res->start;
136 	hcd->rsrc_len = resource_size(res);
137 
138 	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
139 				driver->description)) {
140 		dev_dbg(&pdev->dev, "controller already in use\n");
141 		ret = -EBUSY;
142 		goto fail_request_resource;
143 	}
144 
145 	hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
146 	if (hcd->regs == NULL) {
147 		dev_dbg(&pdev->dev, "error mapping memory\n");
148 		ret = -ENXIO;
149 		goto fail_ioremap;
150 	}
151 
152 	priv = kmalloc(sizeof(struct ehci_sh_priv), GFP_KERNEL);
153 	if (!priv) {
154 		dev_dbg(&pdev->dev, "error allocating priv data\n");
155 		ret = -ENOMEM;
156 		goto fail_alloc;
157 	}
158 
159 	/* These are optional, we don't care if they fail */
160 	priv->fclk = clk_get(&pdev->dev, "usb_fck");
161 	if (IS_ERR(priv->fclk))
162 		priv->fclk = NULL;
163 
164 	priv->iclk = clk_get(&pdev->dev, "usb_ick");
165 	if (IS_ERR(priv->iclk))
166 		priv->iclk = NULL;
167 
168 	clk_enable(priv->fclk);
169 	clk_enable(priv->iclk);
170 
171 	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
172 	if (ret != 0) {
173 		dev_err(&pdev->dev, "Failed to add hcd");
174 		goto fail_add_hcd;
175 	}
176 
177 	priv->hcd = hcd;
178 	platform_set_drvdata(pdev, priv);
179 
180 	return ret;
181 
182 fail_add_hcd:
183 	clk_disable(priv->iclk);
184 	clk_disable(priv->fclk);
185 
186 	clk_put(priv->iclk);
187 	clk_put(priv->fclk);
188 
189 	kfree(priv);
190 fail_alloc:
191 	iounmap(hcd->regs);
192 fail_ioremap:
193 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
194 fail_request_resource:
195 	usb_put_hcd(hcd);
196 fail_create_hcd:
197 	dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
198 
199 	return ret;
200 }
201 
ehci_hcd_sh_remove(struct platform_device * pdev)202 static int __exit ehci_hcd_sh_remove(struct platform_device *pdev)
203 {
204 	struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
205 	struct usb_hcd *hcd = priv->hcd;
206 
207 	usb_remove_hcd(hcd);
208 	iounmap(hcd->regs);
209 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
210 	usb_put_hcd(hcd);
211 	platform_set_drvdata(pdev, NULL);
212 
213 	clk_disable(priv->fclk);
214 	clk_disable(priv->iclk);
215 
216 	clk_put(priv->fclk);
217 	clk_put(priv->iclk);
218 
219 	kfree(priv);
220 
221 	return 0;
222 }
223 
ehci_hcd_sh_shutdown(struct platform_device * pdev)224 static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
225 {
226 	struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
227 	struct usb_hcd *hcd = priv->hcd;
228 
229 	if (hcd->driver->shutdown)
230 		hcd->driver->shutdown(hcd);
231 }
232 
233 static struct platform_driver ehci_hcd_sh_driver = {
234 	.probe		= ehci_hcd_sh_probe,
235 	.remove		= __exit_p(ehci_hcd_sh_remove),
236 	.shutdown	= ehci_hcd_sh_shutdown,
237 	.driver		= {
238 		.name	= "sh_ehci",
239 		.owner	= THIS_MODULE,
240 	},
241 };
242 
243 MODULE_ALIAS("platform:sh_ehci");
244