diff options
| author | Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> | 2009-04-03 12:46:58 +0200 | 
|---|---|---|
| committer | Remy Bohmer <linux@bohmer.net> | 2009-04-06 20:40:46 +0200 | 
| commit | 2731b9a86685190d26b1883f27afda5ac8e1a313 (patch) | |
| tree | 83511c3f504e62eb3ac129f3332d2bcaf25d6e32 /drivers/usb/musb/musb_hcd.c | |
| parent | 712ac6a1a6909a58d6549fb220cc921a7e9f9979 (diff) | |
| download | olio-uboot-2014.01-2731b9a86685190d26b1883f27afda5ac8e1a313.tar.xz olio-uboot-2014.01-2731b9a86685190d26b1883f27afda5ac8e1a313.zip | |
drivers/usb: regorganisation
move to linux usb driver organisation
as following
drivers/usb/gadget
drivers/usb/host
drivers/usb/musb
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: Remy Bohmer <linux@bohmer.net>
Diffstat (limited to 'drivers/usb/musb/musb_hcd.c')
| -rw-r--r-- | drivers/usb/musb/musb_hcd.c | 792 | 
1 files changed, 792 insertions, 0 deletions
| diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c new file mode 100644 index 000000000..352a0d4eb --- /dev/null +++ b/drivers/usb/musb/musb_hcd.c @@ -0,0 +1,792 @@ +/* + * Mentor USB OTG Core host controller driver. + * + * Copyright (c) 2008 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + */ + +#include <common.h> +#include "musb_hcd.h" + +/* MSC control transfers */ +#define USB_MSC_BBB_RESET 	0xFF +#define USB_MSC_BBB_GET_MAX_LUN	0xFE + +/* Endpoint configuration information */ +static struct musb_epinfo epinfo[3] = { +	{MUSB_BULK_EP, 1, 512}, /* EP1 - Bluk Out - 512 Bytes */ +	{MUSB_BULK_EP, 0, 512}, /* EP1 - Bluk In  - 512 Bytes */ +	{MUSB_INTR_EP, 0, 64}   /* EP2 - Interrupt IN - 64 Bytes */ +}; + +/* + * This function writes the data toggle value. + */ +static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out) +{ +	u16 toggle = usb_gettoggle(dev, ep, dir_out); +	u16 csr; + +	if (dir_out) { +		if (!toggle) +			writew(MUSB_TXCSR_CLRDATATOG, &musbr->txcsr); +		else { +			csr = readw(&musbr->txcsr); +			csr |= MUSB_TXCSR_H_WR_DATATOGGLE; +			writew(csr, &musbr->txcsr); +			csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT); +			writew(csr, &musbr->txcsr); +		} +	} else { +		if (!toggle) +			writew(MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr); +		else { +			csr = readw(&musbr->rxcsr); +			csr |= MUSB_RXCSR_H_WR_DATATOGGLE; +			writew(csr, &musbr->rxcsr); +			csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE); +			writew(csr, &musbr->rxcsr); +		} +	} +} + +/* + * This function checks if RxStall has occured on the endpoint. If a RxStall + * has occured, the RxStall is cleared and 1 is returned. If RxStall has + * not occured, 0 is returned. + */ +static u8 check_stall(u8 ep, u8 dir_out) +{ +	u16 csr; + +	/* For endpoint 0 */ +	if (!ep) { +		csr = readw(&musbr->txcsr); +		if (csr & MUSB_CSR0_H_RXSTALL) { +			csr &= ~MUSB_CSR0_H_RXSTALL; +			writew(csr, &musbr->txcsr); +			return 1; +		} +	} else { /* For non-ep0 */ +		if (dir_out) { /* is it tx ep */ +			csr = readw(&musbr->txcsr); +			if (csr & MUSB_TXCSR_H_RXSTALL) { +				csr &= ~MUSB_TXCSR_H_RXSTALL; +				writew(csr, &musbr->txcsr); +				return 1; +			} +		} else { /* is it rx ep */ +			csr = readw(&musbr->rxcsr); +			if (csr & MUSB_RXCSR_H_RXSTALL) { +				csr &= ~MUSB_RXCSR_H_RXSTALL; +				writew(csr, &musbr->rxcsr); +				return 1; +			} +		} +	} +	return 0; +} + +/* + * waits until ep0 is ready. Returns 0 if ep is ready, -1 for timeout + * error and -2 for stall. + */ +static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) +{ +	u16 csr; +	int result = 1; + +	while (result > 0) { +		csr = readw(&musbr->txcsr); +		if (csr & MUSB_CSR0_H_ERROR) { +			csr &= ~MUSB_CSR0_H_ERROR; +			writew(csr, &musbr->txcsr); +			dev->status = USB_ST_CRC_ERR; +			result = -1; +			break; +		} + +		switch (bit_mask) { +		case MUSB_CSR0_TXPKTRDY: +			if (!(csr & MUSB_CSR0_TXPKTRDY)) { +				if (check_stall(MUSB_CONTROL_EP, 0)) { +					dev->status = USB_ST_STALLED; +					result = -2; +				} else +					result = 0; +			} +			break; + +		case MUSB_CSR0_RXPKTRDY: +			if (check_stall(MUSB_CONTROL_EP, 0)) { +				dev->status = USB_ST_STALLED; +				result = -2; +			} else +				if (csr & MUSB_CSR0_RXPKTRDY) +					result = 0; +			break; + +		case MUSB_CSR0_H_REQPKT: +			if (!(csr & MUSB_CSR0_H_REQPKT)) { +				if (check_stall(MUSB_CONTROL_EP, 0)) { +					dev->status = USB_ST_STALLED; +					result = -2; +				} else +					result = 0; +			} +			break; +		} +	} +	return result; +} + +/* + * waits until tx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_txep_ready(struct usb_device *dev, u8 ep) +{ +	u16 csr; + +	do { +		if (check_stall(ep, 1)) { +			dev->status = USB_ST_STALLED; +			return 0; +		} + +		csr = readw(&musbr->txcsr); +		if (csr & MUSB_TXCSR_H_ERROR) { +			dev->status = USB_ST_CRC_ERR; +			return 0; +		} +	} while (csr & MUSB_TXCSR_TXPKTRDY); +	return 1; +} + +/* + * waits until rx ep is ready. Returns 1 when ep is ready and 0 on error. + */ +static u8 wait_until_rxep_ready(struct usb_device *dev, u8 ep) +{ +	u16 csr; + +	do { +		if (check_stall(ep, 0)) { +			dev->status = USB_ST_STALLED; +			return 0; +		} + +		csr = readw(&musbr->rxcsr); +		if (csr & MUSB_RXCSR_H_ERROR) { +			dev->status = USB_ST_CRC_ERR; +			return 0; +		} +	} while (!(csr & MUSB_RXCSR_RXPKTRDY)); +	return 1; +} + +/* + * This function performs the setup phase of the control transfer + */ +static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup) +{ +	int result; +	u16 csr; + +	/* write the control request to ep0 fifo */ +	write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void *)setup); + +	/* enable transfer of setup packet */ +	csr = readw(&musbr->txcsr); +	csr |= (MUSB_CSR0_TXPKTRDY|MUSB_CSR0_H_SETUPPKT); +	writew(csr, &musbr->txcsr); + +	/* wait until the setup packet is transmitted */ +	result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); +	dev->act_len = 0; +	return result; +} + +/* + * This function handles the control transfer in data phase + */ +static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ +	u16 csr; +	u32 rxlen = 0; +	u32 nextlen = 0; +	u8  maxpktsize = (1 << dev->maxpacketsize) * 8; +	u8  *rxbuff = (u8 *)buffer; +	u8  rxedlength; +	int result; + +	while (rxlen < len) { +		/* Determine the next read length */ +		nextlen = ((len-rxlen) > maxpktsize) ? maxpktsize : (len-rxlen); + +		/* Set the ReqPkt bit */ +		csr = readw(&musbr->txcsr); +		writew(csr | MUSB_CSR0_H_REQPKT, &musbr->txcsr); +		result = wait_until_ep0_ready(dev, MUSB_CSR0_RXPKTRDY); +		if (result < 0) +			return result; + +		/* Actual number of bytes received by usb */ +		rxedlength = readb(&musbr->rxcount); + +		/* Read the data from the RxFIFO */ +		read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); + +		/* Clear the RxPktRdy Bit */ +		csr = readw(&musbr->txcsr); +		csr &= ~MUSB_CSR0_RXPKTRDY; +		writew(csr, &musbr->txcsr); + +		/* short packet? */ +		if (rxedlength != nextlen) { +			dev->act_len += rxedlength; +			break; +		} +		rxlen += nextlen; +		dev->act_len = rxlen; +	} +	return 0; +} + +/* + * This function handles the control transfer out data phase + */ +static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ +	u16 csr; +	u32 txlen = 0; +	u32 nextlen = 0; +	u8  maxpktsize = (1 << dev->maxpacketsize) * 8; +	u8  *txbuff = (u8 *)buffer; +	int result = 0; + +	while (txlen < len) { +		/* Determine the next write length */ +		nextlen = ((len-txlen) > maxpktsize) ? maxpktsize : (len-txlen); + +		/* Load the data to send in FIFO */ +		write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]); + +		/* Set TXPKTRDY bit */ +		csr = readw(&musbr->txcsr); +		writew(csr | MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY, +					&musbr->txcsr); +		result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); +		if (result < 0) +			break; + +		txlen += nextlen; +		dev->act_len = txlen; +	} +	return result; +} + +/* + * This function handles the control transfer out status phase + */ +static int ctrlreq_out_status_phase(struct usb_device *dev) +{ +	u16 csr; +	int result; + +	/* Set the StatusPkt bit */ +	csr = readw(&musbr->txcsr); +	csr |= (MUSB_CSR0_H_DIS_PING | MUSB_CSR0_TXPKTRDY | +			MUSB_CSR0_H_STATUSPKT); +	writew(csr, &musbr->txcsr); + +	/* Wait until TXPKTRDY bit is cleared */ +	result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY); +	return result; +} + +/* + * This function handles the control transfer in status phase + */ +static int ctrlreq_in_status_phase(struct usb_device *dev) +{ +	u16 csr; +	int result; + +	/* Set the StatusPkt bit and ReqPkt bit */ +	csr = MUSB_CSR0_H_DIS_PING | MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; +	writew(csr, &musbr->txcsr); +	result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT); + +	/* clear StatusPkt bit and RxPktRdy bit */ +	csr = readw(&musbr->txcsr); +	csr &= ~(MUSB_CSR0_RXPKTRDY | MUSB_CSR0_H_STATUSPKT); +	writew(csr, &musbr->txcsr); +	return result; +} + +/* + * determines the speed of the device (High/Full/Slow) + */ +static u8 get_dev_speed(struct usb_device *dev) +{ +	return (dev->speed & USB_SPEED_HIGH) ? MUSB_TYPE_SPEED_HIGH : +		((dev->speed & USB_SPEED_LOW) ? MUSB_TYPE_SPEED_LOW : +						MUSB_TYPE_SPEED_FULL); +} + +/* + * configure the hub address and the port address. + */ +static void config_hub_port(struct usb_device *dev, u8 ep) +{ +	u8 chid; +	u8 hub; + +	/* Find out the nearest parent which is high speed */ +	while (dev->parent->parent != NULL) +		if (get_dev_speed(dev->parent) !=  MUSB_TYPE_SPEED_HIGH) +			dev = dev->parent; +		else +			break; + +	/* determine the port address at that hub */ +	hub = dev->parent->devnum; +	for (chid = 0; chid < USB_MAXCHILDREN; chid++) +		if (dev->parent->children[chid] == dev) +			break; + +	/* configure the hub address and the port address */ +	writeb(hub, &musbr->tar[ep].txhubaddr); +	writeb((chid + 1), &musbr->tar[ep].txhubport); +	writeb(hub, &musbr->tar[ep].rxhubaddr); +	writeb((chid + 1), &musbr->tar[ep].rxhubport); +} + +/* + * do a control transfer + */ +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +			int len, struct devrequest *setup) +{ +	int devnum = usb_pipedevice(pipe); +	u16 csr; +	u8  devspeed; + +	/* select control endpoint */ +	writeb(MUSB_CONTROL_EP, &musbr->index); +	csr = readw(&musbr->txcsr); + +	/* target addr and (for multipoint) hub addr/port */ +	writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr); +	writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr); + +	/* configure the hub address and the port number as required */ +	devspeed = get_dev_speed(dev); +	if ((musb_ishighspeed()) && (dev->parent != NULL) && +		(devspeed != MUSB_TYPE_SPEED_HIGH)) { +		config_hub_port(dev, MUSB_CONTROL_EP); +		writeb(devspeed << 6, &musbr->txtype); +	} else { +		writeb(musb_cfg.musb_speed << 6, &musbr->txtype); +		writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr); +		writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport); +		writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr); +		writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport); +	} + +	/* Control transfer setup phase */ +	if (ctrlreq_setup_phase(dev, setup) < 0) +		return 0; + +	switch (setup->request) { +	case USB_REQ_GET_DESCRIPTOR: +	case USB_REQ_GET_CONFIGURATION: +	case USB_REQ_GET_INTERFACE: +	case USB_REQ_GET_STATUS: +	case USB_MSC_BBB_GET_MAX_LUN: +		/* control transfer in-data-phase */ +		if (ctrlreq_in_data_phase(dev, len, buffer) < 0) +			return 0; +		/* control transfer out-status-phase */ +		if (ctrlreq_out_status_phase(dev) < 0) +			return 0; +		break; + +	case USB_REQ_SET_ADDRESS: +	case USB_REQ_SET_CONFIGURATION: +	case USB_REQ_SET_FEATURE: +	case USB_REQ_SET_INTERFACE: +	case USB_REQ_CLEAR_FEATURE: +	case USB_MSC_BBB_RESET: +		/* control transfer in status phase */ +		if (ctrlreq_in_status_phase(dev) < 0) +			return 0; +		break; + +	case USB_REQ_SET_DESCRIPTOR: +		/* control transfer out data phase */ +		if (ctrlreq_out_data_phase(dev, len, buffer) < 0) +			return 0; +		/* control transfer in status phase */ +		if (ctrlreq_in_status_phase(dev) < 0) +			return 0; +		break; + +	default: +		/* unhandled control transfer */ +		return -1; +	} + +	dev->status = 0; +	dev->act_len = len; +	return len; +} + +/* + * do a bulk transfer + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, +					void *buffer, int len) +{ +	int dir_out = usb_pipeout(pipe); +	int ep = usb_pipeendpoint(pipe); +	int devnum = usb_pipedevice(pipe); +	u8  type; +	u16 csr; +	u32 txlen = 0; +	u32 nextlen = 0; +	u8  devspeed; + +	/* select bulk endpoint */ +	writeb(MUSB_BULK_EP, &musbr->index); + +	/* write the address of the device */ +	if (dir_out) +		writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr); +	else +		writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr); + +	/* configure the hub address and the port number as required */ +	devspeed = get_dev_speed(dev); +	if ((musb_ishighspeed()) && (dev->parent != NULL) && +		(devspeed != MUSB_TYPE_SPEED_HIGH)) { +		/* +		 * MUSB is in high speed and the destination device is full +		 * speed device. So configure the hub address and port +		 * address registers. +		 */ +		config_hub_port(dev, MUSB_BULK_EP); +	} else { +		if (dir_out) { +			writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr); +			writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport); +		} else { +			writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr); +			writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport); +		} +		devspeed = musb_cfg.musb_speed; +	} + +	/* Write the saved toggle bit value */ +	write_toggle(dev, ep, dir_out); + +	if (dir_out) { /* bulk-out transfer */ +		/* Program the TxType register */ +		type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | +			   (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | +			   (ep & MUSB_TYPE_REMOTE_END); +		writeb(type, &musbr->txtype); + +		/* Write maximum packet size to the TxMaxp register */ +		writew(dev->epmaxpacketout[ep], &musbr->txmaxp); +		while (txlen < len) { +			nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ? +					(len-txlen) : dev->epmaxpacketout[ep]; + +			/* Write the data to the FIFO */ +			write_fifo(MUSB_BULK_EP, nextlen, +					(void *)(((u8 *)buffer) + txlen)); + +			/* Set the TxPktRdy bit */ +			csr = readw(&musbr->txcsr); +			writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr); + +			/* Wait until the TxPktRdy bit is cleared */ +			if (!wait_until_txep_ready(dev, MUSB_BULK_EP)) { +				readw(&musbr->txcsr); +				usb_settoggle(dev, ep, dir_out, +				(csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); +				dev->act_len = txlen; +				return 0; +			} +			txlen += nextlen; +		} + +		/* Keep a copy of the data toggle bit */ +		csr = readw(&musbr->txcsr); +		usb_settoggle(dev, ep, dir_out, +				(csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1); +	} else { /* bulk-in transfer */ +		/* Write the saved toggle bit value */ +		write_toggle(dev, ep, dir_out); + +		/* Program the RxType register */ +		type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | +			   (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) | +			   (ep & MUSB_TYPE_REMOTE_END); +		writeb(type, &musbr->rxtype); + +		/* Write the maximum packet size to the RxMaxp register */ +		writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); +		while (txlen < len) { +			nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? +					(len-txlen) : dev->epmaxpacketin[ep]; + +			/* Set the ReqPkt bit */ +			writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + +			/* Wait until the RxPktRdy bit is set */ +			if (!wait_until_rxep_ready(dev, MUSB_BULK_EP)) { +				csr = readw(&musbr->rxcsr); +				usb_settoggle(dev, ep, dir_out, +				(csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); +				csr &= ~MUSB_RXCSR_RXPKTRDY; +				writew(csr, &musbr->rxcsr); +				dev->act_len = txlen; +				return 0; +			} + +			/* Read the data from the FIFO */ +			read_fifo(MUSB_BULK_EP, nextlen, +					(void *)(((u8 *)buffer) + txlen)); + +			/* Clear the RxPktRdy bit */ +			csr =  readw(&musbr->rxcsr); +			csr &= ~MUSB_RXCSR_RXPKTRDY; +			writew(csr, &musbr->rxcsr); +			txlen += nextlen; +		} + +		/* Keep a copy of the data toggle bit */ +		csr = readw(&musbr->rxcsr); +		usb_settoggle(dev, ep, dir_out, +				(csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); +	} + +	/* bulk transfer is complete */ +	dev->status = 0; +	dev->act_len = len; +	return 0; +} + +/* + * This function initializes the usb controller module. + */ +int usb_lowlevel_init(void) +{ +	u8  power; +	u32 timeout; + +	if (musb_platform_init() == -1) +		return -1; + +	/* Configure all the endpoint FIFO's and start usb controller */ +	musbr = musb_cfg.regs; +	musb_configure_ep(&epinfo[0], +			sizeof(epinfo) / sizeof(struct musb_epinfo)); +	musb_start(); + +	/* +	 * Wait until musb is enabled in host mode with a timeout. There +	 * should be a usb device connected. +	 */ +	timeout = musb_cfg.timeout; +	while (timeout--) +		if (readb(&musbr->devctl) & MUSB_DEVCTL_HM) +			break; + +	/* if musb core is not in host mode, then return */ +	if (!timeout) +		return -1; + +	/* start usb bus reset */ +	power = readb(&musbr->power); +	writeb(power | MUSB_POWER_RESET, &musbr->power); + +	/* After initiating a usb reset, wait for about 20ms to 30ms */ +	udelay(30000); + +	/* stop usb bus reset */ +	power = readb(&musbr->power); +	power &= ~MUSB_POWER_RESET; +	writeb(power, &musbr->power); + +	/* Determine if the connected device is a high/full/low speed device */ +	musb_cfg.musb_speed = (readb(&musbr->power) & MUSB_POWER_HSMODE) ? +			MUSB_TYPE_SPEED_HIGH : +			((readb(&musbr->devctl) & MUSB_DEVCTL_FSDEV) ? +			MUSB_TYPE_SPEED_FULL : MUSB_TYPE_SPEED_LOW); +	return 0; +} + +/* + * This function stops the operation of the davinci usb module. + */ +int usb_lowlevel_stop(void) +{ +	/* Reset the USB module */ +	musb_platform_deinit(); +	writeb(0, &musbr->devctl); +	return 0; +} + +/* + * This function supports usb interrupt transfers. Currently, usb interrupt + * transfers are not supported. + */ +int submit_int_msg(struct usb_device *dev, unsigned long pipe, +				void *buffer, int len, int interval) +{ +	int dir_out = usb_pipeout(pipe); +	int ep = usb_pipeendpoint(pipe); +	int devnum = usb_pipedevice(pipe); +	u8  type; +	u16 csr; +	u32 txlen = 0; +	u32 nextlen = 0; +	u8  devspeed; + +	/* select interrupt endpoint */ +	writeb(MUSB_INTR_EP, &musbr->index); + +	/* write the address of the device */ +	if (dir_out) +		writeb(devnum, &musbr->tar[MUSB_INTR_EP].txfuncaddr); +	else +		writeb(devnum, &musbr->tar[MUSB_INTR_EP].rxfuncaddr); + +	/* configure the hub address and the port number as required */ +	devspeed = get_dev_speed(dev); +	if ((musb_ishighspeed()) && (dev->parent != NULL) && +		(devspeed != MUSB_TYPE_SPEED_HIGH)) { +		/* +		 * MUSB is in high speed and the destination device is full +		 * speed device. So configure the hub address and port +		 * address registers. +		 */ +		config_hub_port(dev, MUSB_INTR_EP); +	} else { +		if (dir_out) { +			writeb(0, &musbr->tar[MUSB_INTR_EP].txhubaddr); +			writeb(0, &musbr->tar[MUSB_INTR_EP].txhubport); +		} else { +			writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubaddr); +			writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubport); +		} +		devspeed = musb_cfg.musb_speed; +	} + +	/* Write the saved toggle bit value */ +	write_toggle(dev, ep, dir_out); + +	if (!dir_out) { /* intrrupt-in transfer */ +		/* Write the saved toggle bit value */ +		write_toggle(dev, ep, dir_out); +		writeb(interval, &musbr->rxinterval); + +		/* Program the RxType register */ +		type = (devspeed << MUSB_TYPE_SPEED_SHIFT) | +			   (MUSB_TYPE_PROTO_INTR << MUSB_TYPE_PROTO_SHIFT) | +			   (ep & MUSB_TYPE_REMOTE_END); +		writeb(type, &musbr->rxtype); + +		/* Write the maximum packet size to the RxMaxp register */ +		writew(dev->epmaxpacketin[ep], &musbr->rxmaxp); + +		while (txlen < len) { +			nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ? +					(len-txlen) : dev->epmaxpacketin[ep]; + +			/* Set the ReqPkt bit */ +			writew(MUSB_RXCSR_H_REQPKT, &musbr->rxcsr); + +			/* Wait until the RxPktRdy bit is set */ +			if (!wait_until_rxep_ready(dev, MUSB_INTR_EP)) { +				csr = readw(&musbr->rxcsr); +				usb_settoggle(dev, ep, dir_out, +				(csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); +				csr &= ~MUSB_RXCSR_RXPKTRDY; +				writew(csr, &musbr->rxcsr); +				dev->act_len = txlen; +				return 0; +			} + +			/* Read the data from the FIFO */ +			read_fifo(MUSB_INTR_EP, nextlen, +					(void *)(((u8 *)buffer) + txlen)); + +			/* Clear the RxPktRdy bit */ +			csr =  readw(&musbr->rxcsr); +			csr &= ~MUSB_RXCSR_RXPKTRDY; +			writew(csr, &musbr->rxcsr); +			txlen += nextlen; +		} + +		/* Keep a copy of the data toggle bit */ +		csr = readw(&musbr->rxcsr); +		usb_settoggle(dev, ep, dir_out, +				(csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1); +	} + +	/* interrupt transfer is complete */ +	dev->irq_status = 0; +	dev->irq_act_len = len; +	dev->irq_handle(dev); +	dev->status = 0; +	dev->act_len = len; +	return 0; +} + + +#ifdef CONFIG_SYS_USB_EVENT_POLL +/* + * This function polls for USB keyboard data. + */ +void usb_event_poll() +{ +	device_t *dev; +	struct usb_device *usb_kbd_dev; +	struct usb_interface_descriptor *iface; +	struct usb_endpoint_descriptor *ep; +	int pipe; +	int maxp; + +	/* Get the pointer to USB Keyboard device pointer */ +	dev = device_get_by_name("usbkbd"); +	usb_kbd_dev = (struct usb_device *)dev->priv; +	iface = &usb_kbd_dev->config.if_desc[0]; +	ep = &iface->ep_desc[0]; +	pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + +	/* Submit a interrupt transfer request */ +	maxp = usb_maxpacket(usb_kbd_dev, pipe); +	usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], +			maxp > 8 ? 8 : maxp, ep->bInterval); +} +#endif /* CONFIG_SYS_USB_EVENT_POLL */ |