⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sl811-hcd.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * SL811HS HCD (Host Controller Driver) for USB. * * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) * Copyright (C) 2004-2005 David Brownell * * Periodic scheduling is based on Roman's OHCI code * 	Copyright (C) 1999 Roman Weissgaerber * * The SL811HS controller handles host side USB (like the SL11H, but with * another register set and SOF generation) as well as peripheral side USB * (like the SL811S).  This driver version doesn't implement the Gadget API * for the peripheral role; or OTG (that'd need much external circuitry). * * For documentation, see the SL811HS spec and the "SL811HS Embedded Host" * document (providing significant pieces missing from that spec); plus * the SL811S spec if you want peripheral side info. *//* * Status:  Passed basic stress testing, works with hubs, mice, keyboards, * and usb-storage. * * TODO: * - usb suspend/resume triggered by sl811 (with USB_SUSPEND) * - various issues noted in the code * - performance work; use both register banks; ... * - use urb->iso_frame_desc[] with ISO transfers */#undef	VERBOSE#undef	PACKET_TRACE#include <linux/config.h>#ifdef CONFIG_USB_DEBUG#	define DEBUG#else#	undef DEBUG#endif#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/usb.h>#include <linux/usb_sl811.h>#include <linux/platform_device.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/byteorder.h>#include "../core/hcd.h"#include "sl811.h"MODULE_DESCRIPTION("SL811HS USB Host Controller Driver");MODULE_LICENSE("GPL");#define DRIVER_VERSION	"19 May 2005"#ifndef DEBUG#	define	STUB_DEBUG_FILE#endif/* for now, use only one transfer register bank */#undef	USE_B/* this doesn't understand urb->iso_frame_desc[], but if you had a driver * that just queued one ISO frame per URB then iso transfers "should" work * using the normal urb status fields. */#define	DISABLE_ISO// #define	QUIRK2#define	QUIRK3static const char hcd_name[] = "sl811-hcd";/*-------------------------------------------------------------------------*/static void port_power(struct sl811 *sl811, int is_on){	struct usb_hcd	*hcd = sl811_to_hcd(sl811);	/* hub is inactive unless the port is powered */	if (is_on) {		if (sl811->port1 & (1 << USB_PORT_FEAT_POWER))			return;		sl811->port1 = (1 << USB_PORT_FEAT_POWER);		sl811->irq_enable = SL11H_INTMASK_INSRMV;		hcd->self.controller->power.power_state = PMSG_ON;	} else {		sl811->port1 = 0;		sl811->irq_enable = 0;		hcd->state = HC_STATE_HALT;		hcd->self.controller->power.power_state = PMSG_SUSPEND;	}	sl811->ctrl1 = 0;	sl811_write(sl811, SL11H_IRQ_ENABLE, 0);	sl811_write(sl811, SL11H_IRQ_STATUS, ~0);	if (sl811->board && sl811->board->port_power) {		/* switch VBUS, at 500mA unless hub power budget gets set */		DBG("power %s\n", is_on ? "on" : "off");		sl811->board->port_power(hcd->self.controller, is_on);	}	/* reset as thoroughly as we can */	if (sl811->board && sl811->board->reset)		sl811->board->reset(hcd->self.controller);	else {		sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0);		mdelay(20);	}	sl811_write(sl811, SL11H_IRQ_ENABLE, 0);	sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);	sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT);	sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);	// if !is_on, put into lowpower mode now}/*-------------------------------------------------------------------------*//* This is a PIO-only HCD.  Queueing appends URBs to the endpoint's queue, * and may start I/O.  Endpoint queues are scanned during completion irq * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation. * * Using an external DMA engine to copy a packet at a time could work, * though setup/teardown costs may be too big to make it worthwhile. *//* SETUP starts a new control request.  Devices are not allowed to * STALL or NAK these; they must cancel any pending control requests. */static void setup_packet(	struct sl811		*sl811,	struct sl811h_ep	*ep,	struct urb		*urb,	u8			bank,	u8			control){	u8			addr;	u8			len;	void __iomem		*data_reg;	addr = SL811HS_PACKET_BUF(bank == 0);	len = sizeof(struct usb_ctrlrequest);	data_reg = sl811->data_reg;	sl811_write_buf(sl811, addr, urb->setup_packet, len);	/* autoincrementing */	sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);	writeb(len, data_reg);	writeb(SL_SETUP /* | ep->epnum */, data_reg);	writeb(usb_pipedevice(urb->pipe), data_reg);	/* always OUT/data0 */ ;	sl811_write(sl811, bank + SL11H_HOSTCTLREG,			control | SL11H_HCTLMASK_OUT);	ep->length = 0;	PACKET("SETUP qh%p\n", ep);}/* STATUS finishes control requests, often after IN or OUT data packets */static void status_packet(	struct sl811		*sl811,	struct sl811h_ep	*ep,	struct urb		*urb,	u8			bank,	u8			control){	int			do_out;	void __iomem		*data_reg;	do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe);	data_reg = sl811->data_reg;	/* autoincrementing */	sl811_write(sl811, bank + SL11H_BUFADDRREG, 0);	writeb(0, data_reg);	writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg);	writeb(usb_pipedevice(urb->pipe), data_reg);	/* always data1; sometimes IN */	control |= SL11H_HCTLMASK_TOGGLE;	if (do_out)		control |= SL11H_HCTLMASK_OUT;	sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);	ep->length = 0;	PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "",			do_out ? "out" : "in", ep);}/* IN packets can be used with any type of endpoint. here we just * start the transfer, data from the peripheral may arrive later. * urb->iso_frame_desc is currently ignored here... */static void in_packet(	struct sl811		*sl811,	struct sl811h_ep	*ep,	struct urb		*urb,	u8			bank,	u8			control){	u8			addr;	u8			len;	void __iomem		*data_reg;	/* avoid losing data on overflow */	len = ep->maxpacket;	addr = SL811HS_PACKET_BUF(bank == 0);	if (!(control & SL11H_HCTLMASK_ISOCH)			&& usb_gettoggle(urb->dev, ep->epnum, 0))		control |= SL11H_HCTLMASK_TOGGLE;	data_reg = sl811->data_reg;	/* autoincrementing */	sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);	writeb(len, data_reg);	writeb(SL_IN | ep->epnum, data_reg);	writeb(usb_pipedevice(urb->pipe), data_reg);	sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);	ep->length = min((int)len,			urb->transfer_buffer_length - urb->actual_length);	PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",			!!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len);}/* OUT packets can be used with any type of endpoint. * urb->iso_frame_desc is currently ignored here... */static void out_packet(	struct sl811		*sl811,	struct sl811h_ep	*ep,	struct urb		*urb,	u8			bank,	u8			control){	void			*buf;	u8			addr;	u8			len;	void __iomem		*data_reg;	buf = urb->transfer_buffer + urb->actual_length;	prefetch(buf);	len = min((int)ep->maxpacket,			urb->transfer_buffer_length - urb->actual_length);	if (!(control & SL11H_HCTLMASK_ISOCH)			&& usb_gettoggle(urb->dev, ep->epnum, 1))		control |= SL11H_HCTLMASK_TOGGLE;	addr = SL811HS_PACKET_BUF(bank == 0);	data_reg = sl811->data_reg;	sl811_write_buf(sl811, addr, buf, len);	/* autoincrementing */	sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);	writeb(len, data_reg);	writeb(SL_OUT | ep->epnum, data_reg);	writeb(usb_pipedevice(urb->pipe), data_reg);	sl811_write(sl811, bank + SL11H_HOSTCTLREG,			control | SL11H_HCTLMASK_OUT);	ep->length = len;	PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",			!!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len);}/*-------------------------------------------------------------------------*//* caller updates on-chip enables later */static inline void sofirq_on(struct sl811 *sl811){	if (sl811->irq_enable & SL11H_INTMASK_SOFINTR)		return;	VDBG("sof irq on\n");	sl811->irq_enable |= SL11H_INTMASK_SOFINTR;}static inline void sofirq_off(struct sl811 *sl811){	if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR))		return;	VDBG("sof irq off\n");	sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR;}/*-------------------------------------------------------------------------*//* pick the next endpoint for a transaction, and issue it. * frames start with periodic transfers (after whatever is pending * from the previous frame), and the rest of the time is async * transfers, scheduled round-robin. */static struct sl811h_ep	*start(struct sl811 *sl811, u8 bank){	struct sl811h_ep	*ep;	struct urb		*urb;	int			fclock;	u8			control;	/* use endpoint at schedule head */	if (sl811->next_periodic) {		ep = sl811->next_periodic;		sl811->next_periodic = ep->next;	} else {		if (sl811->next_async)			ep = sl811->next_async;		else if (!list_empty(&sl811->async))			ep = container_of(sl811->async.next,					struct sl811h_ep, schedule);		else {			/* could set up the first fullspeed periodic			 * transfer for the next frame ...			 */			return NULL;		}#ifdef USE_B		if ((bank && sl811->active_b == ep) || sl811->active_a == ep)			return NULL;#endif		if (ep->schedule.next == &sl811->async)			sl811->next_async = NULL;		else			sl811->next_async = container_of(ep->schedule.next,					struct sl811h_ep, schedule);	}	if (unlikely(list_empty(&ep->hep->urb_list))) {		DBG("empty %p queue?\n", ep);		return NULL;	}	urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);	control = ep->defctrl;	/* if this frame doesn't have enough time left to transfer this	 * packet, wait till the next frame.  too-simple algorithm...	 */	fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6;	fclock -= 100;		/* setup takes not much time */	if (urb->dev->speed == USB_SPEED_LOW) {		if (control & SL11H_HCTLMASK_PREAMBLE) {			/* also note erratum 1: some hubs won't work */			fclock -= 800;		}		fclock -= ep->maxpacket << 8;		/* erratum 2: AFTERSOF only works for fullspeed */		if (fclock < 0) {			if (ep->period)				sl811->stat_overrun++;			sofirq_on(sl811);			return NULL;		}	} else {		fclock -= 12000 / 19;	/* 19 64byte packets/msec */		if (fclock < 0) {			if (ep->period)				sl811->stat_overrun++;			control |= SL11H_HCTLMASK_AFTERSOF;		/* throttle bulk/control irq noise */		} else if (ep->nak_count)			control |= SL11H_HCTLMASK_AFTERSOF;	}	switch (ep->nextpid) {	case USB_PID_IN:		in_packet(sl811, ep, urb, bank, control);		break;	case USB_PID_OUT:		out_packet(sl811, ep, urb, bank, control);		break;	case USB_PID_SETUP:		setup_packet(sl811, ep, urb, bank, control);		break;	case USB_PID_ACK:		/* for control status */		status_packet(sl811, ep, urb, bank, control);		break;	default:		DBG("bad ep%p pid %02x\n", ep, ep->nextpid);		ep = NULL;	}	return ep;}#define MIN_JIFFIES	((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2)static inline void start_transfer(struct sl811 *sl811){	if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))		return;	if (sl811->active_a == NULL) {		sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF));		if (sl811->active_a != NULL)			sl811->jiffies_a = jiffies + MIN_JIFFIES;	}#ifdef USE_B	if (sl811->active_b == NULL) {		sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF));		if (sl811->active_b != NULL)			sl811->jiffies_b = jiffies + MIN_JIFFIES;	}#endif}static void finish_request(	struct sl811		*sl811,	struct sl811h_ep	*ep,	struct urb		*urb,	struct pt_regs		*regs,	int			status) __releases(sl811->lock) __acquires(sl811->lock){	unsigned		i;	if (usb_pipecontrol(urb->pipe))		ep->nextpid = USB_PID_SETUP;	spin_lock(&urb->lock);	if (urb->status == -EINPROGRESS)		urb->status = status;	urb->hcpriv = NULL;	spin_unlock(&urb->lock);	spin_unlock(&sl811->lock);	usb_hcd_giveback_urb(sl811_to_hcd(sl811), urb, regs);	spin_lock(&sl811->lock);	/* leave active endpoints in the schedule */	if (!list_empty(&ep->hep->urb_list))		return;	/* async deschedule? */	if (!list_empty(&ep->schedule)) {		list_del_init(&ep->schedule);		if (ep == sl811->next_async)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -