]>
Commit | Line | Data |
---|---|---|
eb81955b | 1 | #include <common.h> |
7a342785 | 2 | #include <watchdog.h> |
246e3b87 | 3 | #ifdef CONFIG_ARCH_SUNXI |
2aacc423 | 4 | #include <asm/arch/usb_phy.h> |
246e3b87 | 5 | #endif |
eb81955b IY |
6 | #include <asm/errno.h> |
7 | #include <linux/usb/ch9.h> | |
8 | #include <linux/usb/gadget.h> | |
9 | ||
eb81955b IY |
10 | #include <usb.h> |
11 | #include "linux-compat.h" | |
12 | #include "usb-compat.h" | |
13 | #include "musb_core.h" | |
14 | #include "musb_host.h" | |
15 | #include "musb_gadget.h" | |
16 | ||
17 | #ifdef CONFIG_MUSB_HOST | |
904f2a83 HG |
18 | struct int_queue { |
19 | struct usb_host_endpoint hep; | |
20 | struct urb urb; | |
21 | }; | |
22 | ||
eb81955b IY |
23 | static struct musb *host; |
24 | static struct usb_hcd hcd; | |
25 | static enum usb_device_speed host_speed; | |
26 | ||
27 | static void musb_host_complete_urb(struct urb *urb) | |
28 | { | |
29 | urb->dev->status &= ~USB_ST_NOT_PROC; | |
30 | urb->dev->act_len = urb->actual_length; | |
31 | } | |
32 | ||
33 | static struct usb_host_endpoint hep; | |
34 | static struct urb urb; | |
35 | ||
accf04c2 HG |
36 | static void construct_urb(struct urb *urb, struct usb_host_endpoint *hep, |
37 | struct usb_device *dev, int endpoint_type, | |
38 | unsigned long pipe, void *buffer, int len, | |
39 | struct devrequest *setup, int interval) | |
eb81955b IY |
40 | { |
41 | int epnum = usb_pipeendpoint(pipe); | |
42 | int is_in = usb_pipein(pipe); | |
43 | ||
accf04c2 HG |
44 | memset(urb, 0, sizeof(struct urb)); |
45 | memset(hep, 0, sizeof(struct usb_host_endpoint)); | |
46 | INIT_LIST_HEAD(&hep->urb_list); | |
47 | INIT_LIST_HEAD(&urb->urb_list); | |
48 | urb->ep = hep; | |
49 | urb->complete = musb_host_complete_urb; | |
50 | urb->status = -EINPROGRESS; | |
51 | urb->dev = dev; | |
52 | urb->pipe = pipe; | |
53 | urb->transfer_buffer = buffer; | |
54 | urb->transfer_dma = (unsigned long)buffer; | |
55 | urb->transfer_buffer_length = len; | |
56 | urb->setup_packet = (unsigned char *)setup; | |
57 | ||
58 | urb->ep->desc.wMaxPacketSize = | |
eb81955b IY |
59 | __cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] : |
60 | dev->epmaxpacketout[epnum]); | |
accf04c2 HG |
61 | urb->ep->desc.bmAttributes = endpoint_type; |
62 | urb->ep->desc.bEndpointAddress = | |
eb81955b | 63 | (is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum; |
accf04c2 | 64 | urb->ep->desc.bInterval = interval; |
eb81955b IY |
65 | } |
66 | ||
eb81955b IY |
67 | static int submit_urb(struct usb_hcd *hcd, struct urb *urb) |
68 | { | |
69 | struct musb *host = hcd->hcd_priv; | |
70 | int ret; | |
dc9a3912 | 71 | unsigned long timeout; |
eb81955b IY |
72 | |
73 | ret = musb_urb_enqueue(hcd, urb, 0); | |
74 | if (ret < 0) { | |
75 | printf("Failed to enqueue URB to controller\n"); | |
76 | return ret; | |
77 | } | |
78 | ||
dc9a3912 | 79 | timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe); |
eb81955b IY |
80 | do { |
81 | if (ctrlc()) | |
82 | return -EIO; | |
83 | host->isr(0, host); | |
e8672e3f | 84 | } while (urb->status == -EINPROGRESS && |
dc9a3912 | 85 | get_timer(0) < timeout); |
eb81955b | 86 | |
b918a0c6 HG |
87 | if (urb->status == -EINPROGRESS) |
88 | musb_urb_dequeue(hcd, urb, -ETIME); | |
89 | ||
eb81955b IY |
90 | return urb->status; |
91 | } | |
92 | ||
93 | int submit_control_msg(struct usb_device *dev, unsigned long pipe, | |
94 | void *buffer, int len, struct devrequest *setup) | |
95 | { | |
accf04c2 HG |
96 | construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_CONTROL, pipe, |
97 | buffer, len, setup, 0); | |
eb81955b IY |
98 | |
99 | /* Fix speed for non hub-attached devices */ | |
100 | if (!dev->parent) | |
101 | dev->speed = host_speed; | |
102 | ||
accf04c2 | 103 | return submit_urb(&hcd, &urb); |
eb81955b IY |
104 | } |
105 | ||
106 | ||
107 | int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, | |
108 | void *buffer, int len) | |
109 | { | |
accf04c2 HG |
110 | construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_BULK, pipe, |
111 | buffer, len, NULL, 0); | |
112 | return submit_urb(&hcd, &urb); | |
eb81955b IY |
113 | } |
114 | ||
115 | int submit_int_msg(struct usb_device *dev, unsigned long pipe, | |
116 | void *buffer, int len, int interval) | |
117 | { | |
accf04c2 HG |
118 | construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_INT, pipe, |
119 | buffer, len, NULL, interval); | |
120 | return submit_urb(&hcd, &urb); | |
eb81955b IY |
121 | } |
122 | ||
904f2a83 HG |
123 | struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, |
124 | int queuesize, int elementsize, void *buffer, int interval) | |
125 | { | |
126 | struct int_queue *queue; | |
127 | int ret, index = usb_pipein(pipe) * 16 + usb_pipeendpoint(pipe); | |
128 | ||
129 | if (queuesize != 1) { | |
130 | printf("ERROR musb int-queues only support queuesize 1\n"); | |
131 | return NULL; | |
132 | } | |
133 | ||
134 | if (dev->int_pending & (1 << index)) { | |
135 | printf("ERROR int-urb is already pending on pipe %lx\n", pipe); | |
136 | return NULL; | |
137 | } | |
138 | ||
139 | queue = malloc(sizeof(*queue)); | |
140 | if (!queue) | |
141 | return NULL; | |
142 | ||
143 | construct_urb(&queue->urb, &queue->hep, dev, USB_ENDPOINT_XFER_INT, | |
144 | pipe, buffer, elementsize, NULL, interval); | |
145 | ||
146 | ret = musb_urb_enqueue(&hcd, &queue->urb, 0); | |
147 | if (ret < 0) { | |
148 | printf("Failed to enqueue URB to controller\n"); | |
149 | free(queue); | |
150 | return NULL; | |
151 | } | |
152 | ||
153 | dev->int_pending |= 1 << index; | |
154 | return queue; | |
155 | } | |
156 | ||
157 | int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) | |
158 | { | |
159 | int index = usb_pipein(queue->urb.pipe) * 16 + | |
160 | usb_pipeendpoint(queue->urb.pipe); | |
161 | ||
162 | if (queue->urb.status == -EINPROGRESS) | |
163 | musb_urb_dequeue(&hcd, &queue->urb, -ETIME); | |
164 | ||
165 | dev->int_pending &= ~(1 << index); | |
166 | free(queue); | |
167 | return 0; | |
168 | } | |
169 | ||
170 | void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) | |
171 | { | |
172 | if (queue->urb.status != -EINPROGRESS) | |
173 | return NULL; /* URB has already completed in a prev. poll */ | |
174 | ||
175 | host->isr(0, host); | |
176 | ||
177 | if (queue->urb.status != -EINPROGRESS) | |
178 | return queue->urb.transfer_buffer; /* Done */ | |
179 | ||
180 | return NULL; /* URB still pending */ | |
181 | } | |
182 | ||
8802f563 | 183 | int usb_reset_root_port(struct usb_device *dev) |
eb81955b | 184 | { |
90cdc103 | 185 | void *mbase = host->mregs; |
eb81955b | 186 | u8 power; |
90cdc103 HG |
187 | |
188 | power = musb_readb(mbase, MUSB_POWER); | |
189 | power &= 0xf0; | |
190 | musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); | |
191 | mdelay(50); | |
246e3b87 HG |
192 | #ifdef CONFIG_ARCH_SUNXI |
193 | /* | |
194 | * sunxi phy has a bug and it will wrongly detect high speed squelch | |
195 | * when clearing reset on low-speed devices, temporary disable | |
196 | * squelch detection to work around this. | |
197 | */ | |
7b798658 | 198 | sunxi_usb_phy_enable_squelch_detect(0, 0); |
246e3b87 | 199 | #endif |
90cdc103 HG |
200 | power = musb_readb(mbase, MUSB_POWER); |
201 | musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); | |
246e3b87 | 202 | #ifdef CONFIG_ARCH_SUNXI |
7b798658 | 203 | sunxi_usb_phy_enable_squelch_detect(0, 1); |
246e3b87 | 204 | #endif |
90cdc103 HG |
205 | host->isr(0, host); |
206 | host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? | |
207 | USB_SPEED_HIGH : | |
208 | (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? | |
209 | USB_SPEED_FULL : USB_SPEED_LOW; | |
210 | mdelay((host_speed == USB_SPEED_LOW) ? 200 : 50); | |
de31213f SG |
211 | |
212 | return 0; | |
90cdc103 HG |
213 | } |
214 | ||
215 | int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) | |
216 | { | |
eb81955b | 217 | void *mbase; |
dc9a3912 HG |
218 | /* USB spec says it may take up to 1 second for a device to connect */ |
219 | unsigned long timeout = get_timer(0) + 1000; | |
eb81955b IY |
220 | |
221 | if (!host) { | |
222 | printf("MUSB host is not registered\n"); | |
223 | return -ENODEV; | |
224 | } | |
225 | ||
226 | musb_start(host); | |
227 | mbase = host->mregs; | |
228 | do { | |
229 | if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM) | |
230 | break; | |
dc9a3912 HG |
231 | } while (get_timer(0) < timeout); |
232 | if (get_timer(0) >= timeout) | |
eb81955b IY |
233 | return -ENODEV; |
234 | ||
8802f563 | 235 | usb_reset_root_port(NULL); |
eb81955b IY |
236 | host->is_active = 1; |
237 | hcd.hcd_priv = host; | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
242 | int usb_lowlevel_stop(int index) | |
243 | { | |
244 | if (!host) { | |
245 | printf("MUSB host is not registered\n"); | |
246 | return -ENODEV; | |
247 | } | |
248 | ||
249 | musb_stop(host); | |
250 | return 0; | |
251 | } | |
252 | #endif /* CONFIG_MUSB_HOST */ | |
253 | ||
254 | #ifdef CONFIG_MUSB_GADGET | |
255 | static struct musb *gadget; | |
256 | ||
2d48aa69 | 257 | int usb_gadget_handle_interrupts(int index) |
eb81955b | 258 | { |
7a342785 | 259 | WATCHDOG_RESET(); |
eb81955b IY |
260 | if (!gadget || !gadget->isr) |
261 | return -EINVAL; | |
262 | ||
263 | return gadget->isr(0, gadget); | |
264 | } | |
265 | ||
266 | int usb_gadget_register_driver(struct usb_gadget_driver *driver) | |
267 | { | |
268 | int ret; | |
269 | ||
76b09b85 | 270 | if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind || |
eb81955b IY |
271 | !driver->setup) { |
272 | printf("bad parameter.\n"); | |
273 | return -EINVAL; | |
274 | } | |
275 | ||
276 | if (!gadget) { | |
277 | printf("Controller uninitialized\n"); | |
278 | return -ENXIO; | |
279 | } | |
280 | ||
281 | ret = musb_gadget_start(&gadget->g, driver); | |
282 | if (ret < 0) { | |
283 | printf("gadget_start failed with %d\n", ret); | |
284 | return ret; | |
285 | } | |
286 | ||
287 | ret = driver->bind(&gadget->g); | |
288 | if (ret < 0) { | |
289 | printf("bind failed with %d\n", ret); | |
290 | return ret; | |
291 | } | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
296 | int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) | |
297 | { | |
078d7302 RH |
298 | if (driver->disconnect) |
299 | driver->disconnect(&gadget->g); | |
300 | if (driver->unbind) | |
301 | driver->unbind(&gadget->g); | |
eb81955b IY |
302 | return 0; |
303 | } | |
304 | #endif /* CONFIG_MUSB_GADGET */ | |
305 | ||
306 | int musb_register(struct musb_hdrc_platform_data *plat, void *bdata, | |
307 | void *ctl_regs) | |
308 | { | |
309 | struct musb **musbp; | |
310 | ||
311 | switch (plat->mode) { | |
312 | #ifdef CONFIG_MUSB_HOST | |
313 | case MUSB_HOST: | |
314 | musbp = &host; | |
315 | break; | |
316 | #endif | |
317 | #ifdef CONFIG_MUSB_GADGET | |
318 | case MUSB_PERIPHERAL: | |
319 | musbp = &gadget; | |
320 | break; | |
321 | #endif | |
322 | default: | |
323 | return -EINVAL; | |
324 | } | |
325 | ||
326 | *musbp = musb_init_controller(plat, (struct device *)bdata, ctl_regs); | |
327 | if (!musbp) { | |
328 | printf("Failed to init the controller\n"); | |
329 | return -EIO; | |
330 | } | |
331 | ||
332 | return 0; | |
333 | } |