-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
a64_ehci.c
5434 lines (4480 loc) · 170 KB
/
a64_ehci.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/****************************************************************************
* arch/arm64/src/a64/a64_ehci.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/wqueue.h>
#include <nuttx/signal.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbhost.h>
#include <nuttx/usb/ehci.h>
#include <nuttx/usb/usbhost_devaddr.h>
#include <nuttx/usb/usbhost_trace.h>
#include "arm64_internal.h"
#include "arm64_gic.h"
#include "chip.h"
#include "hardware/a64_usbotg.h"
#define CONFIG_A64_EHCI_REGDEBUG // TODO CONFIG_A64_EHCI_REGDEBUG
#define ARMV7M_DCACHE_LINESIZE 32 // TODO ARMV7M_DCACHE_LINESIZE
#define up_udelay(x) up_mdelay(x) // TODO up_udelay
#if defined(CONFIG_A64_USBOTG) && defined(CONFIG_USBHOST)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* Pre-requisites */
#if !defined(CONFIG_SCHED_WORKQUEUE)
# error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
#elif !defined(CONFIG_SCHED_HPWORK)
# error Hi-priority work queue support is required (CONFIG_SCHED_HPWORK)
#endif
/* Configurable number of Queue Head (QH) structures. The default is one per
* Root hub port plus one for EP0.
*/
#ifndef CONFIG_A64_EHCI_NQHS
# define CONFIG_A64_EHCI_NQHS (A64_EHCI_NRHPORT + 1)
#endif
/* Configurable number of Queue Element Transfer Descriptor (qTDs). The
* default is one per root hub plus three from EP0.
*/
#ifndef CONFIG_A64_EHCI_NQTDS
# define CONFIG_A64_EHCI_NQTDS (A64_EHCI_NRHPORT + 3)
#endif
/* Buffers must be aligned to the cache line size */
#define DCACHE_LINEMASK (ARMV7M_DCACHE_LINESIZE -1)
/* Configurable size of a request/descriptor buffers */
#ifndef CONFIG_A64_EHCI_BUFSIZE
# define CONFIG_A64_EHCI_BUFSIZE 128
#endif
#define A64_EHCI_BUFSIZE \
((CONFIG_A64_EHCI_BUFSIZE + DCACHE_LINEMASK) & ~DCACHE_LINEMASK)
/* Debug options */
#ifndef CONFIG_DEBUG_USB_INFO
# undef CONFIG_A64_EHCI_REGDEBUG
#endif
/* Isochronous transfers are not currently supported */
#undef CONFIG_USBHOST_ISOC_DISABLE
#define CONFIG_USBHOST_ISOC_DISABLE 1
/* Registers ****************************************************************
* Traditionally, NuttX specifies register locations using individual
* register offsets from a base address. That tradition is broken here and,
* instead, register blocks are represented as structures. This is done here
* because, in principle, EHCI operational register address may not be known
* at compile time; the operational registers lie at an offset specified in
* the 'caplength' byte of the Host Controller Capability Registers.
*
* However, for the case of the A64 EHCI, we know apriori that locations
* of these register blocks.
*/
/* Host Controller Capability Registers */
#define HCCR ((struct ehci_hccr_s *)A64_USBOTG_HCCR_BASE)
/* Host Controller Operational Registers */
#define HCOR ((volatile struct ehci_hcor_s *)A64_USBOTG_HCOR_BASE)
/* Interrupts ***************************************************************
* This is the set of interrupts handled by this driver.
*/
#define EHCI_HANDLED_INTS (EHCI_INT_USBINT | EHCI_INT_USBERRINT | \
EHCI_INT_PORTSC | EHCI_INT_SYSERROR | \
EHCI_INT_AAINT)
/* The periodic frame list is a 4K-page aligned array of Frame List Link
* pointers. The length of the frame list may be programmable. The
* programmability of the periodic frame list is exported to system software
* via the HCCPARAMS register. If non-programmable, the length is 1024
* elements. If programmable, the length can be selected by system software
* as one of 256, 512, or 1024 elements.
*/
#define FRAME_LIST_SIZE 1024
/* DMA **********************************************************************/
/* For now, we are assuming an identity mapping between physical and virtual
* address spaces.
*/
#define a64_physramaddr(a) (a)
// TODO: Check pointer
#define a64_virtramaddr(a) ((unsigned long)(a))
/* USB trace ****************************************************************/
#ifdef HAVE_USBHOST_TRACE
# define TR_FMT1 false
# define TR_FMT2 true
# define TRENTRY(id,fmt1,string) {string}
# define TRACE1_FIRST ((int)__TRACE1_BASEVALUE + 1)
# define TRACE1_INDEX(id) ((int)(id) - TRACE1_FIRST)
# define TRACE1_NSTRINGS TRACE1_INDEX(__TRACE1_NSTRINGS)
# define TRACE2_FIRST ((int)__TRACE1_NSTRINGS + 1)
# define TRACE2_INDEX(id) ((int)(id) - TRACE2_FIRST)
# define TRACE2_NSTRINGS TRACE2_INDEX(__TRACE2_NSTRINGS)
#endif
/* Port numbers */
#define RHPNDX(rh) ((rh)->hport.hport.port)
#define RHPORT(rh) (RHPNDX(rh)+1)
/****************************************************************************
* Private Types
****************************************************************************/
/* Internal representation of the EHCI Queue Head (QH) */
struct a64_epinfo_s;
struct a64_qh_s
{
/* Fields visible to hardware */
struct ehci_qh_s hw; /* Hardware representation of the queue head */
/* Internal fields used by the EHCI driver */
struct a64_epinfo_s *epinfo; /* Endpoint used for the transfer */
uint32_t fqp; /* First qTD in the list (physical address) */
uint8_t pad[8]; /* Padding to assure 32-byte alignment */
uint8_t pad2[96 - 72]; // TODO: Pad from 72 to 96 bytes for 64-bit platform
};
/* Internal representation of the EHCI Queue Element Transfer Descriptor
* (qTD)
*/
struct a64_qtd_s
{
/* Fields visible to hardware */
struct ehci_qtd_s hw; /* Hardware representation of the queue head */
/* Internal fields used by the EHCI driver */
};
/* The following is used to manage lists of free QHs and qTDs */
struct a64_list_s
{
struct a64_list_s *flink; /* Link to next entry in the list
* Variable length entry data follows
*/
};
/* List traversal call-out functions */
typedef int (*foreach_qh_t)(struct a64_qh_s *qh, uint32_t **bp,
void *arg);
typedef int (*foreach_qtd_t)(struct a64_qtd_s *qtd, uint32_t **bp,
void *arg);
/* This structure describes one endpoint. */
struct a64_epinfo_s
{
uint8_t epno:7; /* Endpoint number */
uint8_t dirin:1; /* 1:IN endpoint 0:OUT endpoint */
uint8_t devaddr:7; /* Device address */
uint8_t toggle:1; /* Next data toggle */
#ifndef CONFIG_USBHOST_INT_DISABLE
uint8_t interval; /* Polling interval */
#endif
uint8_t status; /* Retained token status bits (for debug purposes) */
volatile bool iocwait; /* TRUE: Thread is waiting for transfer completion */
uint16_t maxpacket:11; /* Maximum packet size */
uint16_t xfrtype:2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */
uint16_t speed:2; /* See USB_*_SPEED definitions in ehci.h */
int result; /* The result of the transfer */
uint32_t xfrd; /* On completion, will hold the number of bytes transferred */
sem_t iocsem; /* Semaphore used to wait for transfer completion */
#ifdef CONFIG_USBHOST_ASYNCH
usbhost_asynch_t callback; /* Transfer complete callback */
void *arg; /* Argument that accompanies the callback */
#endif
};
/* This structure retains the state of one root hub port */
struct a64_rhport_s
{
/* Common device fields. This must be the first thing defined in the
* structure so that it is possible to simply cast from struct usbhost_s
* to struct a64_rhport_s.
*/
struct usbhost_driver_s drvr;
/* Root hub port status */
volatile bool connected; /* Connected to device */
volatile bool lowspeed; /* Low speed device attached */
struct a64_epinfo_s ep0; /* EP0 endpoint info */
/* This is the hub port description understood by class drivers */
struct usbhost_roothubport_s hport;
};
/* This structure retains the overall state of the USB host controller */
struct a64_ehci_s
{
volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */
mutex_t lock; /* Support mutually exclusive access */
sem_t pscsem; /* Semaphore to wait for port status change events */
struct a64_epinfo_s ep0; /* Endpoint 0 */
struct a64_list_s *qhfree; /* List of free Queue Head (QH) structures */
struct a64_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */
struct work_s work; /* Supports interrupt bottom half */
#ifdef CONFIG_USBHOST_HUB
/* Used to pass external hub port events */
volatile struct usbhost_hubport_s *hport;
#endif
/* Root hub ports */
struct a64_rhport_s rhport[A64_EHCI_NRHPORT];
};
#ifdef HAVE_USBHOST_TRACE
/* USB trace codes */
enum usbhost_trace1codes_e
{
__TRACE1_BASEVALUE = 0, /* This will force the first value to be 1 */
EHCI_TRACE1_SYSTEMERROR, /* EHCI ERROR: System error */
EHCI_TRACE1_QTDFOREACH_FAILED, /* EHCI ERROR: a64_qtd_foreach failed */
EHCI_TRACE1_QHALLOC_FAILED, /* EHCI ERROR: Failed to allocate a QH */
EHCI_TRACE1_BUFTOOBIG, /* EHCI ERROR: Buffer too big */
EHCI_TRACE1_REQQTDALLOC_FAILED, /* EHCI ERROR: Failed to allocate request qTD */
EHCI_TRACE1_ADDBPL_FAILED, /* EHCI ERROR: a64_qtd_addbpl failed */
EHCI_TRACE1_DATAQTDALLOC_FAILED, /* EHCI ERROR: Failed to allocate data buffer qTD */
EHCI_TRACE1_DEVDISCONNECTED, /* EHCI ERROR: Device disconnected */
EHCI_TRACE1_QHCREATE_FAILED, /* EHCI ERROR: a64_qh_create failed */
EHCI_TRACE1_QTDSETUP_FAILED, /* EHCI ERROR: a64_qtd_setupphase failed */
EHCI_TRACE1_QTDDATA_FAILED, /* EHCI ERROR: a64_qtd_dataphase failed */
EHCI_TRACE1_QTDSTATUS_FAILED, /* EHCI ERROR: a64_qtd_statusphase failed */
EHCI_TRACE1_TRANSFER_FAILED, /* EHCI ERROR: Transfer failed */
EHCI_TRACE1_QHFOREACH_FAILED, /* EHCI ERROR: a64_qh_foreach failed: */
EHCI_TRACE1_SYSERR_INTR, /* EHCI: Host System Error Interrupt */
EHCI_TRACE1_USBERR_INTR, /* EHCI: USB Error Interrupt (USBERRINT) Interrupt */
EHCI_TRACE1_EPALLOC_FAILED, /* EHCI ERROR: Failed to allocate EP info structure */
EHCI_TRACE1_BADXFRTYPE, /* EHCI ERROR: Support for transfer type not implemented */
EHCI_TRACE1_HCHALTED_TIMEOUT, /* EHCI ERROR: Timed out waiting for HCHalted */
EHCI_TRACE1_QHPOOLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the QH pool */
EHCI_TRACE1_QTDPOOLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the qTD pool */
EHCI_TRACE1_PERFLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the periodic frame list */
EHCI_TRACE1_RESET_FAILED, /* EHCI ERROR: a64_reset failed */
EHCI_TRACE1_RUN_FAILED, /* EHCI ERROR: EHCI Failed to run */
EHCI_TRACE1_IRQATTACH_FAILED, /* EHCI ERROR: Failed to attach IRQ */
#ifdef HAVE_USBHOST_TRACE_VERBOSE
EHCI_VTRACE1_PORTSC_CSC, /* EHCI Connect Status Change */
EHCI_VTRACE1_PORTSC_CONNALREADY, /* EHCI Already connected */
EHCI_VTRACE1_PORTSC_DISCALREADY, /* EHCI Already disconnected */
EHCI_VTRACE1_TOPHALF, /* EHCI Interrupt top half */
EHCI_VTRACE1_AAINTR, /* EHCI Async Advance Interrupt */
EHCI_VTRACE1_CLASSENUM, /* EHCI Hub port CLASS enumeration */
EHCI_VTRACE1_USBINTR, /* EHCI USB Interrupt (USBINT) Interrupt */
EHCI_VTRACE1_ENUM_DISCONN, /* EHCI Enumeration not connected */
EHCI_VTRACE1_INITIALIZING, /* EHCI Initializing EHCI Stack */
EHCI_VTRACE1_HCCPARAMS, /* EHCI HCCPARAMS */
EHCI_VTRACE1_INIITIALIZED, /* EHCI USB EHCI Initialized */
#endif
__TRACE1_NSTRINGS, /* Separates the format 1 from the format 2 strings */
EHCI_TRACE2_EPSTALLED, /* EHCI EP Stalled */
EHCI_TRACE2_EPIOERROR, /* EHCI ERROR: EP TOKEN */
EHCI_TRACE2_CLASSENUM_FAILED, /* EHCI usbhost_enumerate() failed */
#ifdef HAVE_USBHOST_TRACE_VERBOSE
EHCI_VTRACE2_ASYNCXFR, /* EHCI Async transfer */
EHCI_VTRACE2_INTRXFR, /* EHCI Interrupt Transfer */
EHCI_VTRACE2_IOCCHECK, /* EHCI IOC */
EHCI_VTRACE2_PORTSC, /* EHCI PORTSC */
EHCI_VTRACE2_PORTSC_CONNECTED, /* EHCI RHPort connected */
EHCI_VTRACE2_PORTSC_DISCONND, /* EHCI RHport disconnected */
EHCI_VTRACE2_MONWAKEUP, /* EHCI RHPort connected wakeup */
EHCI_VTRACE2_EPALLOC, /* EHCI EPALLOC */
EHCI_VTRACE2_CTRLINOUT, /* EHCI CTRLIN/OUT */
EHCI_VTRACE2_HCIVERSION, /* EHCI HCIVERSION */
EHCI_VTRACE2_HCSPARAMS, /* EHCI HCSPARAMS */
#endif
__TRACE2_NSTRINGS /* Total number of enumeration values */
};
/* USB trace data structure */
struct a64_ehci_trace_s
{
#if 0
uint16_t id;
bool fmt2;
#endif
const char *string;
};
#endif /* HAVE_USBHOST_TRACE */
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Register operations ******************************************************/
static uint16_t a64_read16(const uint8_t *addr);
static uint32_t a64_read32(const uint8_t *addr);
#if 0 /* Not used */
static void a64_write16(uint16_t memval, uint8_t *addr);
static void a64_write32(uint32_t memval, uint8_t *addr);
#endif
#ifdef CONFIG_ENDIAN_BIG
static uint16_t a64_swap16(uint16_t value);
static uint32_t a64_swap32(uint32_t value);
#else
# define a64_swap16(value) (value)
# define a64_swap32(value) (value)
#endif
#ifdef CONFIG_A64_EHCI_REGDEBUG
static void a64_printreg(volatile uint32_t *regaddr, uint32_t regval,
bool iswrite);
static void a64_checkreg(volatile uint32_t *regaddr, uint32_t regval,
bool iswrite);
static uint32_t a64_getreg(volatile uint32_t *regaddr);
static void a64_putreg(uint32_t regval, volatile uint32_t *regaddr);
#else
static inline uint32_t a64_getreg(volatile uint32_t *regaddr);
static inline void a64_putreg(uint32_t regval, volatile uint32_t *regaddr);
#endif
static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits,
unsigned int delay);
/* Allocators ***************************************************************/
static struct a64_qh_s *a64_qh_alloc(void);
static void a64_qh_free(struct a64_qh_s *qh);
static struct a64_qtd_s *a64_qtd_alloc(void);
static void a64_qtd_free(struct a64_qtd_s *qtd);
/* List Management **********************************************************/
static int a64_qh_foreach(struct a64_qh_s *qh, uint32_t **bp,
foreach_qh_t handler, void *arg);
static int a64_qtd_foreach(struct a64_qh_s *qh, foreach_qtd_t handler,
void *arg);
static int a64_qtd_discard(struct a64_qtd_s *qtd, uint32_t **bp,
void *arg);
static int a64_qh_discard(struct a64_qh_s *qh);
/* Cache Operations *********************************************************/
#if 0 /* Not used */
static int a64_qtd_invalidate(struct a64_qtd_s *qtd, uint32_t **bp,
void *arg);
static int a64_qh_invalidate(struct a64_qh_s *qh);
#endif
static int a64_qtd_flush(struct a64_qtd_s *qtd, uint32_t **bp,
void *arg);
static int a64_qh_flush(struct a64_qh_s *qh);
/* Endpoint Transfer Handling ***********************************************/
#ifdef CONFIG_A64_EHCI_REGDEBUG
static void a64_qtd_print(struct a64_qtd_s *qtd);
static void a64_qh_print(struct a64_qh_s *qh);
static int a64_qtd_dump(struct a64_qtd_s *qtd, uint32_t **bp, void *arg);
static int a64_qh_dump(struct a64_qh_s *qh, uint32_t **bp, void *arg);
#else
# define a64_qtd_print(qtd)
# define a64_qh_print(qh)
# define a64_qtd_dump(qtd, bp, arg) //// OK
# define a64_qh_dump(qh, bp, arg) //// OK
#endif
static inline uint8_t a64_ehci_speed(uint8_t usbspeed);
static int a64_ioc_setup(struct a64_rhport_s *rhport,
struct a64_epinfo_s *epinfo);
static int a64_ioc_wait(struct a64_epinfo_s *epinfo);
static void a64_qh_enqueue(struct a64_qh_s *qhead,
struct a64_qh_s *qh);
static struct a64_qh_s *a64_qh_create(struct a64_rhport_s *rhport,
struct a64_epinfo_s *epinfo);
static int a64_qtd_addbpl(struct a64_qtd_s *qtd, const void *buffer,
size_t buflen);
static struct a64_qtd_s *a64_qtd_setupphase(
struct a64_epinfo_s *epinfo, const struct usb_ctrlreq_s *req);
static struct a64_qtd_s *a64_qtd_dataphase(struct a64_epinfo_s *epinfo,
void *buffer, int buflen, uint32_t tokenbits);
static struct a64_qtd_s *a64_qtd_statusphase(uint32_t tokenbits);
// static ssize_t a64a64_virtramaddr_async_setup(
// struct a64_rhport_s *rhport, struct a64_epinfo_s *epinfo,
// const struct usb_ctrlreq_s *req, uint8_t *buffer, size_t buflen);
#ifndef CONFIG_USBHOST_INT_DISABLE
static int a64_intr_setup(struct a64_rhport_s *rhport,
struct a64_epinfo_s *epinfo, uint8_t *buffer, size_t buflen);
#endif
static ssize_t a64_transfer_wait(struct a64_epinfo_s *epinfo);
#ifdef CONFIG_USBHOST_ASYNCH
static inline int a64_ioc_async_setup(struct a64_rhport_s *rhport,
struct a64_epinfo_s *epinfo, usbhost_asynch_t callback,
void *arg);
static void a64_asynch_completion(struct a64_epinfo_s *epinfo);
#endif
/* Interrupt Handling *******************************************************/
static int a64_qtd_ioccheck(struct a64_qtd_s *qtd, uint32_t **bp,
void *arg);
static int a64_qh_ioccheck(struct a64_qh_s *qh, uint32_t **bp,
void *arg);
#ifdef CONFIG_USBHOST_ASYNCH
static int a64_qtd_cancel(struct a64_qtd_s *qtd, uint32_t **bp,
void *arg);
static int a64_qh_cancel(struct a64_qh_s *qh, uint32_t **bp, void *arg);
#endif
static inline void a64_ioc_bottomhalf(void);
static inline void a64_portsc_bottomhalf(void);
static inline void a64_syserr_bottomhalf(void);
static inline void a64_async_advance_bottomhalf(void);
static void a64_ehci_bottomhalf(void *arg);
static int a64_ehci_interrupt(int irq, void *context, void *arg);
/* USB Host Controller Operations *******************************************/
static int a64_wait(struct usbhost_connection_s *conn,
struct usbhost_hubport_s **hport);
static int a64_rh_enumerate(struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport);
static int a64_enumerate(struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport);
static int a64_ep0configure(struct usbhost_driver_s *drvr,
usbhost_ep_t ep0, uint8_t funcaddr,
uint8_t speed, uint16_t maxpacketsize);
static int a64_epalloc(struct usbhost_driver_s *drvr,
const struct usbhost_epdesc_s *epdesc,
usbhost_ep_t *ep);
static int a64_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep);
static int a64_alloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t *maxlen);
static int a64_free(struct usbhost_driver_s *drvr,
uint8_t *buffer);
static int a64_ioalloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t buflen);
static int a64_iofree(struct usbhost_driver_s *drvr,
uint8_t *buffer);
static int a64_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
uint8_t *buffer);
static int a64_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
const uint8_t *buffer);
static ssize_t a64_transfer(struct usbhost_driver_s *drvr,
usbhost_ep_t ep, uint8_t *buffer,
size_t buflen);
#ifdef CONFIG_USBHOST_ASYNCH
static int a64_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg);
#endif
static int a64_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep);
#ifdef CONFIG_USBHOST_HUB
static int a64_connect(struct usbhost_driver_s *drvr,
struct usbhost_hubport_s *hport,
bool connected);
#endif
static void a64_disconnect(struct usbhost_driver_s *drvr,
struct usbhost_hubport_s *hport);
/* Initialization ***********************************************************/
static int a64_reset(void);
/****************************************************************************
* Private Data
****************************************************************************/
/* In this driver implementation, support is provided for only a single a
* single USB device. All status information can be simply retained in a
* single global instance.
*/
static struct a64_ehci_s g_ehci =
{
.lock = NXMUTEX_INITIALIZER,
.pscsem = SEM_INITIALIZER(0),
.ep0.iocsem = SEM_INITIALIZER(1),
};
/* This is the connection/enumeration interface */
static struct usbhost_connection_s g_ehciconn =
{
.wait = a64_wait,
.enumerate = a64_enumerate,
};
/* Maps USB chapter 9 speed to EHCI speed */
static const uint8_t g_ehci_speed[4] =
{
0, EHCI_LOW_SPEED, EHCI_FULL_SPEED, EHCI_HIGH_SPEED
};
/* The head of the asynchronous queue */
static struct a64_qh_s g_asynchead aligned_data(32);
#ifndef CONFIG_USBHOST_INT_DISABLE
/* The head of the periodic queue */
static struct a64_qh_s g_intrhead aligned_data(32);
/* The frame list */
#ifdef CONFIG_A64_EHCI_PREALLOCATE
static uint32_t g_framelist[FRAME_LIST_SIZE] aligned_data(4096);
#else
static uint32_t *g_framelist;
#endif
#endif /* CONFIG_USBHOST_INT_DISABLE */
#ifdef CONFIG_A64_EHCI_PREALLOCATE
/* Pools of pre-allocated data structures. These will all be linked into the
* free lists within g_ehci. These must all be aligned to 32-byte boundaries
*/
/* Queue Head (QH) pool */
static struct a64_qh_s g_qhpool[CONFIG_A64_EHCI_NQHS]
aligned_data(32);
/* Queue Element Transfer Descriptor (qTD) pool */
static struct a64_qtd_s g_qtdpool[CONFIG_A64_EHCI_NQTDS]
aligned_data(32);
#else
/* Pools of dynamically data structures. These will all be linked into the
* free lists within g_ehci. These must all be aligned to 32-byte boundaries
*/
/* Queue Head (QH) pool */
static struct a64_qh_s *g_qhpool;
/* Queue Element Transfer Descriptor (qTD) pool */
static struct a64_qtd_s *g_qtdpool;
#endif
#ifdef HAVE_USBHOST_TRACE
/* USB trace strings */
static const struct a64_ehci_trace_s g_trace1[TRACE1_NSTRINGS] =
{
TRENTRY(EHCI_TRACE1_SYSTEMERROR, TR_FMT1,
"EHCI ERROR: System error: %06x\n"),
TRENTRY(EHCI_TRACE1_QTDFOREACH_FAILED, TR_FMT1,
"EHCI ERROR: a64_qtd_foreach failed: %d\n"),
TRENTRY(EHCI_TRACE1_QHALLOC_FAILED, TR_FMT1,
"EHCI ERROR: Failed to allocate a QH\n"),
TRENTRY(EHCI_TRACE1_BUFTOOBIG, TR_FMT1,
"EHCI ERROR: Buffer too big. Remaining %d\n"),
TRENTRY(EHCI_TRACE1_REQQTDALLOC_FAILED, TR_FMT1,
"EHCI ERROR: Failed to allocate request qTD"),
TRENTRY(EHCI_TRACE1_ADDBPL_FAILED, TR_FMT1,
"EHCI ERROR: a64_qtd_addbpl failed: %d\n"),
TRENTRY(EHCI_TRACE1_DATAQTDALLOC_FAILED, TR_FMT1,
"EHCI ERROR: Failed to allocate data buffer qTD, 0"),
TRENTRY(EHCI_TRACE1_DEVDISCONNECTED, TR_FMT1,
"EHCI ERROR: Device disconnected %d\n"),
TRENTRY(EHCI_TRACE1_QHCREATE_FAILED, TR_FMT1,
"EHCI ERROR: a64_qh_create failed\n"),
TRENTRY(EHCI_TRACE1_QTDSETUP_FAILED, TR_FMT1,
"EHCI ERROR: a64_qtd_setupphase failed\n"),
TRENTRY(EHCI_TRACE1_QTDDATA_FAILED, TR_FMT1,
"EHCI ERROR: a64_qtd_dataphase failed\n"),
TRENTRY(EHCI_TRACE1_QTDSTATUS_FAILED, TR_FMT1,
"EHCI ERROR: a64_qtd_statusphase failed\n"),
TRENTRY(EHCI_TRACE1_TRANSFER_FAILED, TR_FMT1,
"EHCI ERROR: Transfer failed %d\n"),
TRENTRY(EHCI_TRACE1_QHFOREACH_FAILED, TR_FMT1,
"EHCI ERROR: a64_qh_foreach failed: %d\n"),
TRENTRY(EHCI_TRACE1_SYSERR_INTR, TR_FMT1,
"EHCI: Host System Error Interrupt\n"),
TRENTRY(EHCI_TRACE1_USBERR_INTR, TR_FMT1,
"EHCI: USB Error Interrupt (USBERRINT) Interrupt: %06x\n"),
TRENTRY(EHCI_TRACE1_EPALLOC_FAILED, TR_FMT1,
"EHCI ERROR: Failed to allocate EP info structure\n"),
TRENTRY(EHCI_TRACE1_BADXFRTYPE, TR_FMT1,
"EHCI ERROR: Support for transfer type %d not implemented\n"),
TRENTRY(EHCI_TRACE1_HCHALTED_TIMEOUT, TR_FMT1,
"EHCI ERROR: Timed out waiting for HCHalted. USBSTS: %06x\n"),
TRENTRY(EHCI_TRACE1_QHPOOLALLOC_FAILED, TR_FMT1,
"EHCI ERROR: Failed to allocate the QH pool\n"),
TRENTRY(EHCI_TRACE1_QTDPOOLALLOC_FAILED, TR_FMT1,
"EHCI ERROR: Failed to allocate the qTD pool\n"),
TRENTRY(EHCI_TRACE1_PERFLALLOC_FAILED, TR_FMT1,
"EHCI ERROR: Failed to allocate the periodic frame list\n"),
TRENTRY(EHCI_TRACE1_RESET_FAILED, TR_FMT1,
"EHCI ERROR: a64_reset failed: %d\n"),
TRENTRY(EHCI_TRACE1_RUN_FAILED, TR_FMT1,
"EHCI ERROR: EHCI Failed to run: USBSTS=%06x\n"),
TRENTRY(EHCI_TRACE1_IRQATTACH_FAILED, TR_FMT1,
"EHCI ERROR: Failed to attach IRQ%d\n"),
#ifdef HAVE_USBHOST_TRACE_VERBOSE
TRENTRY(EHCI_VTRACE1_PORTSC_CSC, TR_FMT1,
"EHCI Connect Status Change: %06x\n"),
TRENTRY(EHCI_VTRACE1_PORTSC_CONNALREADY, TR_FMT1,
"EHCI Already connected: %06x\n"),
TRENTRY(EHCI_VTRACE1_PORTSC_DISCALREADY, TR_FMT1,
"EHCI Already disconnected: %06x\n"),
TRENTRY(EHCI_VTRACE1_TOPHALF, TR_FMT1,
"EHCI Interrupt: %06x\n"),
TRENTRY(EHCI_VTRACE1_AAINTR, TR_FMT1,
"EHCI Async Advance Interrupt\n"),
TRENTRY(EHCI_VTRACE1_CLASSENUM, TR_FMT1,
"EHCI Hub port %d: Enumerate the device\n"),
TRENTRY(EHCI_VTRACE1_USBINTR, TR_FMT1,
"EHCI USB Interrupt (USBINT) Interrupt: %06x\n"),
TRENTRY(EHCI_VTRACE1_ENUM_DISCONN, TR_FMT1,
"EHCI Enumeration not connected\n"),
TRENTRY(EHCI_VTRACE1_INITIALIZING, TR_FMT1,
"EHCI Initializing EHCI Stack\n"),
TRENTRY(EHCI_VTRACE1_HCCPARAMS, TR_FMT1,
"EHCI HCCPARAMS=%06x\n"),
TRENTRY(EHCI_VTRACE1_INIITIALIZED, TR_FMT1,
"EHCI USB EHCI Initialized\n"),
#endif
};
static const struct a64_ehci_trace_s g_trace2[TRACE2_NSTRINGS] =
{
TRENTRY(EHCI_TRACE2_EPSTALLED, TR_FMT2,
"EHCI EP%d Stalled: TOKEN=%04x\n"),
TRENTRY(EHCI_TRACE2_EPIOERROR, TR_FMT2,
"EHCI ERROR: EP%d TOKEN=%04x\n"),
TRENTRY(EHCI_TRACE2_CLASSENUM_FAILED, TR_FMT2,
"EHCI Hub port %d usbhost_enumerate() failed: %d\n"),
#ifdef HAVE_USBHOST_TRACE_VERBOSE
TRENTRY(EHCI_VTRACE2_ASYNCXFR, TR_FMT2,
"EHCI Async transfer EP%d buflen=%d\n"),
TRENTRY(EHCI_VTRACE2_INTRXFR, TR_FMT2,
"EHCI Intr Transfer EP%d buflen=%d\n"),
TRENTRY(EHCI_VTRACE2_IOCCHECK, TR_FMT2,
"EHCI IOC EP%d TOKEN=%04x\n"),
TRENTRY(EHCI_VTRACE2_PORTSC, TR_FMT2,
"EHCI PORTSC%d: %04x\n"),
TRENTRY(EHCI_VTRACE2_PORTSC_CONNECTED, TR_FMT2,
"EHCI RHPort%d connected, pscwait: %d\n"),
TRENTRY(EHCI_VTRACE2_PORTSC_DISCONND, TR_FMT2,
"EHCI RHport%d disconnected, pscwait: %d\n"),
TRENTRY(EHCI_VTRACE2_MONWAKEUP, TR_FMT2,
"EHCI RHPort%d connected: %d\n"),
TRENTRY(EHCI_VTRACE2_EPALLOC, TR_FMT2,
"EHCI EPALLOC: EP%d TYPE=%d\n"),
TRENTRY(EHCI_VTRACE2_CTRLINOUT, TR_FMT2,
"EHCI CTRLIN/OUT: RHPort%d req: %02x\n"),
TRENTRY(EHCI_VTRACE2_HCIVERSION, TR_FMT2,
"EHCI HCIVERSION %x.%02x\n"),
TRENTRY(EHCI_VTRACE2_HCSPARAMS, TR_FMT2,
"EHCI nports=%d, HCSPARAMS=%04x\n"),
#endif
};
#endif /* HAVE_USBHOST_TRACE */
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: a64_read16
*
* Description:
* Read 16-bit little endian data
*
****************************************************************************/
static uint16_t a64_read16(const uint8_t *addr)
{
#ifdef CONFIG_ENDIAN_BIG
return (uint16_t)addr[0] << 8 | (uint16_t)addr[1];
#else
return (uint16_t)addr[1] << 8 | (uint16_t)addr[0];
#endif
}
/****************************************************************************
* Name: a64_read32
*
* Description:
* Read 32-bit little endian data
*
****************************************************************************/
static inline uint32_t a64_read32(const uint8_t *addr)
{
#ifdef CONFIG_ENDIAN_BIG
return (uint32_t)a64_read16(&addr[0]) << 16 |
(uint32_t)a64_read16(&addr[2]);
#else
return (uint32_t)a64_read16(&addr[2]) << 16 |
(uint32_t)a64_read16(&addr[0]);
#endif
}
/****************************************************************************
* Name: a64_write16
*
* Description:
* Write 16-bit little endian data
*
****************************************************************************/
#if 0 /* Not used */
static void a64_write16(uint16_t memval, uint8_t *addr)
{
#ifdef CONFIG_ENDIAN_BIG
addr[0] = memval & 0xff;
addr[1] = memval >> 8;
#else
addr[0] = memval >> 8;
addr[1] = memval & 0xff;
#endif
}
#endif
/****************************************************************************
* Name: a64_write32
*
* Description:
* Write 32-bit little endian data
*
****************************************************************************/
#if 0 /* Not used */
static void a64_write32(uint32_t memval, uint8_t *addr)
{
#ifdef CONFIG_ENDIAN_BIG
a64_write16(memval >> 16, &addr[0]);
a64_write16(memval & 0xffff, &addr[2]);
#else
a64_write16(memval & 0xffff, &addr[0]);
a64_write16(memval >> 16, &addr[2]);
#endif
}
#endif
/****************************************************************************
* Name: a64_swap16
*
* Description:
* Swap bytes on a 16-bit value
*
****************************************************************************/
#ifdef CONFIG_ENDIAN_BIG
static uint16_t a64_swap16(uint16_t value)
{
return ((value >> 8) & 0xff) | ((value & 0xff) << 8);
}
#endif
/****************************************************************************
* Name: a64_swap32
*
* Description:
* Swap bytes on a 32-bit value
*
****************************************************************************/
#ifdef CONFIG_ENDIAN_BIG
static uint32_t a64_swap32(uint32_t value)
{
return (uint32_t)a64_swap16((uint16_t)((value >> 16) & 0xffff)) |
(uint32_t)a64_swap16((uint16_t)(value & 0xffff)) << 16;
}
#endif
/****************************************************************************
* Name: a64_printreg
*
* Description:
* Print the contents of a A64 EHCI register
*
****************************************************************************/
#ifdef CONFIG_A64_EHCI_REGDEBUG
static void a64_printreg(volatile uint32_t *regaddr, uint32_t regval,
bool iswrite)
{
uinfo("%08x%s%08x\n", (uintptr_t)regaddr, iswrite ? "<-" : "->", regval);
}
#endif
/****************************************************************************
* Name: a64_checkreg
*
* Description:
* Check if it is time to output debug information for accesses to a A64
* EHCI register
*
****************************************************************************/
#ifdef CONFIG_A64_EHCI_REGDEBUG
static void a64_checkreg(volatile uint32_t *regaddr, uint32_t regval,
bool iswrite)
{
static uint32_t *prevaddr = NULL;
static uint32_t preval = 0;
static uint32_t count = 0;
static bool prevwrite = false;
/* Is this the same value that we read from/wrote to the same register last
* time? Are we polling the register? If so, suppress the output.
*/
if (regaddr == prevaddr && regval == preval && prevwrite == iswrite)
{
/* Yes.. Just increment the count */
count++;
}
else
{
/* No this is a new address or value or operation. Were there any
* duplicate accesses before this one?
*/
if (count > 0)
{
/* Yes.. Just one? */
if (count == 1)
{
/* Yes.. Just one */
a64_printreg(prevaddr, preval, prevwrite);
}
else
{
/* No.. More than one. */
uinfo("[repeats %d more times]\n", count);
}
}
/* Save the new address, value, count, and operation for next time */
prevaddr = (uint32_t *)regaddr;
preval = regval;
count = 0;
prevwrite = iswrite;
/* Show the new register access */
a64_printreg(regaddr, regval, iswrite);
}
}
#endif
/****************************************************************************
* Name: a64_getreg
*
* Description:
* Get the contents of an A64 register
*
****************************************************************************/
#ifdef CONFIG_A64_EHCI_REGDEBUG
static uint32_t a64_getreg(volatile uint32_t *regaddr)
{
/* Read the value from the register */
uint32_t regval = *regaddr;
/* Check if we need to print this value */
a64_checkreg(regaddr, regval, false);
return regval;
}
#else
static inline uint32_t a64_getreg(volatile uint32_t *regaddr)
{
return *regaddr;
}
#endif
/****************************************************************************
* Name: a64_putreg
*
* Description:
* Set the contents of an A64 register to a value
*
****************************************************************************/
#ifdef CONFIG_A64_EHCI_REGDEBUG
static void a64_putreg(uint32_t regval, volatile uint32_t *regaddr)
{