aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ramips/files/drivers/usb/dwc_otg/dwc_otg_hcd.h
blob: ee41dc96f95b2a95bcf97b7ebaa311bece7819c5 (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
/* ==========================================================================
 * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $
 * $Revision: 1.3 $
 * $Date: 2008-12-15 06:51:32 $
 * $Change: 1064918 $
 *
 * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
 * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
 * otherwise expressly agreed to in writing between Synopsys and you.
 *
 * The Software IS NOT an item of Licensed Software or Licensed Product under
 * any End User Software License Agreement or Agreement for Licensed Product
 * with Synopsys or any supplement thereto. You are permitted to use and
 * redistribute this Software in source and binary forms, with or without
 * modification, provided that redistributions of source code must retain this
 * notice. You may not view, use, disclose, copy or distribute this file or
 * any information contained herein except pursuant to this license grant from
 * Synopsys. If you do not agree with this notice, including the disclaimer
 * below, then you are not authorized to use the Software.
 *
 * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * ========================================================================== */
#ifndef DWC_DEVICE_ONLY
#ifndef __DWC_HCD_H__
#define __DWC_HCD_H__

#include <linux/list.h>
#include <linux/usb.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
#include <linux/usb/hcd.h>
#else
#include <../drivers/usb/core/hcd.h>
#endif

struct dwc_otg_device;

#include "dwc_otg_cil.h"

/**
 * @file
 *
 * This file contains the structures, constants, and interfaces for
 * the Host Contoller Driver (HCD).
 *
 * The Host Controller Driver (HCD) is responsible for translating requests
 * from the USB Driver into the appropriate actions on the DWC_otg controller.
 * It isolates the USBD from the specifics of the controller by providing an
 * API to the USBD.
 */

/**
 * Phases for control transfers.
 */
typedef enum dwc_otg_control_phase {
	DWC_OTG_CONTROL_SETUP,
	DWC_OTG_CONTROL_DATA,
	DWC_OTG_CONTROL_STATUS
} dwc_otg_control_phase_e;

/** Transaction types. */
typedef enum dwc_otg_transaction_type {
	DWC_OTG_TRANSACTION_NONE,
	DWC_OTG_TRANSACTION_PERIODIC,
	DWC_OTG_TRANSACTION_NON_PERIODIC,
	DWC_OTG_TRANSACTION_ALL
} dwc_otg_transaction_type_e;

/**
 * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
 * interrupt, or isochronous transfer. A single QTD is created for each URB
 * (of one of these types) submitted to the HCD. The transfer associated with
 * a QTD may require one or multiple transactions.
 *
 * A QTD is linked to a Queue Head, which is entered in either the
 * non-periodic or periodic schedule for execution. When a QTD is chosen for
 * execution, some or all of its transactions may be executed. After
 * execution, the state of the QTD is updated. The QTD may be retired if all
 * its transactions are complete or if an error occurred. Otherwise, it
 * remains in the schedule so more transactions can be executed later.
 */
typedef struct dwc_otg_qtd {
	/**
	 * Determines the PID of the next data packet for the data phase of
	 * control transfers. Ignored for other transfer types.<br>
	 * One of the following values:
	 *	- DWC_OTG_HC_PID_DATA0
	 *	- DWC_OTG_HC_PID_DATA1
	 */
	uint8_t			data_toggle;

	/** Current phase for control transfers (Setup, Data, or Status). */
	dwc_otg_control_phase_e	control_phase;

	/** Keep track of the current split type
	 * for FS/LS endpoints on a HS Hub */
	uint8_t			complete_split;

	/** How many bytes transferred during SSPLIT OUT */
	uint32_t		ssplit_out_xfer_count;

	/**
	 * Holds the number of bus errors that have occurred for a transaction
	 * within this transfer.
	 */
	uint8_t			error_count;

	/**
	 * Index of the next frame descriptor for an isochronous transfer. A
	 * frame descriptor describes the buffer position and length of the
	 * data to be transferred in the next scheduled (micro)frame of an
	 * isochronous transfer. It also holds status for that transaction.
	 * The frame index starts at 0.
	 */
	int			isoc_frame_index;

	/** Position of the ISOC split on full/low speed */
	uint8_t			isoc_split_pos;

	/** Position of the ISOC split in the buffer for the current frame */
	uint16_t		isoc_split_offset;

	/** URB for this transfer */
	struct urb		*urb;

	/** This list of QTDs */
	struct list_head	qtd_list_entry;

} dwc_otg_qtd_t;

/**
 * A Queue Head (QH) holds the static characteristics of an endpoint and
 * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
 * be entered in either the non-periodic or periodic schedule.
 */
typedef struct dwc_otg_qh {
	/**
	 * Endpoint type.
	 * One of the following values:
	 *	- USB_ENDPOINT_XFER_CONTROL
	 *	- USB_ENDPOINT_XFER_ISOC
	 *	- USB_ENDPOINT_XFER_BULK
	 *	- USB_ENDPOINT_XFER_INT
	 */
	uint8_t			ep_type;
	uint8_t			ep_is_in;

	/** wMaxPacketSize Field of Endpoint Descriptor. */
	uint16_t		maxp;

	/**
	 * Determines the PID of the next data packet for non-control
	 * transfers. Ignored for control transfers.<br>
	 * One of the following values:
	 *	- DWC_OTG_HC_PID_DATA0
	 *	- DWC_OTG_HC_PID_DATA1
	 */
	uint8_t			data_toggle;

	/** Ping state if 1. */
	uint8_t			ping_state;

	/**
	 * List of QTDs for this QH.
	 */
	struct list_head	qtd_list;

	/** Host channel currently processing transfers for this QH. */
	dwc_hc_t		*channel;

	/** QTD currently assigned to a host channel for this QH. */
	dwc_otg_qtd_t		*qtd_in_process;

	/** Full/low speed endpoint on high-speed hub requires split. */
	uint8_t			do_split;

	/** @name Periodic schedule information */
	/** @{ */

	/** Bandwidth in microseconds per (micro)frame. */
	uint8_t			usecs;

	/** Interval between transfers in (micro)frames. */
	uint16_t		interval;

	/**
	 * (micro)frame to initialize a periodic transfer. The transfer
	 * executes in the following (micro)frame.
	 */
	uint16_t		sched_frame;

	/** (micro)frame at which last start split was initialized. */
	uint16_t		start_split_frame;

	/** @} */

	/** Entry for QH in either the periodic or non-periodic schedule. */
	struct list_head	qh_list_entry;

	/* For non-dword aligned buffer support */
	uint8_t			*dw_align_buf;
	dma_addr_t		dw_align_buf_dma;
} dwc_otg_qh_t;

/**
 * This structure holds the state of the HCD, including the non-periodic and
 * periodic schedules.
 */
typedef struct dwc_otg_hcd {
	/** The DWC otg device pointer */
	struct dwc_otg_device	*otg_dev;

	/** DWC OTG Core Interface Layer */
	dwc_otg_core_if_t	*core_if;

	/** Internal DWC HCD Flags */
	volatile union dwc_otg_hcd_internal_flags {
		uint32_t d32;
		struct {
			unsigned port_connect_status_change : 1;
			unsigned port_connect_status : 1;
			unsigned port_reset_change : 1;
			unsigned port_enable_change : 1;
			unsigned port_suspend_change : 1;
			unsigned port_over_current_change : 1;
			unsigned reserved : 27;
		} b;
	} flags;

	/**
	 * Inactive items in the non-periodic schedule. This is a list of
	 * Queue Heads. Transfers associated with these Queue Heads are not
	 * currently assigned to a host channel.
	 */
	struct list_head	non_periodic_sched_inactive;

	/**
	 * Active items in the non-periodic schedule. This is a list of
	 * Queue Heads. Transfers associated with these Queue Heads are
	 * currently assigned to a host channel.
	 */
	struct list_head	non_periodic_sched_active;

	/**
	 * Pointer to the next Queue Head to process in the active
	 * non-periodic schedule.
	 */
	struct list_head	*non_periodic_qh_ptr;

	/**
	 * Inactive items in the periodic schedule. This is a list of QHs for
	 * periodic transfers that are _not_ scheduled for the next frame.
	 * Each QH in the list has an interval counter that determines when it
	 * needs to be scheduled for execution. This scheduling mechanism
	 * allows only a simple calculation for periodic bandwidth used (i.e.
	 * must assume that all periodic transfers may need to execute in the
	 * same frame). However, it greatly simplifies scheduling and should
	 * be sufficient for the vast majority of OTG hosts, which need to
	 * connect to a small number of peripherals at one time.
	 *
	 * Items move from this list to periodic_sched_ready when the QH
	 * interval counter is 0 at SOF.
	 */
	struct list_head	periodic_sched_inactive;

	/**
	 * List of periodic QHs that are ready for execution in the next
	 * frame, but have not yet been assigned to host channels.
	 *
	 * Items move from this list to periodic_sched_assigned as host
	 * channels become available during the current frame.
	 */
	struct list_head	periodic_sched_ready;

	/**
	 * List of periodic QHs to be executed in the next frame that are
	 * assigned to host channels.
	 *
	 * Items move from this list to periodic_sched_queued as the
	 * transactions for the QH are queued to the DWC_otg controller.
	 */
	struct list_head	periodic_sched_assigned;

	/**
	 * List of periodic QHs that have been queued for execution.
	 *
	 * Items move from this list to either periodic_sched_inactive or
	 * periodic_sched_ready when the channel associated with the transfer
	 * is released. If the interval for the QH is 1, the item moves to
	 * periodic_sched_ready because it must be rescheduled for the next
	 * frame. Otherwise, the item moves to periodic_sched_inactive.
	 */
	struct list_head	periodic_sched_queued;

	/**
	 * Total bandwidth claimed so far for periodic transfers. This value
	 * is in microseconds per (micro)frame. The assumption is that all
	 * periodic transfers may occur in the same (micro)frame.
	 */
	uint16_t		periodic_usecs;

	/**
	 * Frame number read from the core at SOF. The value ranges from 0 to
	 * DWC_HFNUM_MAX_FRNUM.
	 */
	uint16_t		frame_number;

	/**
	 * Free host channels in the controller. This is a list of
	 * dwc_hc_t items.
	 */
	struct list_head	free_hc_list;

	/**
	 * Number of host channels assigned to periodic transfers. Currently
	 * assuming that there is a dedicated host channel for each periodic
	 * transaction and at least one host channel available for
	 * non-periodic transactions.
	 */
	int			periodic_channels;

	/**
	 * Number of host channels assigned to non-periodic transfers.
	 */
	int			non_periodic_channels;

	/**
	 * Array of pointers to the host channel descriptors. Allows accessing
	 * a host channel descriptor given the host channel number. This is
	 * useful in interrupt handlers.
	 */
	dwc_hc_t		*hc_ptr_array[MAX_EPS_CHANNELS];

	/**
	 * Buffer to use for any data received during the status phase of a
	 * control transfer. Normally no data is transferred during the status
	 * phase. This buffer is used as a bit bucket.
	 */
	uint8_t			*status_buf;

	/**
	 * DMA address for status_buf.
	 */
	dma_addr_t		status_buf_dma;
#define DWC_OTG_HCD_STATUS_BUF_SIZE 64

	/**
	 * Structure to allow starting the HCD in a non-interrupt context
	 * during an OTG role change.
	 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
	struct work_struct	start_work;
#else
	struct delayed_work	start_work;
#endif

	/**
	 * Connection timer. An OTG host must display a message if the device
	 * does not connect. Started when the VBus power is turned on via
	 * sysfs attribute "buspower".
	 */
	struct timer_list	conn_timer;

	/* Tasket to do a reset */
	struct tasklet_struct	*reset_tasklet;

	/*  */
	spinlock_t lock;

#ifdef DEBUG
	uint32_t		frrem_samples;
	uint64_t		frrem_accum;

	uint32_t		hfnum_7_samples_a;
	uint64_t		hfnum_7_frrem_accum_a;
	uint32_t		hfnum_0_samples_a;
	uint64_t		hfnum_0_frrem_accum_a;
	uint32_t		hfnum_other_samples_a;
	uint64_t		hfnum_other_frrem_accum_a;

	uint32_t		hfnum_7_samples_b;
	uint64_t		hfnum_7_frrem_accum_b;
	uint32_t		hfnum_0_samples_b;
	uint64_t		hfnum_0_frrem_accum_b;
	uint32_t		hfnum_other_samples_b;
	uint64_t		hfnum_other_frrem_accum_b;
#endif
} dwc_otg_hcd_t;

/** Gets the dwc_otg_hcd from a struct usb_hcd */
static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
{
	return (dwc_otg_hcd_t *)(hcd->hcd_priv);
}

/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */
static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t *dwc_otg_hcd)
{
	return container_of((void *)dwc_otg_hcd, struct usb_hcd, hcd_priv);
}

/** @name HCD Create/Destroy Functions */
/** @{ */
extern int dwc_otg_hcd_init(struct device *dev);
extern void dwc_otg_hcd_remove(struct device *dev);
/** @} */

/** @name Linux HC Driver API Functions */
/** @{ */

extern int dwc_otg_hcd_start(struct usb_hcd *hcd);
extern void dwc_otg_hcd_stop(struct usb_hcd *hcd);
extern int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd);
extern void dwc_otg_hcd_free(struct usb_hcd *hcd);
extern int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd,
				   struct urb *urb,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
				   int mem_flags
#else
				   gfp_t mem_flags
#endif
				  );
extern int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
#endif
				   struct urb *urb, int status);
extern void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd,
					 struct usb_host_endpoint *ep);
extern irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
				   , struct pt_regs *regs
#endif
				  );
extern int dwc_otg_hcd_hub_status_data(struct usb_hcd *hcd,
				       char *buf);
extern int dwc_otg_hcd_hub_control(struct usb_hcd *hcd,
				   u16 typeReq,
				   u16 wValue,
				   u16 wIndex,
				   char *buf,
				   u16 wLength);

/** @} */

/** @name Transaction Execution Functions */
/** @{ */
extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *hcd);
extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t *hcd,
					   dwc_otg_transaction_type_e tr_type);
extern void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *_hcd, struct urb *urb,
				     int status);
/** @} */

/** @name Interrupt Handler Functions */
/** @{ */
extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_disconnect_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t *dwc_otg_hcd, uint32_t num);
extern int32_t dwc_otg_hcd_handle_session_req_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr(dwc_otg_hcd_t *dwc_otg_hcd);
/** @} */


/** @name Schedule Queue Functions */
/** @{ */

/* Implemented in dwc_otg_hcd_queue.c */
extern dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t *hcd, struct urb *urb);
extern void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, struct urb *urb);
extern void dwc_otg_hcd_qh_free(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
extern int dwc_otg_hcd_qh_add(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
extern void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
extern void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, int sched_csplit);

/** Remove and free a QH */
static inline void dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd_t *hcd,
						  dwc_otg_qh_t *qh)
{
	dwc_otg_hcd_qh_remove(hcd, qh);
	dwc_otg_hcd_qh_free(hcd, qh);
}

/** Allocates memory for a QH structure.
 * @return Returns the memory allocate or NULL on error. */
static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(void)
{
	return (dwc_otg_qh_t *) kmalloc(sizeof(dwc_otg_qh_t), GFP_KERNEL);
}

extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(struct urb *urb);
extern void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t *qtd, struct urb *urb);
extern int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *qtd, dwc_otg_hcd_t *dwc_otg_hcd);

/** Allocates memory for a QTD structure.
 * @return Returns the memory allocate or NULL on error. */
static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(void)
{
	return (dwc_otg_qtd_t *) kmalloc(sizeof(dwc_otg_qtd_t), GFP_KERNEL);
}

/** Frees the memory for a QTD structure.  QTD should already be removed from
 * list.
 * @param[in] qtd QTD to free.*/
static inline void dwc_otg_hcd_qtd_free(dwc_otg_qtd_t *qtd)
{
	kfree(qtd);
}

/** Removes a QTD from list.
 * @param[in] hcd HCD instance.
 * @param[in] qtd QTD to remove from list. */
static inline void dwc_otg_hcd_qtd_remove(dwc_otg_hcd_t *hcd, dwc_otg_qtd_t *qtd)
{
	unsigned long flags;
	SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
	list_del(&qtd->qtd_list_entry);
	SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
}

/** Remove and free a QTD */
static inline void dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd_t *hcd, dwc_otg_qtd_t *qtd)
{
	dwc_otg_hcd_qtd_remove(hcd, qtd);
	dwc_otg_hcd_qtd_free(qtd);
}

/** @} */


/** @name Internal Functions */
/** @{ */
dwc_otg_qh_t *dwc_urb_to_qh(struct urb *urb);
void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t *hcd);
void dwc_otg_hcd_dump_state(dwc_otg_hcd_t *hcd);
/** @} */

/** Gets the usb_host_endpoint associated with an URB. */
static inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb)
{
	struct usb_device *dev = urb->dev;
	int ep_num = usb_pipeendpoint(urb->pipe);

	if (usb_pipein(urb->pipe))
		return dev->ep_in[ep_num];
	else
		return dev->ep_out[ep_num];
}

/**
 * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
 * qualified with its direction (possible 32 endpoints per device).
 */
#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
						     ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)

/** Gets the QH that contains the list_head */
#define dwc_list_to_qh(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qh_t, qh_list_entry)

/** Gets the QTD that contains the list_head */
#define dwc_list_to_qtd(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qtd_t, qtd_list_entry)

/** Check if QH is non-periodic  */
#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == USB_ENDPOINT_XFER_BULK) || \
				     (_qh_ptr_->ep_type == USB_ENDPOINT_XFER_CONTROL))

/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */
#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))

/** Packet size for any kind of endpoint descriptor */
#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)

/**
 * Returns true if _frame1 is less than or equal to _frame2. The comparison is
 * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
 * frame number when the max frame number is reached.
 */
static inline int dwc_frame_num_le(uint16_t frame1, uint16_t frame2)
{
	return ((frame2 - frame1) & DWC_HFNUM_MAX_FRNUM) <=
		(DWC_HFNUM_MAX_FRNUM >> 1);
}

/**
 * Returns true if _frame1 is greater than _frame2. The comparison is done
 * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
 * number when the max frame number is reached.
 */
static inline int dwc_frame_num_gt(uint16_t frame1, uint16_t frame2)
{
	return (frame1 != frame2) &&
		(((frame1 - frame2) & DWC_HFNUM_MAX_FRNUM) <
		 (DWC_HFNUM_MAX_FRNUM >> 1));
}

/**
 * Increments _frame by the amount specified by _inc. The addition is done
 * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
 */
static inline uint16_t dwc_frame_num_inc(uint16_t frame, uint16_t inc)
{
	return (frame + inc) & DWC_HFNUM_MAX_FRNUM;
}

static inline uint16_t dwc_full_frame_num(uint16_t frame)
{
	return (frame & DWC_HFNUM_MAX_FRNUM) >> 3;
}

static inline uint16_t dwc_micro_frame_num(uint16_t frame)
{
	return frame & 0x7;
}

#ifdef DEBUG
/**
 * Macro to sample the remaining PHY clocks left in the current frame. This
 * may be used during debugging to determine the average time it takes to
 * execute sections of code. There are two possible sample points, "a" and
 * "b", so the _letter argument must be one of these values.
 *
 * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
 * example, "cat /sys/devices/lm0/hcd_frrem".
 */
#define dwc_sample_frrem(_hcd, _qh, _letter) \
{ \
	hfnum_data_t hfnum; \
	dwc_otg_qtd_t *qtd; \
	qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \
	if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \
		hfnum.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hfnum); \
		switch (hfnum.b.frnum & 0x7) { \
		case 7: \
			_hcd->hfnum_7_samples_##_letter++; \
			_hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \
			break; \
		case 0: \
			_hcd->hfnum_0_samples_##_letter++; \
			_hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \
			break; \
		default: \
			_hcd->hfnum_other_samples_##_letter++; \
			_hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \
			break; \
		} \
	} \
}
#else
#define dwc_sample_frrem(_hcd, _qh, _letter)
#endif
#endif
#endif /* DWC_DEVICE_ONLY */