u-boot/drivers/usb/musb/musb_udc.c
Pali Rohár f1e8d2072c usb: musb: Fix handling interrupts for EP0 and SET ADDRESS commmand
Interrupt for EP0 is indicated in intrtx register via first bit. This bit
is set for both RX and TX despite register has only TX in its name. First
bit in intrrx register is reserved, not used and never set.

So remove calling musb_peri_ep0() function at every iteration of udc_irq()
and musb_peri_rx() and call it only from musb_peri_tx() when correct
interrupt bit in initrtx it set.

Address from SET ADDRESS command must be set to faddr register only after
acknowledging SERV_RXPKTRDY followed by received EP0 interrupt. So prior
calling musb_peri_ep0_set_address() check for EP0 interrupt instead of
(incorrect) MUSB_INTR_SOF interrupt.

This patch fixes issue that host (computer) cannot register U-Boot USB
device and is failing with errors:

    usb 1-1: new full-speed USB device number 86 using xhci_hcd
    usb 1-1: Device not responding to setup address.
    usb 1-1: Device not responding to setup address.
    usb 1-1: device not accepting address 86, error -71

U-Boot was writing address to faddr register too early and did not wait for
correct interrupt after which should update address.

Signed-off-by: Pali Rohár <pali@kernel.org>
Reviewed-by: Lukasz Majewski <lukma@denx.de>
Acked-by: Pavel Machek <pavel@ucw.cz>
2021-03-03 04:12:46 +01:00

964 lines
21 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2009 Wind River Systems, Inc.
* Tom Rix <Tom.Rix@windriver.com>
*
* This file is a rewrite of the usb device part of
* repository git.omapzoom.org/repo/u-boot.git, branch master,
* file cpu/omap3/fastboot.c
*
* This is the unique part of its copyright :
*
* -------------------------------------------------------------------------
*
* (C) Copyright 2008 - 2009
* Windriver, <www.windriver.com>
* Tom Rix <Tom.Rix@windriver.com>
*
* -------------------------------------------------------------------------
*
* The details of connecting the device to the uboot usb device subsystem
* came from the old omap3 repository www.sakoman.net/u-boot-omap3.git,
* branch omap3-dev-usb, file drivers/usb/usbdcore_musb.c
*
* This is the unique part of its copyright :
*
* -------------------------------------------------------------------------
*
* (C) Copyright 2008 Texas Instruments Incorporated.
*
* Based on
* u-boot OMAP1510 USB drivers (drivers/usbdcore_omap1510.c)
* twl4030 init based on linux (drivers/i2c/chips/twl4030_usb.c)
*
* Author: Diego Dompe (diego.dompe@ridgerun.com)
* Atin Malaviya (atin.malaviya@gmail.com)
*
* -------------------------------------------------------------------------
*/
#include <common.h>
#include <hang.h>
#include <serial.h>
#include <usbdevice.h>
#include <linux/delay.h>
#include <usb/udc.h>
#include "../gadget/ep0.h"
#include "musb_core.h"
#if defined(CONFIG_USB_OMAP3)
#include "omap3.h"
#elif defined(CONFIG_USB_AM35X)
#include "am35x.h"
#endif
/* Define MUSB_DEBUG for debugging */
/* #define MUSB_DEBUG */
#include "musb_debug.h"
#define MAX_ENDPOINT 15
#define GET_ENDPOINT(dev,ep) \
(((struct usb_device_instance *)(dev))->bus->endpoint_array + ep)
#define SET_EP0_STATE(s) \
do { \
if ((0 <= (s)) && (SET_ADDRESS >= (s))) { \
if ((s) != ep0_state) { \
if ((debug_setup) && (debug_level > 1)) \
serial_printf("INFO : Changing state " \
"from %s to %s in %s at " \
"line %d\n", \
ep0_state_strings[ep0_state],\
ep0_state_strings[s], \
__PRETTY_FUNCTION__, \
__LINE__); \
ep0_state = s; \
} \
} else { \
if (debug_level > 0) \
serial_printf("Error at %s %d with setting " \
"state %d is invalid\n", \
__PRETTY_FUNCTION__, __LINE__, s); \
} \
} while (0)
/* static implies these initialized to 0 or NULL */
static int debug_setup;
static int debug_level;
static struct musb_epinfo epinfo[MAX_ENDPOINT * 2 + 2];
static enum ep0_state_enum {
IDLE = 0,
TX,
RX,
SET_ADDRESS
} ep0_state = IDLE;
static char *ep0_state_strings[4] = {
"IDLE",
"TX",
"RX",
"SET_ADDRESS",
};
static struct urb *ep0_urb;
struct usb_endpoint_instance *ep0_endpoint;
static struct usb_device_instance *udc_device;
static int enabled;
static u16 pending_intrrx;
#ifdef MUSB_DEBUG
static void musb_db_regs(void)
{
u8 b;
u16 w;
b = readb(&musbr->faddr);
serial_printf("\tfaddr 0x%2.2x\n", b);
b = readb(&musbr->power);
musb_print_pwr(b);
w = readw(&musbr->ep[0].ep0.csr0);
musb_print_csr0(w);
b = readb(&musbr->devctl);
musb_print_devctl(b);
b = readb(&musbr->ep[0].ep0.configdata);
musb_print_config(b);
w = readw(&musbr->frame);
serial_printf("\tframe 0x%4.4x\n", w);
b = readb(&musbr->index);
serial_printf("\tindex 0x%2.2x\n", b);
w = readw(&musbr->ep[1].epN.rxmaxp);
musb_print_rxmaxp(w);
w = readw(&musbr->ep[1].epN.rxcsr);
musb_print_rxcsr(w);
w = readw(&musbr->ep[1].epN.txmaxp);
musb_print_txmaxp(w);
w = readw(&musbr->ep[1].epN.txcsr);
musb_print_txcsr(w);
}
#else
#define musb_db_regs()
#endif /* DEBUG_MUSB */
static void musb_peri_softconnect(void)
{
u8 power, devctl;
/* Power off MUSB */
power = readb(&musbr->power);
power &= ~MUSB_POWER_SOFTCONN;
writeb(power, &musbr->power);
/* Read intr to clear */
readb(&musbr->intrusb);
readw(&musbr->intrrx);
readw(&musbr->intrtx);
udelay(1000 * 1000); /* 1 sec */
/* Power on MUSB */
power = readb(&musbr->power);
power |= MUSB_POWER_SOFTCONN;
/*
* The usb device interface is usb 1.1
* Disable 2.0 high speed by clearring the hsenable bit.
*/
power &= ~MUSB_POWER_HSENAB;
writeb(power, &musbr->power);
/* Check if device is in b-peripheral mode */
devctl = readb(&musbr->devctl);
if (!(devctl & MUSB_DEVCTL_BDEVICE) ||
(devctl & MUSB_DEVCTL_HM)) {
serial_printf("ERROR : Unsupport USB mode\n");
serial_printf("Check that mini-B USB cable is attached "
"to the device\n");
}
if (debug_setup && (debug_level > 1))
musb_db_regs();
}
static void musb_peri_reset(void)
{
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s reset\n", __PRETTY_FUNCTION__);
if (ep0_endpoint)
ep0_endpoint->endpoint_address = 0xff;
/* Sync sw and hw addresses */
writeb(udc_device->address, &musbr->faddr);
SET_EP0_STATE(IDLE);
}
static void musb_peri_resume(void)
{
/* noop */
}
static void musb_peri_ep0_stall(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= MUSB_CSR0_P_SENDSTALL;
writew(csr0, &musbr->ep[0].ep0.csr0);
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s stall\n", __PRETTY_FUNCTION__);
}
static void musb_peri_ep0_ack_req(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= MUSB_CSR0_P_SVDRXPKTRDY;
writew(csr0, &musbr->ep[0].ep0.csr0);
}
static void musb_ep0_tx_ready(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= MUSB_CSR0_TXPKTRDY;
writew(csr0, &musbr->ep[0].ep0.csr0);
}
static void musb_ep0_tx_ready_and_last(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_P_DATAEND);
writew(csr0, &musbr->ep[0].ep0.csr0);
}
static void musb_peri_ep0_last(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= MUSB_CSR0_P_DATAEND;
writew(csr0, &musbr->ep[0].ep0.csr0);
}
static void musb_peri_ep0_set_address(void)
{
u8 faddr;
writeb(udc_device->address, &musbr->faddr);
/* Verify */
faddr = readb(&musbr->faddr);
if (udc_device->address == faddr) {
SET_EP0_STATE(IDLE);
usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0);
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s Address set to %d\n",
__PRETTY_FUNCTION__, udc_device->address);
} else {
if (debug_level > 0)
serial_printf("ERROR : %s Address missmatch "
"sw %d vs hw %d\n",
__PRETTY_FUNCTION__,
udc_device->address, faddr);
}
}
static void musb_peri_rx_ack(unsigned int ep)
{
u16 peri_rxcsr;
peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr);
peri_rxcsr &= ~MUSB_RXCSR_RXPKTRDY;
writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
}
static void musb_peri_tx_ready(unsigned int ep)
{
u16 peri_txcsr;
peri_txcsr = readw(&musbr->ep[ep].epN.txcsr);
peri_txcsr |= MUSB_TXCSR_TXPKTRDY;
writew(peri_txcsr, &musbr->ep[ep].epN.txcsr);
}
static void musb_peri_ep0_zero_data_request(int err)
{
musb_peri_ep0_ack_req();
if (err) {
musb_peri_ep0_stall();
SET_EP0_STATE(IDLE);
} else {
musb_peri_ep0_last();
/* USBD state */
switch (ep0_urb->device_request.bRequest) {
case USB_REQ_SET_ADDRESS:
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s received set "
"address\n", __PRETTY_FUNCTION__);
break;
case USB_REQ_SET_CONFIGURATION:
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s Configured\n",
__PRETTY_FUNCTION__);
usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0);
break;
}
/* EP0 state */
if (USB_REQ_SET_ADDRESS == ep0_urb->device_request.bRequest) {
SET_EP0_STATE(SET_ADDRESS);
} else {
SET_EP0_STATE(IDLE);
}
}
}
static void musb_peri_ep0_rx_data_request(void)
{
/*
* This is the completion of the data OUT / RX
*
* Host is sending data to ep0 that is not
* part of setup. This comes from the cdc_recv_setup
* op that is device specific.
*
*/
musb_peri_ep0_ack_req();
ep0_endpoint->rcv_urb = ep0_urb;
ep0_urb->actual_length = 0;
SET_EP0_STATE(RX);
}
static void musb_peri_ep0_tx_data_request(int err)
{
if (err) {
musb_peri_ep0_stall();
SET_EP0_STATE(IDLE);
} else {
musb_peri_ep0_ack_req();
ep0_endpoint->tx_urb = ep0_urb;
ep0_endpoint->sent = 0;
SET_EP0_STATE(TX);
}
}
static void musb_peri_ep0_idle(void)
{
u16 count0;
int err;
u16 csr0;
/*
* Verify addresses
* A lot of confusion can be caused if the address
* in software, udc layer, does not agree with the
* hardware. Since the setting of the hardware address
* must be set after the set address request, the
* usb state machine is out of sync for a few frame.
* It is a good idea to run this check when changes
* are made to the state machine.
*/
if ((debug_level > 0) &&
(ep0_state != SET_ADDRESS)) {
u8 faddr;
faddr = readb(&musbr->faddr);
if (udc_device->address != faddr) {
serial_printf("ERROR : %s addresses do not"
"match sw %d vs hw %d\n",
__PRETTY_FUNCTION__,
udc_device->address, faddr);
udelay(1000 * 1000);
hang();
}
}
csr0 = readw(&musbr->ep[0].ep0.csr0);
if (!(MUSB_CSR0_RXPKTRDY & csr0))
goto end;
count0 = readw(&musbr->ep[0].ep0.count0);
if (count0 == 0)
goto end;
if (count0 != 8) {
if ((debug_setup) && (debug_level > 1))
serial_printf("WARN : %s SETUP incorrect size %d\n",
__PRETTY_FUNCTION__, count0);
musb_peri_ep0_stall();
goto end;
}
read_fifo(0, count0, &ep0_urb->device_request);
if (debug_level > 2)
print_usb_device_request(&ep0_urb->device_request);
if (ep0_urb->device_request.wLength == 0) {
err = ep0_recv_setup(ep0_urb);
/* Zero data request */
musb_peri_ep0_zero_data_request(err);
} else {
/* Is data coming or going ? */
u8 reqType = ep0_urb->device_request.bmRequestType;
if (USB_REQ_DEVICE2HOST == (reqType & USB_REQ_DIRECTION_MASK)) {
err = ep0_recv_setup(ep0_urb);
/* Device to host */
musb_peri_ep0_tx_data_request(err);
} else {
/*
* Host to device
*
* The RX routine will call ep0_recv_setup
* when the data packet has arrived.
*/
musb_peri_ep0_rx_data_request();
}
}
end:
return;
}
static void musb_peri_ep0_rx(void)
{
/*
* This is the completion of the data OUT / RX
*
* Host is sending data to ep0 that is not
* part of setup. This comes from the cdc_recv_setup
* op that is device specific.
*
* Pass the data back to driver ep0_recv_setup which
* should give the cdc_recv_setup the chance to handle
* the rx
*/
u16 csr0;
u16 count0;
if (debug_level > 3) {
if (0 != ep0_urb->actual_length) {
serial_printf("%s finished ? %d of %d\n",
__PRETTY_FUNCTION__,
ep0_urb->actual_length,
ep0_urb->device_request.wLength);
}
}
if (ep0_urb->device_request.wLength == ep0_urb->actual_length) {
musb_peri_ep0_last();
SET_EP0_STATE(IDLE);
ep0_recv_setup(ep0_urb);
return;
}
csr0 = readw(&musbr->ep[0].ep0.csr0);
if (!(MUSB_CSR0_RXPKTRDY & csr0))
return;
count0 = readw(&musbr->ep[0].ep0.count0);
if (count0) {
struct usb_endpoint_instance *endpoint;
u32 length;
u8 *data;
endpoint = ep0_endpoint;
if (endpoint && endpoint->rcv_urb) {
struct urb *urb = endpoint->rcv_urb;
unsigned int remaining_space = urb->buffer_length -
urb->actual_length;
if (remaining_space) {
int urb_bad = 0; /* urb is good */
if (count0 > remaining_space)
length = remaining_space;
else
length = count0;
data = (u8 *) urb->buffer_data;
data += urb->actual_length;
/* The common musb fifo reader */
read_fifo(0, length, data);
musb_peri_ep0_ack_req();
/*
* urb's actual_length is updated in
* usbd_rcv_complete
*/
usbd_rcv_complete(endpoint, length, urb_bad);
} else {
if (debug_level > 0)
serial_printf("ERROR : %s no space in "
"rcv buffer\n",
__PRETTY_FUNCTION__);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s problem with "
"endpoint\n",
__PRETTY_FUNCTION__);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s with nothing to do\n",
__PRETTY_FUNCTION__);
}
}
static void musb_peri_ep0_tx(void)
{
u16 csr0;
int transfer_size = 0;
unsigned int p, pm;
csr0 = readw(&musbr->ep[0].ep0.csr0);
/* Check for pending tx */
if (csr0 & MUSB_CSR0_TXPKTRDY)
goto end;
/* Check if this is the last packet sent */
if (ep0_endpoint->sent >= ep0_urb->actual_length) {
SET_EP0_STATE(IDLE);
goto end;
}
transfer_size = ep0_urb->actual_length - ep0_endpoint->sent;
/* Is the transfer size negative ? */
if (transfer_size <= 0) {
if (debug_level > 0)
serial_printf("ERROR : %s problem with the"
" transfer size %d\n",
__PRETTY_FUNCTION__,
transfer_size);
SET_EP0_STATE(IDLE);
goto end;
}
/* Truncate large transfers to the fifo size */
if (transfer_size > ep0_endpoint->tx_packetSize)
transfer_size = ep0_endpoint->tx_packetSize;
write_fifo(0, transfer_size, &ep0_urb->buffer[ep0_endpoint->sent]);
ep0_endpoint->sent += transfer_size;
/* Done or more to send ? */
if (ep0_endpoint->sent >= ep0_urb->actual_length)
musb_ep0_tx_ready_and_last();
else
musb_ep0_tx_ready();
/* Wait a bit */
pm = 10;
for (p = 0; p < pm; p++) {
csr0 = readw(&musbr->ep[0].ep0.csr0);
if (!(csr0 & MUSB_CSR0_TXPKTRDY))
break;
/* Double the delay. */
udelay(1 << pm);
}
if ((ep0_endpoint->sent >= ep0_urb->actual_length) && (p < pm))
SET_EP0_STATE(IDLE);
end:
return;
}
static void musb_peri_ep0(void)
{
u16 csr0;
if (SET_ADDRESS == ep0_state)
return;
csr0 = readw(&musbr->ep[0].ep0.csr0);
/* Error conditions */
if (MUSB_CSR0_P_SENTSTALL & csr0) {
csr0 &= ~MUSB_CSR0_P_SENTSTALL;
writew(csr0, &musbr->ep[0].ep0.csr0);
SET_EP0_STATE(IDLE);
}
if (MUSB_CSR0_P_SETUPEND & csr0) {
csr0 |= MUSB_CSR0_P_SVDSETUPEND;
writew(csr0, &musbr->ep[0].ep0.csr0);
SET_EP0_STATE(IDLE);
if ((debug_setup) && (debug_level > 1))
serial_printf("WARN: %s SETUPEND\n",
__PRETTY_FUNCTION__);
}
/* Normal states */
if (IDLE == ep0_state)
musb_peri_ep0_idle();
if (TX == ep0_state)
musb_peri_ep0_tx();
if (RX == ep0_state)
musb_peri_ep0_rx();
}
static void musb_peri_rx_ep(unsigned int ep)
{
u16 peri_rxcount;
u16 peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr);
if (!(peri_rxcsr & MUSB_RXCSR_RXPKTRDY)) {
if (debug_level > 0)
serial_printf("ERROR : %s %d without MUSB_RXCSR_RXPKTRDY set\n",
__PRETTY_FUNCTION__, ep);
return;
}
peri_rxcount = readw(&musbr->ep[ep].epN.rxcount);
if (peri_rxcount) {
struct usb_endpoint_instance *endpoint;
u32 length;
u8 *data;
endpoint = GET_ENDPOINT(udc_device, ep);
if (endpoint && endpoint->rcv_urb) {
struct urb *urb = endpoint->rcv_urb;
unsigned int remaining_space = urb->buffer_length -
urb->actual_length;
if (remaining_space) {
int urb_bad = 0; /* urb is good */
if (peri_rxcount > remaining_space)
length = remaining_space;
else
length = peri_rxcount;
data = (u8 *) urb->buffer_data;
data += urb->actual_length;
/* The common musb fifo reader */
read_fifo(ep, length, data);
if (length == peri_rxcount)
musb_peri_rx_ack(ep);
else
pending_intrrx |= (1 << ep);
/*
* urb's actual_length is updated in
* usbd_rcv_complete
*/
usbd_rcv_complete(endpoint, length, urb_bad);
} else {
if (debug_level > 0)
serial_printf("ERROR : %s %d no space "
"in rcv buffer\n",
__PRETTY_FUNCTION__, ep);
pending_intrrx |= (1 << ep);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s %d problem with "
"endpoint\n",
__PRETTY_FUNCTION__, ep);
pending_intrrx |= (1 << ep);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s %d with nothing to do\n",
__PRETTY_FUNCTION__, ep);
musb_peri_rx_ack(ep);
}
}
static void musb_peri_rx(u16 intr)
{
unsigned int ep;
/* First bit is reserved and does not indicate interrupt for EP0 */
for (ep = 1; ep < 16; ep++) {
if ((1 << ep) & intr)
musb_peri_rx_ep(ep);
}
}
static void musb_peri_tx(u16 intr)
{
unsigned int ep;
/* Check for EP0: first bit indicates interrupt for both RX and TX */
if (0x01 & intr)
musb_peri_ep0();
for (ep = 1; ep < 16; ep++) {
if ((1 << ep) & intr)
udc_endpoint_write(GET_ENDPOINT(udc_device, ep));
}
}
void udc_irq(void)
{
/* This is a high freq called function */
if (enabled) {
u8 intrusb;
intrusb = readb(&musbr->intrusb);
/*
* See drivers/usb/gadget/mpc8xx_udc.c for
* state diagram going from detached through
* configuration.
*/
if (MUSB_INTR_RESUME & intrusb) {
usbd_device_event_irq(udc_device,
DEVICE_BUS_ACTIVITY, 0);
musb_peri_resume();
}
if (MUSB_INTR_RESET & intrusb) {
usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
musb_peri_reset();
}
if (MUSB_INTR_DISCONNECT & intrusb) {
/* cable unplugged from hub/host */
usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
musb_peri_reset();
usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0);
}
if (MUSB_INTR_SOF & intrusb) {
usbd_device_event_irq(udc_device,
DEVICE_BUS_ACTIVITY, 0);
musb_peri_resume();
}
if (MUSB_INTR_SUSPEND & intrusb) {
usbd_device_event_irq(udc_device,
DEVICE_BUS_INACTIVE, 0);
}
if (ep0_state != SET_ADDRESS) {
u16 intrrx, intrtx;
intrrx = readw(&musbr->intrrx);
intrtx = readw(&musbr->intrtx);
intrrx |= pending_intrrx;
pending_intrrx = 0;
if (intrrx)
musb_peri_rx(intrrx);
if (intrtx)
musb_peri_tx(intrtx);
} else {
if (readw(&musbr->intrtx) & 0x1) {
u8 faddr;
faddr = readb(&musbr->faddr);
/*
* Setting of the address can fail.
* Normally it succeeds the second time.
*/
if (udc_device->address != faddr)
musb_peri_ep0_set_address();
}
}
}
}
void udc_set_nak(int ep_num)
{
/* noop */
}
void udc_unset_nak(int ep_num)
{
/* noop */
}
int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
{
int ret = 0;
/* Transmit only if the hardware is available */
if (endpoint->tx_urb && endpoint->state == 0) {
unsigned int ep = endpoint->endpoint_address &
USB_ENDPOINT_NUMBER_MASK;
u16 peri_txcsr = readw(&musbr->ep[ep].epN.txcsr);
/* Error conditions */
if (peri_txcsr & MUSB_TXCSR_P_UNDERRUN) {
peri_txcsr &= ~MUSB_TXCSR_P_UNDERRUN;
writew(peri_txcsr, &musbr->ep[ep].epN.txcsr);
}
if (debug_level > 1)
musb_print_txcsr(peri_txcsr);
/* Check if a packet is waiting to be sent */
if (!(peri_txcsr & MUSB_TXCSR_TXPKTRDY)) {
u32 length;
u8 *data;
struct urb *urb = endpoint->tx_urb;
unsigned int remaining_packet = urb->actual_length -
endpoint->sent;
if (endpoint->tx_packetSize < remaining_packet)
length = endpoint->tx_packetSize;
else
length = remaining_packet;
data = (u8 *) urb->buffer;
data += endpoint->sent;
/* common musb fifo function */
write_fifo(ep, length, data);
musb_peri_tx_ready(ep);
endpoint->last = length;
/* usbd_tx_complete will take care of updating 'sent' */
usbd_tx_complete(endpoint);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s Problem with urb %p "
"or ep state %d\n",
__PRETTY_FUNCTION__,
endpoint->tx_urb, endpoint->state);
}
return ret;
}
void udc_setup_ep(struct usb_device_instance *device, unsigned int id,
struct usb_endpoint_instance *endpoint)
{
if (0 == id) {
/* EP0 */
ep0_endpoint = endpoint;
ep0_endpoint->endpoint_address = 0xff;
ep0_urb = usbd_alloc_urb(device, endpoint);
} else if (MAX_ENDPOINT >= id) {
int ep_addr;
/* Check the direction */
ep_addr = endpoint->endpoint_address;
if (USB_DIR_IN == (ep_addr & USB_ENDPOINT_DIR_MASK)) {
/* IN */
epinfo[(id * 2) + 1].epsize = endpoint->tx_packetSize;
} else {
/* OUT */
epinfo[id * 2].epsize = endpoint->rcv_packetSize;
}
musb_configure_ep(&epinfo[0], ARRAY_SIZE(epinfo));
} else {
if (debug_level > 0)
serial_printf("ERROR : %s endpoint request %d "
"exceeds maximum %d\n",
__PRETTY_FUNCTION__, id, MAX_ENDPOINT);
}
}
void udc_connect(void)
{
/* noop */
}
void udc_disconnect(void)
{
/* noop */
}
void udc_enable(struct usb_device_instance *device)
{
/* Save the device structure pointer */
udc_device = device;
enabled = 1;
}
void udc_disable(void)
{
enabled = 0;
}
void udc_startup_events(struct usb_device_instance *device)
{
/* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */
usbd_device_event_irq(device, DEVICE_INIT, 0);
/*
* The DEVICE_CREATE event puts the USB device in the state
* STATE_ATTACHED.
*/
usbd_device_event_irq(device, DEVICE_CREATE, 0);
/* Resets the address to 0 */
usbd_device_event_irq(device, DEVICE_RESET, 0);
udc_enable(device);
}
int udc_init(void)
{
int ret;
int ep_loop;
ret = musb_platform_init();
if (ret < 0)
goto end;
/* Configure all the endpoint FIFO's and start usb controller */
musbr = musb_cfg.regs;
/* Initialize the endpoints */
for (ep_loop = 0; ep_loop <= MAX_ENDPOINT * 2; ep_loop++) {
epinfo[ep_loop].epnum = (ep_loop / 2) + 1;
epinfo[ep_loop].epdir = ep_loop % 2; /* OUT, IN */
epinfo[ep_loop].epsize = 0;
}
musb_peri_softconnect();
ret = 0;
end:
return ret;
}