📄 m8xxhci.c
字号:
/* * 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 + -