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 */
93 CGU_USB
= 1<<5 /* enable */
94 | (CLK_DIV(AS3525_PLLA_FREQ
, 60000000)) << 2
95 | 1; /* source = PLLA */
100 /* 3) clear "stop pclk" */
103 /* 4) clear "power clamp" */
106 /* 5) clear "reset power down module" */
109 /* 6) set "power on program done" */
110 DCTL
|= DCTL_pwronprgdone
;
112 /* 7) core soft reset */
113 GRSTCTL
|= GRSTCTL_csftrst
;
115 /* 8) hclk soft reset */
116 GRSTCTL
|= GRSTCTL_hsftrst
;
118 /* 9) flush and reset everything */
121 /* 10) force device mode*/
122 GUSBCFG
&= ~GUSBCFG_force_host_mode
;
123 GUSBCFG
|= GUSBCFG_force_device_mode
;
125 /* 11) Do something that is probably CCU related but undocumented*/
126 CCU_USB_THINGY
&= ~0x1000;
128 CCU_USB_THINGY
&= ~0x300000;
130 /* 12) reset usb core parameters (dev addr, speed, ...) */
135 static void enable_device_interrupts(void)
137 /* Clear any pending interrupt */
138 GINTSTS
= 0xffffffff;
139 /* Clear any pending otg interrupt */
140 GOTGINT
= 0xffffffff;
141 /* Enable interrupts */
142 GINTMSK
= GINTMSK_usbreset
147 | GINTMSK_disconnect
;
150 static void flush_tx_fifos(int nums
)
154 GRSTCTL
= (nums
<< GRSTCTL_txfnum_bitp
)
155 | GRSTCTL_txfflsh_flush
;
156 while(GRSTCTL
& GRSTCTL_txfflsh_flush
&& i
< 0x300)
158 if(GRSTCTL
& GRSTCTL_txfflsh_flush
)
159 panicf("usb: hang of flush tx fifos (%x)", nums
);
160 /* wait 3 phy clocks */
164 static void reset_endpoints(void)
167 /* disable all endpoints except EP0 */
168 FOR_EACH_IN_EP(i
, ep
)
169 if(DIEPCTL(ep
) & DEPCTL_epena
)
170 DIEPCTL(ep
) = DEPCTL_epdis
| DEPCTL_snak
;
174 FOR_EACH_OUT_EP(i
, ep
)
175 if(DOEPCTL(ep
) & DEPCTL_epena
)
176 DOEPCTL(ep
) = DEPCTL_epdis
| DEPCTL_snak
;
179 /* Setup EP0 OUT with the following parameters:
181 * setup packet count = 1
183 * Setup EP0 IN/OUT with 64 byte maximum packet size and activate both. Enable transfer on EP0 OUT
186 DOEPTSIZ(0) = (1 << DEPTSIZ0_supcnt_bitp
)
187 | (1 << DEPTSIZ0_pkcnt_bitp
)
191 clean_dcache_range((void*)&ep0_setup_pkt
, sizeof ep0_setup_pkt
); /* force write back */
192 DOEPDMA(0) = (unsigned long)&ep0_setup_pkt
; /* virtual address=physical address */
194 /* Enable endpoint, clear nak */
195 DOEPCTL(0) = DEPCTL_epena
| DEPCTL_cnak
| DEPCTL_usbactep
196 | (DEPCTL_MPS_64
<< DEPCTL_mps_bitp
);
198 /* 64 bytes packet size, active endpoint */
199 DIEPCTL(0) = (DEPCTL_MPS_64
<< DEPCTL_mps_bitp
)
203 static void core_dev_init(void)
205 unsigned int num_in_ep
= 0;
206 unsigned int num_out_ep
= 0;
208 /* Restart the phy clock */
210 /* Set phy speed : high speed */
211 DCFG
= (DCFG
& ~bitm(DCFG
, devspd
)) | DCFG_devspd_hs_phy_hs
;
213 /* Check hardware capabilities */
214 if(extract(GHWCFG2
, arch
) != GHWCFG2_ARCH_INTERNAL_DMA
)
215 panicf("usb: wrong architecture (%ld)", extract(GHWCFG2
, arch
));
216 if(extract(GHWCFG2
, hs_phy_type
) != GHWCFG2_PHY_TYPE_UTMI
)
217 panicf("usb: wrong HS phy type (%ld)", extract(GHWCFG2
, hs_phy_type
));
218 if(extract(GHWCFG2
, fs_phy_type
) != GHWCFG2_PHY_TYPE_UNSUPPORTED
)
219 panicf("usb: wrong FS phy type (%ld)", extract(GHWCFG2
, fs_phy_type
));
220 if(extract(GHWCFG4
, utmi_phy_data_width
) != 0x2)
221 panicf("usb: wrong utmi data width (%ld)", extract(GHWCFG4
, utmi_phy_data_width
));
222 if(!(GHWCFG4
& GHWCFG4_ded_fifo_en
)) /* it seems to be multiple tx fifo support */
223 panicf("usb: no multiple tx fifo");
225 #ifdef USE_CUSTOM_FIFO_LAYOUT
226 if(!(GHWCFG2
& GHWCFG2_dyn_fifo
))
227 panicf("usb: no dynamic fifo");
228 if(GRXFSIZ
!= DATA_FIFO_DEPTH
)
229 panicf("usb: wrong data fifo size");
230 #endif /* USE_CUSTOM_FIFO_LAYOUT */
232 /* do some logging */
233 logf("hwcfg1: %08lx", GHWCFG1
);
234 logf("hwcfg2: %08lx", GHWCFG2
);
235 logf("hwcfg3: %08lx", GHWCFG3
);
236 logf("hwcfg4: %08lx", GHWCFG4
);
238 logf("%ld endpoints", extract(GHWCFG2
, num_ep
));
241 for(i
= 0; i
< extract(GHWCFG2
, num_ep
); i
++)
243 bool in
= false, out
= false;
244 switch((GHWCFG1
>> GHWCFG1_epdir_bitp(i
)) & GHWCFG1_epdir_bits
)
246 case GHWCFG1_EPDIR_BIDIR
: in
= out
= true; break;
247 case GHWCFG1_EPDIR_IN
: in
= true; break;
248 case GHWCFG1_EPDIR_OUT
: out
= true; break;
249 default: panicf("usb: invalid epdir");
251 /* don't count EP0 which is special and always bidirectional */
256 logf(" EP%d: IN=%s OUT=%s", i
, in
? "yes" : "no", out
? "yes" : "no");
259 if(num_in_ep
!= extract(GHWCFG4
, num_in_ep
))
260 panicf("usb: num in ep mismatch(%d,%lu)", num_in_ep
, extract(GHWCFG4
, num_in_ep
));
261 if(num_in_ep
!= NUM_IN_EP
)
262 panicf("usb: num in ep static mismatch(%u,%u)", num_in_ep
, NUM_IN_EP
);
263 if(num_out_ep
!= NUM_OUT_EP
)
264 panicf("usb: num out ep static mismatch(%u,%u)", num_out_ep
, NUM_OUT_EP
);
266 logf("%d in ep, %d out ep", num_in_ep
, num_out_ep
);
269 logf(" tot fifo sz: %lx", extract(GHWCFG3
, dfifo_len
));
270 logf(" rx fifo: [%04x,+%4lx]", 0, GRXFSIZ
);
271 logf(" nptx fifo: [%04lx,+%4lx]", GET_FIFOSIZE_START_ADR(GNPTXFSIZ
),
272 GET_FIFOSIZE_DEPTH(GNPTXFSIZ
));
274 #ifdef USE_CUSTOM_FIFO_LAYOUT
276 /* Organize FIFO as follow:
277 * 0 -> rxfsize : RX fifo
278 * rxfsize -> rxfsize + nptxfsize : TX fifo for first IN ep
279 * rxfsize + nptxfsize -> rxfsize + 2 * nptxfsize : TX fifo for second IN ep
280 * rxfsize + 2 * nptxfsize -> rxfsize + 3 * nptxfsize : TX fifo for third IN ep
284 unsigned short adr
= 0;
285 unsigned short depth
= RX_FIFO_SIZE
;
288 depth
= NPTX_FIFO_SIZE
;
289 GNPTXFSIZ
= MAKE_FIFOSIZE_DATA(adr
, depth
);
292 for(i
= 1; i
<= NUM_IN_EP
; i
++)
294 depth
= EPTX_FIFO_SIZE
;
295 DIEPTXFSIZ(i
) = MAKE_FIFOSIZE_DATA(adr
, depth
);
299 if(adr
> DATA_FIFO_DEPTH
)
300 panicf("usb: total data fifo size exceeded");
301 #endif /* USE_CUSTOM_FIFO_LAYOUT */
303 for(i
= 1; i
<= NUM_IN_EP
; i
++)
305 logf(" dieptx fifo(%2u): [%04lx,+%4lx]", i
,
306 GET_FIFOSIZE_START_ADR(DIEPTXFSIZ(i
)),
307 GET_FIFOSIZE_DEPTH(DIEPTXFSIZ(i
)));
310 /* Setup interrupt masks for endpoints */
311 /* Setup interrupt masks */
312 DOEPMSK
= DOEPINT_setup
| DOEPINT_xfercompl
| DOEPINT_ahberr
313 | DOEPINT_epdisabled
;
314 DIEPMSK
= DIEPINT_xfercompl
| DIEPINT_timeout
315 | DIEPINT_epdisabled
| DIEPINT_ahberr
;
316 DAINTMSK
= 0xffffffff;
320 /* fixme: threshold tweaking only takes place if we use multiple tx fifos it seems */
321 /* only dump them for now, leave threshold disabled */
323 logf("threshold control:");
324 logf(" non_iso_thr_en: %d", (DTHRCTL & DTHRCTL_non_iso_thr_en) ? 1 : 0);
325 logf(" iso_thr_en: %d", (DTHRCTL & DTHRCTL_iso_thr_en) ? 1 : 0);
326 logf(" tx_thr_len: %lu", extract(DTHRCTL, tx_thr_len));
327 logf(" rx_thr_en: %d", (DTHRCTL & DTHRCTL_rx_thr_en) ? 1 : 0);
328 logf(" rx_thr_len: %lu", extract(DTHRCTL, rx_thr_len));
331 /* enable USB interrupts */
332 enable_device_interrupts();
335 static void core_init(void)
338 DCTL
|= DCTL_sftdiscon
;
339 /* Select UTMI+ 16 */
340 GUSBCFG
|= GUSBCFG_phy_if
;
342 /* fixme: the current code is for internal DMA only, the clip+ architecture
343 * define the internal DMA model */
344 /* Set burstlen and enable DMA*/
345 GAHBCFG
= (GAHBCFG_INT_DMA_BURST_INCR4
<< GAHBCFG_hburstlen_bitp
)
346 | GAHBCFG_dma_enable
;
347 /* Disable HNP and SRP, not sure it's useful because we already forced dev mode */
348 GUSBCFG
&= ~(GUSBCFG_srpcap
| GUSBCFG_hnpcapp
);
350 /* perform device model specific init */
354 DCTL
&= ~DCTL_sftdiscon
;
357 static void enable_global_interrupts(void)
359 VIC_INT_ENABLE
= INTERRUPT_USB
;
360 GAHBCFG
|= GAHBCFG_glblintrmsk
;
363 static void disable_global_interrupts(void)
365 GAHBCFG
&= ~GAHBCFG_glblintrmsk
;
366 VIC_INT_EN_CLEAR
= INTERRUPT_USB
;
369 void usb_drv_init(void)
371 logf("usb_drv_init");
372 /* Enable PHY and clocks (but leave pullups disabled) */
374 logf("usb: synopsis id: %lx", GSNPSID
);
377 /* Enable global interrupts */
378 enable_global_interrupts();
381 void usb_drv_exit(void)
383 logf("usb_drv_exit");
385 disable_global_interrupts();
388 static void dump_regs(void)
390 logf("DSTS: %lx", DSTS
);
391 logf("DOEPCTL0=%lx", DOEPCTL(0));
392 logf("DOEPTSIZ=%lx", DOEPTSIZ(0));
393 logf("DIEPCTL0=%lx", DIEPCTL(0));
394 logf("DOEPMSK=%lx", DOEPMSK
);
395 logf("DIEPMSK=%lx", DIEPMSK
);
396 logf("DAINTMSK=%lx", DAINTMSK
);
397 logf("DAINT=%lx", DAINT
);
398 logf("GINTSTS=%lx", GINTSTS
);
399 logf("GINTMSK=%lx", GINTMSK
);
400 logf("DCTL=%lx", DCTL
);
401 logf("GAHBCFG=%lx", GAHBCFG
);
402 logf("GUSBCFG=%lx", GUSBCFG
);
403 logf("DCFG=%lx", DCFG
);
404 logf("DTHRCTL=%lx", DTHRCTL
);
407 static bool handle_reset(void)
409 logf("usb: bus reset");
412 /* Clear the Remote Wakeup Signalling */
413 DCTL
&= ~DCTL_rmtwkupsig
;
416 flush_tx_fifos(0x10);
420 /* Reset Device Address */
421 DCFG
&= bitm(DCFG
, devadr
);
423 usb_core_bus_reset();
428 static bool handle_enum_done(void)
430 logf("usb: enum done");
433 switch(extract(DSTS
, enumspd
))
435 case DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ
:
438 case DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ
:
439 case DSTS_ENUMSPD_FS_PHY_48MHZ
:
442 case DSTS_ENUMSPD_LS_PHY_6MHZ
:
443 panicf("usb: LS is not supported");
446 /* fixme: change EP0 mps here */
452 static bool handle_in_ep_int(void)
454 panicf("usb: in ep int");
458 static bool handle_out_ep_int(void)
460 panicf("usb: out ep int");
464 /* interrupt service routine */
467 /* some bits in GINTSTS can be set even though we didn't enable the interrupt source
468 * so AND it with the actual mask */
469 unsigned long sts
= GINTSTS
& GINTMSK
;
470 unsigned long handled_one
= 0; /* mask of all listed one (either handled or not) */
472 #define HANDLED_CASE(bitmask, callfn) \
473 handled_one |= bitmask; \
480 #define UNHANDLED_CASE(bitmask) \
481 handled_one |= bitmask; \
486 HANDLED_CASE(GINTMSK_usbreset
, handle_reset
)
487 HANDLED_CASE(GINTMSK_enumdone
, handle_enum_done
)
488 HANDLED_CASE(GINTMSK_inepintr
, handle_in_ep_int
)
489 HANDLED_CASE(GINTMSK_outepintr
, handle_out_ep_int
)
492 UNHANDLED_CASE(GINTMSK_otgintr
)
493 UNHANDLED_CASE(GINTMSK_conidstschng
)
494 UNHANDLED_CASE(GINTMSK_disconnect
)
497 if(sts
& ~handled_one
)
505 panicf("unhandled usb int: %lx", sts
);
508 panicf("error in usb int: %lx", sts
);
511 int usb_drv_port_speed(void)
516 int usb_drv_request_endpoint(int type
, int dir
)
523 void usb_drv_release_endpoint(int ep
)
528 void usb_drv_cancel_all_transfers(void)
532 int usb_drv_recv(int ep
, void *ptr
, int len
)
540 int usb_drv_send(int ep
, void *ptr
, int len
)
548 int usb_drv_send_nonblocking(int ep
, void *ptr
, int len
)
557 void usb_drv_set_test_mode(int mode
)
562 void usb_drv_set_address(int address
)
567 void usb_drv_stall(int ep
, bool stall
, bool in
)
574 bool usb_drv_stalled(int ep
, bool in
)