1 /*
2  *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3  *
4  *  This program is free software; you can redistribute it and/or modify it
5  *  under  the terms of the GNU General  Public License as published by the
6  *  Free Software Foundation;  either version 2 of the License, or (at your
7  *  option) any later version.
8  *
9  *  You should have received a copy of the  GNU General Public License along
10  *  with this program; if not, write  to the Free Software Foundation, Inc.,
11  *  675 Mass Ave, Cambridge, MA 02139, USA.
12  *
13  */
14 
15 #include <linux/platform_device.h>
16 #include <linux/clk.h>
17 #include <linux/regulator/consumer.h>
18 
19 struct jz4740_ohci_hcd {
20 	struct ohci_hcd ohci_hcd;
21 
22 	struct regulator *vbus;
23 	bool vbus_enabled;
24 	struct clk *clk;
25 };
26 
hcd_to_jz4740_hcd(struct usb_hcd * hcd)27 static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd)
28 {
29 	return (struct jz4740_ohci_hcd *)(hcd->hcd_priv);
30 }
31 
jz4740_hcd_to_hcd(struct jz4740_ohci_hcd * jz4740_ohci)32 static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci)
33 {
34 	return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv);
35 }
36 
ohci_jz4740_start(struct usb_hcd * hcd)37 static int ohci_jz4740_start(struct usb_hcd *hcd)
38 {
39 	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
40 	int	ret;
41 
42 	ret = ohci_init(ohci);
43 	if (ret < 0)
44 		return ret;
45 
46 	ohci->num_ports = 1;
47 
48 	ret = ohci_run(ohci);
49 	if (ret < 0) {
50 		dev_err(hcd->self.controller, "Can not start %s",
51 			hcd->self.bus_name);
52 		ohci_stop(hcd);
53 		return ret;
54 	}
55 	return 0;
56 }
57 
ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd * jz4740_ohci,bool enabled)58 static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci,
59 	bool enabled)
60 {
61 	int ret = 0;
62 
63 	if (!jz4740_ohci->vbus)
64 		return 0;
65 
66 	if (enabled && !jz4740_ohci->vbus_enabled) {
67 		ret = regulator_enable(jz4740_ohci->vbus);
68 		if (ret)
69 			dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller,
70 				"Could not power vbus\n");
71 	} else if (!enabled && jz4740_ohci->vbus_enabled) {
72 		ret = regulator_disable(jz4740_ohci->vbus);
73 	}
74 
75 	if (ret == 0)
76 		jz4740_ohci->vbus_enabled = enabled;
77 
78 	return ret;
79 }
80 
ohci_jz4740_hub_control(struct usb_hcd * hcd,u16 typeReq,u16 wValue,u16 wIndex,char * buf,u16 wLength)81 static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
82 	u16 wIndex, char *buf, u16 wLength)
83 {
84 	struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
85 	int ret;
86 
87 	switch (typeReq) {
88 	case SetHubFeature:
89 		if (wValue == USB_PORT_FEAT_POWER)
90 			ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
91 		break;
92 	case ClearHubFeature:
93 		if (wValue == USB_PORT_FEAT_POWER)
94 			ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false);
95 		break;
96 	}
97 
98 	if (ret)
99 		return ret;
100 
101 	return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
102 }
103 
104 
105 static const struct hc_driver ohci_jz4740_hc_driver = {
106 	.description =		hcd_name,
107 	.product_desc =		"JZ4740 OHCI",
108 	.hcd_priv_size =	sizeof(struct jz4740_ohci_hcd),
109 
110 	/*
111 	 * generic hardware linkage
112 	 */
113 	.irq =			ohci_irq,
114 	.flags =		HCD_USB11 | HCD_MEMORY,
115 
116 	/*
117 	 * basic lifecycle operations
118 	 */
119 	.start =		ohci_jz4740_start,
120 	.stop =			ohci_stop,
121 	.shutdown =		ohci_shutdown,
122 
123 	/*
124 	 * managing i/o requests and associated device resources
125 	 */
126 	.urb_enqueue =		ohci_urb_enqueue,
127 	.urb_dequeue =		ohci_urb_dequeue,
128 	.endpoint_disable =	ohci_endpoint_disable,
129 
130 	/*
131 	 * scheduling support
132 	 */
133 	.get_frame_number =	ohci_get_frame,
134 
135 	/*
136 	 * root hub support
137 	 */
138 	.hub_status_data =	ohci_hub_status_data,
139 	.hub_control =		ohci_jz4740_hub_control,
140 #ifdef	CONFIG_PM
141 	.bus_suspend =		ohci_bus_suspend,
142 	.bus_resume =		ohci_bus_resume,
143 #endif
144 	.start_port_reset =	ohci_start_port_reset,
145 };
146 
147 
jz4740_ohci_probe(struct platform_device * pdev)148 static __devinit int jz4740_ohci_probe(struct platform_device *pdev)
149 {
150 	int ret;
151 	struct usb_hcd *hcd;
152 	struct jz4740_ohci_hcd *jz4740_ohci;
153 	struct resource *res;
154 	int irq;
155 
156 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
157 
158 	if (!res) {
159 		dev_err(&pdev->dev, "Failed to get platform resource\n");
160 		return -ENOENT;
161 	}
162 
163 	irq = platform_get_irq(pdev, 0);
164 	if (irq < 0) {
165 		dev_err(&pdev->dev, "Failed to get platform irq\n");
166 		return irq;
167 	}
168 
169 	hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
170 	if (!hcd) {
171 		dev_err(&pdev->dev, "Failed to create hcd.\n");
172 		return -ENOMEM;
173 	}
174 
175 	jz4740_ohci = hcd_to_jz4740_hcd(hcd);
176 
177 	res = request_mem_region(res->start, resource_size(res), hcd_name);
178 	if (!res) {
179 		dev_err(&pdev->dev, "Failed to request mem region.\n");
180 		ret = -EBUSY;
181 		goto err_free;
182 	}
183 
184 	hcd->rsrc_start = res->start;
185 	hcd->rsrc_len = resource_size(res);
186 	hcd->regs = ioremap(res->start, resource_size(res));
187 
188 	if (!hcd->regs) {
189 		dev_err(&pdev->dev, "Failed to ioremap registers.\n");
190 		ret = -EBUSY;
191 		goto err_release_mem;
192 	}
193 
194 	jz4740_ohci->clk = clk_get(&pdev->dev, "uhc");
195 	if (IS_ERR(jz4740_ohci->clk)) {
196 		ret = PTR_ERR(jz4740_ohci->clk);
197 		dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
198 		goto err_iounmap;
199 	}
200 
201 	jz4740_ohci->vbus = regulator_get(&pdev->dev, "vbus");
202 	if (IS_ERR(jz4740_ohci->vbus))
203 		jz4740_ohci->vbus = NULL;
204 
205 
206 	clk_set_rate(jz4740_ohci->clk, 48000000);
207 	clk_enable(jz4740_ohci->clk);
208 	if (jz4740_ohci->vbus)
209 		ohci_jz4740_set_vbus_power(jz4740_ohci, true);
210 
211 	platform_set_drvdata(pdev, hcd);
212 
213 	ohci_hcd_init(hcd_to_ohci(hcd));
214 
215 	ret = usb_add_hcd(hcd, irq, 0);
216 	if (ret) {
217 		dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
218 		goto err_disable;
219 	}
220 
221 	return 0;
222 
223 err_disable:
224 	platform_set_drvdata(pdev, NULL);
225 	if (jz4740_ohci->vbus) {
226 		regulator_disable(jz4740_ohci->vbus);
227 		regulator_put(jz4740_ohci->vbus);
228 	}
229 	clk_disable(jz4740_ohci->clk);
230 
231 	clk_put(jz4740_ohci->clk);
232 err_iounmap:
233 	iounmap(hcd->regs);
234 err_release_mem:
235 	release_mem_region(res->start, resource_size(res));
236 err_free:
237 	usb_put_hcd(hcd);
238 
239 	return ret;
240 }
241 
jz4740_ohci_remove(struct platform_device * pdev)242 static __devexit int jz4740_ohci_remove(struct platform_device *pdev)
243 {
244 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
245 	struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
246 
247 	usb_remove_hcd(hcd);
248 
249 	platform_set_drvdata(pdev, NULL);
250 
251 	if (jz4740_ohci->vbus) {
252 		regulator_disable(jz4740_ohci->vbus);
253 		regulator_put(jz4740_ohci->vbus);
254 	}
255 
256 	clk_disable(jz4740_ohci->clk);
257 	clk_put(jz4740_ohci->clk);
258 
259 	iounmap(hcd->regs);
260 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
261 
262 	usb_put_hcd(hcd);
263 
264 	return 0;
265 }
266 
267 static struct platform_driver ohci_hcd_jz4740_driver = {
268 	.probe = jz4740_ohci_probe,
269 	.remove = __devexit_p(jz4740_ohci_remove),
270 	.driver = {
271 		.name = "jz4740-ohci",
272 		.owner = THIS_MODULE,
273 	},
274 };
275 
276 MODULE_ALIAS("platform:jz4740-ohci");
277