mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-21 01:18:52 +09:00
![Andrew Murray](/assets/img/avatar_default.png)
The musb driver defines and uses MUSB_CSR0_H_DIS_PING, however this bit is reserved on the DM36x. Thus this patch ensures that the reserved bit is not accesssed. It has been observed that some USB devices will fail to enumerate with errors such as 'error in inquiry' without this patch. See http://www.ti.com/litv/pdf/sprufh9a for details. Cc: Marek Vasut <marex@denx.de> Cc: Tom Rini <trini@ti.com> Signed-off-by: Andrew Murray <amurray@embedded-bits.co.uk> Acked-by: Marek Vasut <marex@denx.de>
1264 lines
30 KiB
C
1264 lines
30 KiB
C
/*
|
|
* Mentor USB OTG Core host controller driver.
|
|
*
|
|
* Copyright (c) 2008 Texas Instruments
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*
|
|
* Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <usb.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 const 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 */
|
|
};
|
|
|
|
/* --- Virtual Root Hub ---------------------------------------------------- */
|
|
#ifdef MUSB_NO_MULTIPOINT
|
|
static int rh_devnum;
|
|
static u32 port_status;
|
|
|
|
/* Device descriptor */
|
|
static const u8 root_hub_dev_des[] = {
|
|
0x12, /* __u8 bLength; */
|
|
0x01, /* __u8 bDescriptorType; Device */
|
|
0x00, /* __u16 bcdUSB; v1.1 */
|
|
0x02,
|
|
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
|
0x00, /* __u8 bDeviceSubClass; */
|
|
0x00, /* __u8 bDeviceProtocol; */
|
|
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
|
|
0x00, /* __u16 idVendor; */
|
|
0x00,
|
|
0x00, /* __u16 idProduct; */
|
|
0x00,
|
|
0x00, /* __u16 bcdDevice; */
|
|
0x00,
|
|
0x00, /* __u8 iManufacturer; */
|
|
0x01, /* __u8 iProduct; */
|
|
0x00, /* __u8 iSerialNumber; */
|
|
0x01 /* __u8 bNumConfigurations; */
|
|
};
|
|
|
|
/* Configuration descriptor */
|
|
static const u8 root_hub_config_des[] = {
|
|
0x09, /* __u8 bLength; */
|
|
0x02, /* __u8 bDescriptorType; Configuration */
|
|
0x19, /* __u16 wTotalLength; */
|
|
0x00,
|
|
0x01, /* __u8 bNumInterfaces; */
|
|
0x01, /* __u8 bConfigurationValue; */
|
|
0x00, /* __u8 iConfiguration; */
|
|
0x40, /* __u8 bmAttributes;
|
|
Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
|
|
0x00, /* __u8 MaxPower; */
|
|
|
|
/* interface */
|
|
0x09, /* __u8 if_bLength; */
|
|
0x04, /* __u8 if_bDescriptorType; Interface */
|
|
0x00, /* __u8 if_bInterfaceNumber; */
|
|
0x00, /* __u8 if_bAlternateSetting; */
|
|
0x01, /* __u8 if_bNumEndpoints; */
|
|
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
|
|
0x00, /* __u8 if_bInterfaceSubClass; */
|
|
0x00, /* __u8 if_bInterfaceProtocol; */
|
|
0x00, /* __u8 if_iInterface; */
|
|
|
|
/* endpoint */
|
|
0x07, /* __u8 ep_bLength; */
|
|
0x05, /* __u8 ep_bDescriptorType; Endpoint */
|
|
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
|
|
0x03, /* __u8 ep_bmAttributes; Interrupt */
|
|
0x00, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
|
|
0x02,
|
|
0xff /* __u8 ep_bInterval; 255 ms */
|
|
};
|
|
|
|
static const unsigned char root_hub_str_index0[] = {
|
|
0x04, /* __u8 bLength; */
|
|
0x03, /* __u8 bDescriptorType; String-descriptor */
|
|
0x09, /* __u8 lang ID */
|
|
0x04, /* __u8 lang ID */
|
|
};
|
|
|
|
static const unsigned char root_hub_str_index1[] = {
|
|
0x1c, /* __u8 bLength; */
|
|
0x03, /* __u8 bDescriptorType; String-descriptor */
|
|
'M', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'U', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'S', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'B', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
' ', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'R', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'o', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'o', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
't', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
' ', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'H', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'u', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
'b', /* __u8 Unicode */
|
|
0, /* __u8 Unicode */
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
* 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) {
|
|
csr = readw(&musbr->txcsr);
|
|
if (!toggle) {
|
|
if (csr & MUSB_TXCSR_MODE)
|
|
csr = MUSB_TXCSR_CLRDATATOG;
|
|
else
|
|
csr = 0;
|
|
writew(csr, &musbr->txcsr);
|
|
} else {
|
|
csr |= MUSB_TXCSR_H_WR_DATATOGGLE;
|
|
writew(csr, &musbr->txcsr);
|
|
csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT);
|
|
writew(csr, &musbr->txcsr);
|
|
}
|
|
} else {
|
|
if (!toggle) {
|
|
csr = readw(&musbr->txcsr);
|
|
if (csr & MUSB_TXCSR_MODE)
|
|
csr = MUSB_RXCSR_CLRDATATOG;
|
|
else
|
|
csr = 0;
|
|
writew(csr, &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;
|
|
int timeout = CONFIG_MUSB_TIMEOUT;
|
|
|
|
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;
|
|
}
|
|
|
|
/* Check the timeout */
|
|
if (--timeout)
|
|
udelay(1);
|
|
else {
|
|
dev->status = USB_ST_CRC_ERR;
|
|
result = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* waits until tx ep is ready. Returns 1 when ep is ready and 0 on error.
|
|
*/
|
|
static int wait_until_txep_ready(struct usb_device *dev, u8 ep)
|
|
{
|
|
u16 csr;
|
|
int timeout = CONFIG_MUSB_TIMEOUT;
|
|
|
|
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;
|
|
}
|
|
|
|
/* Check the timeout */
|
|
if (--timeout)
|
|
udelay(1);
|
|
else {
|
|
dev->status = USB_ST_CRC_ERR;
|
|
return -1;
|
|
}
|
|
|
|
} while (csr & MUSB_TXCSR_TXPKTRDY);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* waits until rx ep is ready. Returns 1 when ep is ready and 0 on error.
|
|
*/
|
|
static int wait_until_rxep_ready(struct usb_device *dev, u8 ep)
|
|
{
|
|
u16 csr;
|
|
int timeout = CONFIG_MUSB_TIMEOUT;
|
|
|
|
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;
|
|
}
|
|
|
|
/* Check the timeout */
|
|
if (--timeout)
|
|
udelay(1);
|
|
else {
|
|
dev->status = USB_ST_CRC_ERR;
|
|
return -1;
|
|
}
|
|
|
|
} 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);
|
|
|
|
csr |= MUSB_CSR0_TXPKTRDY;
|
|
#if !defined(CONFIG_SOC_DM365)
|
|
csr |= MUSB_CSR0_H_DIS_PING;
|
|
#endif
|
|
writew(csr, &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_TXPKTRDY | MUSB_CSR0_H_STATUSPKT);
|
|
#if !defined(CONFIG_SOC_DM365)
|
|
csr |= MUSB_CSR0_H_DIS_PING;
|
|
#endif
|
|
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_REQPKT | MUSB_CSR0_H_STATUSPKT;
|
|
#if !defined(CONFIG_SOC_DM365)
|
|
csr |= MUSB_CSR0_H_DIS_PING;
|
|
#endif
|
|
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;
|
|
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
/* 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);
|
|
#endif
|
|
}
|
|
|
|
#ifdef MUSB_NO_MULTIPOINT
|
|
|
|
static void musb_port_reset(int do_reset)
|
|
{
|
|
u8 power = readb(&musbr->power);
|
|
|
|
if (do_reset) {
|
|
power &= 0xf0;
|
|
writeb(power | MUSB_POWER_RESET, &musbr->power);
|
|
port_status |= USB_PORT_STAT_RESET;
|
|
port_status &= ~USB_PORT_STAT_ENABLE;
|
|
udelay(30000);
|
|
} else {
|
|
writeb(power & ~MUSB_POWER_RESET, &musbr->power);
|
|
|
|
power = readb(&musbr->power);
|
|
if (power & MUSB_POWER_HSMODE)
|
|
port_status |= USB_PORT_STAT_HIGH_SPEED;
|
|
|
|
port_status &= ~(USB_PORT_STAT_RESET | (USB_PORT_STAT_C_CONNECTION << 16));
|
|
port_status |= USB_PORT_STAT_ENABLE
|
|
| (USB_PORT_STAT_C_RESET << 16)
|
|
| (USB_PORT_STAT_C_ENABLE << 16);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* root hub control
|
|
*/
|
|
static int musb_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
|
|
void *buffer, int transfer_len,
|
|
struct devrequest *cmd)
|
|
{
|
|
int leni = transfer_len;
|
|
int len = 0;
|
|
int stat = 0;
|
|
u32 datab[4];
|
|
const u8 *data_buf = (u8 *) datab;
|
|
u16 bmRType_bReq;
|
|
u16 wValue;
|
|
u16 wIndex;
|
|
u16 wLength;
|
|
u16 int_usb;
|
|
|
|
if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
|
|
debug("Root-Hub submit IRQ: NOT implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
bmRType_bReq = cmd->requesttype | (cmd->request << 8);
|
|
wValue = swap_16(cmd->value);
|
|
wIndex = swap_16(cmd->index);
|
|
wLength = swap_16(cmd->length);
|
|
|
|
debug("--- HUB ----------------------------------------\n");
|
|
debug("submit rh urb, req=%x val=%#x index=%#x len=%d\n",
|
|
bmRType_bReq, wValue, wIndex, wLength);
|
|
debug("------------------------------------------------\n");
|
|
|
|
switch (bmRType_bReq) {
|
|
case RH_GET_STATUS:
|
|
debug("RH_GET_STATUS\n");
|
|
|
|
*(__u16 *) data_buf = swap_16(1);
|
|
len = 2;
|
|
break;
|
|
|
|
case RH_GET_STATUS | RH_INTERFACE:
|
|
debug("RH_GET_STATUS | RH_INTERFACE\n");
|
|
|
|
*(__u16 *) data_buf = swap_16(0);
|
|
len = 2;
|
|
break;
|
|
|
|
case RH_GET_STATUS | RH_ENDPOINT:
|
|
debug("RH_GET_STATUS | RH_ENDPOINT\n");
|
|
|
|
*(__u16 *) data_buf = swap_16(0);
|
|
len = 2;
|
|
break;
|
|
|
|
case RH_GET_STATUS | RH_CLASS:
|
|
debug("RH_GET_STATUS | RH_CLASS\n");
|
|
|
|
*(__u32 *) data_buf = swap_32(0);
|
|
len = 4;
|
|
break;
|
|
|
|
case RH_GET_STATUS | RH_OTHER | RH_CLASS:
|
|
debug("RH_GET_STATUS | RH_OTHER | RH_CLASS\n");
|
|
|
|
int_usb = readw(&musbr->intrusb);
|
|
if (int_usb & MUSB_INTR_CONNECT) {
|
|
port_status |= USB_PORT_STAT_CONNECTION
|
|
| (USB_PORT_STAT_C_CONNECTION << 16);
|
|
port_status |= USB_PORT_STAT_HIGH_SPEED
|
|
| USB_PORT_STAT_ENABLE;
|
|
}
|
|
|
|
if (port_status & USB_PORT_STAT_RESET)
|
|
musb_port_reset(0);
|
|
|
|
*(__u32 *) data_buf = swap_32(port_status);
|
|
len = 4;
|
|
break;
|
|
|
|
case RH_CLEAR_FEATURE | RH_ENDPOINT:
|
|
debug("RH_CLEAR_FEATURE | RH_ENDPOINT\n");
|
|
|
|
switch (wValue) {
|
|
case RH_ENDPOINT_STALL:
|
|
debug("C_HUB_ENDPOINT_STALL\n");
|
|
len = 0;
|
|
break;
|
|
}
|
|
port_status &= ~(1 << wValue);
|
|
break;
|
|
|
|
case RH_CLEAR_FEATURE | RH_CLASS:
|
|
debug("RH_CLEAR_FEATURE | RH_CLASS\n");
|
|
|
|
switch (wValue) {
|
|
case RH_C_HUB_LOCAL_POWER:
|
|
debug("C_HUB_LOCAL_POWER\n");
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_C_HUB_OVER_CURRENT:
|
|
debug("C_HUB_OVER_CURRENT\n");
|
|
len = 0;
|
|
break;
|
|
}
|
|
port_status &= ~(1 << wValue);
|
|
break;
|
|
|
|
case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
|
|
debug("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS\n");
|
|
|
|
switch (wValue) {
|
|
case RH_PORT_ENABLE:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_PORT_SUSPEND:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_PORT_POWER:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_C_PORT_CONNECTION:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_C_PORT_ENABLE:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_C_PORT_SUSPEND:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_C_PORT_OVER_CURRENT:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_C_PORT_RESET:
|
|
len = 0;
|
|
break;
|
|
|
|
default:
|
|
debug("invalid wValue\n");
|
|
stat = USB_ST_STALLED;
|
|
}
|
|
|
|
port_status &= ~(1 << wValue);
|
|
break;
|
|
|
|
case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
|
|
debug("RH_SET_FEATURE | RH_OTHER | RH_CLASS\n");
|
|
|
|
switch (wValue) {
|
|
case RH_PORT_SUSPEND:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_PORT_RESET:
|
|
musb_port_reset(1);
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_PORT_POWER:
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_PORT_ENABLE:
|
|
len = 0;
|
|
break;
|
|
|
|
default:
|
|
debug("invalid wValue\n");
|
|
stat = USB_ST_STALLED;
|
|
}
|
|
|
|
port_status |= 1 << wValue;
|
|
break;
|
|
|
|
case RH_SET_ADDRESS:
|
|
debug("RH_SET_ADDRESS\n");
|
|
|
|
rh_devnum = wValue;
|
|
len = 0;
|
|
break;
|
|
|
|
case RH_GET_DESCRIPTOR:
|
|
debug("RH_GET_DESCRIPTOR: %x, %d\n", wValue, wLength);
|
|
|
|
switch (wValue) {
|
|
case (USB_DT_DEVICE << 8): /* device descriptor */
|
|
len = min_t(unsigned int,
|
|
leni, min_t(unsigned int,
|
|
sizeof(root_hub_dev_des),
|
|
wLength));
|
|
data_buf = root_hub_dev_des;
|
|
break;
|
|
|
|
case (USB_DT_CONFIG << 8): /* configuration descriptor */
|
|
len = min_t(unsigned int,
|
|
leni, min_t(unsigned int,
|
|
sizeof(root_hub_config_des),
|
|
wLength));
|
|
data_buf = root_hub_config_des;
|
|
break;
|
|
|
|
case ((USB_DT_STRING << 8) | 0x00): /* string 0 descriptors */
|
|
len = min_t(unsigned int,
|
|
leni, min_t(unsigned int,
|
|
sizeof(root_hub_str_index0),
|
|
wLength));
|
|
data_buf = root_hub_str_index0;
|
|
break;
|
|
|
|
case ((USB_DT_STRING << 8) | 0x01): /* string 1 descriptors */
|
|
len = min_t(unsigned int,
|
|
leni, min_t(unsigned int,
|
|
sizeof(root_hub_str_index1),
|
|
wLength));
|
|
data_buf = root_hub_str_index1;
|
|
break;
|
|
|
|
default:
|
|
debug("invalid wValue\n");
|
|
stat = USB_ST_STALLED;
|
|
}
|
|
|
|
break;
|
|
|
|
case RH_GET_DESCRIPTOR | RH_CLASS: {
|
|
u8 *_data_buf = (u8 *) datab;
|
|
debug("RH_GET_DESCRIPTOR | RH_CLASS\n");
|
|
|
|
_data_buf[0] = 0x09; /* min length; */
|
|
_data_buf[1] = 0x29;
|
|
_data_buf[2] = 0x1; /* 1 port */
|
|
_data_buf[3] = 0x01; /* per-port power switching */
|
|
_data_buf[3] |= 0x10; /* no overcurrent reporting */
|
|
|
|
/* Corresponds to data_buf[4-7] */
|
|
_data_buf[4] = 0;
|
|
_data_buf[5] = 5;
|
|
_data_buf[6] = 0;
|
|
_data_buf[7] = 0x02;
|
|
_data_buf[8] = 0xff;
|
|
|
|
len = min_t(unsigned int, leni,
|
|
min_t(unsigned int, data_buf[0], wLength));
|
|
break;
|
|
}
|
|
|
|
case RH_GET_CONFIGURATION:
|
|
debug("RH_GET_CONFIGURATION\n");
|
|
|
|
*(__u8 *) data_buf = 0x01;
|
|
len = 1;
|
|
break;
|
|
|
|
case RH_SET_CONFIGURATION:
|
|
debug("RH_SET_CONFIGURATION\n");
|
|
|
|
len = 0;
|
|
break;
|
|
|
|
default:
|
|
debug("*** *** *** unsupported root hub command *** *** ***\n");
|
|
stat = USB_ST_STALLED;
|
|
}
|
|
|
|
len = min_t(int, len, leni);
|
|
if (buffer != data_buf)
|
|
memcpy(buffer, data_buf, len);
|
|
|
|
dev->act_len = len;
|
|
dev->status = stat;
|
|
debug("dev act_len %d, status %lu\n", dev->act_len, dev->status);
|
|
|
|
return stat;
|
|
}
|
|
|
|
static void musb_rh_init(void)
|
|
{
|
|
rh_devnum = 0;
|
|
port_status = 0;
|
|
}
|
|
|
|
#else
|
|
|
|
static void musb_rh_init(void) {}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* 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);
|
|
u8 devspeed;
|
|
|
|
#ifdef MUSB_NO_MULTIPOINT
|
|
/* Control message is for the HUB? */
|
|
if (devnum == rh_devnum) {
|
|
int stat = musb_submit_rh_msg(dev, pipe, buffer, len, setup);
|
|
if (stat)
|
|
return stat;
|
|
}
|
|
#endif
|
|
|
|
/* select control endpoint */
|
|
writeb(MUSB_CONTROL_EP, &musbr->index);
|
|
readw(&musbr->txcsr);
|
|
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
/* target addr and (for multipoint) hub addr/port */
|
|
writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr);
|
|
writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr);
|
|
#endif
|
|
|
|
/* 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);
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
/* 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;
|
|
|
|
#ifdef MUSB_NO_MULTIPOINT
|
|
/* Set device address to USB_FADDR register */
|
|
if (setup->request == USB_REQ_SET_ADDRESS)
|
|
writeb(dev->devnum, &musbr->faddr);
|
|
#endif
|
|
|
|
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);
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
int devnum = usb_pipedevice(pipe);
|
|
#endif
|
|
u8 type;
|
|
u16 csr;
|
|
u32 txlen = 0;
|
|
u32 nextlen = 0;
|
|
u8 devspeed;
|
|
|
|
/* select bulk endpoint */
|
|
writeb(MUSB_BULK_EP, &musbr->index);
|
|
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
/* 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);
|
|
#endif
|
|
|
|
/* 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 {
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
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);
|
|
}
|
|
#endif
|
|
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];
|
|
|
|
#ifdef CONFIG_USB_BLACKFIN
|
|
/* Set the transfer data size */
|
|
writew(nextlen, &musbr->txcount);
|
|
#endif
|
|
|
|
/* 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) != 1) {
|
|
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 */
|
|
csr = readw(&musbr->rxcsr);
|
|
writew(csr | MUSB_RXCSR_H_REQPKT, &musbr->rxcsr);
|
|
|
|
/* Wait until the RxPktRdy bit is set */
|
|
if (wait_until_rxep_ready(dev, MUSB_BULK_EP) != 1) {
|
|
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(int index, void **controller)
|
|
{
|
|
u8 power;
|
|
u32 timeout;
|
|
|
|
musb_rh_init();
|
|
|
|
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], ARRAY_SIZE(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(int index)
|
|
{
|
|
/* 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);
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
int devnum = usb_pipedevice(pipe);
|
|
#endif
|
|
u8 type;
|
|
u16 csr;
|
|
u32 txlen = 0;
|
|
u32 nextlen = 0;
|
|
u8 devspeed;
|
|
|
|
/* select interrupt endpoint */
|
|
writeb(MUSB_INTR_EP, &musbr->index);
|
|
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
/* 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);
|
|
#endif
|
|
|
|
/* 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 {
|
|
#ifndef MUSB_NO_MULTIPOINT
|
|
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);
|
|
}
|
|
#endif
|
|
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 */
|
|
csr = readw(&musbr->rxcsr);
|
|
writew(csr | MUSB_RXCSR_H_REQPKT, &musbr->rxcsr);
|
|
|
|
/* Wait until the RxPktRdy bit is set */
|
|
if (wait_until_rxep_ready(dev, MUSB_INTR_EP) != 1) {
|
|
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;
|
|
}
|