aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/adm5120/files-3.18/drivers/usb/host/adm5120.h
blob: e47aac8e7aa91310f344872b1533b73e2cced7a9 (plain)
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
/*
 * ADM5120 HCD (Host Controller Driver) for USB
 *
 * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
 *
 * This file was derived from: drivers/usb/host/ohci.h
 *   (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
 *   (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License version 2 as published
 *  by the Free Software Foundation.
 *
 */

/*
 * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
 * __leXX (normally) or __beXX (given OHCI_BIG_ENDIAN), depending on the
 * host controller implementation.
 */
typedef __u32 __bitwise __hc32;
typedef __u16 __bitwise __hc16;

/*
 * OHCI Endpoint Descriptor (ED) ... holds TD queue
 * See OHCI spec, section 4.2
 *
 * This is a "Queue Head" for those transfers, which is why
 * both EHCI and UHCI call similar structures a "QH".
 */

#define TD_DATALEN_MAX	4096

#define ED_ALIGN	16
#define ED_MASK	((u32)~(ED_ALIGN-1))	/* strip hw status in low addr bits */

struct ed {
	/* first fields are hardware-specified */
	__hc32			hwINFO;      /* endpoint config bitmap */
	/* info bits defined by hcd */
#define ED_DEQUEUE	(1 << 27)
	/* info bits defined by the hardware */
#define ED_MPS_SHIFT	16
#define ED_MPS_MASK	((1 << 11)-1)
#define ED_MPS_GET(x)	(((x) >> ED_MPS_SHIFT) & ED_MPS_MASK)
#define ED_ISO		(1 << 15)		/* isochronous endpoint */
#define ED_SKIP		(1 << 14)
#define ED_SPEED_FULL	(1 << 13)		/* fullspeed device */
#define ED_INT		(1 << 11)		/* interrupt endpoint */
#define ED_EN_SHIFT	7			/* endpoint shift */
#define ED_EN_MASK	((1 << 4)-1)		/* endpoint mask */
#define ED_EN_GET(x)	(((x) >> ED_EN_SHIFT) & ED_EN_MASK)
#define ED_FA_MASK	((1 << 7)-1)		/* function address mask */
#define ED_FA_GET(x)	((x) & ED_FA_MASK)
	__hc32			hwTailP;	/* tail of TD list */
	__hc32			hwHeadP;	/* head of TD list (hc r/w) */
#define ED_C		(0x02)			/* toggle carry */
#define ED_H		(0x01)			/* halted */
	__hc32			hwNextED;	/* next ED in list */

	/* rest are purely for the driver's use */
	dma_addr_t		dma;		/* addr of ED */
	struct td		*dummy;		/* next TD to activate */

	struct list_head	urb_list;	/* list of our URBs */

	/* host's view of schedule */
	struct ed		*ed_next;	/* on schedule list */
	struct ed		*ed_prev;	/* for non-interrupt EDs */
	struct ed		*ed_rm_next;	/* on rm list */
	struct list_head	td_list;	/* "shadow list" of our TDs */

	/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
	 * usually:  OPER --> UNLINK --> (IDLE | OPER) --> ...
	 */
	u8			state;		/* ED_{IDLE,UNLINK,OPER} */
#define ED_IDLE		0x00		/* NOT linked to HC */
#define ED_UNLINK	0x01		/* being unlinked from hc */
#define ED_OPER		0x02		/* IS linked to hc */

	u8			type;		/* PIPE_{BULK,...} */

	/* periodic scheduling params (for intr and iso) */
	u8			branch;
	u16			interval;
	u16			load;
	u16			last_iso;	/* iso only */

	/* HC may see EDs on rm_list until next frame (frame_no == tick) */
	u16			tick;
} __attribute__ ((aligned(ED_ALIGN)));

/*
 * OHCI Transfer Descriptor (TD) ... one per transfer segment
 * See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)
 * and 4.3.2 (iso)
 */

#define TD_ALIGN	32
#define TD_MASK	((u32)~(TD_ALIGN-1))	/* strip hw status in low addr bits */

struct td {
	/* first fields are hardware-specified */
	__hc32		hwINFO;		/* transfer info bitmask */

	/* hwINFO bits */
#define TD_OWN		(1 << 31)		/* owner of the descriptor */
#define TD_CC_SHIFT	27			/* condition code */
#define TD_CC_MASK	0xf
#define TD_CC		(TD_CC_MASK << TD_CC_SHIFT)
#define TD_CC_GET(x)	(((x) >> TD_CC_SHIFT) & TD_CC_MASK)

#define TD_EC_SHIFT	25			/* error count */
#define TD_EC_MASK	0x3
#define TD_EC		(TD_EC_MASK << TD_EC_SHIFT)
#define TD_EC_GET(x)	((x >> TD_EC_SHIFT) & TD_EC_MASK)
#define TD_T_SHIFT	23			/* data toggle state */
#define TD_T_MASK	0x3
#define TD_T		(TD_T_MASK << TD_T_SHIFT)
#define TD_T_DATA0	(0x2 << TD_T_SHIFT)	/* DATA0 */
#define TD_T_DATA1	(0x3 << TD_T_SHIFT)	/* DATA1 */
#define TD_T_CARRY	(0x0 << TD_T_SHIFT)	/* uses ED_C */
#define TD_T_GET(x)	(((x) >> TD_T_SHIFT) & TD_T_MASK)
#define TD_DP_SHIFT	21			/* direction/pid */
#define TD_DP_MASK	0x3
#define TD_DP		(TD_DP_MASK << TD_DP_SHIFT)
#define TD_DP_GET	(((x) >> TD_DP_SHIFT) & TD_DP_MASK)
#define TD_DP_SETUP	(0x0 << TD_DP_SHIFT)	/* SETUP pid */
#define TD_DP_OUT	(0x1 << TD_DP_SHIFT)	/* OUT pid */
#define TD_DP_IN	(0x2 << TD_DP_SHIFT)	/* IN pid */
#define TD_ISI_SHIFT	8			/* Interrupt Service Interval */
#define TD_ISI_MASK	0x3f
#define TD_ISI_GET(x)	(((x) >> TD_ISI_SHIFT) & TD_ISI_MASK)
#define TD_FN_MASK	0x3f			/* frame number */
#define TD_FN_GET(x)	((x) & TD_FN_MASK)

	__hc32		hwDBP;		/* Data Buffer Pointer (or 0) */
	__hc32		hwCBL;		/* Controller/Buffer Length */

	/* hwCBL bits */
#define TD_BL_MASK	0xffff		/* buffer length */
#define TD_BL_GET(x)	((x) & TD_BL_MASK)
#define TD_IE		(1 << 16)	/* interrupt enable */
	__hc32		hwNextTD;	/* Next TD Pointer */

	/* rest are purely for the driver's use */
	__u8		index;
	struct ed	*ed;
	struct td	*td_hash;	/* dma-->td hashtable */
	struct td	*next_dl_td;
	struct urb	*urb;

	dma_addr_t	td_dma;		/* addr of this TD */
	dma_addr_t	data_dma;	/* addr of data it points to */

	struct list_head td_list;	/* "shadow list", TDs on same ED */

	u32		flags;
#define TD_FLAG_DONE	(1 << 17)	/* retired to done list */
#define TD_FLAG_ISO	(1 << 16)	/* copy of ED_ISO */
} __attribute__ ((aligned(TD_ALIGN)));	/* c/b/i need 16; only iso needs 32 */

/*
 * Hardware transfer status codes -- CC from td->hwINFO
 */
#define TD_CC_NOERROR		0x00
#define TD_CC_CRC		0x01
#define TD_CC_BITSTUFFING	0x02
#define TD_CC_DATATOGGLEM	0x03
#define TD_CC_STALL		0x04
#define TD_CC_DEVNOTRESP	0x05
#define TD_CC_PIDCHECKFAIL	0x06
#define TD_CC_UNEXPECTEDPID	0x07
#define TD_CC_DATAOVERRUN	0x08
#define TD_CC_DATAUNDERRUN	0x09
    /* 0x0A, 0x0B reserved for hardware */
#define TD_CC_BUFFEROVERRUN	0x0C
#define TD_CC_BUFFERUNDERRUN	0x0D
    /* 0x0E, 0x0F reserved for HCD */
#define TD_CC_HCD0		0x0E
#define TD_CC_NOTACCESSED	0x0F

/*
 * preshifted status codes
 */
#define TD_SCC_NOTACCESSED	(TD_CC_NOTACCESSED << TD_CC_SHIFT)


/* map OHCI TD status codes (CC) to errno values */
static const int cc_to_error[16] = {
	/* No  Error  */	0,
	/* CRC Error  */	-EILSEQ,
	/* Bit Stuff  */	-EPROTO,
	/* Data Togg  */	-EILSEQ,
	/* Stall      */	-EPIPE,
	/* DevNotResp */	-ETIME,
	/* PIDCheck   */	-EPROTO,
	/* UnExpPID   */	-EPROTO,
	/* DataOver   */	-EOVERFLOW,
	/* DataUnder  */	-EREMOTEIO,
	/* (for hw)   */	-EIO,
	/* (for hw)   */	-EIO,
	/* BufferOver */	-ECOMM,
	/* BuffUnder  */	-ENOSR,
	/* (for HCD)  */	-EALREADY,
	/* (for HCD)  */	-EALREADY
};

#define NUM_INTS	32

/*
 * This is the structure of the OHCI controller's memory mapped I/O region.
 * You must use readl() and writel() (in <asm/io.h>) to access these fields!!
 * Layout is in section 7 (and appendix B) of the spec.
 */
struct admhcd_regs {
	__hc32	gencontrol;	/* General Control */
	__hc32	int_status;	/* Interrupt Status */
	__hc32	int_enable;	/* Interrupt Enable */
	__hc32	reserved00;
	__hc32	host_control;	/* Host General Control */
	__hc32	reserved01;
	__hc32	fminterval;	/* Frame Interval */
	__hc32	fmnumber;	/* Frame Number */
	__hc32	reserved02;
	__hc32	reserved03;
	__hc32	reserved04;
	__hc32	reserved05;
	__hc32	reserved06;
	__hc32	reserved07;
	__hc32	reserved08;
	__hc32	reserved09;
	__hc32	reserved10;
	__hc32	reserved11;
	__hc32	reserved12;
	__hc32	reserved13;
	__hc32	reserved14;
	__hc32	reserved15;
	__hc32	reserved16;
	__hc32	reserved17;
	__hc32	reserved18;
	__hc32	reserved19;
	__hc32	reserved20;
	__hc32	reserved21;
	__hc32	lsthresh;	/* Low Speed Threshold */
	__hc32	rhdesc;		/* Root Hub Descriptor */
#define MAX_ROOT_PORTS	2
	__hc32	portstatus[MAX_ROOT_PORTS]; /* Port Status */
	__hc32	hosthead;	/* Host Descriptor Head */
} __attribute__ ((aligned(32)));

/*
 * General Control register bits
 */
#define ADMHC_CTRL_UHFE	(1 << 0)	/* USB Host Function Enable */
#define ADMHC_CTRL_SIR	(1 << 1)	/* Software Interrupt request */
#define ADMHC_CTRL_DMAA	(1 << 2)	/* DMA Arbitration Control */
#define ADMHC_CTRL_SR	(1 << 3)	/* Software Reset */

/*
 * Host General Control register bits
 */
#define ADMHC_HC_BUSS		0x3		/* USB bus state */
#define   ADMHC_BUSS_RESET	0x0
#define   ADMHC_BUSS_RESUME	0x1
#define   ADMHC_BUSS_OPER	0x2
#define   ADMHC_BUSS_SUSPEND	0x3
#define ADMHC_HC_DMAE		(1 << 2)	/* DMA enable */

/*
 * Interrupt Status/Enable register bits
 */
#define ADMHC_INTR_SOFI	(1 << 4)	/* start of frame */
#define ADMHC_INTR_RESI	(1 << 5)	/* resume detected */
#define ADMHC_INTR_6	(1 << 6)	/* unknown */
#define ADMHC_INTR_7	(1 << 7)	/* unknown */
#define ADMHC_INTR_BABI	(1 << 8)	/* babble detected */
#define ADMHC_INTR_INSM	(1 << 9)	/* root hub status change */
#define ADMHC_INTR_SO	(1 << 10)	/* scheduling overrun */
#define ADMHC_INTR_FNO	(1 << 11)	/* frame number overflow */
#define ADMHC_INTR_TDC	(1 << 20)	/* transfer descriptor completed */
#define ADMHC_INTR_SWI	(1 << 29)	/* software interrupt */
#define ADMHC_INTR_FATI	(1 << 30)	/* fatal error */
#define ADMHC_INTR_INTA	(1 << 31)	/* interrupt active */

#define ADMHC_INTR_MIE	(1 << 31)	/* master interrupt enable */

/*
 * SOF Frame Interval register bits
 */
#define ADMHC_SFI_FI_MASK	((1 << 14)-1)	/* Frame Interval value */
#define ADMHC_SFI_FSLDP_SHIFT	16
#define ADMHC_SFI_FSLDP_MASK	((1 << 15)-1)
#define ADMHC_SFI_FIT		(1 << 31)	/* Frame Interval Toggle */

/*
 * SOF Frame Number register bits
 */
#define ADMHC_SFN_FN_MASK	((1 << 16)-1)	/* Frame Number Mask */
#define ADMHC_SFN_FR_SHIFT	16		/* Frame Remaining Shift */
#define ADMHC_SFN_FR_MASK	((1 << 14)-1)	/* Frame Remaining Mask */
#define ADMHC_SFN_FRT		(1 << 31)	/* Frame Remaining Toggle */

/*
 * Root Hub Descriptor register bits
 */
#define ADMHC_RH_NUMP	0xff		/* number of ports */
#define	ADMHC_RH_PSM	(1 << 8)	/* power switching mode */
#define	ADMHC_RH_NPS	(1 << 9)	/* no power switching */
#define	ADMHC_RH_OCPM	(1 << 10)	/* over current protection mode */
#define	ADMHC_RH_NOCP	(1 << 11)	/* no over current protection */
#define	ADMHC_RH_PPCM	(0xff << 16)	/* port power control */

#define ADMHC_RH_LPS	(1 << 24)	/* local power switch */
#define ADMHC_RH_OCI	(1 << 25)	/* over current indicator */

/* status change bits */
#define ADMHC_RH_LPSC	(1 << 26)	/* local power switch change */
#define ADMHC_RH_OCIC	(1 << 27)	/* over current indicator change */

#define ADMHC_RH_DRWE	(1 << 28)	/* device remote wakeup enable */
#define ADMHC_RH_CRWE	(1 << 29)	/* clear remote wakeup enable */

#define ADMHC_RH_CGP	(1 << 24)	/* clear global power */
#define ADMHC_RH_SGP	(1 << 26)	/* set global power */

/*
 * Port Status register bits
 */
#define ADMHC_PS_CCS	(1 << 0)	/* current connect status */
#define ADMHC_PS_PES	(1 << 1)	/* port enable status */
#define ADMHC_PS_PSS	(1 << 2)	/* port suspend status */
#define ADMHC_PS_POCI	(1 << 3)	/* port over current indicator */
#define ADMHC_PS_PRS	(1 << 4)	/* port reset status */
#define ADMHC_PS_PPS	(1 << 8)	/* port power status */
#define ADMHC_PS_LSDA	(1 << 9)	/* low speed device attached */

/* status change bits */
#define ADMHC_PS_CSC	(1 << 16)	/* connect status change */
#define ADMHC_PS_PESC	(1 << 17)	/* port enable status change */
#define ADMHC_PS_PSSC	(1 << 18)	/* port suspend status change */
#define ADMHC_PS_OCIC	(1 << 19)	/* over current indicator change */
#define ADMHC_PS_PRSC	(1 << 20)	/* port reset status change */

/* port feature bits */
#define ADMHC_PS_CPE	(1 << 0)	/* clear port enable */
#define ADMHC_PS_SPE	(1 << 1)	/* set port enable */
#define ADMHC_PS_SPS	(1 << 2)	/* set port suspend */
#define ADMHC_PS_CPS	(1 << 3)	/* clear suspend status */
#define ADMHC_PS_SPR	(1 << 4)	/* set port reset */
#define ADMHC_PS_SPP	(1 << 8)	/* set port power */
#define ADMHC_PS_CPP	(1 << 9)	/* clear port power */

/*
 * the POTPGT value is not defined in the ADMHC, so define a dummy value
 */
#define ADMHC_POTPGT	2		/* in ms */

/* hcd-private per-urb state */
struct urb_priv {
	struct ed		*ed;
	struct list_head	pending;	/* URBs on the same ED */

	u32			td_cnt;		/* # tds in this request */
	u32			td_idx;		/* index of the current td */
	struct td		*td[0];		/* all TDs in this request */
};

#define TD_HASH_SIZE    64    /* power'o'two */
/* sizeof (struct td) ~= 64 == 2^6 ... */
#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)

/*
 * This is the full ADMHCD controller description
 *
 * Note how the "proper" USB information is just
 * a subset of what the full implementation needs. (Linus)
 */

struct admhcd {
	spinlock_t		lock;

	/*
	 * I/O memory used to communicate with the HC (dma-consistent)
	 */
	struct admhcd_regs __iomem *regs;

	/*
	 * hcd adds to schedule for a live hc any time, but removals finish
	 * only at the start of the next frame.
	 */

	struct ed		*ed_head;
	struct ed		*ed_tails[4];

	struct ed		*ed_rm_list;	/* to be removed */

	struct ed		*periodic[NUM_INTS];	/* shadow int_table */

#if 0	/* TODO: remove? */
	/*
	 * OTG controllers and transceivers need software interaction;
	 * other external transceivers should be software-transparent
	 */
	struct otg_transceiver	*transceiver;
	void (*start_hnp)(struct admhcd *ahcd);
#endif

	/*
	 * memory management for queue data structures
	 */
	struct dma_pool		*td_cache;
	struct dma_pool		*ed_cache;
	struct td		*td_hash[TD_HASH_SIZE];
	struct list_head	pending;

	/*
	 * driver state
	 */
	int			num_ports;
	int			load[NUM_INTS];
	u32			host_control;	/* copy of the host_control reg */
	unsigned long		next_statechange;	/* suspend/resume */
	u32			fminterval;		/* saved register */
	unsigned		autostop:1;	/* rh auto stopping/stopped */

	unsigned long		flags;		/* for HC bugs */
#define	OHCI_QUIRK_AMD756	0x01			/* erratum #4 */
#define	OHCI_QUIRK_SUPERIO	0x02			/* natsemi */
#define	OHCI_QUIRK_INITRESET	0x04			/* SiS, OPTi, ... */
#define	OHCI_QUIRK_BE_DESC	0x08			/* BE descriptors */
#define	OHCI_QUIRK_BE_MMIO	0x10			/* BE registers */
#define	OHCI_QUIRK_ZFMICRO	0x20			/* Compaq ZFMicro chipset*/
	/* there are also chip quirks/bugs in init logic */

#ifdef DEBUG
	struct dentry		*debug_dir;
	struct dentry		*debug_async;
	struct dentry		*debug_periodic;
	struct dentry		*debug_registers;
#endif
};

/* convert between an hcd pointer and the corresponding ahcd_hcd */
static inline struct admhcd *hcd_to_admhcd(struct usb_hcd *hcd)
{
	return (struct admhcd *)(hcd->hcd_priv);
}
static inline struct usb_hcd *admhcd_to_hcd(const struct admhcd *ahcd)
{
	return container_of((void *)ahcd, struct usb_hcd, hcd_priv);
}

/*-------------------------------------------------------------------------*/

#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif	/* DEBUG */

#ifdef DEBUG
#	define admhc_dbg(ahcd, fmt, args...) \
		printk(KERN_DEBUG "adm5120-hcd: " fmt, ## args)
#else
#	define admhc_dbg(ahcd, fmt, args...) do { } while (0)
#endif

#define admhc_err(ahcd, fmt, args...) \
	printk(KERN_ERR "adm5120-hcd: " fmt, ## args)
#define admhc_info(ahcd, fmt, args...) \
	printk(KERN_INFO "adm5120-hcd: " fmt, ## args)
#define admhc_warn(ahcd, fmt, args...) \
	printk(KERN_WARNING "adm5120-hcd: " fmt, ## args)

#ifdef ADMHC_VERBOSE_DEBUG
#	define admhc_vdbg admhc_dbg
#else
#	define admhc_vdbg(ahcd, fmt, args...) do { } while (0)
#endif

/*-------------------------------------------------------------------------*/

/*
 * While most USB host controllers implement their registers and
 * in-memory communication descriptors in little-endian format,
 * a minority (notably the IBM STB04XXX and the Motorola MPC5200
 * processors) implement them in big endian format.
 *
 * In addition some more exotic implementations like the Toshiba
 * Spider (aka SCC) cell southbridge are "mixed" endian, that is,
 * they have a different endianness for registers vs. in-memory
 * descriptors.
 *
 * This attempts to support either format at compile time without a
 * runtime penalty, or both formats with the additional overhead
 * of checking a flag bit.
 *
 * That leads to some tricky Kconfig rules howevber. There are
 * different defaults based on some arch/ppc platforms, though
 * the basic rules are:
 *
 * Controller type              Kconfig options needed
 * ---------------              ----------------------
 * little endian                CONFIG_USB_ADMHC_LITTLE_ENDIAN
 *
 * fully big endian             CONFIG_USB_ADMHC_BIG_ENDIAN_DESC _and_
 *                              CONFIG_USB_ADMHC_BIG_ENDIAN_MMIO
 *
 * mixed endian                 CONFIG_USB_ADMHC_LITTLE_ENDIAN _and_
 *                              CONFIG_USB_OHCI_BIG_ENDIAN_{MMIO,DESC}
 *
 * (If you have a mixed endian controller, you -must- also define
 * CONFIG_USB_ADMHC_LITTLE_ENDIAN or things will not work when building
 * both your mixed endian and a fully big endian controller support in
 * the same kernel image).
 */

#ifdef CONFIG_USB_ADMHC_BIG_ENDIAN_DESC
#ifdef CONFIG_USB_ADMHC_LITTLE_ENDIAN
#define big_endian_desc(ahcd)	(ahcd->flags & OHCI_QUIRK_BE_DESC)
#else
#define big_endian_desc(ahcd)	1		/* only big endian */
#endif
#else
#define big_endian_desc(ahcd)	0		/* only little endian */
#endif

#ifdef CONFIG_USB_ADMHC_BIG_ENDIAN_MMIO
#ifdef CONFIG_USB_ADMHC_LITTLE_ENDIAN
#define big_endian_mmio(ahcd)	(ahcd->flags & OHCI_QUIRK_BE_MMIO)
#else
#define big_endian_mmio(ahcd)	1		/* only big endian */
#endif
#else
#define big_endian_mmio(ahcd)	0		/* only little endian */
#endif

/*
 * Big-endian read/write functions are arch-specific.
 * Other arches can be added if/when they're needed.
 *
 */
static inline unsigned int admhc_readl(const struct admhcd *ahcd,
	__hc32 __iomem *regs)
{
#ifdef CONFIG_USB_ADMHC_BIG_ENDIAN_MMIO
	return big_endian_mmio(ahcd) ?
		readl_be(regs) :
		readl(regs);
#else
	return readl(regs);
#endif
}

static inline void admhc_writel(const struct admhcd *ahcd,
	const unsigned int val, __hc32 __iomem *regs)
{
#ifdef CONFIG_USB_ADMHC_BIG_ENDIAN_MMIO
	big_endian_mmio(ahcd) ?
		writel_be(val, regs) :
		writel(val, regs);
#else
		writel(val, regs);
#endif
}

static inline void admhc_writel_flush(const struct admhcd *ahcd)
{
#if 0
	/* TODO: remove? */
	(void) admhc_readl(ahcd, &ahcd->regs->gencontrol);
#endif
}


/*-------------------------------------------------------------------------*/

/* cpu to ahcd */
static inline __hc16 cpu_to_hc16(const struct admhcd *ahcd, const u16 x)
{
	return big_endian_desc(ahcd) ?
		(__force __hc16)cpu_to_be16(x) :
		(__force __hc16)cpu_to_le16(x);
}

static inline __hc16 cpu_to_hc16p(const struct admhcd *ahcd, const u16 *x)
{
	return big_endian_desc(ahcd) ?
		cpu_to_be16p(x) :
		cpu_to_le16p(x);
}

static inline __hc32 cpu_to_hc32(const struct admhcd *ahcd, const u32 x)
{
	return big_endian_desc(ahcd) ?
		(__force __hc32)cpu_to_be32(x) :
		(__force __hc32)cpu_to_le32(x);
}

static inline __hc32 cpu_to_hc32p(const struct admhcd *ahcd, const u32 *x)
{
	return big_endian_desc(ahcd) ?
		cpu_to_be32p(x) :
		cpu_to_le32p(x);
}

/* ahcd to cpu */
static inline u16 hc16_to_cpu(const struct admhcd *ahcd, const __hc16 x)
{
	return big_endian_desc(ahcd) ?
		be16_to_cpu((__force __be16)x) :
		le16_to_cpu((__force __le16)x);
}

static inline u16 hc16_to_cpup(const struct admhcd *ahcd, const __hc16 *x)
{
	return big_endian_desc(ahcd) ?
		be16_to_cpup((__force __be16 *)x) :
		le16_to_cpup((__force __le16 *)x);
}

static inline u32 hc32_to_cpu(const struct admhcd *ahcd, const __hc32 x)
{
	return big_endian_desc(ahcd) ?
		be32_to_cpu((__force __be32)x) :
		le32_to_cpu((__force __le32)x);
}

static inline u32 hc32_to_cpup(const struct admhcd *ahcd, const __hc32 *x)
{
	return big_endian_desc(ahcd) ?
		be32_to_cpup((__force __be32 *)x) :
		le32_to_cpup((__force __le32 *)x);
}

/*-------------------------------------------------------------------------*/

static inline u16 admhc_frame_no(const struct admhcd *ahcd)
{
	u32	t;

	t = admhc_readl(ahcd, &ahcd->regs->fmnumber) & ADMHC_SFN_FN_MASK;
	return (u16)t;
}

static inline u16 admhc_frame_remain(const struct admhcd *ahcd)
{
	u32	t;

	t = admhc_readl(ahcd, &ahcd->regs->fmnumber) >> ADMHC_SFN_FR_SHIFT;
	t &= ADMHC_SFN_FR_MASK;
	return (u16)t;
}

/*-------------------------------------------------------------------------*/

static inline void admhc_disable(struct admhcd *ahcd)
{
	admhcd_to_hcd(ahcd)->state = HC_STATE_HALT;
}

#define	FI		0x2edf		/* 12000 bits per frame (-1) */
#define	FSLDP(fi)	(0x7fff & ((6 * ((fi) - 1200)) / 7))
#define	FIT		ADMHC_SFI_FIT
#define LSTHRESH	0x628		/* lowspeed bit threshold */

static inline void periodic_reinit(struct admhcd *ahcd)
{
#if 0
	u32	fi = ahcd->fminterval & ADMHC_SFI_FI_MASK;
	u32	fit = admhc_readl(ahcd, &ahcd->regs->fminterval) & FIT;

	/* TODO: adjust FSLargestDataPacket value too? */
	admhc_writel(ahcd, (fit ^ FIT) | ahcd->fminterval,
					&ahcd->regs->fminterval);
#else
	u32	fit = admhc_readl(ahcd, &ahcd->regs->fminterval) & FIT;

	/* TODO: adjust FSLargestDataPacket value too? */
	admhc_writel(ahcd, (fit ^ FIT) | ahcd->fminterval,
					&ahcd->regs->fminterval);
#endif
}

static inline u32 admhc_read_rhdesc(struct admhcd *ahcd)
{
	return admhc_readl(ahcd, &ahcd->regs->rhdesc);
}

static inline u32 admhc_read_portstatus(struct admhcd *ahcd, int port)
{
	return admhc_readl(ahcd, &ahcd->regs->portstatus[port]);
}

static inline void admhc_write_portstatus(struct admhcd *ahcd, int port,
		u32 value)
{
	admhc_writel(ahcd, value, &ahcd->regs->portstatus[port]);
}

static inline void roothub_write_status(struct admhcd *ahcd, u32 value)
{
	/* FIXME: read-only bits must be masked out */
	admhc_writel(ahcd, value, &ahcd->regs->rhdesc);
}

static inline void admhc_intr_disable(struct admhcd *ahcd, u32 ints)
{
	u32	t;

	t = admhc_readl(ahcd, &ahcd->regs->int_enable);
	t &= ~(ints);
	admhc_writel(ahcd, t, &ahcd->regs->int_enable);
	/* TODO: flush writes ?*/
}

static inline void admhc_intr_enable(struct admhcd *ahcd, u32 ints)
{
	u32	t;

	t = admhc_readl(ahcd, &ahcd->regs->int_enable);
	t |= ints;
	admhc_writel(ahcd, t, &ahcd->regs->int_enable);
	/* TODO: flush writes ?*/
}

static inline void admhc_intr_ack(struct admhcd *ahcd, u32 ints)
{
	admhc_writel(ahcd, ints, &ahcd->regs->int_status);
}

static inline void admhc_dma_enable(struct admhcd *ahcd)
{
	u32 t;

	t = admhc_readl(ahcd, &ahcd->regs->host_control);
	if (t & ADMHC_HC_DMAE)
		return;

	t |= ADMHC_HC_DMAE;
	admhc_writel(ahcd, t, &ahcd->regs->host_control);
	admhc_vdbg(ahcd, "DMA enabled\n");
}

static inline void admhc_dma_disable(struct admhcd *ahcd)
{
	u32 t;

	t = admhc_readl(ahcd, &ahcd->regs->host_control);
	if (!(t & ADMHC_HC_DMAE))
		return;

	t &= ~ADMHC_HC_DMAE;
	admhc_writel(ahcd, t, &ahcd->regs->host_control);
	admhc_vdbg(ahcd, "DMA disabled\n");
}