]>
Commit | Line | Data |
---|---|---|
16c8d5e7 WD |
1 | /* |
2 | * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit | |
386eda02 | 3 | * bodonoghue@CodeHermit.ie |
16c8d5e7 WD |
4 | * |
5 | * References | |
7817cb20 MZ |
6 | * DasUBoot/drivers/usb/usbdcore_omap1510.c, for design and implementation |
7 | * ideas. | |
16c8d5e7 WD |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
386eda02 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16c8d5e7 WD |
17 | * GNU General Public License for more details. |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the | |
21 | * Free Software Foundation, Inc., | |
22 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
23 | * | |
24 | */ | |
25 | ||
26 | /* | |
27 | * Notes : | |
386eda02 | 28 | * 1. #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX |
16c8d5e7 WD |
29 | * packet to force the USB re-transmit protocol. |
30 | * | |
31 | * 2. #define __DEBUG_UDC__ to switch on debug tracing to serial console | |
386eda02 WD |
32 | * be careful that tracing doesn't create Hiesen-bugs with respect to |
33 | * response timeouts to control requests. | |
16c8d5e7 WD |
34 | * |
35 | * 3. This driver should be able to support any higher level driver that | |
36 | * that wants to do either of the two standard UDC implementations | |
37 | * Control-Bulk-Interrupt or Bulk-IN/Bulk-Out standards. Hence | |
38 | * gserial and cdc_acm should work with this code. | |
39 | * | |
40 | * 4. NAK events never actually get raised at all, the documentation | |
41 | * is just wrong ! | |
42 | * | |
43 | * 5. For some reason, cbd_datlen is *always* +2 the value it should be. | |
44 | * this means that having an RX cbd of 16 bytes is not possible, since | |
386eda02 | 45 | * the same size is reported for 14 bytes received as 16 bytes received |
16c8d5e7 WD |
46 | * until we can find out why this happens, RX cbds must be limited to 8 |
47 | * bytes. TODO: check errata for this behaviour. | |
48 | * | |
49 | * 6. Right now this code doesn't support properly powering up with the USB | |
386eda02 WD |
50 | * cable attached to the USB host my development board the Adder87x doesn't |
51 | * have a pull-up fitted to allow this, so it is necessary to power the | |
52 | * board and *then* attached the USB cable to the host. However somebody | |
53 | * with a different design in their board may be able to keep the cable | |
54 | * constantly connected and simply enable/disable a pull-up re | |
55 | * figure 31.1 in MPC885RM.pdf instead of having to power up the board and | |
56 | * then attach the cable ! | |
16c8d5e7 WD |
57 | * |
58 | */ | |
59 | #include <common.h> | |
60 | #include <config.h> | |
61 | ||
62 | #if defined(CONFIG_MPC885_FAMILY) && defined(CONFIG_USB_DEVICE) | |
63 | #include <commproc.h> | |
386eda02 | 64 | #include "usbdcore.h" |
16c8d5e7 WD |
65 | #include "usbdcore_mpc8xx.h" |
66 | #include "usbdcore_ep0.h" | |
67 | ||
1218abf1 WD |
68 | DECLARE_GLOBAL_DATA_PTR; |
69 | ||
16c8d5e7 WD |
70 | #define ERR(fmt, args...)\ |
71 | serial_printf("ERROR : [%s] %s:%d: "fmt,\ | |
72 | __FILE__,__FUNCTION__,__LINE__, ##args) | |
73 | #ifdef __DEBUG_UDC__ | |
386eda02 | 74 | #define DBG(fmt,args...)\ |
16c8d5e7 WD |
75 | serial_printf("[%s] %s:%d: "fmt,\ |
76 | __FILE__,__FUNCTION__,__LINE__, ##args) | |
77 | #else | |
386eda02 | 78 | #define DBG(fmt,args...) |
16c8d5e7 WD |
79 | #endif |
80 | ||
81 | /* Static Data */ | |
82 | #ifdef __SIMULATE_ERROR__ | |
386eda02 | 83 | static char err_poison_test = 0; |
16c8d5e7 WD |
84 | #endif |
85 | static struct mpc8xx_ep ep_ref[MAX_ENDPOINTS]; | |
86 | static u32 address_base = STATE_NOT_READY; | |
87 | static mpc8xx_udc_state_t udc_state = 0; | |
88 | static struct usb_device_instance *udc_device = 0; | |
386eda02 WD |
89 | static volatile usb_epb_t *endpoints[MAX_ENDPOINTS]; |
90 | static volatile cbd_t *tx_cbd[TX_RING_SIZE]; | |
91 | static volatile cbd_t *rx_cbd[RX_RING_SIZE]; | |
16c8d5e7 WD |
92 | static volatile immap_t *immr = 0; |
93 | static volatile cpm8xx_t *cp = 0; | |
94 | static volatile usb_pram_t *usb_paramp = 0; | |
95 | static volatile usb_t *usbp = 0; | |
96 | static int rx_ct = 0; | |
97 | static int tx_ct = 0; | |
98 | ||
99 | /* Static Function Declarations */ | |
100 | static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, | |
386eda02 | 101 | usb_device_state_t final); |
16c8d5e7 | 102 | static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, |
386eda02 | 103 | usb_device_state_t final); |
16c8d5e7 | 104 | static void mpc8xx_udc_stall (unsigned int ep); |
386eda02 WD |
105 | static void mpc8xx_udc_flush_tx_fifo (int epid); |
106 | static void mpc8xx_udc_flush_rx_fifo (void); | |
16c8d5e7 | 107 | static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp); |
386eda02 WD |
108 | static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi, |
109 | struct urb *tx_urb); | |
110 | static void mpc8xx_udc_dump_request (struct usb_device_request *request); | |
111 | static void mpc8xx_udc_clock_init (volatile immap_t * immr, | |
112 | volatile cpm8xx_t * cp); | |
16c8d5e7 WD |
113 | static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi); |
114 | static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp); | |
386eda02 | 115 | static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp); |
16c8d5e7 WD |
116 | static void mpc8xx_udc_cbd_init (void); |
117 | static void mpc8xx_udc_endpoint_init (void); | |
118 | static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size); | |
119 | static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment); | |
120 | static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp); | |
121 | static void mpc8xx_udc_set_nak (unsigned int ep); | |
386eda02 WD |
122 | static short mpc8xx_udc_handle_txerr (void); |
123 | static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid); | |
16c8d5e7 WD |
124 | |
125 | /****************************************************************************** | |
386eda02 | 126 | Global Linkage |
16c8d5e7 WD |
127 | *****************************************************************************/ |
128 | ||
129 | /* udc_init | |
130 | * | |
131 | * Do initial bus gluing | |
132 | */ | |
386eda02 | 133 | int udc_init (void) |
16c8d5e7 WD |
134 | { |
135 | /* Init various pointers */ | |
6d0f6bcf | 136 | immr = (immap_t *) CONFIG_SYS_IMMR; |
386eda02 WD |
137 | cp = (cpm8xx_t *) & (immr->im_cpm); |
138 | usb_paramp = (usb_pram_t *) & (cp->cp_dparam[PROFF_USB]); | |
139 | usbp = (usb_t *) & (cp->cp_scc[0]); | |
140 | ||
141 | memset (ep_ref, 0x00, (sizeof (struct mpc8xx_ep) * MAX_ENDPOINTS)); | |
16c8d5e7 | 142 | |
16c8d5e7 WD |
143 | udc_device = 0; |
144 | udc_state = STATE_NOT_READY; | |
386eda02 WD |
145 | |
146 | usbp->usmod = 0x00; | |
147 | usbp->uscom = 0; | |
148 | ||
16c8d5e7 WD |
149 | /* Set USB Frame #0, Respond at Address & Get a clock source */ |
150 | usbp->usaddr = 0x00; | |
151 | mpc8xx_udc_clock_init (immr, cp); | |
386eda02 | 152 | |
16c8d5e7 | 153 | /* PA15, PA14 as perhiperal USBRXD and USBOE */ |
386eda02 WD |
154 | immr->im_ioport.iop_padir &= ~0x0003; |
155 | immr->im_ioport.iop_papar |= 0x0003; | |
156 | ||
16c8d5e7 | 157 | /* PC11/PC10 as peripheral USBRXP USBRXN */ |
386eda02 WD |
158 | immr->im_ioport.iop_pcso |= 0x0030; |
159 | ||
16c8d5e7 | 160 | /* PC7/PC6 as perhiperal USBTXP and USBTXN */ |
386eda02 WD |
161 | immr->im_ioport.iop_pcdir |= 0x0300; |
162 | immr->im_ioport.iop_pcpar |= 0x0300; | |
163 | ||
16c8d5e7 | 164 | /* Set the base address */ |
386eda02 | 165 | address_base = (u32) (cp->cp_dpmem + CPM_USB_BASE); |
16c8d5e7 WD |
166 | |
167 | /* Initialise endpoints and circular buffers */ | |
386eda02 WD |
168 | mpc8xx_udc_endpoint_init (); |
169 | mpc8xx_udc_cbd_init (); | |
170 | ||
16c8d5e7 | 171 | /* Assign allocated Dual Port Endpoint descriptors */ |
386eda02 WD |
172 | usb_paramp->ep0ptr = (u32) endpoints[0]; |
173 | usb_paramp->ep1ptr = (u32) endpoints[1]; | |
174 | usb_paramp->ep2ptr = (u32) endpoints[2]; | |
175 | usb_paramp->ep3ptr = (u32) endpoints[3]; | |
16c8d5e7 WD |
176 | usb_paramp->frame_n = 0; |
177 | ||
386eda02 WD |
178 | DBG ("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n", |
179 | usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr, | |
180 | usb_paramp->ep3ptr); | |
181 | ||
16c8d5e7 WD |
182 | return 0; |
183 | } | |
184 | ||
185 | /* udc_irq | |
186 | * | |
187 | * Poll for whatever events may have occured | |
188 | */ | |
386eda02 | 189 | void udc_irq (void) |
16c8d5e7 WD |
190 | { |
191 | int epid = 0; | |
386eda02 WD |
192 | volatile cbd_t *rx_cbdp = 0; |
193 | volatile cbd_t *rx_cbdp_base = 0; | |
16c8d5e7 | 194 | |
386eda02 | 195 | if (udc_state != STATE_READY) { |
16c8d5e7 WD |
196 | return; |
197 | } | |
386eda02 WD |
198 | |
199 | if (usbp->usber & USB_E_BSY) { | |
16c8d5e7 | 200 | /* This shouldn't happen. If it does then it's a bug ! */ |
386eda02 WD |
201 | usbp->usber |= USB_E_BSY; |
202 | mpc8xx_udc_flush_rx_fifo (); | |
16c8d5e7 WD |
203 | } |
204 | ||
16c8d5e7 | 205 | /* Scan all RX/Bidirectional Endpoints for RX data. */ |
386eda02 WD |
206 | for (epid = 0; epid < MAX_ENDPOINTS; epid++) { |
207 | if (!ep_ref[epid].prx) { | |
16c8d5e7 WD |
208 | continue; |
209 | } | |
16c8d5e7 | 210 | rx_cbdp = rx_cbdp_base = ep_ref[epid].prx; |
386eda02 WD |
211 | |
212 | do { | |
213 | if (!(rx_cbdp->cbd_sc & RX_BD_E)) { | |
214 | ||
215 | if (rx_cbdp->cbd_sc & 0x1F) { | |
16c8d5e7 | 216 | /* Corrupt data discard it. |
386eda02 | 217 | * Controller has NAK'd this packet. |
16c8d5e7 | 218 | */ |
386eda02 | 219 | mpc8xx_udc_clear_rxbd (rx_cbdp); |
16c8d5e7 | 220 | |
386eda02 WD |
221 | } else { |
222 | if (!epid) { | |
223 | mpc8xx_udc_ep0_rx (rx_cbdp); | |
16c8d5e7 | 224 | |
386eda02 | 225 | } else { |
16c8d5e7 | 226 | /* Process data */ |
386eda02 WD |
227 | mpc8xx_udc_set_nak (epid); |
228 | mpc8xx_udc_epn_rx (epid, rx_cbdp); | |
229 | mpc8xx_udc_clear_rxbd (rx_cbdp); | |
230 | } | |
16c8d5e7 | 231 | } |
386eda02 | 232 | |
16c8d5e7 | 233 | /* Advance RX CBD pointer */ |
386eda02 | 234 | mpc8xx_udc_advance_rx (&rx_cbdp, epid); |
16c8d5e7 | 235 | ep_ref[epid].prx = rx_cbdp; |
386eda02 | 236 | } else { |
16c8d5e7 | 237 | /* Advance RX CBD pointer */ |
386eda02 | 238 | mpc8xx_udc_advance_rx (&rx_cbdp, epid); |
16c8d5e7 WD |
239 | } |
240 | ||
386eda02 | 241 | } while (rx_cbdp != rx_cbdp_base); |
16c8d5e7 WD |
242 | } |
243 | ||
244 | /* Handle TX events as appropiate, the correct place to do this is | |
245 | * in a tx routine. Perhaps TX on epn was pre-empted by ep0 | |
246 | */ | |
247 | ||
386eda02 WD |
248 | if (usbp->usber & USB_E_TXB) { |
249 | usbp->usber |= USB_E_TXB; | |
16c8d5e7 | 250 | } |
386eda02 WD |
251 | |
252 | if (usbp->usber & (USB_TX_ERRMASK)) { | |
253 | mpc8xx_udc_handle_txerr (); | |
16c8d5e7 WD |
254 | } |
255 | ||
256 | /* Switch to the default state, respond at the default address */ | |
386eda02 WD |
257 | if (usbp->usber & USB_E_RESET) { |
258 | usbp->usber |= USB_E_RESET; | |
259 | usbp->usaddr = 0x00; | |
16c8d5e7 WD |
260 | udc_device->device_state = STATE_DEFAULT; |
261 | } | |
262 | ||
386eda02 WD |
263 | /* if(usbp->usber&USB_E_IDLE){ |
264 | We could suspend here ! | |
265 | usbp->usber|=USB_E_IDLE; | |
266 | DBG("idle state change\n"); | |
267 | } | |
268 | if(usbp->usbs){ | |
269 | We could resume here when IDLE is deasserted ! | |
270 | Not worth doing, so long as we are self powered though. | |
271 | } | |
272 | */ | |
16c8d5e7 WD |
273 | |
274 | return; | |
275 | } | |
276 | ||
16c8d5e7 WD |
277 | /* udc_endpoint_write |
278 | * | |
279 | * Write some data to an endpoint | |
280 | */ | |
386eda02 | 281 | int udc_endpoint_write (struct usb_endpoint_instance *epi) |
16c8d5e7 WD |
282 | { |
283 | int ep = 0; | |
284 | short epid = 1, unnak = 0, ret = 0; | |
285 | ||
386eda02 WD |
286 | if (udc_state != STATE_READY) { |
287 | ERR ("invalid udc_state != STATE_READY!\n"); | |
16c8d5e7 WD |
288 | return -1; |
289 | } | |
290 | ||
386eda02 | 291 | if (!udc_device || !epi) { |
16c8d5e7 WD |
292 | return -1; |
293 | } | |
386eda02 WD |
294 | |
295 | if (udc_device->device_state != STATE_CONFIGURED) { | |
16c8d5e7 WD |
296 | return -1; |
297 | } | |
298 | ||
299 | ep = epi->endpoint_address & 0x03; | |
386eda02 | 300 | if (ep >= MAX_ENDPOINTS) { |
16c8d5e7 WD |
301 | return -1; |
302 | } | |
386eda02 | 303 | |
16c8d5e7 | 304 | /* Set NAK for all RX endpoints during TX */ |
386eda02 | 305 | for (epid = 1; epid < MAX_ENDPOINTS; epid++) { |
16c8d5e7 WD |
306 | |
307 | /* Don't set NAK on DATA IN/CONTROL endpoints */ | |
386eda02 | 308 | if (ep_ref[epid].sc & USB_DIR_IN) { |
16c8d5e7 WD |
309 | continue; |
310 | } | |
311 | ||
386eda02 WD |
312 | if (!(usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK))) { |
313 | unnak |= 1 << epid; | |
16c8d5e7 WD |
314 | } |
315 | ||
386eda02 | 316 | mpc8xx_udc_set_nak (epid); |
16c8d5e7 WD |
317 | } |
318 | ||
386eda02 WD |
319 | mpc8xx_udc_init_tx (&udc_device->bus->endpoint_array[ep], |
320 | epi->tx_urb); | |
321 | ret = mpc8xx_udc_ep_tx (&udc_device->bus->endpoint_array[ep]); | |
322 | ||
16c8d5e7 | 323 | /* Remove temporary NAK */ |
386eda02 WD |
324 | for (epid = 1; epid < MAX_ENDPOINTS; epid++) { |
325 | if (unnak & (1 << epid)) { | |
326 | udc_unset_nak (epid); | |
16c8d5e7 WD |
327 | } |
328 | } | |
386eda02 | 329 | |
16c8d5e7 WD |
330 | return ret; |
331 | } | |
332 | ||
333 | /* mpc8xx_udc_assign_urb | |
334 | * | |
335 | * Associate a given urb to an endpoint TX or RX transmit/receive buffers | |
336 | */ | |
386eda02 | 337 | static int mpc8xx_udc_assign_urb (int ep, char direction) |
16c8d5e7 WD |
338 | { |
339 | struct usb_endpoint_instance *epi = 0; | |
386eda02 WD |
340 | |
341 | if (ep >= MAX_ENDPOINTS) { | |
16c8d5e7 WD |
342 | goto err; |
343 | } | |
344 | epi = &udc_device->bus->endpoint_array[ep]; | |
386eda02 | 345 | if (!epi) { |
16c8d5e7 WD |
346 | goto err; |
347 | } | |
348 | ||
386eda02 WD |
349 | if (!ep_ref[ep].urb) { |
350 | ep_ref[ep].urb = usbd_alloc_urb (udc_device, udc_device->bus->endpoint_array); | |
351 | if (!ep_ref[ep].urb) { | |
16c8d5e7 WD |
352 | goto err; |
353 | } | |
386eda02 | 354 | } else { |
16c8d5e7 WD |
355 | ep_ref[ep].urb->actual_length = 0; |
356 | } | |
357 | ||
386eda02 WD |
358 | switch (direction) { |
359 | case USB_DIR_IN: | |
360 | epi->tx_urb = ep_ref[ep].urb; | |
361 | break; | |
362 | case USB_DIR_OUT: | |
363 | epi->rcv_urb = ep_ref[ep].urb; | |
364 | break; | |
365 | default: | |
366 | goto err; | |
16c8d5e7 WD |
367 | } |
368 | return 0; | |
369 | ||
386eda02 | 370 | err: |
16c8d5e7 WD |
371 | udc_state = STATE_ERROR; |
372 | return -1; | |
373 | } | |
374 | ||
375 | /* udc_setup_ep | |
376 | * | |
377 | * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram | |
378 | * Isochronous endpoints aren't yet supported! | |
379 | */ | |
386eda02 WD |
380 | void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, |
381 | struct usb_endpoint_instance *epi) | |
16c8d5e7 WD |
382 | { |
383 | uchar direction = 0; | |
384 | int ep_attrib = 0; | |
385 | ||
386eda02 | 386 | if (epi && (ep < MAX_ENDPOINTS)) { |
16c8d5e7 | 387 | |
386eda02 WD |
388 | if (ep == 0) { |
389 | if (epi->rcv_attributes != USB_ENDPOINT_XFER_CONTROL | |
390 | || epi->tx_attributes != | |
391 | USB_ENDPOINT_XFER_CONTROL) { | |
392 | ||
393 | /* ep0 must be a control endpoint */ | |
16c8d5e7 WD |
394 | udc_state = STATE_ERROR; |
395 | return; | |
396 | ||
397 | } | |
386eda02 WD |
398 | if (!(ep_ref[ep].sc & EP_ATTACHED)) { |
399 | mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize, | |
400 | epi->rcv_packetSize); | |
16c8d5e7 WD |
401 | } |
402 | usbp->usep[ep] = 0x0000; | |
403 | return; | |
404 | } | |
386eda02 WD |
405 | |
406 | if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK) | |
407 | == USB_DIR_IN) { | |
16c8d5e7 WD |
408 | |
409 | direction = 1; | |
410 | ep_attrib = epi->tx_attributes; | |
411 | epi->rcv_packetSize = 0; | |
386eda02 | 412 | ep_ref[ep].sc |= USB_DIR_IN; |
16c8d5e7 | 413 | } else { |
386eda02 | 414 | |
16c8d5e7 WD |
415 | direction = 0; |
416 | ep_attrib = epi->rcv_attributes; | |
386eda02 | 417 | epi->tx_packetSize = 0; |
16c8d5e7 WD |
418 | ep_ref[ep].sc &= ~USB_DIR_IN; |
419 | } | |
420 | ||
386eda02 WD |
421 | if (mpc8xx_udc_assign_urb (ep, epi->endpoint_address |
422 | & USB_ENDPOINT_DIR_MASK)) { | |
16c8d5e7 WD |
423 | return; |
424 | } | |
425 | ||
386eda02 WD |
426 | switch (ep_attrib) { |
427 | case USB_ENDPOINT_XFER_CONTROL: | |
428 | if (!(ep_ref[ep].sc & EP_ATTACHED)) { | |
429 | mpc8xx_udc_cbd_attach (ep, | |
430 | epi->tx_packetSize, | |
431 | epi->rcv_packetSize); | |
432 | } | |
433 | usbp->usep[ep] = ep << 12; | |
434 | epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb; | |
435 | ||
436 | break; | |
437 | case USB_ENDPOINT_XFER_BULK: | |
438 | case USB_ENDPOINT_XFER_INT: | |
439 | if (!(ep_ref[ep].sc & EP_ATTACHED)) { | |
440 | if (direction) { | |
16c8d5e7 | 441 | mpc8xx_udc_cbd_attach (ep, |
386eda02 WD |
442 | epi->tx_packetSize, |
443 | 0); | |
444 | } else { | |
445 | mpc8xx_udc_cbd_attach (ep, | |
446 | 0, | |
447 | epi->rcv_packetSize); | |
16c8d5e7 | 448 | } |
386eda02 WD |
449 | } |
450 | usbp->usep[ep] = (ep << 12) | ((ep_attrib) << 8); | |
16c8d5e7 | 451 | |
386eda02 WD |
452 | break; |
453 | case USB_ENDPOINT_XFER_ISOC: | |
454 | default: | |
455 | serial_printf ("Error endpoint attrib %d>3\n", ep_attrib); | |
456 | udc_state = STATE_ERROR; | |
457 | break; | |
16c8d5e7 WD |
458 | } |
459 | } | |
460 | ||
461 | } | |
462 | ||
463 | /* udc_connect | |
464 | * | |
465 | * Move state, switch on the USB | |
466 | */ | |
386eda02 | 467 | void udc_connect (void) |
16c8d5e7 | 468 | { |
386eda02 | 469 | /* Enable pull-up resistor on D+ |
16c8d5e7 WD |
470 | * TODO: fit a pull-up resistor to drive SE0 for > 2.5us |
471 | */ | |
386eda02 WD |
472 | |
473 | if (udc_state != STATE_ERROR) { | |
16c8d5e7 | 474 | udc_state = STATE_READY; |
386eda02 | 475 | usbp->usmod |= USMOD_EN; |
16c8d5e7 | 476 | } |
386eda02 | 477 | } |
16c8d5e7 WD |
478 | |
479 | /* udc_disconnect | |
480 | * | |
481 | * Disconnect is not used but, is included for completeness | |
482 | */ | |
386eda02 | 483 | void udc_disconnect (void) |
16c8d5e7 WD |
484 | { |
485 | /* Disable pull-up resistor on D- | |
486 | * TODO: fix a pullup resistor to control this | |
487 | */ | |
488 | ||
386eda02 | 489 | if (udc_state != STATE_ERROR) { |
16c8d5e7 WD |
490 | udc_state = STATE_NOT_READY; |
491 | } | |
386eda02 | 492 | usbp->usmod &= ~USMOD_EN; |
16c8d5e7 WD |
493 | } |
494 | ||
495 | /* udc_enable | |
386eda02 | 496 | * |
16c8d5e7 WD |
497 | * Grab an EP0 URB, register interest in a subset of USB events |
498 | */ | |
386eda02 | 499 | void udc_enable (struct usb_device_instance *device) |
16c8d5e7 | 500 | { |
386eda02 | 501 | if (udc_state == STATE_ERROR) { |
16c8d5e7 WD |
502 | return; |
503 | } | |
504 | ||
505 | udc_device = device; | |
386eda02 WD |
506 | |
507 | if (!ep_ref[0].urb) { | |
508 | ep_ref[0].urb = usbd_alloc_urb (device, device->bus->endpoint_array); | |
16c8d5e7 WD |
509 | } |
510 | ||
511 | /* Register interest in all events except SOF, enable transceiver */ | |
386eda02 WD |
512 | usbp->usber = 0x03FF; |
513 | usbp->usbmr = 0x02F7; | |
16c8d5e7 WD |
514 | |
515 | return; | |
516 | } | |
517 | ||
518 | /* udc_disable | |
519 | * | |
520 | * disable the currently hooked device | |
521 | */ | |
386eda02 | 522 | void udc_disable (void) |
16c8d5e7 WD |
523 | { |
524 | int i = 0; | |
525 | ||
386eda02 WD |
526 | if (udc_state == STATE_ERROR) { |
527 | DBG ("Won't disable UDC. udc_state==STATE_ERROR !\n"); | |
16c8d5e7 WD |
528 | return; |
529 | } | |
530 | ||
531 | udc_device = 0; | |
532 | ||
386eda02 WD |
533 | for (; i < MAX_ENDPOINTS; i++) { |
534 | if (ep_ref[i].urb) { | |
535 | usbd_dealloc_urb (ep_ref[i].urb); | |
16c8d5e7 WD |
536 | ep_ref[i].urb = 0; |
537 | } | |
538 | } | |
386eda02 WD |
539 | |
540 | usbp->usbmr = 0x00; | |
541 | usbp->usmod = ~USMOD_EN; | |
16c8d5e7 WD |
542 | udc_state = STATE_NOT_READY; |
543 | } | |
544 | ||
545 | /* udc_startup_events | |
546 | * | |
547 | * Enable the specified device | |
548 | */ | |
386eda02 | 549 | void udc_startup_events (struct usb_device_instance *device) |
16c8d5e7 | 550 | { |
386eda02 WD |
551 | udc_enable (device); |
552 | if (udc_state == STATE_READY) { | |
16c8d5e7 WD |
553 | usbd_device_event_irq (device, DEVICE_CREATE, 0); |
554 | } | |
555 | } | |
556 | ||
557 | /* udc_set_nak | |
386eda02 | 558 | * |
16c8d5e7 WD |
559 | * Allow upper layers to signal lower layers should not accept more RX data |
560 | * | |
561 | */ | |
386eda02 | 562 | void udc_set_nak (int epid) |
16c8d5e7 | 563 | { |
386eda02 WD |
564 | if (epid) { |
565 | mpc8xx_udc_set_nak (epid); | |
16c8d5e7 WD |
566 | } |
567 | } | |
568 | ||
386eda02 WD |
569 | /* udc_unset_nak |
570 | * | |
16c8d5e7 WD |
571 | * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint. |
572 | * Switch off NAKing on this endpoint to accept more data output from host. | |
573 | * | |
574 | */ | |
575 | void udc_unset_nak (int epid) | |
576 | { | |
386eda02 | 577 | if (epid > MAX_ENDPOINTS) { |
16c8d5e7 WD |
578 | return; |
579 | } | |
580 | ||
386eda02 WD |
581 | if (usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK)) { |
582 | usbp->usep[epid] &= ~(USEP_THS_NAK | USEP_RHS_NAK); | |
16c8d5e7 WD |
583 | __asm__ ("eieio"); |
584 | } | |
585 | } | |
586 | ||
587 | /****************************************************************************** | |
386eda02 | 588 | Static Linkage |
16c8d5e7 WD |
589 | ******************************************************************************/ |
590 | ||
591 | /* udc_state_transition_up | |
592 | * udc_state_transition_down | |
593 | * | |
594 | * Helper functions to implement device state changes. The device states and | |
595 | * the events that transition between them are: | |
596 | * | |
597 | * STATE_ATTACHED | |
598 | * || /\ | |
599 | * \/ || | |
600 | * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET | |
601 | * || /\ | |
602 | * \/ || | |
603 | * STATE_POWERED | |
604 | * || /\ | |
605 | * \/ || | |
606 | * DEVICE_RESET DEVICE_POWER_INTERRUPTION | |
607 | * || /\ | |
608 | * \/ || | |
609 | * STATE_DEFAULT | |
610 | * || /\ | |
611 | * \/ || | |
612 | * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET | |
613 | * || /\ | |
614 | * \/ || | |
615 | * STATE_ADDRESSED | |
616 | * || /\ | |
617 | * \/ || | |
618 | * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED | |
619 | * || /\ | |
620 | * \/ || | |
621 | * STATE_CONFIGURED | |
622 | * | |
623 | * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED | |
624 | * to STATE_CONFIGURED) from the specified initial state to the specified final | |
625 | * state, passing through each intermediate state on the way. If the initial | |
626 | * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then | |
627 | * no state transitions will take place. | |
628 | * | |
629 | * udc_state_transition_down transitions down (in the direction from | |
630 | * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the | |
631 | * specified final state, passing through each intermediate state on the way. | |
632 | * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final | |
633 | * state, then no state transitions will take place. | |
634 | * | |
635 | */ | |
386eda02 | 636 | |
16c8d5e7 | 637 | static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, |
386eda02 WD |
638 | usb_device_state_t final) |
639 | { | |
16c8d5e7 WD |
640 | if (initial < final) { |
641 | switch (initial) { | |
642 | case STATE_ATTACHED: | |
643 | usbd_device_event_irq (udc_device, | |
644 | DEVICE_HUB_CONFIGURED, 0); | |
645 | if (final == STATE_POWERED) | |
646 | break; | |
647 | case STATE_POWERED: | |
648 | usbd_device_event_irq (udc_device, DEVICE_RESET, 0); | |
649 | if (final == STATE_DEFAULT) | |
650 | break; | |
651 | case STATE_DEFAULT: | |
652 | usbd_device_event_irq (udc_device, | |
653 | DEVICE_ADDRESS_ASSIGNED, 0); | |
654 | if (final == STATE_ADDRESSED) | |
655 | break; | |
656 | case STATE_ADDRESSED: | |
657 | usbd_device_event_irq (udc_device, DEVICE_CONFIGURED, | |
658 | 0); | |
659 | case STATE_CONFIGURED: | |
660 | break; | |
661 | default: | |
662 | break; | |
663 | } | |
664 | } | |
665 | } | |
666 | ||
667 | static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, | |
386eda02 | 668 | usb_device_state_t final) |
16c8d5e7 WD |
669 | { |
670 | if (initial > final) { | |
671 | switch (initial) { | |
672 | case STATE_CONFIGURED: | |
386eda02 WD |
673 | usbd_device_event_irq (udc_device, |
674 | DEVICE_DE_CONFIGURED, 0); | |
16c8d5e7 WD |
675 | if (final == STATE_ADDRESSED) |
676 | break; | |
677 | case STATE_ADDRESSED: | |
678 | usbd_device_event_irq (udc_device, DEVICE_RESET, 0); | |
679 | if (final == STATE_DEFAULT) | |
680 | break; | |
681 | case STATE_DEFAULT: | |
386eda02 WD |
682 | usbd_device_event_irq (udc_device, |
683 | DEVICE_POWER_INTERRUPTION, 0); | |
16c8d5e7 WD |
684 | if (final == STATE_POWERED) |
685 | break; | |
686 | case STATE_POWERED: | |
687 | usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, | |
386eda02 | 688 | 0); |
16c8d5e7 WD |
689 | case STATE_ATTACHED: |
690 | break; | |
691 | default: | |
692 | break; | |
693 | } | |
694 | } | |
695 | } | |
696 | ||
697 | /* mpc8xx_udc_stall | |
698 | * | |
699 | * Force returning of STALL tokens on the given endpoint. Protocol or function | |
700 | * STALL conditions are permissable here | |
701 | */ | |
702 | static void mpc8xx_udc_stall (unsigned int ep) | |
703 | { | |
704 | usbp->usep[ep] |= STALL_BITMASK; | |
705 | } | |
706 | ||
707 | /* mpc8xx_udc_set_nak | |
708 | * | |
709 | * Force returning of NAK responses for the given endpoint as a kind of very | |
710 | * simple flow control | |
386eda02 | 711 | */ |
16c8d5e7 WD |
712 | static void mpc8xx_udc_set_nak (unsigned int ep) |
713 | { | |
714 | usbp->usep[ep] |= NAK_BITMASK; | |
715 | __asm__ ("eieio"); | |
716 | } | |
717 | ||
718 | /* mpc8xx_udc_handle_txerr | |
719 | * | |
720 | * Handle errors relevant to TX. Return a status code to allow calling | |
721 | * indicative of what if anything happened | |
722 | */ | |
386eda02 | 723 | static short mpc8xx_udc_handle_txerr () |
16c8d5e7 WD |
724 | { |
725 | short ep = 0, ret = 0; | |
386eda02 WD |
726 | |
727 | for (; ep < TX_RING_SIZE; ep++) { | |
728 | if (usbp->usber & (0x10 << ep)) { | |
729 | ||
16c8d5e7 | 730 | /* Timeout or underrun */ |
386eda02 | 731 | if (tx_cbd[ep]->cbd_sc & 0x06) { |
16c8d5e7 | 732 | ret = 1; |
386eda02 | 733 | mpc8xx_udc_flush_tx_fifo (ep); |
16c8d5e7 | 734 | |
386eda02 WD |
735 | } else { |
736 | if (usbp->usep[ep] & STALL_BITMASK) { | |
737 | if (!ep) { | |
738 | usbp->usep[ep] &= ~STALL_BITMASK; | |
16c8d5e7 | 739 | } |
386eda02 | 740 | } /* else NAK */ |
16c8d5e7 | 741 | } |
386eda02 | 742 | usbp->usber |= (0x10 << ep); |
16c8d5e7 WD |
743 | } |
744 | } | |
745 | return ret; | |
746 | } | |
747 | ||
748 | /* mpc8xx_udc_advance_rx | |
749 | * | |
750 | * Advance cbd rx | |
751 | */ | |
386eda02 | 752 | static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid) |
16c8d5e7 | 753 | { |
386eda02 | 754 | if ((*rx_cbdp)->cbd_sc & RX_BD_W) { |
6d0f6bcf | 755 | *rx_cbdp = (volatile cbd_t *) (endpoints[epid]->rbase + CONFIG_SYS_IMMR); |
386eda02 WD |
756 | |
757 | } else { | |
16c8d5e7 WD |
758 | (*rx_cbdp)++; |
759 | } | |
760 | } | |
761 | ||
762 | ||
763 | /* mpc8xx_udc_flush_tx_fifo | |
764 | * | |
765 | * Flush a given TX fifo. Assumes one tx cbd per endpoint | |
766 | */ | |
386eda02 WD |
767 | static void mpc8xx_udc_flush_tx_fifo (int epid) |
768 | { | |
769 | volatile cbd_t *tx_cbdp = 0; | |
16c8d5e7 | 770 | |
386eda02 | 771 | if (epid > MAX_ENDPOINTS) { |
16c8d5e7 WD |
772 | return; |
773 | } | |
774 | ||
775 | /* TX stop */ | |
386eda02 | 776 | immr->im_cpm.cp_cpcr = ((epid << 2) | 0x1D01); |
16c8d5e7 | 777 | __asm__ ("eieio"); |
386eda02 WD |
778 | while (immr->im_cpm.cp_cpcr & 0x01); |
779 | ||
16c8d5e7 | 780 | usbp->uscom = 0x40 | 0; |
386eda02 | 781 | |
16c8d5e7 | 782 | /* reset ring */ |
6d0f6bcf | 783 | tx_cbdp = (cbd_t *) (endpoints[epid]->tbptr + CONFIG_SYS_IMMR); |
16c8d5e7 WD |
784 | tx_cbdp->cbd_sc = (TX_BD_I | TX_BD_W); |
785 | ||
386eda02 | 786 | |
16c8d5e7 | 787 | endpoints[epid]->tptr = endpoints[epid]->tbase; |
386eda02 WD |
788 | endpoints[epid]->tstate = 0x00; |
789 | endpoints[epid]->tbcnt = 0x00; | |
16c8d5e7 WD |
790 | |
791 | /* TX start */ | |
386eda02 | 792 | immr->im_cpm.cp_cpcr = ((epid << 2) | 0x2D01); |
16c8d5e7 | 793 | __asm__ ("eieio"); |
386eda02 | 794 | while (immr->im_cpm.cp_cpcr & 0x01); |
16c8d5e7 WD |
795 | |
796 | return; | |
797 | } | |
798 | ||
799 | /* mpc8xx_udc_flush_rx_fifo | |
800 | * | |
801 | * For the sake of completeness of the namespace, it seems like | |
802 | * a good-design-decision (tm) to include mpc8xx_udc_flush_rx_fifo(); | |
803 | * If RX_BD_E is true => a driver bug either here or in an upper layer | |
804 | * not polling frequently enough. If RX_BD_E is true we have told the host | |
805 | * we have accepted data but, the CPM found it had no-where to put that data | |
806 | * which needless to say would be a bad thing. | |
807 | */ | |
386eda02 | 808 | static void mpc8xx_udc_flush_rx_fifo () |
16c8d5e7 WD |
809 | { |
810 | int i = 0; | |
386eda02 WD |
811 | |
812 | for (i = 0; i < RX_RING_SIZE; i++) { | |
813 | if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) { | |
814 | ERR ("buf %p used rx data len = 0x%x sc=0x%x!\n", | |
815 | rx_cbd[i], rx_cbd[i]->cbd_datlen, | |
816 | rx_cbd[i]->cbd_sc); | |
16c8d5e7 WD |
817 | |
818 | } | |
819 | } | |
386eda02 | 820 | ERR ("BUG : Input over-run\n"); |
16c8d5e7 WD |
821 | } |
822 | ||
823 | /* mpc8xx_udc_clear_rxbd | |
386eda02 | 824 | * |
16c8d5e7 WD |
825 | * Release control of RX CBD to CP. |
826 | */ | |
386eda02 | 827 | static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp) |
16c8d5e7 WD |
828 | { |
829 | rx_cbdp->cbd_datlen = 0x0000; | |
386eda02 | 830 | rx_cbdp->cbd_sc = ((rx_cbdp->cbd_sc & RX_BD_W) | (RX_BD_E | RX_BD_I)); |
16c8d5e7 WD |
831 | __asm__ ("eieio"); |
832 | } | |
833 | ||
834 | /* mpc8xx_udc_tx_irq | |
835 | * | |
836 | * Parse for tx timeout, control RX or USB reset/busy conditions | |
837 | * Return -1 on timeout, -2 on fatal error, else return zero | |
838 | */ | |
386eda02 | 839 | static int mpc8xx_udc_tx_irq (int ep) |
16c8d5e7 WD |
840 | { |
841 | int i = 0; | |
842 | ||
386eda02 WD |
843 | if (usbp->usber & (USB_TX_ERRMASK)) { |
844 | if (mpc8xx_udc_handle_txerr ()) { | |
16c8d5e7 WD |
845 | /* Timeout, controlling function must retry send */ |
846 | return -1; | |
847 | } | |
848 | } | |
849 | ||
386eda02 | 850 | if (usbp->usber & (USB_E_RESET | USB_E_BSY)) { |
16c8d5e7 WD |
851 | /* Fatal, abandon TX transaction */ |
852 | return -2; | |
853 | } | |
386eda02 WD |
854 | |
855 | if (usbp->usber & USB_E_RXB) { | |
856 | for (i = 0; i < RX_RING_SIZE; i++) { | |
857 | if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) { | |
858 | if ((rx_cbd[i] == ep_ref[0].prx) || ep) { | |
859 | return -2; | |
16c8d5e7 WD |
860 | } |
861 | } | |
862 | } | |
863 | } | |
864 | ||
865 | return 0; | |
866 | } | |
867 | ||
868 | /* mpc8xx_udc_ep_tx | |
869 | * | |
870 | * Transmit in a re-entrant fashion outbound USB packets. | |
871 | * Implement retry/timeout mechanism described in USB specification | |
872 | * Toggle DATA0/DATA1 pids as necessary | |
873 | * Introduces non-standard tx_retry. The USB standard has no scope for slave | |
874 | * devices to give up TX, however tx_retry stops us getting stuck in an endless | |
875 | * TX loop. | |
876 | */ | |
386eda02 | 877 | static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi) |
16c8d5e7 WD |
878 | { |
879 | struct urb *urb = epi->tx_urb; | |
386eda02 | 880 | volatile cbd_t *tx_cbdp = 0; |
16c8d5e7 WD |
881 | unsigned int ep = 0, pkt_len = 0, x = 0, tx_retry = 0; |
882 | int ret = 0; | |
386eda02 WD |
883 | |
884 | if (!epi || (epi->endpoint_address & 0x03) >= MAX_ENDPOINTS || !urb) { | |
16c8d5e7 WD |
885 | return -1; |
886 | } | |
887 | ||
888 | ep = epi->endpoint_address & 0x03; | |
6d0f6bcf | 889 | tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CONFIG_SYS_IMMR); |
386eda02 WD |
890 | |
891 | if (tx_cbdp->cbd_sc & TX_BD_R || usbp->usber & USB_E_TXB) { | |
892 | mpc8xx_udc_flush_tx_fifo (ep); | |
16c8d5e7 WD |
893 | usbp->usber |= USB_E_TXB; |
894 | }; | |
895 | ||
386eda02 WD |
896 | while (tx_retry++ < 100) { |
897 | ret = mpc8xx_udc_tx_irq (ep); | |
898 | if (ret == -1) { | |
16c8d5e7 | 899 | /* ignore timeout here */ |
386eda02 | 900 | } else if (ret == -2) { |
16c8d5e7 | 901 | /* Abandon TX */ |
386eda02 | 902 | mpc8xx_udc_flush_tx_fifo (ep); |
16c8d5e7 | 903 | return -1; |
386eda02 WD |
904 | } |
905 | ||
6d0f6bcf | 906 | tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CONFIG_SYS_IMMR); |
386eda02 WD |
907 | while (tx_cbdp->cbd_sc & TX_BD_R) { |
908 | }; | |
909 | tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc & TX_BD_W); | |
16c8d5e7 | 910 | |
16c8d5e7 WD |
911 | pkt_len = urb->actual_length - epi->sent; |
912 | ||
386eda02 WD |
913 | if (pkt_len > epi->tx_packetSize || pkt_len > EP_MAX_PKT) { |
914 | pkt_len = MIN (epi->tx_packetSize, EP_MAX_PKT); | |
16c8d5e7 WD |
915 | } |
916 | ||
386eda02 WD |
917 | for (x = 0; x < pkt_len; x++) { |
918 | *((unsigned char *) (tx_cbdp->cbd_bufaddr + x)) = | |
16c8d5e7 WD |
919 | urb->buffer[epi->sent + x]; |
920 | } | |
921 | tx_cbdp->cbd_datlen = pkt_len; | |
386eda02 | 922 | tx_cbdp->cbd_sc |= (CBD_TX_BITMASK | ep_ref[ep].pid); |
16c8d5e7 WD |
923 | __asm__ ("eieio"); |
924 | ||
386eda02 WD |
925 | #ifdef __SIMULATE_ERROR__ |
926 | if (++err_poison_test == 2) { | |
927 | err_poison_test = 0; | |
928 | tx_cbdp->cbd_sc &= ~TX_BD_TC; | |
929 | } | |
930 | #endif | |
16c8d5e7 | 931 | |
386eda02 | 932 | usbp->uscom = (USCOM_STR | ep); |
16c8d5e7 | 933 | |
386eda02 WD |
934 | while (!(usbp->usber & USB_E_TXB)) { |
935 | ret = mpc8xx_udc_tx_irq (ep); | |
936 | if (ret == -1) { | |
16c8d5e7 WD |
937 | /* TX timeout */ |
938 | break; | |
386eda02 WD |
939 | } else if (ret == -2) { |
940 | if (usbp->usber & USB_E_TXB) { | |
941 | usbp->usber |= USB_E_TXB; | |
16c8d5e7 | 942 | } |
386eda02 | 943 | mpc8xx_udc_flush_tx_fifo (ep); |
16c8d5e7 WD |
944 | return -1; |
945 | } | |
946 | }; | |
947 | ||
386eda02 WD |
948 | if (usbp->usber & USB_E_TXB) { |
949 | usbp->usber |= USB_E_TXB; | |
16c8d5e7 WD |
950 | } |
951 | ||
952 | /* ACK must be present <= 18bit times from TX */ | |
386eda02 | 953 | if (ret == -1) { |
16c8d5e7 WD |
954 | continue; |
955 | } | |
386eda02 | 956 | |
16c8d5e7 WD |
957 | /* TX ACK : USB 2.0 8.7.2, Toggle PID, Advance TX */ |
958 | epi->sent += pkt_len; | |
386eda02 WD |
959 | epi->last = MIN (urb->actual_length - epi->sent, epi->tx_packetSize); |
960 | TOGGLE_TX_PID (ep_ref[ep].pid); | |
961 | ||
962 | if (epi->sent >= epi->tx_urb->actual_length) { | |
16c8d5e7 | 963 | |
16c8d5e7 WD |
964 | epi->tx_urb->actual_length = 0; |
965 | epi->sent = 0; | |
386eda02 WD |
966 | |
967 | if (ep_ref[ep].sc & EP_SEND_ZLP) { | |
16c8d5e7 | 968 | ep_ref[ep].sc &= ~EP_SEND_ZLP; |
386eda02 | 969 | } else { |
16c8d5e7 WD |
970 | return 0; |
971 | } | |
972 | } | |
973 | } | |
386eda02 WD |
974 | |
975 | ERR ("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n", ep, epi->sent, | |
976 | epi->tx_urb->actual_length); | |
16c8d5e7 WD |
977 | |
978 | return -1; | |
979 | } | |
980 | ||
981 | /* mpc8xx_udc_dump_request | |
982 | * | |
983 | * Dump a control request to console | |
984 | */ | |
386eda02 | 985 | static void mpc8xx_udc_dump_request (struct usb_device_request *request) |
16c8d5e7 | 986 | { |
386eda02 WD |
987 | DBG ("bmRequestType:%02x bRequest:%02x wValue:%04x " |
988 | "wIndex:%04x wLength:%04x ?\n", | |
989 | request->bmRequestType, | |
990 | request->bRequest, | |
991 | request->wValue, request->wIndex, request->wLength); | |
16c8d5e7 WD |
992 | |
993 | return; | |
994 | } | |
995 | ||
386eda02 WD |
996 | /* mpc8xx_udc_ep0_rx_setup |
997 | * | |
16c8d5e7 WD |
998 | * Decode received ep0 SETUP packet. return non-zero on error |
999 | */ | |
1000 | static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp) | |
1001 | { | |
1002 | unsigned int x = 0; | |
386eda02 WD |
1003 | struct urb *purb = ep_ref[0].urb; |
1004 | struct usb_endpoint_instance *epi = | |
16c8d5e7 WD |
1005 | &udc_device->bus->endpoint_array[0]; |
1006 | ||
386eda02 WD |
1007 | for (; x < rx_cbdp->cbd_datlen; x++) { |
1008 | *(((unsigned char *) &ep_ref[0].urb->device_request) + x) = | |
1009 | *((unsigned char *) (rx_cbdp->cbd_bufaddr + x)); | |
16c8d5e7 | 1010 | } |
16c8d5e7 | 1011 | |
386eda02 WD |
1012 | mpc8xx_udc_clear_rxbd (rx_cbdp); |
1013 | ||
1014 | if (ep0_recv_setup (purb)) { | |
1015 | mpc8xx_udc_dump_request (&purb->device_request); | |
16c8d5e7 WD |
1016 | return -1; |
1017 | } | |
1018 | ||
386eda02 | 1019 | if ((purb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) |
16c8d5e7 WD |
1020 | == USB_REQ_HOST2DEVICE) { |
1021 | ||
386eda02 WD |
1022 | switch (purb->device_request.bRequest) { |
1023 | case USB_REQ_SET_ADDRESS: | |
1024 | /* Send the Status OUT ZLP */ | |
1025 | ep_ref[0].pid = TX_BD_PID_DATA1; | |
1026 | purb->actual_length = 0; | |
1027 | mpc8xx_udc_init_tx (epi, purb); | |
1028 | mpc8xx_udc_ep_tx (epi); | |
1029 | ||
1030 | /* Move to the addressed state */ | |
1031 | usbp->usaddr = udc_device->address; | |
1032 | mpc8xx_udc_state_transition_up (udc_device->device_state, | |
1033 | STATE_ADDRESSED); | |
1034 | return 0; | |
1035 | ||
1036 | case USB_REQ_SET_CONFIGURATION: | |
1037 | if (!purb->device_request.wValue) { | |
1038 | /* Respond at default address */ | |
1039 | usbp->usaddr = 0x00; | |
1040 | mpc8xx_udc_state_transition_down (udc_device->device_state, | |
1041 | STATE_ADDRESSED); | |
1042 | } else { | |
1043 | /* TODO: Support multiple configurations */ | |
1044 | mpc8xx_udc_state_transition_up (udc_device->device_state, | |
1045 | STATE_CONFIGURED); | |
1046 | for (x = 1; x < MAX_ENDPOINTS; x++) { | |
1047 | if ((udc_device->bus->endpoint_array[x].endpoint_address & USB_ENDPOINT_DIR_MASK) | |
1048 | == USB_DIR_IN) { | |
1049 | ep_ref[x].pid = TX_BD_PID_DATA0; | |
1050 | } else { | |
1051 | ep_ref[x].pid = RX_BD_PID_DATA0; | |
16c8d5e7 | 1052 | } |
386eda02 WD |
1053 | /* Set configuration must unstall endpoints */ |
1054 | usbp->usep[x] &= ~STALL_BITMASK; | |
16c8d5e7 | 1055 | } |
386eda02 WD |
1056 | } |
1057 | break; | |
1058 | default: | |
1059 | /* CDC/Vendor specific */ | |
1060 | break; | |
16c8d5e7 WD |
1061 | } |
1062 | ||
1063 | /* Send ZLP as ACK in Status OUT phase */ | |
1064 | ep_ref[0].pid = TX_BD_PID_DATA1; | |
1065 | purb->actual_length = 0; | |
386eda02 WD |
1066 | mpc8xx_udc_init_tx (epi, purb); |
1067 | mpc8xx_udc_ep_tx (epi); | |
16c8d5e7 | 1068 | |
386eda02 WD |
1069 | } else { |
1070 | ||
1071 | if (purb->actual_length) { | |
16c8d5e7 | 1072 | ep_ref[0].pid = TX_BD_PID_DATA1; |
386eda02 | 1073 | mpc8xx_udc_init_tx (epi, purb); |
16c8d5e7 | 1074 | |
386eda02 | 1075 | if (!(purb->actual_length % EP0_MAX_PACKET_SIZE)) { |
16c8d5e7 WD |
1076 | ep_ref[0].sc |= EP_SEND_ZLP; |
1077 | } | |
1078 | ||
386eda02 WD |
1079 | if (purb->device_request.wValue == |
1080 | USB_DESCRIPTOR_TYPE_DEVICE) { | |
1081 | if (le16_to_cpu (purb->device_request.wLength) | |
1082 | > purb->actual_length) { | |
16c8d5e7 WD |
1083 | /* Send EP0_MAX_PACKET_SIZE bytes |
1084 | * unless correct size requested. | |
1085 | */ | |
386eda02 WD |
1086 | if (purb->actual_length > epi->tx_packetSize) { |
1087 | purb->actual_length = epi->tx_packetSize; | |
16c8d5e7 | 1088 | } |
16c8d5e7 WD |
1089 | } |
1090 | } | |
386eda02 | 1091 | mpc8xx_udc_ep_tx (epi); |
16c8d5e7 | 1092 | |
386eda02 | 1093 | } else { |
16c8d5e7 | 1094 | /* Corrupt SETUP packet? */ |
386eda02 | 1095 | ERR ("Zero length data or SETUP with DATA-IN phase ?\n"); |
16c8d5e7 WD |
1096 | return 1; |
1097 | } | |
1098 | } | |
1099 | return 0; | |
1100 | } | |
1101 | ||
1102 | /* mpc8xx_udc_init_tx | |
1103 | * | |
1104 | * Setup some basic parameters for a TX transaction | |
1105 | */ | |
386eda02 WD |
1106 | static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi, |
1107 | struct urb *tx_urb) | |
16c8d5e7 WD |
1108 | { |
1109 | epi->sent = 0; | |
1110 | epi->last = 0; | |
1111 | epi->tx_urb = tx_urb; | |
1112 | } | |
1113 | ||
1114 | /* mpc8xx_udc_ep0_rx | |
1115 | * | |
1116 | * Receive ep0/control USB data. Parse and possibly send a response. | |
1117 | */ | |
386eda02 | 1118 | static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp) |
16c8d5e7 | 1119 | { |
386eda02 WD |
1120 | if (rx_cbdp->cbd_sc & RX_BD_PID_SETUP) { |
1121 | ||
16c8d5e7 | 1122 | /* Unconditionally accept SETUP packets */ |
386eda02 WD |
1123 | if (mpc8xx_udc_ep0_rx_setup (rx_cbdp)) { |
1124 | mpc8xx_udc_stall (0); | |
16c8d5e7 | 1125 | } |
386eda02 | 1126 | |
16c8d5e7 | 1127 | } else { |
386eda02 WD |
1128 | |
1129 | mpc8xx_udc_clear_rxbd (rx_cbdp); | |
1130 | ||
1131 | if ((rx_cbdp->cbd_datlen - 2)) { | |
16c8d5e7 | 1132 | /* SETUP with a DATA phase |
386eda02 WD |
1133 | * outside of SETUP packet. |
1134 | * Reply with STALL. | |
1135 | */ | |
16c8d5e7 WD |
1136 | mpc8xx_udc_stall (0); |
1137 | } | |
1138 | } | |
1139 | } | |
1140 | ||
1141 | /* mpc8xx_udc_epn_rx | |
1142 | * | |
1143 | * Receive some data from cbd into USB system urb data abstraction | |
386eda02 | 1144 | * Upper layers should NAK if there is insufficient RX data space |
16c8d5e7 WD |
1145 | */ |
1146 | static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp) | |
1147 | { | |
1148 | struct usb_endpoint_instance *epi = 0; | |
1149 | struct urb *urb = 0; | |
1150 | unsigned int x = 0; | |
1151 | ||
386eda02 | 1152 | if (epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen) { |
16c8d5e7 WD |
1153 | return 0; |
1154 | } | |
386eda02 WD |
1155 | |
1156 | /* USB 2.0 PDF section 8.6.4 | |
16c8d5e7 WD |
1157 | * Discard data with invalid PID it is a resend. |
1158 | */ | |
386eda02 | 1159 | if (ep_ref[epid].pid != (rx_cbdp->cbd_sc & 0xC0)) { |
16c8d5e7 WD |
1160 | return 1; |
1161 | } | |
386eda02 WD |
1162 | TOGGLE_RX_PID (ep_ref[epid].pid); |
1163 | ||
16c8d5e7 WD |
1164 | epi = &udc_device->bus->endpoint_array[epid]; |
1165 | urb = epi->rcv_urb; | |
1166 | ||
386eda02 WD |
1167 | for (; x < (rx_cbdp->cbd_datlen - 2); x++) { |
1168 | *((unsigned char *) (urb->buffer + urb->actual_length + x)) = | |
1169 | *((unsigned char *) (rx_cbdp->cbd_bufaddr + x)); | |
16c8d5e7 WD |
1170 | } |
1171 | ||
386eda02 | 1172 | if (x) { |
16c8d5e7 | 1173 | usbd_rcv_complete (epi, x, 0); |
386eda02 WD |
1174 | if (ep_ref[epid].urb->status == RECV_ERROR) { |
1175 | DBG ("RX error unset NAK\n"); | |
1176 | udc_unset_nak (epid); | |
16c8d5e7 | 1177 | } |
386eda02 | 1178 | } |
16c8d5e7 WD |
1179 | return x; |
1180 | } | |
1181 | ||
1182 | /* mpc8xx_udc_clock_init | |
1183 | * | |
386eda02 | 1184 | * Obtain a clock reference for Full Speed Signaling |
16c8d5e7 | 1185 | */ |
386eda02 WD |
1186 | static void mpc8xx_udc_clock_init (volatile immap_t * immr, |
1187 | volatile cpm8xx_t * cp) | |
16c8d5e7 WD |
1188 | { |
1189 | ||
6d0f6bcf | 1190 | #if defined(CONFIG_SYS_USB_EXTC_CLK) |
16c8d5e7 WD |
1191 | |
1192 | /* This has been tested with a 48MHz crystal on CLK6 */ | |
6d0f6bcf | 1193 | switch (CONFIG_SYS_USB_EXTC_CLK) { |
386eda02 WD |
1194 | case 1: |
1195 | immr->im_ioport.iop_papar |= 0x0100; | |
1196 | immr->im_ioport.iop_padir &= ~0x0100; | |
1197 | cp->cp_sicr |= 0x24; | |
1198 | break; | |
1199 | case 2: | |
1200 | immr->im_ioport.iop_papar |= 0x0200; | |
1201 | immr->im_ioport.iop_padir &= ~0x0200; | |
1202 | cp->cp_sicr |= 0x2D; | |
1203 | break; | |
1204 | case 3: | |
1205 | immr->im_ioport.iop_papar |= 0x0400; | |
1206 | immr->im_ioport.iop_padir &= ~0x0400; | |
1207 | cp->cp_sicr |= 0x36; | |
1208 | break; | |
1209 | case 4: | |
1210 | immr->im_ioport.iop_papar |= 0x0800; | |
1211 | immr->im_ioport.iop_padir &= ~0x0800; | |
1212 | cp->cp_sicr |= 0x3F; | |
1213 | break; | |
1214 | default: | |
1215 | udc_state = STATE_ERROR; | |
1216 | break; | |
16c8d5e7 WD |
1217 | } |
1218 | ||
6d0f6bcf | 1219 | #elif defined(CONFIG_SYS_USB_BRGCLK) |
16c8d5e7 | 1220 | |
386eda02 | 1221 | /* This has been tested with brgclk == 50MHz */ |
16c8d5e7 WD |
1222 | int divisor = 0; |
1223 | ||
386eda02 WD |
1224 | if (gd->cpu_clk < 48000000L) { |
1225 | ERR ("brgclk is too slow for full-speed USB!\n"); | |
16c8d5e7 WD |
1226 | udc_state = STATE_ERROR; |
1227 | return; | |
1228 | } | |
1229 | ||
1230 | /* Assume the brgclk is 'good enough', we want !(gd->cpu_clk%48Mhz) | |
1231 | * but, can /probably/ live with close-ish alternative rates. | |
386eda02 WD |
1232 | */ |
1233 | divisor = (gd->cpu_clk / 48000000L) - 1; | |
16c8d5e7 | 1234 | cp->cp_sicr &= ~0x0000003F; |
386eda02 | 1235 | |
6d0f6bcf | 1236 | switch (CONFIG_SYS_USB_BRGCLK) { |
386eda02 WD |
1237 | case 1: |
1238 | cp->cp_brgc1 |= (divisor | CPM_BRG_EN); | |
1239 | cp->cp_sicr &= ~0x2F; | |
1240 | break; | |
1241 | case 2: | |
1242 | cp->cp_brgc2 |= (divisor | CPM_BRG_EN); | |
1243 | cp->cp_sicr |= 0x00000009; | |
1244 | break; | |
1245 | case 3: | |
1246 | cp->cp_brgc3 |= (divisor | CPM_BRG_EN); | |
1247 | cp->cp_sicr |= 0x00000012; | |
1248 | break; | |
1249 | case 4: | |
1250 | cp->cp_brgc4 = (divisor | CPM_BRG_EN); | |
1251 | cp->cp_sicr |= 0x0000001B; | |
1252 | break; | |
1253 | default: | |
1254 | udc_state = STATE_ERROR; | |
1255 | break; | |
16c8d5e7 WD |
1256 | } |
1257 | ||
1258 | #else | |
6d0f6bcf | 1259 | #error "CONFIG_SYS_USB_EXTC_CLK or CONFIG_SYS_USB_BRGCLK must be defined" |
16c8d5e7 WD |
1260 | #endif |
1261 | ||
1262 | } | |
1263 | ||
1264 | /* mpc8xx_udc_cbd_attach | |
1265 | * | |
1266 | * attach a cbd to and endpoint | |
1267 | */ | |
1268 | static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size) | |
1269 | { | |
386eda02 WD |
1270 | |
1271 | if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS) { | |
16c8d5e7 WD |
1272 | udc_state = STATE_ERROR; |
1273 | return; | |
1274 | } | |
1275 | ||
386eda02 WD |
1276 | if (tx_size > USB_MAX_PKT || rx_size > USB_MAX_PKT || |
1277 | (!tx_size && !rx_size)) { | |
16c8d5e7 WD |
1278 | udc_state = STATE_ERROR; |
1279 | return; | |
1280 | } | |
1281 | ||
1282 | /* Attach CBD to appropiate Parameter RAM Endpoint data structure */ | |
386eda02 WD |
1283 | if (rx_size) { |
1284 | endpoints[ep]->rbase = (u32) rx_cbd[rx_ct]; | |
1285 | endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; | |
16c8d5e7 WD |
1286 | rx_ct++; |
1287 | ||
386eda02 WD |
1288 | if (!ep) { |
1289 | ||
1290 | endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; | |
16c8d5e7 WD |
1291 | rx_cbd[rx_ct]->cbd_sc |= RX_BD_W; |
1292 | rx_ct++; | |
1293 | ||
386eda02 | 1294 | } else { |
16c8d5e7 | 1295 | rx_ct += 2; |
386eda02 | 1296 | endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; |
16c8d5e7 WD |
1297 | rx_cbd[rx_ct]->cbd_sc |= RX_BD_W; |
1298 | rx_ct++; | |
1299 | } | |
1300 | ||
1301 | /* Where we expect to RX data on this endpoint */ | |
386eda02 WD |
1302 | ep_ref[ep].prx = rx_cbd[rx_ct - 1]; |
1303 | } else { | |
16c8d5e7 WD |
1304 | |
1305 | ep_ref[ep].prx = 0; | |
1306 | endpoints[ep]->rbase = 0; | |
1307 | endpoints[ep]->rbptr = 0; | |
1308 | } | |
1309 | ||
386eda02 WD |
1310 | if (tx_size) { |
1311 | endpoints[ep]->tbase = (u32) tx_cbd[tx_ct]; | |
1312 | endpoints[ep]->tbptr = (u32) tx_cbd[tx_ct]; | |
16c8d5e7 | 1313 | tx_ct++; |
386eda02 | 1314 | } else { |
16c8d5e7 WD |
1315 | endpoints[ep]->tbase = 0; |
1316 | endpoints[ep]->tbptr = 0; | |
1317 | } | |
1318 | ||
1319 | endpoints[ep]->tstate = 0; | |
1320 | endpoints[ep]->tbcnt = 0; | |
1321 | endpoints[ep]->mrblr = EP_MAX_PKT; | |
386eda02 | 1322 | endpoints[ep]->rfcr = 0x18; |
16c8d5e7 WD |
1323 | endpoints[ep]->tfcr = 0x18; |
1324 | ep_ref[ep].sc |= EP_ATTACHED; | |
1325 | ||
386eda02 WD |
1326 | DBG ("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n", |
1327 | ep, endpoints[ep]->rbase, endpoints[ep]->rbptr, | |
1328 | endpoints[ep]->tbase, endpoints[ep]->tbptr, | |
1329 | ep_ref[ep].prx); | |
16c8d5e7 WD |
1330 | |
1331 | return; | |
1332 | } | |
1333 | ||
1334 | /* mpc8xx_udc_cbd_init | |
1335 | * | |
1336 | * Allocate space for a cbd and allocate TX/RX data space | |
1337 | */ | |
1338 | static void mpc8xx_udc_cbd_init (void) | |
1339 | { | |
1340 | int i = 0; | |
1341 | ||
386eda02 WD |
1342 | for (; i < TX_RING_SIZE; i++) { |
1343 | tx_cbd[i] = (cbd_t *) | |
1344 | mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int)); | |
1345 | } | |
1346 | ||
1347 | for (i = 0; i < RX_RING_SIZE; i++) { | |
1348 | rx_cbd[i] = (cbd_t *) | |
1349 | mpc8xx_udc_alloc (sizeof (cbd_t), sizeof (int)); | |
16c8d5e7 WD |
1350 | } |
1351 | ||
386eda02 WD |
1352 | for (i = 0; i < TX_RING_SIZE; i++) { |
1353 | tx_cbd[i]->cbd_bufaddr = | |
1354 | mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int)); | |
16c8d5e7 | 1355 | |
386eda02 | 1356 | tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W); |
16c8d5e7 WD |
1357 | tx_cbd[i]->cbd_datlen = 0x0000; |
1358 | } | |
1359 | ||
1360 | ||
386eda02 WD |
1361 | for (i = 0; i < RX_RING_SIZE; i++) { |
1362 | rx_cbd[i]->cbd_bufaddr = | |
1363 | mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int)); | |
16c8d5e7 WD |
1364 | rx_cbd[i]->cbd_sc = (RX_BD_I | RX_BD_E); |
1365 | rx_cbd[i]->cbd_datlen = 0x0000; | |
1366 | ||
1367 | } | |
1368 | ||
1369 | return; | |
1370 | } | |
1371 | ||
1372 | /* mpc8xx_udc_endpoint_init | |
1373 | * | |
1374 | * Attach an endpoint to some dpram | |
1375 | */ | |
1376 | static void mpc8xx_udc_endpoint_init (void) | |
1377 | { | |
1378 | int i = 0; | |
1379 | ||
386eda02 WD |
1380 | for (; i < MAX_ENDPOINTS; i++) { |
1381 | endpoints[i] = (usb_epb_t *) | |
1382 | mpc8xx_udc_alloc (sizeof (usb_epb_t), 32); | |
16c8d5e7 WD |
1383 | } |
1384 | } | |
1385 | ||
1386 | /* mpc8xx_udc_alloc | |
1387 | * | |
386eda02 | 1388 | * Grab the address of some dpram |
16c8d5e7 WD |
1389 | */ |
1390 | static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment) | |
1391 | { | |
1392 | u32 retaddr = address_base; | |
386eda02 WD |
1393 | |
1394 | while (retaddr % alignment) { | |
16c8d5e7 | 1395 | retaddr++; |
386eda02 WD |
1396 | } |
1397 | address_base += data_size; | |
1398 | ||
16c8d5e7 WD |
1399 | return retaddr; |
1400 | } | |
1401 | ||
1402 | #endif /* CONFIG_MPC885_FAMILY && CONFIG_USB_DEVICE) */ |