diff options
Diffstat (limited to 'drivers/usb/host/ohci-hcd.c')
| -rw-r--r-- | drivers/usb/host/ohci-hcd.c | 381 | 
1 files changed, 211 insertions, 170 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f98d03f3144c..46987735a2e3 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -72,12 +72,14 @@  static const char	hcd_name [] = "ohci_hcd";  #define	STATECHANGE_DELAY	msecs_to_jiffies(300) +#define	IO_WATCHDOG_DELAY	msecs_to_jiffies(250)  #include "ohci.h"  #include "pci-quirks.h" -static void ohci_dump (struct ohci_hcd *ohci, int verbose); -static void ohci_stop (struct usb_hcd *hcd); +static void ohci_dump(struct ohci_hcd *ohci); +static void ohci_stop(struct usb_hcd *hcd); +static void io_watchdog_func(unsigned long _ohci);  #include "ohci-hub.c"  #include "ohci-dbg.c" @@ -109,6 +111,33 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");  /*-------------------------------------------------------------------------*/ +static int number_of_tds(struct urb *urb) +{ +	int			len, i, num, this_sg_len; +	struct scatterlist	*sg; + +	len = urb->transfer_buffer_length; +	i = urb->num_mapped_sgs; + +	if (len > 0 && i > 0) {		/* Scatter-gather transfer */ +		num = 0; +		sg = urb->sg; +		for (;;) { +			this_sg_len = min_t(int, sg_dma_len(sg), len); +			num += DIV_ROUND_UP(this_sg_len, 4096); +			len -= this_sg_len; +			if (--i <= 0 || len <= 0) +				break; +			sg = sg_next(sg); +		} + +	} else {			/* Non-SG transfer */ +		/* one TD for every 4096 Bytes (could be up to 8K) */ +		num = DIV_ROUND_UP(len, 4096); +	} +	return num; +} +  /*   * queue up an urb for anything except the root hub   */ @@ -142,12 +171,8 @@ static int ohci_urb_enqueue (  		// case PIPE_INTERRUPT:  		// case PIPE_BULK:  		default: -			/* one TD for every 4096 Bytes (can be up to 8K) */ -			size += urb->transfer_buffer_length / 4096; -			/* ... and for any remaining bytes ... */ -			if ((urb->transfer_buffer_length % 4096) != 0) -				size++; -			/* ... and maybe a zero length packet to wrap it up */ +			size += number_of_tds(urb); +			/* maybe a zero-length packet to wrap it up */  			if (size == 0)  				size++;  			else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 @@ -202,6 +227,16 @@ static int ohci_urb_enqueue (  			usb_hcd_unlink_urb_from_ep(hcd, urb);  			goto fail;  		} + +		/* Start up the I/O watchdog timer, if it's not running */ +		if (!timer_pending(&ohci->io_watchdog) && +				list_empty(&ohci->eds_in_use)) { +			ohci->prev_frame_no = ohci_frame_no(ohci); +			mod_timer(&ohci->io_watchdog, +					jiffies + IO_WATCHDOG_DELAY); +		} +		list_add(&ed->in_use_list, &ohci->eds_in_use); +  		if (ed->type == PIPE_ISOCHRONOUS) {  			u16	frame = ohci_frame_no(ohci); @@ -277,30 +312,24 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)  	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);  	unsigned long		flags;  	int			rc; +	urb_priv_t		*urb_priv;  	spin_lock_irqsave (&ohci->lock, flags);  	rc = usb_hcd_check_unlink_urb(hcd, urb, status); -	if (rc) { -		;	/* Do nothing */ -	} else if (ohci->rh_state == OHCI_RH_RUNNING) { -		urb_priv_t  *urb_priv; +	if (rc == 0) {  		/* Unless an IRQ completed the unlink while it was being  		 * handed to us, flag it for unlink and giveback, and force  		 * some upcoming INTR_SF to call finish_unlinks()  		 */  		urb_priv = urb->hcpriv; -		if (urb_priv) { -			if (urb_priv->ed->state == ED_OPER) -				start_ed_unlink (ohci, urb_priv->ed); +		if (urb_priv->ed->state == ED_OPER) +			start_ed_unlink(ohci, urb_priv->ed); + +		if (ohci->rh_state != OHCI_RH_RUNNING) { +			/* With HC dead, we can clean up right away */ +			ohci_work(ohci);  		} -	} else { -		/* -		 * with HC dead, we won't respect hc queue pointers -		 * any more ... just clean up every urb's memory. -		 */ -		if (urb->hcpriv) -			finish_urb(ohci, urb, status);  	}  	spin_unlock_irqrestore (&ohci->lock, flags);  	return rc; @@ -332,9 +361,7 @@ rescan:  	if (ohci->rh_state != OHCI_RH_RUNNING) {  sanitize:  		ed->state = ED_IDLE; -		if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) -			ohci->eds_scheduled--; -		finish_unlinks (ohci, 0); +		ohci_work(ohci);  	}  	switch (ed->state) { @@ -342,11 +369,6 @@ sanitize:  		/* major IRQ delivery trouble loses INTR_SF too... */  		if (limit-- == 0) {  			ohci_warn(ohci, "ED unlink timeout\n"); -			if (quirk_zfmicro(ohci)) { -				ohci_warn(ohci, "Attempting ZF TD recovery\n"); -				ohci->ed_to_check = ed; -				ohci->zf_delay = 2; -			}  			goto sanitize;  		}  		spin_unlock_irqrestore (&ohci->lock, flags); @@ -406,93 +428,7 @@ ohci_shutdown (struct usb_hcd *hcd)  	udelay(10);  	ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval); -} - -static int check_ed(struct ohci_hcd *ohci, struct ed *ed) -{ -	return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0 -		&& (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK) -			== (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK) -		&& !list_empty(&ed->td_list); -} - -/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes - * an interrupt TD but neglects to add it to the donelist.  On systems with - * this chipset, we need to periodically check the state of the queues to look - * for such "lost" TDs. - */ -static void unlink_watchdog_func(unsigned long _ohci) -{ -	unsigned long	flags; -	unsigned	max; -	unsigned	seen_count = 0; -	unsigned	i; -	struct ed	**seen = NULL; -	struct ohci_hcd	*ohci = (struct ohci_hcd *) _ohci; - -	spin_lock_irqsave(&ohci->lock, flags); -	max = ohci->eds_scheduled; -	if (!max) -		goto done; - -	if (ohci->ed_to_check) -		goto out; - -	seen = kcalloc(max, sizeof *seen, GFP_ATOMIC); -	if (!seen) -		goto out; - -	for (i = 0; i < NUM_INTS; i++) { -		struct ed	*ed = ohci->periodic[i]; - -		while (ed) { -			unsigned	temp; - -			/* scan this branch of the periodic schedule tree */ -			for (temp = 0; temp < seen_count; temp++) { -				if (seen[temp] == ed) { -					/* we've checked it and what's after */ -					ed = NULL; -					break; -				} -			} -			if (!ed) -				break; -			seen[seen_count++] = ed; -			if (!check_ed(ohci, ed)) { -				ed = ed->ed_next; -				continue; -			} - -			/* HC's TD list is empty, but HCD sees at least one -			 * TD that's not been sent through the donelist. -			 */ -			ohci->ed_to_check = ed; -			ohci->zf_delay = 2; - -			/* The HC may wait until the next frame to report the -			 * TD as done through the donelist and INTR_WDH.  (We -			 * just *assume* it's not a multi-TD interrupt URB; -			 * those could defer the IRQ more than one frame, using -			 * DI...)  Check again after the next INTR_SF. -			 */ -			ohci_writel(ohci, OHCI_INTR_SF, -					&ohci->regs->intrstatus); -			ohci_writel(ohci, OHCI_INTR_SF, -					&ohci->regs->intrenable); - -			/* flush those writes */ -			(void) ohci_readl(ohci, &ohci->regs->control); - -			goto out; -		} -	} -out: -	kfree(seen); -	if (ohci->eds_scheduled) -		mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ)); -done: -	spin_unlock_irqrestore(&ohci->lock, flags); +	ohci->rh_state = OHCI_RH_HALTED;  }  /*-------------------------------------------------------------------------* @@ -506,6 +442,9 @@ static int ohci_init (struct ohci_hcd *ohci)  	int ret;  	struct usb_hcd *hcd = ohci_to_hcd(ohci); +	/* Accept arbitrarily long scatter-gather lists */ +	hcd->self.sg_tablesize = ~0; +  	if (distrust_firmware)  		ohci->flags |= OHCI_QUIRK_HUB_POWER; @@ -558,8 +497,12 @@ static int ohci_init (struct ohci_hcd *ohci)  	if (ohci->hcca)  		return 0; +	setup_timer(&ohci->io_watchdog, io_watchdog_func, +			(unsigned long) ohci); +	set_timer_slack(&ohci->io_watchdog, msecs_to_jiffies(20)); +  	ohci->hcca = dma_alloc_coherent (hcd->self.controller, -			sizeof *ohci->hcca, &ohci->hcca_dma, 0); +			sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);  	if (!ohci->hcca)  		return -ENOMEM; @@ -735,16 +678,7 @@ retry:  	// POTPGT delay is bits 24-31, in 2 ms units.  	mdelay ((val >> 23) & 0x1fe); -	if (quirk_zfmicro(ohci)) { -		/* Create timer to watch for bad queue state on ZF Micro */ -		setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func, -				(unsigned long) ohci); - -		ohci->eds_scheduled = 0; -		ohci->ed_to_check = NULL; -	} - -	ohci_dump (ohci, 1); +	ohci_dump(ohci);  	return 0;  } @@ -777,6 +711,142 @@ static int ohci_start(struct usb_hcd *hcd)  /*-------------------------------------------------------------------------*/ +/* + * Some OHCI controllers are known to lose track of completed TDs.  They + * don't add the TDs to the hardware done queue, which means we never see + * them as being completed. + * + * This watchdog routine checks for such problems.  Without some way to + * tell when those TDs have completed, we would never take their EDs off + * the unlink list.  As a result, URBs could never be dequeued and + * endpoints could never be released. + */ +static void io_watchdog_func(unsigned long _ohci) +{ +	struct ohci_hcd	*ohci = (struct ohci_hcd *) _ohci; +	bool		takeback_all_pending = false; +	u32		status; +	u32		head; +	struct ed	*ed; +	struct td	*td, *td_start, *td_next; +	unsigned	frame_no; +	unsigned long	flags; + +	spin_lock_irqsave(&ohci->lock, flags); + +	/* +	 * One way to lose track of completed TDs is if the controller +	 * never writes back the done queue head.  If it hasn't been +	 * written back since the last time this function ran and if it +	 * was non-empty at that time, something is badly wrong with the +	 * hardware. +	 */ +	status = ohci_readl(ohci, &ohci->regs->intrstatus); +	if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { +		if (ohci->prev_donehead) { +			ohci_err(ohci, "HcDoneHead not written back; disabled\n"); + died: +			usb_hc_died(ohci_to_hcd(ohci)); +			ohci_dump(ohci); +			ohci_shutdown(ohci_to_hcd(ohci)); +			goto done; +		} else { +			/* No write back because the done queue was empty */ +			takeback_all_pending = true; +		} +	} + +	/* Check every ED which might have pending TDs */ +	list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) { +		if (ed->pending_td) { +			if (takeback_all_pending || +					OKAY_TO_TAKEBACK(ohci, ed)) { +				unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO); + +				ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n", +						0x007f & tmp, +						(0x000f & (tmp >> 7)) + +							((tmp & ED_IN) >> 5)); +				add_to_done_list(ohci, ed->pending_td); +			} +		} + +		/* Starting from the latest pending TD, */ +		td = ed->pending_td; + +		/* or the last TD on the done list, */ +		if (!td) { +			list_for_each_entry(td_next, &ed->td_list, td_list) { +				if (!td_next->next_dl_td) +					break; +				td = td_next; +			} +		} + +		/* find the last TD processed by the controller. */ +		head = hc32_to_cpu(ohci, ACCESS_ONCE(ed->hwHeadP)) & TD_MASK; +		td_start = td; +		td_next = list_prepare_entry(td, &ed->td_list, td_list); +		list_for_each_entry_continue(td_next, &ed->td_list, td_list) { +			if (head == (u32) td_next->td_dma) +				break; +			td = td_next;	/* head pointer has passed this TD */ +		} +		if (td != td_start) { +			/* +			 * In case a WDH cycle is in progress, we will wait +			 * for the next two cycles to complete before assuming +			 * this TD will never get on the done queue. +			 */ +			ed->takeback_wdh_cnt = ohci->wdh_cnt + 2; +			ed->pending_td = td; +		} +	} + +	ohci_work(ohci); + +	if (ohci->rh_state == OHCI_RH_RUNNING) { + +		/* +		 * Sometimes a controller just stops working.  We can tell +		 * by checking that the frame counter has advanced since +		 * the last time we ran. +		 * +		 * But be careful: Some controllers violate the spec by +		 * stopping their frame counter when no ports are active. +		 */ +		frame_no = ohci_frame_no(ohci); +		if (frame_no == ohci->prev_frame_no) { +			int		active_cnt = 0; +			int		i; +			unsigned	tmp; + +			for (i = 0; i < ohci->num_ports; ++i) { +				tmp = roothub_portstatus(ohci, i); +				/* Enabled and not suspended? */ +				if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS)) +					++active_cnt; +			} + +			if (active_cnt > 0) { +				ohci_err(ohci, "frame counter not updating; disabled\n"); +				goto died; +			} +		} +		if (!list_empty(&ohci->eds_in_use)) { +			ohci->prev_frame_no = frame_no; +			ohci->prev_wdh_cnt = ohci->wdh_cnt; +			ohci->prev_donehead = ohci_readl(ohci, +					&ohci->regs->donehead); +			mod_timer(&ohci->io_watchdog, +					jiffies + IO_WATCHDOG_DELAY); +		} +	} + + done: +	spin_unlock_irqrestore(&ohci->lock, flags); +} +  /* an interrupt happens */  static irqreturn_t ohci_irq (struct usb_hcd *hcd) @@ -825,7 +895,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)  			usb_hc_died(hcd);  		} -		ohci_dump (ohci, 1); +		ohci_dump(ohci);  		ohci_usb_reset (ohci);  	} @@ -863,58 +933,30 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)  			usb_hcd_resume_root_hub(hcd);  	} -	if (ints & OHCI_INTR_WDH) { -		spin_lock (&ohci->lock); -		dl_done_list (ohci); -		spin_unlock (&ohci->lock); -	} - -	if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) { -		spin_lock(&ohci->lock); -		if (ohci->ed_to_check) { -			struct ed *ed = ohci->ed_to_check; - -			if (check_ed(ohci, ed)) { -				/* HC thinks the TD list is empty; HCD knows -				 * at least one TD is outstanding -				 */ -				if (--ohci->zf_delay == 0) { -					struct td *td = list_entry( -						ed->td_list.next, -						struct td, td_list); -					ohci_warn(ohci, -						  "Reclaiming orphan TD %p\n", -						  td); -					takeback_td(ohci, td); -					ohci->ed_to_check = NULL; -				} -			} else -				ohci->ed_to_check = NULL; -		} -		spin_unlock(&ohci->lock); -	} +	spin_lock(&ohci->lock); +	if (ints & OHCI_INTR_WDH) +		update_done_list(ohci);  	/* could track INTR_SO to reduce available PCI/... bandwidth */  	/* handle any pending URB/ED unlinks, leaving INTR_SF enabled  	 * when there's still unlinking to be done (next frame).  	 */ -	spin_lock (&ohci->lock); -	if (ohci->ed_rm_list) -		finish_unlinks (ohci, ohci_frame_no(ohci)); -	if ((ints & OHCI_INTR_SF) != 0 -			&& !ohci->ed_rm_list -			&& !ohci->ed_to_check +	ohci_work(ohci); +	if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list  			&& ohci->rh_state == OHCI_RH_RUNNING)  		ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); -	spin_unlock (&ohci->lock);  	if (ohci->rh_state == OHCI_RH_RUNNING) {  		ohci_writel (ohci, ints, ®s->intrstatus); +		if (ints & OHCI_INTR_WDH) +			++ohci->wdh_cnt; +  		ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable);  		// flush those writes  		(void) ohci_readl (ohci, &ohci->regs->control);  	} +	spin_unlock(&ohci->lock);  	return IRQ_HANDLED;  } @@ -925,18 +967,17 @@ static void ohci_stop (struct usb_hcd *hcd)  {  	struct ohci_hcd		*ohci = hcd_to_ohci (hcd); -	ohci_dump (ohci, 1); +	ohci_dump(ohci);  	if (quirk_nec(ohci))  		flush_work(&ohci->nec_work); +	del_timer_sync(&ohci->io_watchdog);  	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);  	ohci_usb_reset(ohci);  	free_irq(hcd->irq, hcd);  	hcd->irq = 0; -	if (quirk_zfmicro(ohci)) -		del_timer(&ohci->unlink_watchdog);  	if (quirk_amdiso(ohci))  		usb_amd_dev_put(); @@ -993,7 +1034,7 @@ int ohci_restart(struct ohci_hcd *ohci)  		if (!urb->unlinked)  			urb->unlinked = -ESHUTDOWN;  	} -	finish_unlinks (ohci, 0); +	ohci_work(ohci);  	spin_unlock_irq(&ohci->lock);  	/* paranoia, in case that didn't work: */  | 
