⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usb_ohci.c

📁 基于s3c2440的U盘读写程序
💻 C
📖 第 1 页 / 共 4 页
字号:

/*
 * URB OHCI HCD (Host Controller Driver) for USB on the S3C2400.
 *
 * (C) Copyright 2003
 * Gary Jennejohn, DENX Software Engineering <gj@denx.de>
 *
 * Note: Much of this code has been derived from Linux 2.4
 * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
 * (C) Copyright 2000-2002 David Brownell
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * 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
 *
 */
/*
 * IMPORTANT NOTES
 * 1 - you MUST define LITTLEENDIAN in the configuration file for the
 *     board or this driver will NOT work!
 * 2 - this driver is intended for use with USB Mass Storage Devices
 *     (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes!
 */
#include "s3c2410.h"
#include "usb.h"
#include "usb_ohci.h"
#define OHCI_USE_NPS		/* force NoPowerSwitching mode */
/* For initializing controller (mask in an HCFS mode too) */
#define	OHCI_CONTROL_INIT \
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
#define readl(a) (*((vu_long *)(a)))
#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a))
/*
#define min_t(type,x,y) (\
{\
    type __x = (x); type __y = (y); __x < __y ? __x: __y; \
}\
)
*/
unsigned int min_t(unsigned int x,unsigned int y)
{
	return x<y?x:y;
}
int min_t_int(int x,int y)
{
	return x<y?x:y;
}

#define mdelay(n)	{unsigned long msec=(n); while (msec--) udelay(1000);}
#define du_debug
//#undef DEBUG
#define DEBUG
#ifdef DEBUG
void DbgPrintf(char *fmt,...){}
//#define dbg(format, arg...) s_UartPrint("DEBUG: " format "\n",arg...)
void dbg(char *fmt,...){}
#else
#define dbg(format, arg...) do {} while(0)
#endif /* DEBUG */
//#define err(format, arg...) s_UartPrint("ERROR: " format "\n", ## arg)
void err(char *fmt,...){}
//#define	err	s_UartPrint
//#undef SHOW_INFO
#define SHOW_INFO
#ifdef SHOW_INFO
//#define info(format, arg...) s_UartPrint("INFO: " format "\n", ## arg)
void info(char *fmt,...){}
//#define info err
#else
#define info(format, arg...) do {} while(0)
#endif
#define m16_swap(x) swap_16(x)
#define m32_swap(x) swap_32(x)
/* global ohci_t */
static ohci_t gohci;
/* this must be aligned to a 256 byte boundary */
static __align(256) struct  ohci_hcca ghcca[1];//1         
/* a pointer to the aligned storage */
struct ohci_hcca *phcca;
/* this allocates EDs for all possible endpoints */
__align(16) struct ohci_device ohci_dev;
/* urb_priv */
urb_priv_t urb_priv;
/* RHSC flag */
int got_rhsc;
/* device which was disconnected */
struct usb_device *devgone;
/* flag guarding URB transation */
int urb_finished = 0;
int hc_stat=0xff;
/*-------------------------------------------------------------------------*/
/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
 * The erratum (#4) description is incorrect.  AMD's workaround waits
 * till some bits (mostly reserved) are clear; ok for all revs.
 */
#define OHCI_QUIRK_AMD756 0xabcd
/*
#define read_roothub(hc, register, mask) (\
{    \
    U32 temp = readl (&hc->regs->roothub.register); \
    if (hc->flags & OHCI_QUIRK_AMD756) \
    while (temp & mask) \
    temp = readl (&hc->regs->roothub.register); \
    return	temp; \
}\
)
*/
static U32 roothub_a (struct ohci *hc)
{
    //return	read_roothub (hc, a, 0xfc0fe000);
	U32 temp = readl (&hc->regs->roothub.a);
	U32 mask=0xfc0fe000;
    if (hc->flags & OHCI_QUIRK_AMD756)
    {
		while (temp & mask)
		{ 	
			temp = readl (&hc->regs->roothub.a);
		}
	} 
    return	temp;
}


static __inline U32 roothub_b (struct ohci *hc)
{
    return readl (&hc->regs->roothub.b); 
}


static __inline U32 roothub_status (struct ohci *hc)
{
    return readl (&hc->regs->roothub.status); 
}


static U32 roothub_portstatus (struct ohci *hc, int i)
{
    //return read_roothub (hc, portstatus [i], 0xffe0fce0);
	U32 temp = readl (&hc->regs->roothub.portstatus[i]);
	U32 mask=0xffe0fce0;
	if (hc->flags & OHCI_QUIRK_AMD756)
		while (temp & mask) 
			temp = readl (&hc->regs->roothub.portstatus[i]); 
    return	temp;
}


/* forward declaration */
static int hc_interrupt (void);
static void
td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer,
int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval);
/*-------------------------------------------------------------------------*
 * URB support functions
 *-------------------------------------------------------------------------*/
/* free HCD-private data associated with this URB */
static void urb_free_priv (urb_priv_t * urb)
{
    int		i;
    int		last;
    struct td	* td;
    last = urb->length - 1;
    if (last >= 0) 
    {
        for (i = 0; i <= last; i++) 
        {
            td = urb->td[i];
            if (td) 
            {
                td->usb_dev = NULL;
                urb->td[i] = NULL;
            }
        }
    }
}


/*-------------------------------------------------------------------------*/
#ifdef DEBUG
static int sohci_get_current_frame_number (struct usb_device * dev);
/* debug| print the main components of an URB
 * small: 0) header + data packets 1) just header */
static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer,
int transfer_len, struct devrequest * setup, char * str, int small)
{
    urb_priv_t * purb = &urb_priv;
    dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx",
    str,
    sohci_get_current_frame_number (dev),
    usb_pipedevice (pipe),
    usb_pipeendpoint (pipe),
    usb_pipeout (pipe)? 'O': 'I',
usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
    (usb_pipecontrol (pipe)? "CTRL": "BULK"),
    purb->actual_length,
    transfer_len, dev->status);
}


/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/
void ep_print_int_eds (ohci_t *ohci, char * str) 
{
    int i, j;
    U32 * ed_p;
    for (i= 0; i < 32; i++) 
    {
        j = 5;
        ed_p = &(ohci->hcca->int_table [i]);
        if (*ed_p == 0)
        continue;
        dbg (__FILE__ ": %s branch int %2d(%2x):", str, i, i);
        while (*ed_p != 0 && j--) 
        {
            ed_t *ed = (ed_t *)m32_swap(ed_p);
            dbg(" ed: %4x;", ed->hwINFO);
            ed_p = &ed->hwNextED;
        }
    }
}


static void ohci_dump_intr_mask (char *label, U32 mask)
{
    dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
    label,
    mask,
    (mask & OHCI_INTR_MIE) ? " MIE" : "",
    (mask & OHCI_INTR_OC) ? " OC" : "",
    (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
    (mask & OHCI_INTR_FNO) ? " FNO" : "",
    (mask & OHCI_INTR_UE) ? " UE" : "",
    (mask & OHCI_INTR_RD) ? " RD" : "",
    (mask & OHCI_INTR_SF) ? " SF" : "",
    (mask & OHCI_INTR_WDH) ? " WDH" : "",
    (mask & OHCI_INTR_SO) ? " SO" : ""
    );
}


static void maybe_print_eds (char *label, U32 value)
{
    ed_t *edp = (ed_t *)value;
    if (value) 
    {
        dbg ("%s %08x", label, value);
        dbg ("%08x", edp->hwINFO);
        dbg ("%08x", edp->hwTailP);
        dbg ("%08x", edp->hwHeadP);
        dbg ("%08x", edp->hwNextED);
    }
}


static char * hcfs2string (int state)
{
    switch (state) 
    {
        case OHCI_USB_RESET:	return "reset";
        case OHCI_USB_RESUME:	return "resume";
        case OHCI_USB_OPER:	return "operational";
        case OHCI_USB_SUSPEND:	return "suspend";
    }
    return "?";
}


/* dump control and status registers */
static void ohci_dump_status (ohci_t *controller)
{
    struct ohci_regs	*regs = controller->regs;
    U32			temp;
    temp = readl (&regs->revision) & 0xff;
    if (temp != 0x10)
    dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
    temp = readl (&regs->control);
    dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
    (temp & OHCI_CTRL_RWE) ? " RWE" : "",
    (temp & OHCI_CTRL_RWC) ? " RWC" : "",
    (temp & OHCI_CTRL_IR) ? " IR" : "",
    hcfs2string (temp & OHCI_CTRL_HCFS),
    (temp & OHCI_CTRL_BLE) ? " BLE" : "",
    (temp & OHCI_CTRL_CLE) ? " CLE" : "",
    (temp & OHCI_CTRL_IE) ? " IE" : "",
    (temp & OHCI_CTRL_PLE) ? " PLE" : "",
    temp & OHCI_CTRL_CBSR
    );
    temp = readl (&regs->cmdstatus);
    dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
    (temp & OHCI_SOC) >> 16,
    (temp & OHCI_OCR) ? " OCR" : "",
    (temp & OHCI_BLF) ? " BLF" : "",
    (temp & OHCI_CLF) ? " CLF" : "",
    (temp & OHCI_HCR) ? " HCR" : ""
    );
    ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
    ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
    maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
    maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
    maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
    maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
    maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
    maybe_print_eds ("donehead", readl (&regs->donehead));
}


static void ohci_dump_roothub (ohci_t *controller, int verbose)
{
    U32			temp, ndp, i;
    temp = roothub_a (controller);
    ndp = (temp & RH_A_NDP);
    if (verbose) 
    {
        dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
        ((temp & RH_A_POTPGT) >> 24) & 0xff,
        (temp & RH_A_NOCP) ? " NOCP" : "",
        (temp & RH_A_OCPM) ? " OCPM" : "",
        (temp & RH_A_DT) ? " DT" : "",
        (temp & RH_A_NPS) ? " NPS" : "",
        (temp & RH_A_PSM) ? " PSM" : "",
        ndp
        );
        temp = roothub_b (controller);
        dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
        temp,
        (temp & RH_B_PPCM) >> 16,
        (temp & RH_B_DR)
        );
        temp = roothub_status (controller);
        dbg ("roothub.status: %08x%s%s%s%s%s%s",
        temp,
        (temp & RH_HS_CRWE) ? " CRWE" : "",
        (temp & RH_HS_OCIC) ? " OCIC" : "",
        (temp & RH_HS_LPSC) ? " LPSC" : "",
        (temp & RH_HS_DRWE) ? " DRWE" : "",
        (temp & RH_HS_OCI) ? " OCI" : "",
        (temp & RH_HS_LPS) ? " LPS" : ""
        );
    }
    for (i = 0; i < ndp; i++) 
    {
        temp = roothub_portstatus (controller, i);
        dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
        i,
        temp,
        (temp & RH_PS_PRSC) ? " PRSC" : "",
        (temp & RH_PS_OCIC) ? " OCIC" : "",
        (temp & RH_PS_PSSC) ? " PSSC" : "",
        (temp & RH_PS_PESC) ? " PESC" : "",
        (temp & RH_PS_CSC) ? " CSC" : "",
        (temp & RH_PS_LSDA) ? " LSDA" : "",
        (temp & RH_PS_PPS) ? " PPS" : "",
        (temp & RH_PS_PRS) ? " PRS" : "",
        (temp & RH_PS_POCI) ? " POCI" : "",
        (temp & RH_PS_PSS) ? " PSS" : "",
        (temp & RH_PS_PES) ? " PES" : "",
        (temp & RH_PS_CCS) ? " CCS" : ""
        );
    }
}


static void ohci_dump (ohci_t *controller, int verbose)
{
    dbg ("OHCI controller usb-%s state", controller->slot_name);
    /* dumps some of the state we know about */
    ohci_dump_status (controller);
    if (verbose)
    ep_print_int_eds (controller, "hcca");
    dbg ("hcca frame #%04x", controller->hcca->frame_no);
    ohci_dump_roothub (controller, 1);
}


#endif /* DEBUG */
/*-------------------------------------------------------------------------*
 * Interface functions (URB)
 *-------------------------------------------------------------------------*/
/* get a transfer request */
int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len, struct devrequest *setup, int interval)
{
    ohci_t *ohci;
    ed_t * ed;
    urb_priv_t *purb_priv;
    int i, size = 0;
    ohci = &gohci;
    /* when controller's hung, permit only roothub cleanup attempts
    	 * such as powering down ports */
    if (ohci->disabled) 
    {
        err("sohci_submit_job: EPIPE");
        return -1;
    }
    /* if we have an unfinished URB from previous transaction let's
    	 * fail and scream as quickly as possible so as not to corrupt
    	 * further communication */
    if (!urb_finished) 
    {
        err("sohci_submit_job: URB NOT FINISHED");
        return -1;
    }
    /* we're about to begin a new transaction here so mark the URB unfinished */
    urb_finished = 0;
    /* every endpoint has a ed, locate and fill it */
    if (!(ed = ep_add_ed (dev, pipe))) 
    {
        err("sohci_submit_job: ENOMEM");
        return -1;
    }
    /* for the private part of the URB we need the number of TDs (size) */
    switch (usb_pipetype (pipe)) 
    {
    case PIPE_BULK:	
        /* one TD for every 4096 Byte */
        size = (transfer_len - 1) / 4096 + 1;
        break;
    case PIPE_CONTROL: 
        /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
    size = (transfer_len == 0)? 2:
        (transfer_len - 1) / 4096 + 3;
        break;
    }
    if (size >= (N_URB_TD - 1)) 
    {
        err("need %d TDs, only have %d", size, N_URB_TD);
        return -1;
    }
    purb_priv = &urb_priv;
    purb_priv->pipe = pipe;
    /* fill the private part of the URB */
    purb_priv->length = size;
    purb_priv->ed = ed;
    purb_priv->actual_length = 0;
    /* allocate the TDs */
    /* note that td[0] was allocated in ep_add_ed */
    for (i = 0; i < size; i++) 
    {
        purb_priv->td[i] = td_alloc (dev);
        if (!purb_priv->td[i]) 
        {
            purb_priv->length = i;
            urb_free_priv (purb_priv);
            err("sohci_submit_job: ENOMEM");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -