www.pudn.com > Linux2410_device.rar > tc86c001.c


/*
 * linux/drivers/usbd/bi/tc86c001.c -- USB Device Controller driver. 
 *
 * Copyright (c) 2000, 2001, 2002 Lineo
 *
 * By: 
 *      Stuart Lynne , 
 *      Tom Rushworth , 
 *      Bruce Balden 
 *
 * Copyright (C) 2002 Toshiba Corporation
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/***************************************************************************/

#include 
#include 

#include "../usbd-export.h"
#include "../usbd-build.h"
#include "../usbd-module.h"

MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com, TOSHIBA Corporation");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION ("USB Device TC86C001 Bus Interface");

USBD_MODULE_INFO ("tc86c001_bi 0.1-alpha");

#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 

#include 
#include 

#include 

#include 
#include 
#include 

#include "../usbd.h"
#include "../usbd-debug.h"
#include "../usbd-func.h"
#include "../usbd-bus.h"
#include "../usbd-inline.h"
#include "usbd-bi.h"

#define EP0_PACKETSIZE  0x8

#define UDC_MAX_ENDPOINTS       4

#define UDC_NAME        "TC86C001 USBD"


/* offset 0x000-0x1ff */
struct tc_udc_regs
{
    volatile u32 IntStatus;
    volatile u32 IntEnable;
    volatile u32 MstSetting;
    volatile u32 MstWrStart;
    volatile u32 MstWrEnd;        /* 0x010 */
    volatile u32 MstWrCurr;
    volatile u32 MstRdStart;
    volatile u32 MstRdEnd;
    volatile u32 MstRdCurr;        /* 0x020 */
    volatile u32 PowerDetect;
};

/* offset 0x200-0x7ff */
struct tc_udc_udcregs
{
    volatile u32 EPxFIFO[8];        /* 0x00 */
    volatile u32 EPxMode[8];        /* 0x20 (0:reserved) */
    volatile u32 EPxStatus[8];        /* 0x40 */
    volatile u32 EPxSizeLA[8];        /* 0x60 */
    volatile u32 EPxSizeLB[8];        /* 0x80 (0:reserved) */
    volatile u32 EPxSizeHA[8];        /* 0xa0 (0:reserved) */
    volatile u32 EPxSizeHB[8];        /* 0xc0 (0:reserved) */
    volatile u32 unused1[8];
    volatile u32 bmReqType;        /* 0x100 */
    volatile u32 bRequest;
    volatile u32 wValueL;
    volatile u32 wValueH;
    volatile u32 wIndexL;
    volatile u32 wIndexH;
    volatile u32 wLengthL;
    volatile u32 wLengthH;
    volatile u32 SetupRecv;        /* 0x120 */
    volatile u32 CurrConfig;
    volatile u32 StdRequest;
    volatile u32 Request;
    volatile u32 DataSet[2];
    volatile u32 UsbState;
    volatile u32 EOP;
    volatile u32 Command;        /* 0x140 */
    volatile u32 EPxSingle[2];
    volatile u32 EPxBCS[2];
    volatile u32 unused2;
    volatile u32 IntControl;
    volatile u32 unused3;
    volatile u32 StdReqMode;        /* 0x160 */
    volatile u32 ReqMode;
    volatile u32 unused4[6];
    volatile u32 PortStatus;        /* 0x180 */
    volatile u32 unused5[2];
    volatile u32 Address;
    volatile u32 BuffTest;
    volatile u32 unused6;
    volatile u32 UsbReady;
    volatile u32 unused7;
    volatile u32 SetDescStall;        /* 0x1a0 */
};

/* MstSetting.MST_connection == 0 */
#define UDC_MSTWR_ENDPOINT        1
#define UDC_MSTRD_ENDPOINT        2

#define UDC_DESCRAM_SIZE        0x80

/* offset 0x800-0x9ff */
struct tc_udc_ramregs
{
    volatile u32 DescRam[UDC_DESCRAM_SIZE];
};

/* IntStatus/IntEnable */
#define INT_SUSPEND        0x00001
#define INT_USBRESET        0x00002
#define INT_ENDPOINT0        0x00004
#define INT_SETUP        0x00008
#define INT_STATUS        0x00010
#define INT_STATUSNAK        0x00020
#define INT_EP1DATASET        0x00040
#define INT_EP2DATASET        0x00080
#define INT_EP3DATASET        0x00100
#define INT_EPxDATASET(n)        (0x00040 << ((n) - 1))
#define INT_EP1NAK        0x00200
#define INT_EP2NAK        0x00400
#define INT_EP3NAK        0x00800
#define INT_EPnNAK(n)        (0x00200 < ((n) - 1))
#define INT_SOF        0x01000
#define INT_ERR        0x02000
#define INT_MSTWRSET        0x04000
#define INT_MSTWREND        0x08000
#define INT_MSTWRTMOUT        0x10000
#define INT_MSTRDEND        0x20000
#define INT_SYSERROR        0x40000
#define INT_PWRDETECT        0x80000

/* MstSetting */
#define MST_EOPB_DIS        0x0800
#define MST_EOPB_ENA        0x0400
#define MST_TIMEOUT_DIS        0x0200
#define MST_TIMEOUT_ENA        0x0100
#define MST_RD_EOPB        0x0080
#define MST_RD_RESET        0x0040
#define MST_WR_RESET        0x0020
#define MST_RD_ENA        0x0004        /* 1:start, 0:ignore */
#define MST_WR_ENA        0x0002        /* 1:start, 0:ignore */
#define MST_CONNECTION        0x0001
#define MST_RW_BITS        0x0f67        /* read/write bits */

/* PowerDetect */
#define PW_DETECT        0x04
#define PW_RESETB        0x02
#define PW_PLLUPENB        0x01

/* udcregs StdRequest/StdReqMode */
#define STDREQ_S_INTERFACE        0x80
#define STDREQ_G_INTERFACE        0x40
#define STDREQ_S_CONFIG                0x20
#define STDREQ_G_CONFIG                0x10
#define STDREQ_G_DESCRIPT        0x08
#define STDREQ_S_FEATURE        0x04
#define STDREQ_C_FEATURE        0x02
#define STDREQ_G_STATUS                0x01

/* udcregs EPxStatus */
#define EPxSTATUS_TOGGLE        0x40
#define EPxSTATUS_SUSPEND        0x20
#define EPxSTATUS_EP_MASK        0x1c
#define EPxSTATUS_EP_READY        0x00
#define EPxSTATUS_EP_DATAIN        0x04
#define EPxSTATUS_EP_FULL        0x08
#define EPxSTATUS_EP_TX_ERR        0x0c
#define EPxSTATUS_EP_RX_ERR        0x10
#define EPxSTATUS_EP_BUSY        0x14
#define EPxSTATUS_EP_STALL        0x18
#define EPxSTATUS_EP_INVALID        0x1c
#define EPxSTATUS_FIFO_DISABLE        0x02
#define EPxSTATUS_STAGE_ERROR        0x01

/* udcregs Command */
#define COMMAND_SETDATA0        2
#define COMMAND_RESET        3
#define COMMAND_STALL        4
#define COMMAND_INVALID        5
#define COMMAND_EP(n)        ((n) << 4)

/* udcregs UsbState */
#define USBSTATE_CONFIGURED        0x04
#define USBSTATE_ADDRESSED        0x02
#define USBSTATE_DEFAULT        0x01

static struct pci_dev *udc_pci_dev;        /* only one instance */
static struct tc_udc_regs *tc_regs;
static struct tc_udc_udcregs *tc_udcregs;
static struct tc_udc_ramregs *tc_ramregs;

static struct usb_device_instance *udc_device;        // required for the interrupt handler

/*
 * ep_endpoints - map physical endpoints to logical endpoints
 */
static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS];
static dma_addr_t ep_dmaaddrs[UDC_MAX_ENDPOINTS];

static struct urb *ep0_urb;

extern unsigned int udc_interrupts;

static int nodma;
MODULE_PARM (nodma, "i");
MODULE_PARM_DESC (nodma, "Disable DMA");

/* *********************************************************************** */
/* IO
 */

/**
 * tc_write_buffer - write a buffer to the tc fifo
 * @ep: endpoint
 * @b: pointer to buffer to write
 * @size: number of bytes to write
 */
static void
tc_write_buffer (unsigned char ep, unsigned char *b, unsigned char size)
{
    while (size--) {
        writel (*b++, &tc_udcregs->EPxFIFO[ep]);
    }
}

/**
 * tc_read_buffer - fill a buffer from the tc fifo
 * @ep: endpoint
 * @b: pointer to buffer to fill
 * @size: number of bytes to read
 */
static void
tc_read_buffer (unsigned char ep, unsigned char *b, unsigned char size)
{
    while (size--) {
        *b++ = readl (&tc_udcregs->EPxFIFO[ep]);
    }
}

/**
 * tc_read_epxsize - read data count in FIFO of the endpoint.
 * @ep: endpoint
 */
static int
tc_read_epxsize (unsigned int ep)
{
    int low = readl (&tc_udcregs->EPxSizeLA[ep]);
    int size;
    /* SizeH: DATA[9:7], SizeH: DATA[6:0] */
    if (low & 0x80) {
        size = ((readl (&tc_udcregs->EPxSizeHA[ep]) & 0x03) << 7) |
            (low & 0x7f);
    } else {
        low = readl (&tc_udcregs->EPxSizeLB[ep]);
        size = ((readl (&tc_udcregs->EPxSizeHB[ep]) & 0x03) << 7) |
            (low & 0x7f);
    }
    return size;
}

static void
copy_descram (const void *data, int len, int *ofs)
{
    const unsigned char *p = (const unsigned char *) data;
    int i;
    if (*ofs <= UDC_DESCRAM_SIZE && *ofs + len > UDC_DESCRAM_SIZE)
        printk (KERN_ERR UDC_NAME ": too big descriptor\n");
    if (*ofs + len <= UDC_DESCRAM_SIZE) {
        for (i = 0; i < len; i++)
            writel (*p++, &tc_ramregs->DescRam[(*ofs) + i]);
    }
    *ofs += len;
}

/* *********************************************************************** */
/* Control (endpoint zero)
 */

/**
 * tc_in_ep0 - start transmit
 * @ep:
 */
static void
tc_in_ep0 (struct usb_endpoint_instance *endpoint)
{
    if (!endpoint)
        return;
    if (endpoint->tx_urb) {
        struct urb *urb = endpoint->tx_urb;
        dbg_ep0 (2, "length: %d sent %d",
            endpoint->tx_urb->actual_length, endpoint->sent);

        if ((urb->actual_length - endpoint->sent) > 0) {
            if (readl (&tc_udcregs->DataSet[0]) & 1) {
                printk (KERN_ERR UDC_NAME ": ep0 FIFO not empty\n");
                return;
            }
            endpoint->last =
                min_t (int, urb->actual_length - endpoint->sent,
                endpoint->tx_packetSize);
            tc_write_buffer (0, urb->buffer + endpoint->sent, endpoint->last);
            if (endpoint->last < endpoint->tx_packetSize) {
                dbg_ep0 (2, "EOP");
                writel (~1, &tc_udcregs->EOP);
            }
        } else {
            // XXX ZLP
            endpoint->last = 0;
            dbg_ep0 (2, "EOP");
            writel (~1, &tc_udcregs->EOP);
        }
    } else if (endpoint->last == 0 ||
        endpoint->last == endpoint->tx_packetSize) {
        // XXX ZLP
        dbg_ep0 (2, "EOP (last %d)", endpoint->last);
        endpoint->last = 0;
        writel (~1, &tc_udcregs->EOP);
    }
}
static void
tc_out_ep0 (struct usb_endpoint_instance *endpoint)
{
    printk (KERN_ERR UDC_NAME "tc_out_ep0: not supported.\n");
}

static void
tc_ep0_setup (void)
{
    struct usb_device_request *req = &ep0_urb->device_request;
    u8 *buf = (u8 *) req;
    struct usb_endpoint_instance *endpoint = ep_endpoints[0];

    if (!endpoint) {
        dbg_udc (0, "no endpoint 0");
        return;
    }
    /* store in little endian (see ep0_recv_setup) */
    buf[0] = readl (&tc_udcregs->bmReqType);
    buf[1] = readl (&tc_udcregs->bRequest);
    buf[2] = readl (&tc_udcregs->wValueL);
    buf[3] = readl (&tc_udcregs->wValueH);
    buf[4] = readl (&tc_udcregs->wIndexL);
    buf[5] = readl (&tc_udcregs->wIndexH);
    buf[6] = readl (&tc_udcregs->wLengthL);
    buf[7] = readl (&tc_udcregs->wLengthH);
    writel (0, &tc_udcregs->SetupRecv);
    dbg_ep0 (1,
        "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x",
        req->bmRequestType, req->bRequest, le16_to_cpu (req->wValue),
        le16_to_cpu (req->wIndex), le16_to_cpu (req->wLength));

    if (udc_device->device_state == STATE_DEFAULT &&
        ((req->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE
            || (req->bmRequestType & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS)
        && (readl (&tc_udcregs->Address) & 0xff)) {
        /* first Host-to-Device setup or Class setup packet */
        u8 new_address = readl (&tc_udcregs->Address) & 0xff;
        /* SET_ADDRESS are processed by hardware only */
        dbg_ep0 (1, "address assigned (%x)", new_address);
        if (udc_device->address && udc_device->address != new_address)
            printk (KERN_ERR "address changed\n");
        udc_device->address = new_address;
        usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0);
    }

    if (usbd_recv_setup (ep0_urb)) {
        dbg_ep0 (0, "usb_recv_setup failed, stalling");
        udc_stall_ep (0);
        return;
    }
    // check data direction
    if ((req->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) {
        // should we setup to receive data
        if (le16_to_cpu (req->wLength)) {
            dbg_ep0 (1, "setup to read data %d", le16_to_cpu (req->wLength));
            endpoint->rcv_urb = ep0_urb;
            endpoint->rcv_urb->actual_length = 0;
            tc_out_ep0 (endpoint);
            return;
        }
        if (req->bRequest == USB_REQ_SET_CONFIGURATION) {
            switch (req->wValue) {
            case 0:
                /* configured --> addressed */
                usbd_device_event (udc_device, DEVICE_DE_CONFIGURED, 0);
                writel (0, &tc_udcregs->UsbState);
                dbg_udc (1, "UsbState %x", readl (&tc_udcregs->UsbState));
                break;
            default:
                /* addressed --> configured */
                //usbd_device_event(udc_device, DEVICE_CONFIGURED, 0);
                writel (USBSTATE_CONFIGURED, &tc_udcregs->UsbState);
                dbg_udc (1, "UsbState %x", readl (&tc_udcregs->UsbState));
            }
        }
        dbg_ep0 (2, "EOP");
        writel (~1, &tc_udcregs->EOP);
        return;
    }
    // we should be sending data back
    // check request length, zero is not legal
    if (!le16_to_cpu (ep0_urb->device_request.wLength)) {
        dbg_ep0 (0, "wLength zero, stall");
        udc_stall_ep (0);
        return;
    }
    // check that we have some data to send back, zero should not be possible
    if (!ep0_urb->actual_length) {
        dbg_ep0 (0, "no data, stall");
        udc_stall_ep (0);
        return;
    }
    // start sending
    endpoint->tx_urb = ep0_urb;
    endpoint->sent = 0;
    endpoint->last = 0;
    tc_in_ep0 (endpoint);
}

/* *********************************************************************** */
/* Bulk OUT (recv)
 */

static void
tc_mstrd_start (u32 start, u32 len, int eop)
{
    u32 mst = readl (&tc_regs->MstSetting);
    mst &= MST_RW_BITS & ~(MST_WR_ENA | MST_EOPB_ENA | MST_EOPB_DIS);
    mst |= eop ? MST_EOPB_ENA : MST_EOPB_DIS;
    mst |= MST_RD_ENA;
    dbg_tx (2, "mstrd 0x%x 0x%x 0x%x", start, len, mst);
    writel (start, &tc_regs->MstRdStart);
    writel (start + len - 1, &tc_regs->MstRdEnd);
    writel (mst, &tc_regs->MstSetting);
}
static void
tc_mstwr_start (u32 start, u32 len)
{
    u32 mst = readl (&tc_regs->MstSetting);
    mst &= MST_RW_BITS & ~(MST_RD_ENA);
    mst |= MST_WR_ENA;
    dbg_rx (2, "mstwr 0x%x 0x%x 0x%x", start, len, mst);
    writel (start, &tc_regs->MstWrStart);
    writel (start + len - 1, &tc_regs->MstWrEnd);
    writel (mst | MST_WR_ENA, &tc_regs->MstSetting);
}

static unsigned long tc_do_task_ep;
static void tc_start_out (unsigned int ep);
static void
tc_do_task (void *data)                // runs as process
{
    unsigned long flags;
    unsigned int ep;
    if (!udc_device || udc_device->status != USBD_OK)
        return;
    for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) {
        if (test_and_clear_bit (ep, &tc_do_task_ep)) {
            local_irq_save (flags);
            while (!ep_endpoints[ep]->rcv_urb &&
                !(ep_endpoints[ep]->rcv_urb =
                    first_urb_detached_irq (&(ep_endpoints[ep]->rdy)))) {
                local_irq_restore (flags);
                /* usbd_recycle_urb will be called via device_bh */
                run_task_queue (&tq_immediate);
                local_irq_save (flags);
            }
            dbg_rx (1, "ep%d rcv urb available.", ep);
            tc_start_out (ep);
            local_irq_restore (flags);
        }
    }
}
static struct tq_struct tc_task = {
    routine:tc_do_task
};

static void
tc_start_out (unsigned int ep)
{
    struct usb_endpoint_instance *endpoint = ep_endpoints[ep];
    struct urb *urb = endpoint->rcv_urb;
    int size;

    if (!urb) {
        dbg_rx (2, "no rcv_urb");
        dbg_rx (1, "ep%d no rcv_urb available.  try again.", ep);
        set_bit (ep, &tc_do_task_ep);
        schedule_task (&tc_task);
        // XXX could we call usbd_fill_rcv() here?
        return;
    }

    if (!nodma && ep == UDC_MSTWR_ENDPOINT) {
        /* DMA endpoint */
        if (urb->actual_length == 0) {
            if (ep_dmaaddrs[ep]) {
                printk (KERN_ERR UDC_NAME ": receive DMA busy.\n");
                //return;
            }
            ep_dmaaddrs[ep] =
                pci_map_single (udc_pci_dev,
                urb->buffer, urb->buffer_length, PCI_DMA_FROMDEVICE);
            dbg_rx (2, "rx dmaaddr(%d) %x", ep, ep_dmaaddrs[ep]);
            if (!ep_dmaaddrs[ep]) {
                printk (KERN_ERR UDC_NAME ": pci_map_single failed.\n");
                usbd_rcv_complete_irq (endpoint, 0, 1);
                return;
            }
        }
        /* start DMA (master write) */
        /* check short packet without following three SOFs... */
        size = tc_read_epxsize (ep);
        if (size == endpoint->rcv_packetSize) {
            /* fulll packet: transfer continuous packets */
            size = min ((int) (urb->buffer_length - urb->actual_length),
                endpoint->rcv_transferSize);
        }
        tc_mstwr_start (ep_dmaaddrs[ep] + urb->actual_length, size);
        return;
    }
    /* check EPx_DSETA */
    if (!(readl (&tc_udcregs->DataSet[0]) & (1 << (ep * 2)))) {
        printk (KERN_ERR UDC_NAME ": ep%d FIFO empty\n", ep);
        return;
    }
    size = tc_read_epxsize (ep);
    if (size == 0) {
        printk (KERN_ERR UDC_NAME ": ep%d SIZE 0\n", ep);
        return;
    }
    dbg_rx (2, "ep%d rx size 0x%x (status %x)",
        ep, size, readl (&tc_udcregs->EPxStatus[ep]));
    tc_read_buffer (ep, urb->buffer + urb->actual_length, size);
    usbd_rcv_complete_irq (endpoint, size, 0);
}

static void
tc_end_out (unsigned int ep, int tout)
{
    struct usb_endpoint_instance *endpoint = ep_endpoints[ep];
    struct urb *urb = endpoint->rcv_urb;
    int size;
    int org_buffer_length;

    if (!ep_dmaaddrs[ep] || !urb) {
        dbg_rx (0, "not mstwr");
        return;
    }
    size = readl (&tc_regs->MstWrCurr) + 1 - ep_dmaaddrs[ep];
    dbg_rx (2, "mstwr end (size 0x%x tout %d)", size, tout);
    org_buffer_length = urb->buffer_length;
    while (size > 0) {
        int len = min (size, endpoint->rcv_packetSize);
        if (endpoint->rcv_urb != urb) {
            dbg_rx (0, "BUG: rcv_urb consumed!!!");
            break;
        }
        usbd_rcv_complete_irq (endpoint, len, 0);
        size -= len;
        if (size == 0 && len == endpoint->rcv_packetSize &&
            endpoint->rcv_urb == urb) {
            /* null packet (send current rcv_urb to function driver) */
            usbd_rcv_complete_irq (endpoint, 0, 0);
        }
    }
    if (endpoint->rcv_urb != urb) {
        pci_unmap_single (udc_pci_dev, ep_dmaaddrs[ep], org_buffer_length,
            PCI_DMA_FROMDEVICE);
        ep_dmaaddrs[ep] = 0;
    }
}

/* *********************************************************************** */
/* Bulk IN (tx)
 */

/**
 * start_in - start transmit
 * @ep:
 */
static void
tc_start_in (unsigned int ep)
{
    struct usb_endpoint_instance *endpoint = ep_endpoints[ep];
    struct urb *urb = endpoint->tx_urb;

    if (!nodma) {
        if (ep_dmaaddrs[ep]) {
            dbg_tx (0, "ep%d DMA busy", ep);
            return;
        }
        if (readl (&tc_regs->MstSetting) & MST_RD_ENA) {
            dbg_tx (0, "ep%d DMARD busy", ep);
            return;
        }
    }
    usbd_tx_complete_irq (endpoint, 0);

    /* send next urb */
    if ((urb = endpoint->tx_urb) != NULL) {
        if (!nodma && ep == UDC_MSTRD_ENDPOINT) {
            /* start DMA (master read) */
            if (endpoint->sent == 0) {
                ep_dmaaddrs[ep] =
                    pci_map_single (udc_pci_dev,
                    urb->buffer, urb->buffer_length, PCI_DMA_TODEVICE);
                dbg_tx (2, "tx dmaaddr(%d) %x", ep, ep_dmaaddrs[ep]);
                if (!ep_dmaaddrs[ep]) {
                    printk (KERN_ERR UDC_NAME ": pci_map_single failed.\n");
                    endpoint->last = urb->actual_length;
                    usbd_tx_complete_irq (endpoint, 0);
                    return;
                }
            }
            /* invoke master read for whole urb buffer */
            endpoint->last = urb->actual_length - endpoint->sent;
            if (endpoint->last == 0) {
                /* send NULL packet */
                dbg_tx (2, "ep%d EOPB", ep);
                writel ((readl (&tc_regs->
                            MstSetting) & MST_RW_BITS) | MST_RD_EOPB,
                    &tc_regs->MstSetting);
                while (readl (&tc_regs->MstSetting) & MST_RD_EOPB);
            } else {
                /* disable EOP if transfersize is multiple of packetsize */
                int eop = (endpoint->last % endpoint->tx_packetSize != 0);
                tc_mstrd_start (ep_dmaaddrs[ep] + endpoint->sent,
                    endpoint->last, eop);
            }
            return;
        }
        /* check EPx_DSETA */
        if (readl (&tc_udcregs->DataSet[0]) & (1 << (ep * 2))) {
            printk (KERN_ERR UDC_NAME ": ep%d FIFO not empty"
                " (urb length %d sent %d)\n",
                ep, urb->actual_length, endpoint->sent);
            return;
        }
        if ((urb->actual_length - endpoint->sent) > 0) {
            endpoint->last =
                min_t (int, urb->actual_length - endpoint->sent,
                endpoint->tx_packetSize);
            dbg_tx (2, "ep%d tx size 0x%x", ep, endpoint->last);
            tc_write_buffer (ep, urb->buffer + endpoint->sent,
                endpoint->last);
            if (endpoint->last < endpoint->tx_packetSize) {
                dbg_tx (2, "EOP %d", ep);
                writel (~(1 << ep), &tc_udcregs->EOP);
            }
        } else {
            // XXX ZLP
            endpoint->last = 0;
            dbg_tx (2, "EOP %d", ep);
            writel (~(1 << ep), &tc_udcregs->EOP);
        }
    } else if (nodma && (endpoint->last == endpoint->tx_packetSize)) {
        // XXX ZLP
        dbg_tx (2, "EOP %d (last %d)", ep, endpoint->last);
        endpoint->last = 0;
        writel (~(1 << ep), &tc_udcregs->EOP);
    }
}

static void
tc_end_in (unsigned int ep)
{
    struct usb_endpoint_instance *endpoint = ep_endpoints[ep];
    struct urb *urb = endpoint->tx_urb;

    if (!ep_dmaaddrs[ep] || !urb)
        return;
    dbg_tx (2, "mstrd end (size 0x%x)",
        readl (&tc_regs->MstRdCurr) + 1 - ep_dmaaddrs[ep]);
    if (endpoint->sent + endpoint->last >= urb->actual_length) {
        pci_unmap_single (udc_pci_dev, ep_dmaaddrs[ep], urb->buffer_length,
            PCI_DMA_TODEVICE);
        ep_dmaaddrs[ep] = 0;
    }
    /* complete current tx packet and start next transmit (if exists) */
    tc_start_in (ep);
}

/* *********************************************************************** */
/* Interrupt Handler
 */

static void
tc_setup_descram_if (struct usb_device_instance *device,
    int port, int nconf, int nint, int *ofs)
{
    struct usb_interface_instance *iinst;
    int nalt;
    iinst = usbd_device_interface_instance (device, port, nconf, nint);
    if (!iinst)
        return;
    for (nalt = 0; nalt < iinst->alternates; nalt++) {
        struct usb_interface_descriptor *idesc;
        struct usb_alternate_instance *ainst;
        int nclass, nep;
        ainst = usbd_device_alternate_instance (device, port,
            nconf, nint, nalt);
        if (!ainst)
            break;
        idesc = ainst->interface_descriptor;
        copy_descram (idesc, sizeof (*idesc), ofs);
        for (nclass = 0; nclass < ainst->classes; nclass++) {
            struct usb_class_descriptor *cldesc;
            cldesc = usbd_device_class_descriptor_index (device,
                port, nconf, nint, nalt, nclass);
            if (!cldesc)
                break;
            copy_descram (cldesc, cldesc->descriptor.generic.bFunctionLength,
                ofs);
        }
        for (nep = 0; nep < ainst->endpoints; nep++) {
            struct usb_endpoint_descriptor *edesc;
            edesc = usbd_device_endpoint_descriptor_index (device,
                port, nconf, nint, nalt, nep);
            if (!edesc)
                break;
            copy_descram (edesc, sizeof (*edesc), ofs);
        }
    }
}

static void
tc_setup_descram_str (int *ofs)
{
    u8 bLength[4] = { 0, 0, 0, 0 };
    copy_descram (&bLength, 4, ofs);
}

static void
tc_setup_descram (void)
{
    struct usb_device_descriptor *ddesc;
    int ofs = 0, nconf;
    int i;

    ddesc = usbd_device_device_descriptor (udc_device, 0);
    if (!ddesc)
        return;
    copy_descram (ddesc, sizeof (*ddesc), &ofs);
    for (nconf = 0; nconf < ddesc->bNumConfigurations; nconf++) {
        struct usb_configuration_descriptor *cdesc;
        int nint;
        cdesc = usbd_device_configuration_descriptor (udc_device, 0, nconf);
        if (!cdesc)
            break;
        copy_descram (cdesc, sizeof (*cdesc), &ofs);
        for (nint = 0; nint < cdesc->bNumInterfaces; nint++) {
            tc_setup_descram_if (udc_device, 0, nconf, nint, &ofs);
        }
    }

    tc_setup_descram_str (&ofs);

    for (i = ofs; i < UDC_DESCRAM_SIZE; i++)
        writel (0, &tc_ramregs->DescRam[i]);

    for (i = 0; i < ofs; i += 8)
        dbg_udc (2, "Descram: %02x %02x %02x %02x %02x %02x %02x %02x",
            readl (&tc_ramregs->DescRam[i + 0]),
            readl (&tc_ramregs->DescRam[i + 1]),
            readl (&tc_ramregs->DescRam[i + 2]),
            readl (&tc_ramregs->DescRam[i + 3]),
            readl (&tc_ramregs->DescRam[i + 4]),
            readl (&tc_ramregs->DescRam[i + 5]),
            readl (&tc_ramregs->DescRam[i + 6]),
            readl (&tc_ramregs->DescRam[i + 7]));
}

static void
tc_power_detected (void)
{
    int i;
    u8 bcs, single;

    if (readl (&tc_regs->PowerDetect) & PW_DETECT) {
        /* power detected */
        dbg_udc (1, "Power detected");
        usbd_device_event_irq (udc_device, DEVICE_HUB_CONFIGURED, 0);

        /* assert reset */
        writel (0, &tc_regs->PowerDetect);
        readl (&tc_regs->IntStatus);        /* wbflush */
        udelay (1);
        /* deassert reset (USB_RESET still disabled) */
        writel (MST_EOPB_ENA | MST_TIMEOUT_ENA, &tc_regs->MstSetting);
        writel (PW_RESETB, &tc_regs->PowerDetect);
        writel (STDREQ_S_CONFIG | STDREQ_G_CONFIG | STDREQ_G_DESCRIPT,
            &tc_udcregs->StdReqMode);
        /* EP[2:1]:DMA access, EP[3]:CPU access (single) */
        bcs = single = 0;
        for (i = 1; i < 4; i++) {
            single |= 0x11 << i;
            bcs |= ((i == 3 || nodma) ? 0x11 /*CPU*/ : 0x10 /*DMA*/) << i;
        }
        writel (single, &tc_udcregs->EPxSingle[0]);
        writel (bcs, &tc_udcregs->EPxBCS[0]);
        tc_setup_descram ();
        /* enable USB_RESET detection */
        writel (0, &tc_udcregs->UsbReady);
    } else {
        /* power down */
        dbg_udc (1, "Power down");
        usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
        usbd_device_event_irq (udc_device, DEVICE_POWER_INTERRUPTION, 0);
        usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, 0);
    }
}

/**
 * int_hndlr - interrupt handler
 *
 */
static void
tc_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
{
    int count, i;
    u32 stat, mask;
    udc_interrupts++;

    for (count = 0; count < 10; count++) {
        stat = readl (&tc_regs->IntStatus);
        mask = readl (&tc_regs->IntEnable);
        if ((stat & mask) == 0)
            break;
        dbg_udc (3, "stat %x mask %x", stat, mask);
        stat &= mask;
        if (stat & INT_ERR) {
            writel (~INT_ERR, &tc_regs->IntStatus);
            printk (KERN_ERR UDC_NAME ": ERR interrupt.\n");
            continue;
        }
        if (stat & INT_SYSERROR) {
            writel (~INT_SYSERROR, &tc_regs->IntStatus);
            printk (KERN_ERR UDC_NAME ": SYSERROR interrupt.\n");
            continue;
        }
        if (stat & INT_PWRDETECT) {
            writel (~INT_PWRDETECT, &tc_regs->IntStatus);
            if (!udc_device)
                continue;
            //udc_cable_event();
            tc_power_detected ();
            continue;
        }
        if (stat & INT_USBRESET) {
            writel (~INT_USBRESET, &tc_regs->IntStatus);
            if (!udc_device)
                continue;
            /* USBRESET received */
            usbd_device_event_irq (udc_device, DEVICE_RESET, 0);
            /* enable pull-up */
            dbg_udc (1, "Enabling pullup");
            writel (PW_RESETB | PW_PLLUPENB, &tc_regs->PowerDetect);
            continue;
        }
        if (stat & INT_SUSPEND) {
            writel (~INT_SUSPEND, &tc_regs->IntStatus);
            if (!udc_device)
                continue;
#if 0
            if (udc_device->device_state == STATE_UNKNOWN ||
                udc_device->device_state == STATE_INIT ||
                udc_device->device_state == STATE_ATTACHED)
                continue;
#endif
            if (readl (&tc_udcregs->EPxStatus[0]) & EPxSTATUS_SUSPEND) {
                dbg_udc (1, "Suspend");
                usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0);
            } else {
                dbg_udc (1, "Resume");
                usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0);
            }
            continue;
        }
        if (stat & INT_SETUP) {
            dbg_udc (1, "Setup");
            writel (~INT_SETUP, &tc_regs->IntStatus);
            tc_ep0_setup ();
            continue;
        }
        if (stat & INT_ENDPOINT0) {
            writel (~INT_ENDPOINT0, &tc_regs->IntStatus);
            usbd_tx_complete_irq (ep_endpoints[0], 0);
            tc_in_ep0 (ep_endpoints[0]);
            // XXX how about control write?
            continue;
        }
        if (stat & INT_MSTWRTMOUT) {
            writel (~INT_MSTWRTMOUT, &tc_regs->IntStatus);
            tc_end_out (UDC_MSTWR_ENDPOINT, 1);
            continue;
        }
        if (stat & INT_MSTWREND) {
            writel (~INT_MSTWREND, &tc_regs->IntStatus);
            tc_end_out (UDC_MSTWR_ENDPOINT, 0);
            continue;
        }
        if (stat & INT_MSTWRSET) {
            writel (~INT_MSTWRSET, &tc_regs->IntStatus);
            /* start master write */
            tc_start_out (UDC_MSTWR_ENDPOINT);
            continue;
        }
        if (stat & INT_MSTRDEND) {
            writel (~INT_MSTRDEND, &tc_regs->IntStatus);
            tc_end_in (UDC_MSTRD_ENDPOINT);
            continue;
        }
        for (i = 1; i < 4; i++) {
            if (stat & INT_EPxDATASET (i)) {
                writel (~INT_EPxDATASET (i), &tc_regs->IntStatus);
                if (!ep_endpoints[i])
                    continue;
                if (ep_endpoints[i]->endpoint_address & USB_DIR_IN) {
                    tc_start_in (i);
                } else {
                    tc_start_out (i);
                }
            }
        }
    }
}


/* *********************************************************************** */

static int __devinit
udc_probe (struct pci_dev *pdev, const struct pci_device_id *ent)
{
    void *mmio;

    if (udc_pci_dev) {
        /* multiple instance is not supported */
        return -ENODEV;
    }
    /* make sure PCI base addr 0 is MMIO */
    if (!(pci_resource_flags (pdev, 0) & IORESOURCE_MEM)) {
        printk (KERN_ERR UDC_NAME
            ": region #0 not an MMIO resource, aborting\n");
        return -ENODEV;
    }
    if (request_irq (pdev->irq, &tc_int_hndlr,
            SA_SHIRQ | SA_SAMPLE_RANDOM, UDC_NAME, pdev)) {
        printk (KERN_ERR UDC_NAME " could not request irq %d\n", pdev->irq);
        return -EBUSY;
    }
    if (pci_request_regions (pdev, UDC_NAME)) {
        printk (KERN_ERR UDC_NAME " could not request regions\n");
        free_irq (pdev->irq, pdev);
        return -EBUSY;
    }
    pci_set_master (pdev);

    mmio = ioremap (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0));
    if (mmio == NULL) {
        printk (KERN_ERR UDC_NAME " could not remap 0x%08lx(0x%lx)\n",
            pci_resource_start (pdev, 0), pci_resource_len (pdev, 0));
        free_irq (pdev->irq, pdev);
        pci_release_regions (pdev);
        return -EBUSY;
    }
    tc_regs = mmio;
    tc_udcregs = (struct tc_udc_udcregs *) (mmio + 0x200);
    tc_ramregs = (struct tc_udc_ramregs *) (mmio + 0x800);

    printk (KERN_DEBUG UDC_NAME " found at %p irq %d (%s)\n",
        mmio, pdev->irq, nodma ? "PIO" : "DMA");

    udc_pci_dev = pdev;
    return 0;
}

static void __devexit
udc_remove (struct pci_dev *pdev)
{
    if (tc_regs)
        iounmap (tc_regs);
    pci_release_regions (pdev);
    free_irq (pdev->irq, pdev);
    tc_regs = NULL;
    tc_udcregs = NULL;
    tc_ramregs = NULL;
    udc_pci_dev = NULL;
}


static struct pci_device_id udc_pci_tbl[] __devinitdata = {
    {PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_USBD, PCI_ANY_ID,
            PCI_ANY_ID, 0, 0, 0},
    {0,}
};

MODULE_DEVICE_TABLE (pci, udc_pci_tbl);
static struct pci_driver udc_pci_driver = {
    name:UDC_NAME,
    id_table:udc_pci_tbl,
    probe:udc_probe,
    remove:__devexit_p (udc_remove),
};


/* *********************************************************************** */
/*
 * Start of public functions.
 */

/**
 * udc_start_in_irq - start transmit
 * @eendpoint: endpoint instance
 *
 * Called by bus interface driver to see if we need to start a data transmission.
 */
void
udc_start_in_irq (struct usb_endpoint_instance *endpoint)
{
    int i;
    for (i = 1; i < UDC_MAX_ENDPOINTS; i++)
        if (ep_endpoints[i] == endpoint)
            tc_start_in (i);
}

/**
 * udc_init - initialize
 *
 * Return non-zero if we cannot see device.
 **/

int
udc_init (void)
{
    return pci_module_init (&udc_pci_driver);
}


#if 0                                /* not used */
/**
 * udc_start_in - start transmit
 * @eendpoint: endpoint instance
 *
 * Called by bus interface driver to see if we need to start a data transmission.
 */
void
udc_start_in (struct usb_endpoint_instance *endpoint)
{
}
#endif


/**
 * udc_stall_ep - stall endpoint
 * @ep: physical endpoint
 *
 * Stall the endpoint.
 */
void
udc_stall_ep (unsigned int ep)
{
    if (ep < UDC_MAX_ENDPOINTS) {
        writel (COMMAND_EP (ep) | COMMAND_STALL, &tc_udcregs->Command);
    }
}


/**
 * udc_reset_ep - reset endpoint
 * @ep: physical endpoint
 * reset the endpoint.
 *
 * returns : 0 if ok, -1 otherwise
 */
void
udc_reset_ep (unsigned int ep)
{
    if (ep < UDC_MAX_ENDPOINTS) {
        writel (COMMAND_EP (ep) | COMMAND_RESET, &tc_udcregs->Command);
    }
}


#if 0                                /* not used */
/**
 * udc_endpoint_halted - is endpoint halted
 * @ep:
 *
 * Return non-zero if endpoint is halted
 */
int
udc_endpoint_halted (unsigned int ep)
{
    return 0;
}
#endif


/**
 * udc_set_address - set the USB address for this device
 * @address:
 *
 * Called from control endpoint function after it decodes a set address setup packet.
 */
void
udc_set_address (unsigned char address)
{
    // address cannot be setup until ack received
    /* tc_udcregs->Address register is read-only */
}

/**
 * udc_serial_init - set a serial number if available
 */
int __init
udc_serial_init (struct usb_bus_instance *bus)
{
    return -EINVAL;
}

/* *********************************************************************** */

/**
 * udc_max_endpoints - max physical endpoints 
 *
 * Return number of physical endpoints.
 */
int
udc_max_endpoints (void)
{
    return UDC_MAX_ENDPOINTS;
}


/**
 * udc_check_ep - check logical endpoint 
 * @lep:
 *
 * Return physical endpoint number to use for this logical endpoint or zero if not valid.
 */
int
udc_check_ep (int logical_endpoint, int packetsize)
{
    int ep = logical_endpoint & 0xf;
    if (ep >= UDC_MAX_ENDPOINTS || packetsize > 64)
        return 0;
    /* ASSUMPTION: MstSetting.MST_connection == 0 */
    /* MSTWR endpoint must be OUT */
    if (ep == UDC_MSTWR_ENDPOINT && (logical_endpoint & USB_DIR_IN))
        return 0;
    /* MSTRD endpoint must be IN */
    if (ep == UDC_MSTRD_ENDPOINT && !(logical_endpoint & USB_DIR_IN))
        return 0;
    return ep;
}


/**
 * udc_set_ep - setup endpoint 
 * @ep:
 * @endpoint:
 *
 * Associate a physical endpoint with endpoint_instance
 */
void
udc_setup_ep (struct usb_device_instance *device, unsigned int ep,
    struct usb_endpoint_instance *endpoint)
{
    if (ep < UDC_MAX_ENDPOINTS) {
        ep_endpoints[ep] = endpoint;
        dbg_udc (1, "endpoint %d %p", ep, endpoint);
        // ep0
        if (ep == 0) {
        }
        // IN
        else if (endpoint->endpoint_address & 0x80) {
            int mode = endpoint->tx_attributes & USB_ENDPOINT_XFERTYPE_MASK;
            int payload = 0;
            while (8 << payload < endpoint->tx_packetSize)
                payload++;
            writel ((payload << 3) | (mode << 1) | 1,
                &tc_udcregs->EPxMode[ep]);
            dbg_udc (1, "EP%dMode %x", ep, readl (&tc_udcregs->EPxMode[ep]));
        }
        // OUT
        else if (endpoint->endpoint_address) {
            int mode = endpoint->rcv_attributes & USB_ENDPOINT_XFERTYPE_MASK;
            int payload = 0;
            while (8 << payload < endpoint->rcv_packetSize)
                payload++;
            writel ((payload << 3) | (mode << 1) | 0,
                &tc_udcregs->EPxMode[ep]);
            if (nodma) {
                int payload_size = (8 << payload);
                if (endpoint->rcv_transferSize % payload_size == 0 &&
                    endpoint->rcv_transferSize > payload_size)
                    endpoint->rcv_transferSize = payload_size;
                dbg_rx (1, "ep%d rcv_transferSize %d", ep, payload_size);
            }
            usbd_fill_rcv (device, endpoint, 64);        /* increased for storage */
            endpoint->rcv_urb = first_urb_detached (&endpoint->rdy);
            dbg_udc (1, "EP%dMode %x", ep, readl (&tc_udcregs->EPxMode[ep]));
        }
    }
}


/**
 * udc_disable_ep - disable endpoint
 * @ep:
 *
 * Disable specified endpoint 
 */
void
udc_disable_ep (unsigned int ep)
{
    if (ep < UDC_MAX_ENDPOINTS) {
        struct usb_endpoint_instance *endpoint;

        if ((endpoint = ep_endpoints[ep])) {
            dbg_udc (1, "disabling endpoint %d %p", ep, endpoint);
            ep_endpoints[ep] = NULL;
            usbd_flush_ep (endpoint);
        }
    }
}

/* *********************************************************************** */

/**
 * udc_connected - is the USB cable connected
 *
 * Return non-zeron if cable is connected.
 */
int
udc_connected ()
{
    return readl (&tc_regs->PowerDetect) & PW_DETECT;
}


/**
 * udc_connect - enable pullup resistor
 *
 * Turn on the USB connection by enabling the pullup resistor.
 */
void
udc_connect (void)
{
    /* pullup will enabled on INT_USBRESET interrupt */
}


/**
 * udc_disconnect - disable pullup resistor
 *
 * Turn off the USB connection by disabling the pullup resistor.
 */
void
udc_disconnect (void)
{
    dbg_udc (1, "Disabling pullup");
    writel (PW_RESETB, &tc_regs->PowerDetect);
}

/* *********************************************************************** */

/**
 * udc_all_interrupts - enable interrupts
 *
 * Switch on UDC interrupts.
 *
 */
void
udc_all_interrupts (struct usb_device_instance *device)
{
    // set interrupt mask
    u32 val = INT_PWRDETECT | INT_SYSERROR |
        INT_ERR | INT_SETUP |
        INT_EPxDATASET (3) | INT_ENDPOINT0 | INT_USBRESET | INT_SUSPEND;
    /* ASSUMPTION: MstSetting.MST_connection == 0 */
    /* endpoint 1 must be OUT */
    if (nodma)
        val |= INT_EPxDATASET (1) | INT_EPxDATASET (2);
    else
        val |= INT_MSTWRSET | INT_MSTWREND | INT_MSTWRTMOUT | INT_MSTRDEND;
    writel (val, &tc_regs->IntEnable);
    dbg_udc (1, "udc_enable_interrupts");
}


/**
 * udc_suspended_interrupts - enable suspended interrupts
 *
 * Switch on only UDC resume interrupt.
 *
 */
void
udc_suspended_interrupts (struct usb_device_instance *device)
{
    dbg_udc (1, "udc_suspended_interrupts");
    // set interrupt mask
    writel (INT_SUSPEND | INT_PWRDETECT | INT_USBRESET, &tc_regs->IntEnable);
}


/**
 * udc_disable_interrupts - disable interrupts.
 *
 * switch off interrupts
 */
void
udc_disable_interrupts (struct usb_device_instance *device)
{
    printk (KERN_DEBUG "udc_disable_interrupts:\n");
    // reset interrupt mask
    writel (0, &tc_regs->IntEnable);
    tc_do_task_ep = 0;
}

/* *********************************************************************** */

/**
 * udc_ep0_packetsize - return ep0 packetsize
 */
int
udc_ep0_packetsize (void)
{
    return EP0_PACKETSIZE;
}

/**
 * udc_enable - enable the UDC
 *
 * Switch on the UDC
 */
void
udc_enable (struct usb_device_instance *device)
{
    int i;

    dbg_udc (1, "enable");
    // save the device structure pointer
    udc_device = device;

    // ep0 urb
    if (!ep0_urb) {
        ep0_urb =
            usbd_alloc_urb (device, device->function_instance_array, 0, 512);
        if (!ep0_urb) {
            printk (KERN_ERR "ep0_enable: usbd_alloc_urb failed\n");
        }
    } else {
        printk (KERN_ERR "udc_enable: ep0_urb already allocated\n");
    }

    for (i = 0; i < UDC_MAX_ENDPOINTS; i++)
        ep_dmaaddrs[i] = 0;
    // enable UDC

    /* reset and initialize */
    writel (0, &tc_regs->PowerDetect);
    writel (0, &tc_regs->IntEnable);
    /* need to read tc registers... */
    writel (PW_RESETB, &tc_regs->PowerDetect);

    tc_power_detected ();
}


/**
 * udc_disable - disable the UDC
 *
 * Switch off the UDC
 */
void
udc_disable (void)
{
    dbg_udc (1, "disable");
    // disable UDC
    writel (0, &tc_regs->PowerDetect);
    writel (0, &tc_regs->IntEnable);
    tc_do_task_ep = 0;
    /* need to read tc registers... */
    writel (PW_RESETB, &tc_regs->PowerDetect);

    // reset device pointer
    udc_device = NULL;

    // ep0 urb
    if (ep0_urb) {
        usbd_dealloc_urb (ep0_urb);
        ep0_urb = NULL;
    }
}


/**
 * udc_startup - allow udc code to do any additional startup
 */
void
udc_startup_events (struct usb_device_instance *device)
{
    usbd_device_event (device, DEVICE_INIT, 0);
    usbd_device_event (device, DEVICE_CREATE, 0);
#if 0
    usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0);
    usbd_device_event (device, DEVICE_RESET, 0);        // XXX should be done from device event
#endif
}


/* *********************************************************************** */

/**
 * udc_name - return name of USB Device Controller
 */
char *
udc_name (void)
{
    return UDC_NAME;
}

/**
 * udc_request_udc_irq - request UDC interrupt
 *
 * Return non-zero if not successful.
 */
int
udc_request_udc_irq ()
{
    return 0;                        /* request_irq is called in udc_probe */
}

/**
 * udc_request_cable_irq - request Cable interrupt
 *
 * Return non-zero if not successful.
 */
int
udc_request_cable_irq ()
{
    return -1;
}

/**
 * udc_request_udc_io - request UDC io region
 *
 * Return non-zero if not successful.
 */
int
udc_request_io ()
{
    return 0;                        /* request_region is called in udc_probe */
}

/**
 * udc_release_udc_irq - release UDC irq
 */
void
udc_release_udc_irq ()
{
}

/**
 * udc_release_cable_irq - release Cable irq
 */
void
udc_release_cable_irq ()
{
}

/**
 * udc_release_release_io - release UDC io region
 */
void
udc_release_io ()
{
    if (tc_task.sync) {
        flush_scheduled_tasks ();
    }
    pci_unregister_driver (&udc_pci_driver);
}


/**
 * udc_regs - dump registers
 *
 * Dump registers with printk
 */
void
udc_regs (void)
{
    printk ("\n");
}

/*
 * Local variables:
 * c-basic-offset: 4
 * End:
 */