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

📄 m8xxhci.c

📁 linux2.4.20下的针对三星公司的s3c2410的usb模块驱动代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * MPC8xx Host Controller Interface driver for USB. * Brad Parker, brad@heeltoe.com * * designed for the EmbeddedPlanet RPX lite board * (C) Copyright 2000 Embedded Planet * http://www.embeddedplanet.com * * 8/2001 brad@heeltoe.com * fixed isr's to be locked; more bug fixes * * 2/2001 brad@heeltoe.com * added support for Brightstar IPEengine * added support for root hub * * 3/2001 brad@parker.boston.ma.us * integrate Roman Weissgaerber's weissg@vienna.at fixes & changes * * The MPC850/832 processors don't provide either the OHCI or UHCI * interface.  They have, however, all the basics needed to be a host * controller. * * [except for the sending of the 1ms SOF frames; for that we need a *  microcode path] * * In the USB world (high speed) everything happens inside 1ms frames.  The * 1ms frame is the basic scheduling element. * * A controller needs to schedule frames being send on the bus according * to this basic time line: * * 0                             -- ms --                                   1 * +------------------------------------------------------------------------+ * |SOF|   isochronous xfers | interrupt xfers   |control xfers |bulk xfers | * +------------------------------------------------------------------------+ * * Isochronous and interrupt transfers are supposed to get 90% of the * available bandwidth.  Control transfers get 10%.  The rest is available for * bulk transfers. * * Each 1ms 'frame' is proceeded by an SOF packet containing a frame number. * * Polling of devices (like keyboards) is done by sending an IN transfer * every 'n' frame times (ms).  If the device has no data it responds with NAK. * These are 'interrupt' transfers. * * Setup of devices is done with control transfers. * * Larger data transfers are done with bulk (acknowledged) and isochronous * (unacknowledged) transfers. * * The 8xx has no support for scheduling.  So, we use a 1ms timer to generate * an interrupt and run the scheduler in software.  At each SOF time we  * add an SOF packet to the tx list and look at the list of packets to send. * Packets are added to the tx list based on the priorities shown above. * * The internal scheduling structure is a "qe" or queue element.  Pending * urbs are bound to to a qe and queued on a list by transaction type. * At the begining of each frame the list of pending transactions is  * scanned and eligible qe's are put on the current frame list.  The * driver keeps track of which devices are busy and won't send pending * transactions to devices which are in the middle of an existing transaction. * The busy nature of a device is separated into input and output pipes so * ISO IN transactions can be pending and an ISO OUT can still be sent. * * So, a qe pends on the class queues until the device it refers to is  * idle and then it is moved to a frame list. * * There are two frame lists, one for the current (in progress) frame * and one for the 'next' frame.  Transactions often span multiple * frames, either due to NAK's, timeouts or pacing for slow devices. * The use of a 'next' frame list gives us a place to put qe's which * need to be sent during the next frame. * * Interrupt transactions which are periodic are kept on a time ordered * list, sorted by ms.  A timer interrupt services the list and takes * qe's which are ready and puts them on the current frame list. * * Some notes on the CPM USB implementation: * * the buffer descriptors for a typical setup transaction should look * like this: (note that we don't really do it this way because we need * to allow for timeouts and retransmission) * * #1 3 bytes flags=R,L *      buffer -> 0x2d, 0x00, 0x01 * * #2 8 bytes flags=R,L,TC,CNF,DATA0    <-- note, set the CNF bit to wait *      buffer -> (8 bytes of config)       for an ACK/NAK/STALL * * #3 3 bytes flags=R,L,CNF             <-- set CNF to wait for DATA1 *      buffer -> 0x69, 0x00, 0x01      when tx bd is marked done, the *                                      rx packet is in the rx descriptor *                                      (i.e. the cpm won't mark the tx done *                                      (until the DATAx packet is received) * * This should *      send a SETUP, a DATA0 *      wait for an ACK (or NAK/STALL) *      send a IN *      wait for a DATA1 * * --- * * An engineer from MOT claimed that the internal logic won't complete a * transmit descriptor until the response is received.  This implies that * if *      the host sends IN *      function responds with DATAx *      the host sends ACK, * * The rx descriptor will complete *before* the transmit descriptor does. * (or worst case, at the same time, which is why the isr services the *  rx side before the tx side). */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/smp_lock.h>#include <linux/usb.h>#include <asm/io.h>#include <asm/8xx_immap.h>#include <asm/pgtable.h>#include <asm/mpc8xx.h>#define USB_UCODE_PATCH         /* SOF via external 1Khz signal */#define DEBUG_CHECKS#define HUB_SLOW_DEVICES        /* try slow devs on root hub *///#define DEBUG_CONNECT#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC)#define CONFIG_RPXLITE_CW#define CONFIG_RPXLITE_DW#define POLL_FOR_HUB            /* don't use RXP/RXP, poll with msg */#define USE_PA5_CLK3#define USE_BRG1_FOR_SOF#define FIX_SMC1_BRG1         /* move smc1 from brg1 to brg2 */#define USE_TIMER4_FOR_SOF      /* timer4 shadows external sof clock */#endif#ifdef CONFIG_BSEIP#define USE_BRG3                /* 48mhz clock via brg3 *///#define USE_TIMER2_FOR_SOF      /* timer2 generates external sof clock *///#define USB_XCVER_ECHOS_SOF   /* usb xcver gives back sof */#define USE_FPGA_CLOCK_FOR_SOF	/* fpga generates sof patch clock */#endif#include <asm/commproc.h>#include "m8xxhci.h"/* functions in commproc.c */extern void m8xx_cpm_lockbrg(int brg_index);/* local functions */void cleanup_drivers(void);void m8xxhci_kick_xmit(void);void m8xxhci_flush_xmit(void);void m8xxhci_flush_recv(void);void m8xxhci_stop_controller(void);int m8xxhci_start_controller(void);static void m8xxhci_interrupt(void *, struct pt_regs *regs);static int m8xxhci_unlink_urb(urb_t *urb);void m8xxhci_dump(void);static inline cbd_t *next_bd(void);static inline cbd_t *next_bd_qe(struct m8xxhci_qe *qe);static void complete_iso_rx(struct m8xxhci_qe *qe);static void process_done_rxbds(void);static void process_done_txbds(void);static void mark_endpoint_busy(struct m8xxhci_qe *qe);static void mark_endpoint_idle(struct m8xxhci_qe *qe);static int send_qe(struct m8xxhci_qe *qe);static void enqueue_qe(struct m8xxhci_qe *qe, int qtype);static struct m8xxhci_qe *allocate_qe(struct m8xxhci_device *dev, int qtype);static void deallocate_qe(struct m8xxhci_qe *qe);static void advance_qe_state(struct m8xxhci_qe *qe);static void make_active_qe(struct m8xxhci_qe *qe);static void make_inactive_qe(struct m8xxhci_qe *qe);static void make_inactive_qe_idle_endpoint(struct m8xxhci_qe *qe);static void run_queues(void);static int time_left_in_frame(int *bytes);static void continue_xmit(void);static int unlink_urb(urb_t *urb, int qtype);static struct m8xxhci_device *add_local_dev(struct usb_device *usb_dev);static void process_iso_rx(struct m8xxhci_qe *qe, int status);static void complete_iso_rx(struct m8xxhci_qe *qe);static int submit_urb(urb_t *urb, int qtype);static int controller_enabled(void);#if 0static void assert_resume(void);#endifstatic void assert_reset(void);/* debug */static void dump_tx_state(void);static void dump_tx_bds(char *str);static void dump_rx_bds(char *str);static void dump_state(int stats, int rx, int tx);/* vars */static volatile struct m8xxhci_private *m8xxhci_ptr;static volatile int m8xxhci_xcver_echos_sof = 0;static volatile int m8xxhci_debug = 0;static volatile int m8xxhci_verbose = 0;#ifdef USE_FPGA_CLOCK_FOR_SOFstatic volatile unsigned short *fpga_addr;#endifstatic volatile unsigned int pending_isrs;#define	PENDING_SOF	0x00010000#ifdef POLL_FOR_HUBstatic wait_queue_head_t m8xxhci_configure;static int m8xxhci_events;#define EV_IDLE         0x01#define EV_RESET        0x02#define EV_QUERY        0x04#endif /* POLL_FOR_HUB *//* ---- *//* get ppc time base register (64 bits) */static long long_get_TBR(void){    long long t;    __asm__ __volatile__ ("\n\1:  mftbu   %0\n\    mftb    %0+1\n\    mftbu   5\n\    cmpw    5,%0\n\    bne     1b\n\    isync"    : "=r" (t)    : /* no inputs */    : "cc", "5" );    return t;}#define MAX_EVENTS      500 /*4000*//*2000*/#define EVENT_STOP_WHEN_FULLstatic int e_count;static int e_wrapped;static int e_level;static int e_disabled;static struct {        long long e_time;        char *e_str;        int e_num;} events[MAX_EVENTS], events_copy[MAX_EVENTS];static spinlock_t event_lock = SPIN_LOCK_UNLOCKED;static voiddump_events(void){        struct m8xxhci_private *hp = (struct m8xxhci_private *)m8xxhci_ptr;        int i, count;        u_long t1, t2;        unsigned long flags;        spin_lock_irqsave(&event_lock, flags);        count = e_wrapped ? MAX_EVENTS : e_count;        printk("event count %d\n", count);        printk("timer ticks %lu\n", hp->stats.tmr_interrupts);        for (i = 0; i < count; i++) {                t1 = events[i].e_time >> 32;                t2 = events[i].e_time;                printk("%08x:%08x %s %d (0x%x)\n",                       (int)t1, (int)t2, events[i].e_str,                       events[i].e_num, events[i].e_num);        }        e_count = 0;        e_wrapped = 0;        spin_unlock_irqrestore(&event_lock, flags);}voidm8xxhci_dump_events(void){        if (0) dump_events();}#if 0static voidreset_events(void){        e_count = 0;        e_wrapped = 0;	e_disabled = 0;}#endifstatic voidset_event_level(int l){        e_level = l;}static voidlog_event(int level, char *s, int n){        if (e_disabled || level > e_level)                return;        if (e_count >= MAX_EVENTS) {#ifdef EVENT_STOP_WHEN_FULL		e_disabled = 1;		return;#else                e_count = 0;                e_wrapped++;#endif        }        if (e_count < MAX_EVENTS) {                events[e_count].e_time = _get_TBR();                events[e_count].e_str = s;                events[e_count].e_num = n;                e_count++;        }}static voidlogging_on(void){	e_disabled = 0;        log_event(3, "logging enabled", 0);}#if 0static voidlogging_off(void){        log_event(3, "logging disabled", 0);	e_disabled = 1;}#endif#ifdef CONFIG_PROC_FS/* This macro frees the machine specific function from bounds checking and * this like that... */#define PRINT_PROC(fmt,args...)                                 \        do {                                                    \                *len += sprintf( buffer+*len, fmt, ##args );    \                if (*begin + *len > offset + size)              \                        return( 0 );                            \                if (*begin + *len < offset) {                   \                        *begin += *len;                         \                        *len = 0;                               \                }                                               \        } while(0)static intproc_frame_list(char *what, struct m8xxhci_frame *f, char *buf){        int i, len;        struct list_head *head, *l;        struct m8xxhci_qe *qe;#define PPRINT(fmt, args...) len += sprintf(buf+len, fmt, ##args );        len = 0;        if (f == 0) {                PPRINT("%s: %p; <unset>\n", what, f);                return len;        }        PPRINT("%s: %p, total_bytes %d\n", what, f, f->total_bytes);        for (i = 0; i < MAX_Q_TYPES; i++) {		if (f->heads[i].next == &f->heads[i]) {			continue;		}                PPRINT("[%d] bytes %d, head %p next %p prev %p\n",                       i, f->bytes[i],                       &f->heads[i], f->heads[i].next, f->heads[i].prev);                head = &f->heads[i];                for (l = head->next; l != head; l = l->next) {                        qe = list_entry(l, struct m8xxhci_qe, qe_list);                        PPRINT(" l %p, next %p, prev %p, qe %p\n",                               l, l->next, l->prev, qe);			PPRINT(" qe: type %d, state %d, status %d, urb %p\n",			       qe->qtype, qe->qstate, qe->status, qe->urb);			PPRINT("  len data %d, recv %d, send %d, iso_ptr %d\n",			   qe->data_len, qe->recv_len, qe->send_len,			   qe->iso_ptr);			PPRINT("  retries %d, busys %d\n",			       qe->retries, qe->busys);			PPRINT("  pipe %08x, devnum %d, endpoint %d\n",			       qe->pipe, qe->devnum, qe->endpoint);                }        }        return len;}static int events_copy_count;static voidusb_proc_snapshot_events(void){        unsigned long flags;        int i, count;        spin_lock_irqsave(&event_lock, flags);

⌨️ 快捷键说明

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