mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-09-07 05:13:21 +09:00
![Tom Rix](/assets/img/avatar_default.png)
The header files usb.h and usbdescriptors.h have the same nameed structure definitions for usb_config_descriptor usb_interface_descriptor usb_endpoint_descriptor usb_device_descriptor usb_string_descriptor These are out right duplicates in usb.h usb_device_descriptor usb_string_descriptor This one has extra unused elements usb_endpoint_descriptor unsigned char bRefresh unsigned char bSynchAddress; These in usb.h have extra elements at the end of the usb 2.0 specified descriptor and are used. usb_config_descriptor usb_interface_descriptor The change is to consolidate the definition of the descriptors to usbdescriptors.h. The dublicates in usb.h are removed. The extra element structure will have their name shorted by removing the '_descriptor' suffix. So usb_config_descriptor -> usb_config usb_interface_descriptor -> usb_interface For these, the common descriptor elements are accessed now by an element 'desc'. As an example - if (iface->bInterfaceClass != USB_CLASS_HUB) + if (iface->desc.bInterfaceClass != USB_CLASS_HUB) This has been compile tested on MAKEALL arm, ppc and mips. Signed-off-by: Tom Rix <Tom.Rix@windriver.com>
824 lines
20 KiB
C
824 lines
20 KiB
C
/*
|
|
* 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;
|
|
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 u8 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 u8 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);
|
|
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()
|
|
{
|
|
struct stdio_dev *dev;
|
|
struct usb_device *usb_kbd_dev;
|
|
struct usb_interface *iface;
|
|
struct usb_endpoint_descriptor *ep;
|
|
int pipe;
|
|
int maxp;
|
|
|
|
/* Get the pointer to USB Keyboard device pointer */
|
|
dev = stdio_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 */
|