• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 0;
86 
87 	switch (typeReq) {
88 	case SetPortFeature:
89 		if (wValue == USB_PORT_FEAT_POWER)
90 			ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
91 		break;
92 	case ClearPortFeature:
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 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 	irq = platform_get_irq(pdev, 0);
157 	if (irq < 0) {
158 		dev_err(&pdev->dev, "Failed to get platform irq\n");
159 		return irq;
160 	}
161 
162 	hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
163 	if (!hcd) {
164 		dev_err(&pdev->dev, "Failed to create hcd.\n");
165 		return -ENOMEM;
166 	}
167 
168 	jz4740_ohci = hcd_to_jz4740_hcd(hcd);
169 
170 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
171 	hcd->regs = devm_ioremap_resource(&pdev->dev, res);
172 	if (IS_ERR(hcd->regs)) {
173 		ret = PTR_ERR(hcd->regs);
174 		goto err_free;
175 	}
176 	hcd->rsrc_start = res->start;
177 	hcd->rsrc_len = resource_size(res);
178 
179 	jz4740_ohci->clk = devm_clk_get(&pdev->dev, "uhc");
180 	if (IS_ERR(jz4740_ohci->clk)) {
181 		ret = PTR_ERR(jz4740_ohci->clk);
182 		dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
183 		goto err_free;
184 	}
185 
186 	jz4740_ohci->vbus = devm_regulator_get(&pdev->dev, "vbus");
187 	if (IS_ERR(jz4740_ohci->vbus))
188 		jz4740_ohci->vbus = NULL;
189 
190 
191 	clk_set_rate(jz4740_ohci->clk, 48000000);
192 	clk_enable(jz4740_ohci->clk);
193 	if (jz4740_ohci->vbus)
194 		ohci_jz4740_set_vbus_power(jz4740_ohci, true);
195 
196 	platform_set_drvdata(pdev, hcd);
197 
198 	ohci_hcd_init(hcd_to_ohci(hcd));
199 
200 	ret = usb_add_hcd(hcd, irq, 0);
201 	if (ret) {
202 		dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
203 		goto err_disable;
204 	}
205 	device_wakeup_enable(hcd->self.controller);
206 
207 	return 0;
208 
209 err_disable:
210 	if (jz4740_ohci->vbus)
211 		regulator_disable(jz4740_ohci->vbus);
212 	clk_disable(jz4740_ohci->clk);
213 
214 err_free:
215 	usb_put_hcd(hcd);
216 
217 	return ret;
218 }
219 
jz4740_ohci_remove(struct platform_device * pdev)220 static int jz4740_ohci_remove(struct platform_device *pdev)
221 {
222 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
223 	struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
224 
225 	usb_remove_hcd(hcd);
226 
227 	if (jz4740_ohci->vbus)
228 		regulator_disable(jz4740_ohci->vbus);
229 
230 	clk_disable(jz4740_ohci->clk);
231 
232 	usb_put_hcd(hcd);
233 
234 	return 0;
235 }
236 
237 static struct platform_driver ohci_hcd_jz4740_driver = {
238 	.probe = jz4740_ohci_probe,
239 	.remove = jz4740_ohci_remove,
240 	.driver = {
241 		.name = "jz4740-ohci",
242 	},
243 };
244 
245 MODULE_ALIAS("platform:jz4740-ohci");
246