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

📄 ohci-sl811-emu.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * SL811HS OHCI HCD (Host Controller Driver) for USB. * * linux/drivers/usb/host/ohci-sl811-emu.c * * Copyright (C) 2004 by Lothar Wassmann <LW at KARO-electronics.de> * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * * * SL811HS OHCI emulation * */#include <asm/arch/dma.h>#define get_hw_fld(p,n)		le32_to_cpu((p)->hw##n)#define set_hw_fld(p,n,v)	(p)->hw##n = cpu_to_le32(v)#define get_hwinfo(p)		get_hw_fld(p, INFO)#define set_hwinfo(p,v)		set_hw_fld(p, INFO, v)#define ED_HEADP(ed)		get_hw_fld(ed, HeadP)#define ED_TAILP(ed)		get_hw_fld(ed, TailP)#define ED_HALTED(ed)		(ED_HEADP(ed) & ED_H)#define EP_SKIP(info)		((info) & ED_SKIP)#define EP_ISO(info)		((info) & ED_ISO)#define TD_CC_SET_HW(td, cc)	set_hwinfo(td, (get_hwinfo(td) & ~TD_CC) | (((cc) << 28) & TD_CC))#define TD_FC_GET(info)		(((info) >> 24) & 7)#define TD_SF_GET(info)		(((info) >> 0) & 0xffff)static u32 TD_BUF_LEN(struct td *td){	u32 len = 0;	u32 cbp = get_hw_fld(td, CBP);	if (cbp != 0) {		u32 be = get_hw_fld(td, BE);		if (unlikely((cbp ^ be) & PAGE_MASK)) {			// CBP and BE in different pages			// length till end of first page  +  length in second page			len = (PAGE_SIZE - (cbp & ~PAGE_MASK)) + (be & ~PAGE_MASK) + 1;		} else {			len = be - cbp + 1;		}	}	return len;}#define ED_MPS		(((1 << 10) - 1) << 16)#define ed_pid(info)	(((info) & (ED_IN | ED_OUT)) >> 11)#define td_pid(info)	(((info) & TD_DP) >> 19)#define ed_mps(i)	(((i) & ED_MPS) >> 16)#define ed_fa(info)	((info) & ((1 << 7) - 1))#define ed_en(info)	(((info) >> 7) & ((1 << 4) - 1))static const u8 ed_td_to_pid[4][4] = {	{ PID_SETUP, PID_OUT, PID_IN, 0 },	// PID from TD	{ PID_OUT, PID_OUT, PID_OUT, PID_OUT },	// PID from ED	{ PID_IN, PID_IN, PID_IN, PID_IN },	// PID from ED	{ PID_SETUP, PID_OUT, PID_IN, 0 },	// PID from TD};static inline u32 DMA_XFER_SIZE(u32 addr, u32 len){	u32 count = len;	if (unlikely(((addr & ~PAGE_MASK) + len) > PAGE_SIZE)) {		count = PAGE_ALIGN(addr + len) - addr;	}	return count;}#define OHCI_FR ((1 << 14) - 1)#define OHCI_FRT (1 << 31)static u16 update_fm_remaining(struct ohci_hcd *ohci){	struct hc_sl811_dev *dev = ohci_to_sl811_dev(ohci);	struct ohci_regs *regs = ohci->regs;	u8 rem = __hc_sl811_read_reg(dev, SL11H_CTLREG2);	u8 int_stat = __hc_sl811_read_reg(dev, SL11H_INTSTATREG);	u16 fmremaining = (int_stat & SL11H_INTMASK_SOFINTR) ? 0 : rem << 6;	//struct usb_hcd *hcd = ohci_to_hcd(ohci);	HcFmRemaining = (HcFmRemaining & ~OHCI_FR) | fmremaining;	return fmremaining;}static int check_bustime(struct ohci_hcd *ohci, struct xfer_buf *buf, u32 ed_flags){	unsigned int time;	unsigned int available = update_fm_remaining(ohci);	struct ohci_regs *regs = ohci->regs;	struct usb_hcd *hcd = ohci_to_hcd(ohci);	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	if (ed_flags & ED_LOWSPEED) {		if (available < HcLsThreshold) {			return 0;		}		time = 8 * 8 * buf->len + 1024;	} else {		if (buf->hc & SL11H_HCTLMASK_PREAMBLE) {			time = 8 * 8 * buf->len + 2048;		} else {			time = 8 * buf->len + 256;		}	}	time += 2 * 10 * buf->len;	return time < available;}static void kill_xfer_bufs(struct usb_hcd *hcd, struct hc_sl811_dev *dev, struct td *td){	int flip;	for (flip = 0; flip < FLIP_BUFFERS; flip++) {		if ((dev->buf_map & (1 << flip))) {			struct xfer_buf *buf = &dev->xfer_buf[flip];			if (buf->td == td) {				buf->td = NULL;			}			dev->buf_map &= ~(1 << flip);		}	}}static void sl811_dma_recv_data(struct hc_sl811_dev *dev, struct xfer_buf *buf, u8 offset){	struct usb_hcd *hcd = hc_sl811_dev_to_hcd(dev);	if (buf->count == 0) {		// nothing to to, if no transfer buffer given		return;	}	BUG_ON(buf->count > buf->buf_len || buf->count > (SL811_BUF_SIZE / FLIP_BUFFERS));	BUG_ON(buf->buf_addr == NULL);	__hc_sl811_read_regs(dev, offset, buf->buf_addr, buf->count);	dma_sync_single_for_cpu(hcd->self.controller, get_hw_fld(buf->td, CBP), buf->count,				DMA_TO_DEVICE);}static void sl811_dma_send_data(struct hc_sl811_dev *dev, struct xfer_buf *buf, u8 offset){	struct usb_hcd *hcd = hc_sl811_dev_to_hcd(dev);	if (buf->len == 0) {		// nothing to to, if no transfer buffer given		return;	}	BUG_ON(buf->len > buf->buf_len || buf->len > (SL811_BUF_SIZE / FLIP_BUFFERS));	dma_sync_single_for_cpu(hcd->self.controller, get_hw_fld(buf->td, CBP), buf->len,				DMA_FROM_DEVICE);	__hc_sl811_write_regs(dev, offset, buf->buf_addr, buf->len);}static int start_xfer(struct usb_hcd *hcd, struct hc_sl811_dev *dev, int flip){	struct xfer_buf *buf = &dev->xfer_buf[flip];	struct td *td = dev->current_td;	struct ohci_hcd *ohci = hcd_to_ohci(hcd);	int reg_offs = flip * 8;	int buf_offs = flip * (SL811_BUF_SIZE / FLIP_BUFFERS);	BUG_ON(flip >= FLIP_BUFFERS);	BUG_ON(dev->num_bufs >= FLIP_BUFFERS);	BUG_ON(dev->current_td == NULL);	BUG_ON((dev->buf_map & (1 << flip)));	BUG_ON(td->ed == NULL);	if (!check_bustime(ohci, buf, get_hwinfo(td->ed))) {		dev->current_td = NULL;		return 0;	}	BUG_ON(td == NULL);	BUG_ON(buf_offs + buf->len > SL811_BUF_SIZE);	BUG_ON(buf->len > (SL811_BUF_SIZE / FLIP_BUFFERS));	if (buf->hc & SL11H_HCTLMASK_WRITE && buf->len > 0) {		// transfer data from TD buffer to SL811 buffer memory		BUG_ON(buf->buf_addr == NULL);		ohci_vdbg(hcd_to_ohci(hcd), "%s: Transferring %d of %d byte from %08x to %02x\n",			  __FUNCTION__, buf->len, buf->buf_len, (u32)buf->buf_addr,			  SL11H_DATA_START + buf_offs);		sl811_dma_send_data(dev, buf, SL11H_DATA_START + buf_offs);	}	__hc_sl811_write_reg(dev, SL11H_BUFADDRREG + reg_offs, SL11H_DATA_START + buf_offs);	__hc_sl811_write_regs(dev, SL11H_BUFLNTHREG + reg_offs, &buf->send_data,			      sizeof(buf->send_data));	__hc_sl811_write_reg(dev, SL11H_HOSTCTLREG + reg_offs, buf->hc);	dev->buf_map |= (1 << flip);	dev->num_bufs++;	BUG_ON(dev->num_bufs > FLIP_BUFFERS);	dev->flip = (dev->flip << 1) | flip;	return 1;}static int process_td(struct ohci_hcd *ohci, struct td *td, int flip){	struct usb_hcd *hcd = ohci_to_hcd(ohci);	struct ohci_regs *regs = ohci->regs;	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	struct ed *ed = td->ed;	u8 pid;	struct xfer_buf *buf;	u32 ed_flags = get_hwinfo(ed);	u32 td_flags = get_hwinfo(td);	u8 hc_flags = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP;	u8 dev_addr;	u32 max_packet_size = ed_mps(ed_flags);	int len;	if (!(dev->dev_state & DEV_SOF)) {		ohci_info(ohci, "%s: Retiring TD because device disconnected\n", __FUNCTION__);		retire_td(hcd, td, ed, TD_DEVNOTRESP);	}	if (dev->num_bufs == FLIP_BUFFERS) {		return -EBUSY;	}	if (dev->current_td == NULL) {		dev->current_td = td;		if (td->hwCBP != 0) {			dev->buf_addr = (void*)dma_to_virt(hcd->self.controller,							   get_hw_fld(td, CBP));			dev->buf_len = TD_BUF_LEN(td);		} else {			dev->buf_addr = NULL;			dev->buf_len = 0;		}	}	pid = ed_td_to_pid[ed_pid(ed_flags)][td_pid(td_flags)];	len = max_packet_size;	if (pid != PID_IN) {		hc_flags |= SL11H_HCTLMASK_WRITE;	}	if (pid != PID_SETUP) {		if ((td_flags & TD_T) == TD_T_DATA1) {			hc_flags |= SL11H_HCTLMASK_SEQ;		} else if ((td_flags & TD_T) != TD_T_DATA0) {			hc_flags |= (ED_HEADP(ed) & ED_C) ? SL11H_HCTLMASK_SEQ : 0;		}	}	if (ed_flags & ED_ISO) {		hc_flags |= SL11H_HCTLMASK_ISOCH;	}	if (ed_flags & ED_LOWSPEED) {		if (!(HcRhPortStatus[0] & RH_PS_LSDA)) {			hc_flags |= SL11H_HCTLMASK_PREAMBLE;		}	}	pid = (pid << 4) | ed_en(ed_flags);	dev_addr = ed_fa(ed_flags);	BUG_ON(dev->buf_map & (1 << flip));	buf = &dev->xfer_buf[flip];	buf->td = td;	if (len > dev->buf_len) {		len = dev->buf_len;	}	buf->buf_addr = dev->buf_addr;	buf->buf_len = dev->buf_len;	buf->hc = hc_flags;	buf->pid_ep = pid;	buf->dev_addr = dev_addr;	buf->len = len;	BUG_ON(len > (SL811_BUF_SIZE / FLIP_BUFFERS));	if (start_xfer(hcd, dev, flip)) {		if (dev->buf_len > len) {			dev->buf_addr += len;			dev->buf_len -= len;		} else {			dev->buf_len = 0;			dev->buf_addr = NULL;		}	}	return 1;}static struct td *find_td(struct ohci_hcd *ohci, struct hc_sl811_dev *dev, struct ed *ed){	struct td *td;	td = dma_to_td(ohci, ED_HEADP(ed) & TD_MASK);	if ((td == NULL) || (ED_TAILP(ed) == td->td_dma)) {		return NULL;	}	return td;}/* * process_ed_list * * generic ED list processing routine * * Return codes:	1	TD found *			0	no TD needed processing *			<0	Error */static struct ed *next_ed(struct usb_hcd *hcd, u32 *head_reg){	u32 ed_head = le32_to_cpup(head_reg);	struct ed *ed;	if (ed_head == 0) {		return NULL;	}	ed = (struct ed*)dma_to_virt(hcd->self-controller, ed_head);	dma_sync_single_for_cpu(hcd->self.controller, ed_head, sizeof(struct ed), DMA_FROM_DEVICE);	*head_reg = ed->hwNextED;	return ed;}static int process_ed_list(struct ohci_hcd *ohci, u32 *head_reg, int flip){	int ret = 0;	int done = 0;	struct usb_hcd *hcd = ohci_to_hcd(ohci);	struct ohci_regs *regs = ohci->regs;	struct hc_sl811_dev *dev = ohci_to_sl811_dev(ohci);	struct ed *ed;	// walk ED list and find TD that needs processing	while (!done && ((ed = next_ed(hcd, head_reg)) != NULL)) {		struct td *td = NULL;		u32 ed_flags = get_hwinfo(ed);		u16 rem = update_fm_remaining(ohci);		if (!EP_SKIP(ed_flags) && !ED_HALTED(ed) &&		    !(EP_ISO(ed_flags) && !(HcControl & OHCI_CTRL_IE))) {			td = find_td(ohci, dev, ed);		}		if ((td && (ed_flags & ED_LOWSPEED) && (rem < HcLsThreshold)) ||		    ((td == dev->current_td) && (dev->buf_len == 0))) {			td = NULL;			ret = 1;		}		if (td != NULL) {			done = process_td(ohci, td, flip);			ret = done;		}	}	return ret;}/* * process_ctrl_bulk_lists * * try to find a TD that needs processing in the ctrl or bulk ED list * according to cbsr and cbc * * Return codes:	1	TD found *			0	no TD needed processing *			<0	Error * */static int process_ctrl_bulk_lists(struct ohci_hcd *ohci, int flip){	int ret = 0;	struct hc_sl811_dev *dev = ohci_to_sl811_dev(ohci);	//struct usb_hcd *hcd = ohci_to_hcd(ohci);	struct ohci_regs *regs = ohci->regs;	int cbsr = HcControl & OHCI_CTRL_CBSR;	// ctrl/bulk ratio	int bulk_ena = (HcControl & OHCI_CTRL_BLE) && (HcBulkCurrentED != 0);	// reset ctrl/bulk ratio counter, if bulk processing disabled or	// no bulk descriptors to process	if (!bulk_ena) {		dev->cbc = 0;	}	// process ctrl EDs until cbsr limit reached	if ((HcControl & OHCI_CTRL_CLE) && (dev->cbc <= cbsr)) {		if ((HcControlCurrentED == 0) && (HcCmdStatus & OHCI_CLF)) {			HcControlCurrentED = HcControlHeadED;			HcCmdStatus &= ~OHCI_CLF;		}		if (HcControlCurrentED != 0) {			ret = process_ed_list(ohci, &HcControlCurrentED, flip);			if (ret > 0) {				dev->cbc++;				HcCmdStatus |= OHCI_CLF;				return ret;			} else if (ret != 0) {				return ret;			}		}	}	if (HcControl & OHCI_CTRL_BLE) {		if ((HcBulkCurrentED == 0) && (HcCmdStatus & OHCI_BLF)) {			HcBulkCurrentED = HcBulkHeadED;			HcCmdStatus &= ~OHCI_BLF;		}		if (HcBulkCurrentED != 0) {			ret = process_ed_list(ohci, &HcBulkCurrentED, flip);			if (ret > 0) {				HcCmdStatus |= OHCI_BLF;				dev->cbc = 0;			} else if (ret != 0) {				return ret;			}		}	}	return ret;}/* * process_ed_lists * * try to find a TD that needs processing in the appropriate ED list * depending on frame number and HcFmRemaining... * * Return codes:	1	TD found and processed *			0	no TD needed processing *			<0	Error * */#define OHCI_PS ((1 << 14) - 1)static int process_ed_lists(struct ohci_hcd *ohci, int flip){	int ret = 0;	struct ohci_regs *regs = ohci->regs;	u16 periodicstart = HcPeriodicStart & OHCI_PS;	// limit for ctrl/bulk processing	u16 fmremaining = update_fm_remaining(ohci);	/*	 * process cbsr control desc.	 * process bulk desc.	 */	// do control/bulk processing until periodicstart reached	if (fmremaining > periodicstart) {		ret = process_ctrl_bulk_lists(ohci, flip);		if (ret != 0) {			return ret;		}		fmremaining = update_fm_remaining(ohci);	}	// do interrupt/iso processing	if ((HcControl & OHCI_CTRL_PLE) && (HcPeriodCurrentED != 0)) {		ret = process_ed_list(ohci, &HcPeriodCurrentED, flip);		if (ret != 0) {			return ret;		}		fmremaining = update_fm_remaining(ohci);	}	if (fmremaining > 0) {		// do control/bulk processing again until end of frame		ret = process_ctrl_bulk_lists(ohci, flip);		if (ret != 0) {			return ret;		}	}	return ret;}static u32 retire_td(struct usb_hcd *hcd, struct td *td, struct ed *ed, u32 cc){	u32 ohci_int_status = 0;	struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd);	struct ohci_hcd *ohci = hcd_to_ohci(hcd);	struct ohci_regs *regs = ohci->regs;	u32 ed_flags = get_hwinfo(ed);	u32 td_flags = get_hwinfo(td);	u32 td_dma = td->td_dma;	u32 int_delay = (cc == TD_CC_NOERROR) ? (td_flags & TD_DI) >> 21 : 0;	u32 ed_halt = 0;	BUG_ON(TD_CC_GET(td_flags) != TD_NOTACCESSED);	if (dev->current_td == td) {		dev->current_td = NULL;	}	ohci_dbg(ohci, "%s: Retiring TD %08x of ED %08x\n", __FUNCTION__, (u32)td, (u32)ed);	if (dev->num_bufs > 1) {		WARN_ON(1);		kill_xfer_bufs(hcd, dev, td);	}	TD_CC_SET_HW(td, cc);	if (!(ed_flags & ED_ISO)) {		ed_halt = (cc != TD_CC_NOERROR) ? cpu_to_le32(ED_H) : 0;		if (ed_halt) {			ohci_dbg(ohci, "%s: ED %08x (TD %08x) Halted due to error: %d\n",				 __FUNCTION__, (u32)ed, (u32)td, cc);		}		ed_halt |= ((td_flags & TD_T) == TD_T_DATA1) ? cpu_to_le32(ED_C) : 0;	}	ed->hwHeadP = td->hwNextTD | ed_halt;	td->hwNextTD = HcDoneHead;	HcDoneHead = td_dma;	if ((int_delay != 7) && (int_delay < dev->intrdelay)) {		dev->intrdelay = int_delay;	}	return ohci_int_status;}static void update_data_toggle(struct usb_hcd *hcd, struct td *td){	u32 td_flags = get_hwinfo(td);	u32 ed_carry = ED_HEADP(td->ed) & ED_C;	if ((td_flags & TD_T) == TD_T_TOGGLE) {		td_flags |= (ed_carry ? TD_T_DATA0 : TD_T_DATA1);

⌨️ 快捷键说明

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