1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright © 2010 Amaury Pouly
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
25 #include "clock-target.h"
36 #include "usb-drv-as3525v2.h"
39 static int __in_ep_list
[NUM_IN_EP
] = {IN_EP_LIST
};
40 static int __out_ep_list
[NUM_OUT_EP
] = {OUT_EP_LIST
};
42 /* iterate through each in/out ep except EP0
43 * 'counter' is the counter, 'ep' is the actual value */
44 #define FOR_EACH_IN_EP(counter, ep) \
45 for(counter = 0, ep = __in_ep_list[0]; counter < NUM_IN_EP; counter++, ep = __in_ep_list[counter])
47 #define FOR_EACH_OUT_EP(counter, ep) \
48 for(counter = 0, ep = __out_ep_list[0]; counter < NUM_OUT_EP; counter++, ep = __out_ep_list[counter])
57 unsigned int received
;
64 static struct usb_endpoint endpoints
[USB_NUM_ENDPOINTS
*2];
66 static struct usb_ctrlrequest ep0_setup_pkt
__attribute__((aligned(16)));
74 static void usb_delay(void)
84 static void as3525v2_connect(void)
86 logf("usb: init as3525v2");
87 /* 1) enable usb core clock */
88 CGU_PERI
|= CGU_USB_CLOCK_ENABLE
;
90 /* 2) enable usb phy clock */
92 CGU_USB
= 1<<5 /* enable */
93 | (CLK_DIV(AS3525_PLLA_FREQ
, 60000000)) << 2
94 | 1; /* source = PLLA */
96 /* 3) clear "stop pclk" */
99 /* 4) clear "power clamp" */
102 /* 5) clear "reset power down module" */
105 /* 6) set "power on program done" */
106 DCTL
|= DCTL_pwronprgdone
;
108 /* 7) core soft reset */
109 GRSTCTL
|= GRSTCTL_csftrst
;
111 /* 8) hclk soft reset */
112 GRSTCTL
|= GRSTCTL_hsftrst
;
114 /* 9) flush and reset everything */
117 /* 10) force device mode*/
118 GUSBCFG
&= ~GUSBCFG_force_host_mode
;
119 GUSBCFG
|= GUSBCFG_force_device_mode
;
121 /* 11) Do something that is probably CCU related but undocumented*/
122 CCU_USB_THINGY
&= ~0x1000;
124 CCU_USB_THINGY
&= ~0x300000;
126 /* 12) reset usb core parameters (dev addr, speed, ...) */
131 static void enable_device_interrupts(void)
133 /* Clear any pending interrupt */
134 GINTSTS
= 0xffffffff;
135 /* Clear any pending otg interrupt */
136 GOTGINT
= 0xffffffff;
137 /* Enable interrupts */
138 GINTMSK
= GINTMSK_usbreset
143 | GINTMSK_disconnect
;
146 static void flush_tx_fifos(int nums
)
150 GRSTCTL
= (nums
<< GRSTCTL_txfnum_bitp
)
151 | GRSTCTL_txfflsh_flush
;
152 while(GRSTCTL
& GRSTCTL_txfflsh_flush
&& i
< 0x300)
154 if(GRSTCTL
& GRSTCTL_txfflsh_flush
)
155 panicf("usb: hang of flush tx fifos (%x)", nums
);
156 /* wait 3 phy clocks */
160 static void reset_endpoints(void)
163 /* disable all endpoints except EP0 */
164 FOR_EACH_IN_EP(i
, ep
)
165 if(DIEPCTL(ep
) & DEPCTL_epena
)
166 DIEPCTL(ep
) = DEPCTL_epdis
| DEPCTL_snak
;
170 FOR_EACH_OUT_EP(i
, ep
)
171 if(DOEPCTL(ep
) & DEPCTL_epena
)
172 DOEPCTL(ep
) = DEPCTL_epdis
| DEPCTL_snak
;
175 /* Setup EP0 OUT with the following parameters:
177 * setup packet count = 1
179 * Setup EP0 IN/OUT with 64 byte maximum packet size and activate both. Enable transfer on EP0 OUT
182 DOEPTSIZ(0) = (1 << DEPTSIZ0_supcnt_bitp
)
183 | (1 << DEPTSIZ0_pkcnt_bitp
)
187 clean_dcache_range((void*)&ep0_setup_pkt
, sizeof ep0_setup_pkt
); /* force write back */
188 DOEPDMA(0) = (unsigned long)&ep0_setup_pkt
; /* virtual address=physical address */
190 /* Enable endpoint, clear nak */
191 DOEPCTL(0) = DEPCTL_epena
| DEPCTL_cnak
| DEPCTL_usbactep
192 | (DEPCTL_MPS_64
<< DEPCTL_mps_bitp
);
194 /* 64 bytes packet size, active endpoint */
195 DIEPCTL(0) = (DEPCTL_MPS_64
<< DEPCTL_mps_bitp
)
199 static void core_dev_init(void)
201 unsigned int num_in_ep
= 0;
202 unsigned int num_out_ep
= 0;
204 /* Restart the phy clock */
206 /* Set phy speed : high speed */
207 DCFG
= (DCFG
& ~bitm(DCFG
, devspd
)) | DCFG_devspd_hs_phy_hs
;
209 /* Check hardware capabilities */
210 if(extract(GHWCFG2
, arch
) != GHWCFG2_ARCH_INTERNAL_DMA
)
211 panicf("usb: wrong architecture (%ld)", extract(GHWCFG2
, arch
));
212 if(extract(GHWCFG2
, hs_phy_type
) != GHWCFG2_PHY_TYPE_UTMI
)
213 panicf("usb: wrong HS phy type (%ld)", extract(GHWCFG2
, hs_phy_type
));
214 if(extract(GHWCFG2
, fs_phy_type
) != GHWCFG2_PHY_TYPE_UNSUPPORTED
)
215 panicf("usb: wrong FS phy type (%ld)", extract(GHWCFG2
, fs_phy_type
));
216 if(extract(GHWCFG4
, utmi_phy_data_width
) != 0x2)
217 panicf("usb: wrong utmi data width (%ld)", extract(GHWCFG4
, utmi_phy_data_width
));
218 if(!(GHWCFG4
& GHWCFG4_ded_fifo_en
)) /* it seems to be multiple tx fifo support */
219 panicf("usb: no multiple tx fifo");
221 #ifdef USE_CUSTOM_FIFO_LAYOUT
222 if(!(GHWCFG2
& GHWCFG2_dyn_fifo
))
223 panicf("usb: no dynamic fifo");
224 if(GRXFSIZ
!= DATA_FIFO_DEPTH
)
225 panicf("usb: wrong data fifo size");
226 #endif /* USE_CUSTOM_FIFO_LAYOUT */
228 /* do some logging */
229 logf("hwcfg1: %08lx", GHWCFG1
);
230 logf("hwcfg2: %08lx", GHWCFG2
);
231 logf("hwcfg3: %08lx", GHWCFG3
);
232 logf("hwcfg4: %08lx", GHWCFG4
);
234 logf("%ld endpoints", extract(GHWCFG2
, num_ep
));
237 for(i
= 0; i
< extract(GHWCFG2
, num_ep
); i
++)
239 bool in
= false, out
= false;
240 switch((GHWCFG1
>> GHWCFG1_epdir_bitp(i
)) & GHWCFG1_epdir_bits
)
242 case GHWCFG1_EPDIR_BIDIR
: in
= out
= true; break;
243 case GHWCFG1_EPDIR_IN
: in
= true; break;
244 case GHWCFG1_EPDIR_OUT
: out
= true; break;
245 default: panicf("usb: invalid epdir");
247 /* don't count EP0 which is special and always bidirectional */
252 logf(" EP%d: IN=%s OUT=%s", i
, in
? "yes" : "no", out
? "yes" : "no");
255 if(num_in_ep
!= extract(GHWCFG4
, num_in_ep
))
256 panicf("usb: num in ep mismatch(%d,%lu)", num_in_ep
, extract(GHWCFG4
, num_in_ep
));
257 if(num_in_ep
!= NUM_IN_EP
)
258 panicf("usb: num in ep static mismatch(%u,%u)", num_in_ep
, NUM_IN_EP
);
259 if(num_out_ep
!= NUM_OUT_EP
)
260 panicf("usb: num out ep static mismatch(%u,%u)", num_out_ep
, NUM_OUT_EP
);
262 logf("%d in ep, %d out ep", num_in_ep
, num_out_ep
);
265 logf(" tot fifo sz: %lx", extract(GHWCFG3
, dfifo_len
));
266 logf(" rx fifo: [%04x,+%4lx]", 0, GRXFSIZ
);
267 logf(" nptx fifo: [%04lx,+%4lx]", GET_FIFOSIZE_START_ADR(GNPTXFSIZ
),
268 GET_FIFOSIZE_DEPTH(GNPTXFSIZ
));
270 #ifdef USE_CUSTOM_FIFO_LAYOUT
272 /* Organize FIFO as follow:
273 * 0 -> rxfsize : RX fifo
274 * rxfsize -> rxfsize + nptxfsize : TX fifo for first IN ep
275 * rxfsize + nptxfsize -> rxfsize + 2 * nptxfsize : TX fifo for second IN ep
276 * rxfsize + 2 * nptxfsize -> rxfsize + 3 * nptxfsize : TX fifo for third IN ep
280 unsigned short adr
= 0;
281 unsigned short depth
= RX_FIFO_SIZE
;
284 depth
= NPTX_FIFO_SIZE
;
285 GNPTXFSIZ
= MAKE_FIFOSIZE_DATA(adr
, depth
);
288 for(i
= 1; i
<= NUM_IN_EP
; i
++)
290 depth
= EPTX_FIFO_SIZE
;
291 DIEPTXFSIZ(i
) = MAKE_FIFOSIZE_DATA(adr
, depth
);
295 if(adr
> DATA_FIFO_DEPTH
)
296 panicf("usb: total data fifo size exceeded");
297 #endif /* USE_CUSTOM_FIFO_LAYOUT */
299 for(i
= 1; i
<= NUM_IN_EP
; i
++)
301 logf(" dieptx fifo(%2u): [%04lx,+%4lx]", i
,
302 GET_FIFOSIZE_START_ADR(DIEPTXFSIZ(i
)),
303 GET_FIFOSIZE_DEPTH(DIEPTXFSIZ(i
)));
306 /* Setup interrupt masks for endpoints */
307 /* Setup interrupt masks */
308 DOEPMSK
= DOEPINT_setup
| DOEPINT_xfercompl
| DOEPINT_ahberr
309 | DOEPINT_epdisabled
;
310 DIEPMSK
= DIEPINT_xfercompl
| DIEPINT_timeout
311 | DIEPINT_epdisabled
| DIEPINT_ahberr
;
312 DAINTMSK
= 0xffffffff;
316 /* fixme: threshold tweaking only takes place if we use multiple tx fifos it seems */
317 /* only dump them for now, leave threshold disabled */
319 logf("threshold control:");
320 logf(" non_iso_thr_en: %d", (DTHRCTL & DTHRCTL_non_iso_thr_en) ? 1 : 0);
321 logf(" iso_thr_en: %d", (DTHRCTL & DTHRCTL_iso_thr_en) ? 1 : 0);
322 logf(" tx_thr_len: %lu", extract(DTHRCTL, tx_thr_len));
323 logf(" rx_thr_en: %d", (DTHRCTL & DTHRCTL_rx_thr_en) ? 1 : 0);
324 logf(" rx_thr_len: %lu", extract(DTHRCTL, rx_thr_len));
327 /* enable USB interrupts */
328 enable_device_interrupts();
331 static void core_init(void)
334 DCTL
|= DCTL_sftdiscon
;
335 /* Select UTMI+ 16 */
336 GUSBCFG
|= GUSBCFG_phy_if
;
338 /* fixme: the current code is for internal DMA only, the clip+ architecture
339 * define the internal DMA model */
340 /* Set burstlen and enable DMA*/
341 GAHBCFG
= (GAHBCFG_INT_DMA_BURST_INCR4
<< GAHBCFG_hburstlen_bitp
)
342 | GAHBCFG_dma_enable
;
343 /* Disable HNP and SRP, not sure it's useful because we already forced dev mode */
344 GUSBCFG
&= ~(GUSBCFG_srpcap
| GUSBCFG_hnpcapp
);
346 /* perform device model specific init */
350 DCTL
&= ~DCTL_sftdiscon
;
353 static void enable_global_interrupts(void)
355 VIC_INT_ENABLE
= INTERRUPT_USB
;
356 GAHBCFG
|= GAHBCFG_glblintrmsk
;
359 static void disable_global_interrupts(void)
361 GAHBCFG
&= ~GAHBCFG_glblintrmsk
;
362 VIC_INT_EN_CLEAR
= INTERRUPT_USB
;
365 void usb_drv_init(void)
367 logf("usb_drv_init");
368 /* Enable PHY and clocks (but leave pullups disabled) */
370 logf("usb: synopsis id: %lx", GSNPSID
);
373 /* Enable global interrupts */
374 enable_global_interrupts();
377 void usb_drv_exit(void)
379 logf("usb_drv_exit");
381 disable_global_interrupts();
384 static void dump_regs(void)
386 logf("DSTS: %lx", DSTS
);
387 logf("DOEPCTL0=%lx", DOEPCTL(0));
388 logf("DOEPTSIZ=%lx", DOEPTSIZ(0));
389 logf("DIEPCTL0=%lx", DIEPCTL(0));
390 logf("DOEPMSK=%lx", DOEPMSK
);
391 logf("DIEPMSK=%lx", DIEPMSK
);
392 logf("DAINTMSK=%lx", DAINTMSK
);
393 logf("DAINT=%lx", DAINT
);
394 logf("GINTSTS=%lx", GINTSTS
);
395 logf("GINTMSK=%lx", GINTMSK
);
396 logf("DCTL=%lx", DCTL
);
397 logf("GAHBCFG=%lx", GAHBCFG
);
398 logf("GUSBCFG=%lx", GUSBCFG
);
399 logf("DCFG=%lx", DCFG
);
400 logf("DTHRCTL=%lx", DTHRCTL
);
403 static bool handle_reset(void)
405 logf("usb: bus reset");
408 /* Clear the Remote Wakeup Signalling */
409 DCTL
&= ~DCTL_rmtwkupsig
;
412 flush_tx_fifos(0x10);
416 /* Reset Device Address */
417 DCFG
&= bitm(DCFG
, devadr
);
419 usb_core_bus_reset();
424 static bool handle_enum_done(void)
426 logf("usb: enum done");
429 switch(extract(DSTS
, enumspd
))
431 case DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ
:
434 case DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ
:
435 case DSTS_ENUMSPD_FS_PHY_48MHZ
:
438 case DSTS_ENUMSPD_LS_PHY_6MHZ
:
439 panicf("usb: LS is not supported");
442 /* fixme: change EP0 mps here */
448 static bool handle_in_ep_int(void)
450 panicf("usb: in ep int");
454 static bool handle_out_ep_int(void)
456 panicf("usb: out ep int");
460 /* interrupt service routine */
463 /* some bits in GINTSTS can be set even though we didn't enable the interrupt source
464 * so AND it with the actual mask */
465 unsigned long sts
= GINTSTS
& GINTMSK
;
466 unsigned long handled_one
= 0; /* mask of all listed one (either handled or not) */
468 #define HANDLED_CASE(bitmask, callfn) \
469 handled_one |= bitmask; \
476 #define UNHANDLED_CASE(bitmask) \
477 handled_one |= bitmask; \
482 HANDLED_CASE(GINTMSK_usbreset
, handle_reset
)
483 HANDLED_CASE(GINTMSK_enumdone
, handle_enum_done
)
484 HANDLED_CASE(GINTMSK_inepintr
, handle_in_ep_int
)
485 HANDLED_CASE(GINTMSK_outepintr
, handle_out_ep_int
)
488 UNHANDLED_CASE(GINTMSK_otgintr
)
489 UNHANDLED_CASE(GINTMSK_conidstschng
)
490 UNHANDLED_CASE(GINTMSK_disconnect
)
493 if(sts
& ~handled_one
)
501 panicf("unhandled usb int: %lx", sts
);
504 panicf("error in usb int: %lx", sts
);
507 int usb_drv_port_speed(void)
512 int usb_drv_request_endpoint(int type
, int dir
)
519 void usb_drv_release_endpoint(int ep
)
524 void usb_drv_cancel_all_transfers(void)
528 int usb_drv_recv(int ep
, void *ptr
, int len
)
536 int usb_drv_send(int ep
, void *ptr
, int len
)
544 int usb_drv_send_nonblocking(int ep
, void *ptr
, int len
)
553 void usb_drv_set_test_mode(int mode
)
558 void usb_drv_set_address(int address
)
563 void usb_drv_stall(int ep
, bool stall
, bool in
)
570 bool usb_drv_stalled(int ep
, bool in
)