1/*****************************************************************************
2 **   FILE NAME       : ifxhcd_queue.c
3 **   PROJECT         : IFX USB sub-system V3
4 **   MODULES         : IFX USB sub-system Host and Device driver
5 **   SRC VERSION     : 1.0
6 **   DATE            : 1/Jan/2009
7 **   AUTHOR          : Chen, Howard
8 **   DESCRIPTION     : This file contains the functions to manage Queue Heads and Queue
9 **                     Transfer Descriptors.
10 *****************************************************************************/
11
12/*!
13 \file ifxhcd_queue.c
14 \ingroup IFXUSB_DRIVER_V3
15  \brief This file contains the functions to manage Queue Heads and Queue
16  Transfer Descriptors.
17*/
18#include <linux/version.h>
19#include "ifxusb_version.h"
20
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/moduleparam.h>
24#include <linux/init.h>
25#include <linux/device.h>
26#include <linux/errno.h>
27#include <linux/list.h>
28#include <linux/interrupt.h>
29#include <linux/string.h>
30
31#include "ifxusb_plat.h"
32#include "ifxusb_regs.h"
33#include "ifxusb_cif.h"
34#include "ifxhcd.h"
35
36#ifdef __EPQD_DESTROY_TIMEOUT__
37	#define epqh_self_destroy_timeout 5
38	static void eqph_destroy_func(unsigned long _ptr)
39	{
40		ifxhcd_epqh_t *epqh=(ifxhcd_epqh_t *)_ptr;
41		if(epqh)
42		{
43			ifxhcd_epqh_free (epqh);
44		}
45	}
46#endif
47
48#define SCHEDULE_SLOP 10
49
50/*!
51  \brief This function allocates and initializes a EPQH.
52
53  \param _ifxhcd The HCD state structure for the USB Host controller.
54  \param[in] _urb Holds the information about the device/endpoint that we need
55  to initialize the EPQH.
56
57  \return Returns pointer to the newly allocated EPQH, or NULL on error.
58 */
59ifxhcd_epqh_t *ifxhcd_epqh_create (ifxhcd_hcd_t *_ifxhcd, struct urb *_urb)
60{
61	ifxhcd_epqh_t *epqh;
62
63	hprt0_data_t   hprt0;
64	struct usb_host_endpoint *sysep = ifxhcd_urb_to_endpoint(_urb);
65
66	/* Allocate memory */
67//	epqh=(ifxhcd_epqh_t *) kmalloc (sizeof(ifxhcd_epqh_t), GFP_KERNEL);
68	epqh=(ifxhcd_epqh_t *) kmalloc (sizeof(ifxhcd_epqh_t), GFP_ATOMIC);
69
70	if(epqh == NULL)
71		return NULL;
72
73	memset (epqh, 0, sizeof (ifxhcd_epqh_t));
74
75	epqh->sysep=sysep;
76
77	/* Initialize EPQH */
78	switch (usb_pipetype(_urb->pipe))
79	{
80		case PIPE_CONTROL    : epqh->ep_type = IFXUSB_EP_TYPE_CTRL; break;
81		case PIPE_BULK       : epqh->ep_type = IFXUSB_EP_TYPE_BULK; break;
82		case PIPE_ISOCHRONOUS: epqh->ep_type = IFXUSB_EP_TYPE_ISOC; break;
83		case PIPE_INTERRUPT  : epqh->ep_type = IFXUSB_EP_TYPE_INTR; break;
84	}
85
86	//epqh->data_toggle = IFXUSB_HC_PID_DATA0;
87
88	epqh->mps = usb_maxpacket(_urb->dev, _urb->pipe, !(usb_pipein(_urb->pipe)));
89
90	hprt0.d32 = ifxusb_read_hprt0 (&_ifxhcd->core_if);
91
92	INIT_LIST_HEAD(&epqh->urbd_list);
93	INIT_LIST_HEAD(&epqh->epqh_list_entry);
94	epqh->hc = NULL;
95
96	epqh->dump_buf = ifxusb_alloc_buf(epqh->mps, 0);
97
98	/* FS/LS Enpoint on HS Hub
99	 * NOT virtual root hub */
100	epqh->need_split = 0;
101	epqh->pkt_count_limit=0;
102	if(epqh->ep_type == IFXUSB_EP_TYPE_BULK && !(usb_pipein(_urb->pipe)) )
103		epqh->pkt_count_limit=4;
104	if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED &&
105	    ((_urb->dev->speed == USB_SPEED_LOW) ||
106	     (_urb->dev->speed == USB_SPEED_FULL)) &&
107	     (_urb->dev->tt) && (_urb->dev->tt->hub->devnum != 1))
108	{
109		IFX_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n",
110		       usb_pipeendpoint(_urb->pipe), _urb->dev->tt->hub->devnum,
111		       _urb->dev->ttport);
112		epqh->need_split = 1;
113		epqh->pkt_count_limit=1;
114	}
115
116	if (epqh->ep_type == IFXUSB_EP_TYPE_INTR ||
117	    epqh->ep_type == IFXUSB_EP_TYPE_ISOC)
118	{
119		/* Compute scheduling parameters once and save them. */
120		epqh->interval    = _urb->interval;
121		if(epqh->need_split)
122			epqh->interval *= 8;
123	}
124
125	epqh->period_counter=0;
126	epqh->is_active=0;
127
128	#ifdef __EPQD_DESTROY_TIMEOUT__
129		/* Start a timer for this transfer. */
130		init_timer(&epqh->destroy_timer);
131		epqh->destroy_timer.function = eqph_destroy_func;
132		epqh->destroy_timer.data = (unsigned long)(epqh);
133	#endif
134
135	#ifdef __DEBUG__
136		IFX_DEBUGPL(DBG_HCD , "IFXUSB HCD EPQH Initialized\n");
137		IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH  - epqh = %p\n", epqh);
138		IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH  - Device Address = %d EP %d, %s\n",
139			    _urb->dev->devnum,
140			    usb_pipeendpoint(_urb->pipe),
141			    usb_pipein(_urb->pipe) == USB_DIR_IN ? "IN" : "OUT");
142		IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH  - Speed = %s\n",
143			    ({ char *speed; switch (_urb->dev->speed) {
144			    case USB_SPEED_LOW: speed  = "low" ; break;
145			    case USB_SPEED_FULL: speed = "full"; break;
146			    case USB_SPEED_HIGH: speed = "high"; break;
147			    default: speed = "?";	break;
148			    }; speed;}));
149		IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH  - Type = %s\n",
150			({
151				char *type; switch (epqh->ep_type)
152				{
153				    case IFXUSB_EP_TYPE_ISOC: type = "isochronous"; break;
154				    case IFXUSB_EP_TYPE_INTR: type = "interrupt"  ; break;
155				    case IFXUSB_EP_TYPE_CTRL: type = "control"    ; break;
156				    case IFXUSB_EP_TYPE_BULK: type = "bulk"       ; break;
157				    default: type = "?";	break;
158				};
159				type;
160			}));
161		if (epqh->ep_type == IFXUSB_EP_TYPE_INTR)
162			IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - interval = %d\n", epqh->interval);
163	#endif
164
165	return epqh;
166}
167
168
169
170
171
172
173/*!
174  \brief Free the EPQH.  EPQH should already be removed from a list.
175  URBD list should already be empty if called from URB Dequeue.
176
177  \param[in] _epqh The EPQH to free.
178 */
179void ifxhcd_epqh_free (ifxhcd_epqh_t *_epqh)
180{
181	unsigned long     flags;
182
183	if(_epqh->sysep) _epqh->sysep->hcpriv=NULL;
184	_epqh->sysep=NULL;
185
186	if(!_epqh)
187		return;
188
189	/* Free each QTD in the QTD list */
190	local_irq_save (flags);
191	if (!list_empty(&_epqh->urbd_list))
192		IFX_WARN("%s() invalid epqh state\n",__func__);
193
194	#if defined(__UNALIGNED_BUFFER_ADJ__)
195		if(_epqh->aligned_buf)
196			ifxusb_free_buf(_epqh->aligned_buf);
197		if(_epqh->aligned_setup)
198			ifxusb_free_buf(_epqh->aligned_setup);
199	#endif
200
201	if (!list_empty(&_epqh->epqh_list_entry))
202		list_del_init(&_epqh->epqh_list_entry);
203
204	#ifdef __EPQD_DESTROY_TIMEOUT__
205		del_timer(&_epqh->destroy_timer);
206	#endif
207	if(_epqh->dump_buf)
208		ifxusb_free_buf(_epqh->dump_buf);
209	_epqh->dump_buf=0;
210
211
212	kfree (_epqh);
213	local_irq_restore (flags);
214}
215
216/*!
217  \brief This function adds a EPQH to
218
219  \return 0 if successful, negative error code otherwise.
220 */
221void ifxhcd_epqh_ready(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh)
222{
223	unsigned long flags;
224	local_irq_save(flags);
225	if (list_empty(&_epqh->epqh_list_entry))
226	{
227		#ifdef __EN_ISOC__
228		if     (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC)
229			list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready);
230		else
231		#endif
232		if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR)
233			list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready);
234		else
235			list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready);
236		_epqh->is_active=0;
237	}
238	else if(!_epqh->is_active)
239	{
240		#ifdef __EN_ISOC__
241		if     (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC)
242			list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready);
243		else
244		#endif
245		if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR)
246			list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready);
247		else
248			list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready);
249	}
250	#ifdef __EPQD_DESTROY_TIMEOUT__
251		del_timer(&_epqh->destroy_timer);
252	#endif
253	local_irq_restore(flags);
254}
255
256void ifxhcd_epqh_active(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh)
257{
258	unsigned long flags;
259	local_irq_save(flags);
260	if (list_empty(&_epqh->epqh_list_entry))
261		IFX_WARN("%s() invalid epqh state\n",__func__);
262	#ifdef __EN_ISOC__
263		if     (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC)
264			list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_active);
265		else
266	#endif
267	if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR)
268		list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_active);
269	else
270		list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_active);
271	_epqh->is_active=1;
272	#ifdef __EPQD_DESTROY_TIMEOUT__
273		del_timer(&_epqh->destroy_timer);
274	#endif
275	local_irq_restore(flags);
276}
277
278void ifxhcd_epqh_idle(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh)
279{
280	unsigned long flags;
281	local_irq_save(flags);
282
283	if (list_empty(&_epqh->urbd_list))
284	{
285		if(_epqh->ep_type == IFXUSB_EP_TYPE_ISOC || _epqh->ep_type == IFXUSB_EP_TYPE_INTR)
286		{
287			list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_stdby);
288		}
289		else
290		{
291			list_del_init(&_epqh->epqh_list_entry);
292			#ifdef __EPQD_DESTROY_TIMEOUT__
293				del_timer(&_epqh->destroy_timer);
294				_epqh->destroy_timer.expires = jiffies + (HZ*epqh_self_destroy_timeout);
295				add_timer(&_epqh->destroy_timer );
296			#endif
297		}
298	}
299	else
300	{
301		#ifdef __EN_ISOC__
302		if     (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC)
303			list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready);
304		else
305		#endif
306		if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR)
307			list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready);
308		else
309			list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready);
310	}
311	_epqh->is_active=0;
312	local_irq_restore(flags);
313}
314
315
316void ifxhcd_epqh_idle_periodic(ifxhcd_epqh_t *_epqh)
317{
318	unsigned long flags;
319	if(_epqh->ep_type != IFXUSB_EP_TYPE_ISOC && _epqh->ep_type != IFXUSB_EP_TYPE_INTR)
320		return;
321
322	local_irq_save(flags);
323
324	if (list_empty(&_epqh->epqh_list_entry))
325		IFX_WARN("%s() invalid epqh state\n",__func__);
326	if (!list_empty(&_epqh->urbd_list))
327		IFX_WARN("%s() invalid epqh state(not empty)\n",__func__);
328
329	_epqh->is_active=0;
330	list_del_init(&_epqh->epqh_list_entry);
331	#ifdef __EPQD_DESTROY_TIMEOUT__
332		del_timer(&_epqh->destroy_timer);
333		_epqh->destroy_timer.expires = jiffies + (HZ*epqh_self_destroy_timeout);
334		add_timer(&_epqh->destroy_timer );
335	#endif
336
337	local_irq_restore(flags);
338}
339
340
341int ifxhcd_urbd_create (ifxhcd_hcd_t *_ifxhcd,struct urb *_urb)
342{
343	ifxhcd_urbd_t            *urbd;
344	struct usb_host_endpoint *sysep;
345	ifxhcd_epqh_t            *epqh;
346	unsigned long             flags;
347	/* == AVM/WK 20100714 retval correctly initialized ==*/
348	int                       retval = -ENOMEM;
349
350	/*== AVM/BC 20100630 - Spinlock ==*/
351	//local_irq_save(flags);
352	SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags);
353
354//		urbd =  (ifxhcd_urbd_t *) kmalloc (sizeof(ifxhcd_urbd_t), GFP_KERNEL);
355	urbd =  (ifxhcd_urbd_t *) kmalloc (sizeof(ifxhcd_urbd_t), GFP_ATOMIC);
356	if (urbd != NULL) /* Initializes a QTD structure.*/
357	{
358		retval = 0;
359		memset (urbd, 0, sizeof (ifxhcd_urbd_t));
360
361		sysep = ifxhcd_urb_to_endpoint(_urb);
362		epqh = (ifxhcd_epqh_t *)sysep->hcpriv;
363		if (epqh == NULL)
364		{
365			epqh = ifxhcd_epqh_create (_ifxhcd, _urb);
366			if (epqh == NULL)
367			{
368				retval = -ENOSPC;
369				kfree(urbd);
370				//local_irq_restore (flags);
371				SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags);
372				return retval;
373			}
374			sysep->hcpriv = epqh;
375		}
376
377		INIT_LIST_HEAD(&urbd->urbd_list_entry);
378
379		/*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/
380		retval = usb_hcd_link_urb_to_ep(ifxhcd_to_syshcd(_ifxhcd), _urb);
381
382		if (unlikely(retval)){
383			kfree(urbd);
384			kfree(epqh);
385			SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags);
386			return retval;
387		}
388
389		list_add_tail(&urbd->urbd_list_entry, &epqh->urbd_list);
390		urbd->urb = _urb;
391		_urb->hcpriv = urbd;
392
393		urbd->epqh=epqh;
394		urbd->is_in=usb_pipein(_urb->pipe) ? 1 : 0;;
395
396		urbd->xfer_len=_urb->transfer_buffer_length;
397#define URB_NO_SETUP_DMA_MAP 0
398
399		if(urbd->xfer_len>0)
400		{
401			if(_urb->transfer_flags && URB_NO_TRANSFER_DMA_MAP)
402				urbd->xfer_buff = (uint8_t *) (KSEG1ADDR((uint32_t *)_urb->transfer_dma));
403			else
404				urbd->xfer_buff = (uint8_t *) _urb->transfer_buffer;
405		}
406		if(epqh->ep_type == IFXUSB_EP_TYPE_CTRL)
407		{
408			if(_urb->transfer_flags && URB_NO_SETUP_DMA_MAP)
409				urbd->setup_buff = (uint8_t *) (KSEG1ADDR((uint32_t *)_urb->setup_dma));
410			else
411				urbd->setup_buff = (uint8_t *) _urb->setup_packet;
412		}
413	}
414	//local_irq_restore (flags);
415	SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags);
416	return retval;
417}
418
419