📄 at91_udc.c
字号:
/* * at91_udc -- driver for at91-series USB peripheral controller * * Copyright (C) 2004 by Thomas Rathbone * Copyright (C) 2005 by HP Labs * Copyright (C) 2005 by David Brownell * * 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. */#undef DEBUG#undef VERBOSE#undef PACKET_TRACE#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/clk.h>#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>#include <asm/byteorder.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/mach-types.h>#include <asm/arch/gpio.h>#include <asm/arch/board.h>#include <asm/arch/cpu.h>#include <asm/arch/at91sam9261_matrix.h>#include "at91_udc.h"/* * This controller is simple and PIO-only. It's used in many AT91-series * full speed USB controllers, including the at91rm9200 (arm920T, with MMU), * at91sam926x (arm926ejs, with MMU), and several no-mmu versions. * * This driver expects the board has been wired with two GPIOs suppporting * a VBUS sensing IRQ, and a D+ pullup. (They may be omitted, but the * testing hasn't covered such cases.) * * The pullup is most important (so it's integrated on sam926x parts). It * provides software control over whether the host enumerates the device. * * The VBUS sensing helps during enumeration, and allows both USB clocks * (and the transceiver) to stay gated off until they're necessary, saving * power. During USB suspend, the 48 MHz clock is gated off in hardware; * it may also be gated off by software during some Linux sleep states. */#define DRIVER_VERSION "3 May 2006"static const char driver_name [] = "at91_udc";static const char ep0name[] = "ep0";#define at91_udp_read(dev, reg) \ __raw_readl((dev)->udp_baseaddr + (reg))#define at91_udp_write(dev, reg, val) \ __raw_writel((val), (dev)->udp_baseaddr + (reg))/*-------------------------------------------------------------------------*/#ifdef CONFIG_USB_GADGET_DEBUG_FILES#include <linux/seq_file.h>static const char debug_filename[] = "driver/udc";#define FOURBITS "%s%s%s%s"#define EIGHTBITS FOURBITS FOURBITSstatic void proc_ep_show(struct seq_file *s, struct at91_ep *ep){ static char *types[] = { "control", "out-iso", "out-bulk", "out-int", "BOGUS", "in-iso", "in-bulk", "in-int"}; u32 csr; struct at91_request *req; unsigned long flags; local_irq_save(flags); csr = __raw_readl(ep->creg); /* NOTE: not collecting per-endpoint irq statistics... */ seq_printf(s, "\n"); seq_printf(s, "%s, maxpacket %d %s%s %s%s\n", ep->ep.name, ep->ep.maxpacket, ep->is_in ? "in" : "out", ep->is_iso ? " iso" : "", ep->is_pingpong ? (ep->fifo_bank ? "pong" : "ping") : "", ep->stopped ? " stopped" : ""); seq_printf(s, "csr %08x rxbytes=%d %s %s %s" EIGHTBITS "\n", csr, (csr & 0x07ff0000) >> 16, (csr & (1 << 15)) ? "enabled" : "disabled", (csr & (1 << 11)) ? "DATA1" : "DATA0", types[(csr & 0x700) >> 8], /* iff type is control then print current direction */ (!(csr & 0x700)) ? ((csr & (1 << 7)) ? " IN" : " OUT") : "", (csr & (1 << 6)) ? " rxdatabk1" : "", (csr & (1 << 5)) ? " forcestall" : "", (csr & (1 << 4)) ? " txpktrdy" : "", (csr & (1 << 3)) ? " stallsent" : "", (csr & (1 << 2)) ? " rxsetup" : "", (csr & (1 << 1)) ? " rxdatabk0" : "", (csr & (1 << 0)) ? " txcomp" : ""); if (list_empty (&ep->queue)) seq_printf(s, "\t(queue empty)\n"); else list_for_each_entry (req, &ep->queue, queue) { unsigned length = req->req.actual; seq_printf(s, "\treq %p len %d/%d buf %p\n", &req->req, length, req->req.length, req->req.buf); } local_irq_restore(flags);}static void proc_irq_show(struct seq_file *s, const char *label, u32 mask){ int i; seq_printf(s, "%s %04x:%s%s" FOURBITS, label, mask, (mask & (1 << 13)) ? " wakeup" : "", (mask & (1 << 12)) ? " endbusres" : "", (mask & (1 << 11)) ? " sofint" : "", (mask & (1 << 10)) ? " extrsm" : "", (mask & (1 << 9)) ? " rxrsm" : "", (mask & (1 << 8)) ? " rxsusp" : ""); for (i = 0; i < 8; i++) { if (mask & (1 << i)) seq_printf(s, " ep%d", i); } seq_printf(s, "\n");}static int proc_udc_show(struct seq_file *s, void *unused){ struct at91_udc *udc = s->private; struct at91_ep *ep; u32 tmp; seq_printf(s, "%s: version %s\n", driver_name, DRIVER_VERSION); seq_printf(s, "vbus %s, pullup %s, %s powered%s, gadget %s\n\n", udc->vbus ? "present" : "off", udc->enabled ? (udc->vbus ? "active" : "enabled") : "disabled", udc->selfpowered ? "self" : "VBUS", udc->suspended ? ", suspended" : "", udc->driver ? udc->driver->driver.name : "(none)"); /* don't access registers when interface isn't clocked */ if (!udc->clocked) { seq_printf(s, "(not clocked)\n"); return 0; } tmp = at91_udp_read(udc, AT91_UDP_FRM_NUM); seq_printf(s, "frame %05x:%s%s frame=%d\n", tmp, (tmp & AT91_UDP_FRM_OK) ? " ok" : "", (tmp & AT91_UDP_FRM_ERR) ? " err" : "", (tmp & AT91_UDP_NUM)); tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT); seq_printf(s, "glbstate %02x:%s" FOURBITS "\n", tmp, (tmp & AT91_UDP_RMWUPE) ? " rmwupe" : "", (tmp & AT91_UDP_RSMINPR) ? " rsminpr" : "", (tmp & AT91_UDP_ESR) ? " esr" : "", (tmp & AT91_UDP_CONFG) ? " confg" : "", (tmp & AT91_UDP_FADDEN) ? " fadden" : ""); tmp = at91_udp_read(udc, AT91_UDP_FADDR); seq_printf(s, "faddr %03x:%s fadd=%d\n", tmp, (tmp & AT91_UDP_FEN) ? " fen" : "", (tmp & AT91_UDP_FADD)); proc_irq_show(s, "imr ", at91_udp_read(udc, AT91_UDP_IMR)); proc_irq_show(s, "isr ", at91_udp_read(udc, AT91_UDP_ISR)); if (udc->enabled && udc->vbus) { proc_ep_show(s, &udc->ep[0]); list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { if (ep->desc) proc_ep_show(s, ep); } } return 0;}static int proc_udc_open(struct inode *inode, struct file *file){ return single_open(file, proc_udc_show, PDE(inode)->data);}static const struct file_operations proc_ops = { .open = proc_udc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};static void create_debug_file(struct at91_udc *udc){ struct proc_dir_entry *pde; pde = create_proc_entry (debug_filename, 0, NULL); udc->pde = pde; if (pde == NULL) return; pde->proc_fops = &proc_ops; pde->data = udc;}static void remove_debug_file(struct at91_udc *udc){ if (udc->pde) remove_proc_entry(debug_filename, NULL);}#elsestatic inline void create_debug_file(struct at91_udc *udc) {}static inline void remove_debug_file(struct at91_udc *udc) {}#endif/*-------------------------------------------------------------------------*/static void done(struct at91_ep *ep, struct at91_request *req, int status){ unsigned stopped = ep->stopped; struct at91_udc *udc = ep->udc; list_del_init(&req->queue); if (req->req.status == -EINPROGRESS) req->req.status = status; else status = req->req.status; if (status && status != -ESHUTDOWN) VDBG("%s done %p, status %d\n", ep->ep.name, req, status); ep->stopped = 1; req->req.complete(&ep->ep, &req->req); ep->stopped = stopped; /* ep0 is always ready; other endpoints need a non-empty queue */ if (list_empty(&ep->queue) && ep->int_mask != (1 << 0)) at91_udp_write(udc, AT91_UDP_IDR, ep->int_mask);}/*-------------------------------------------------------------------------*//* bits indicating OUT fifo has data ready */#define RX_DATA_READY (AT91_UDP_RX_DATA_BK0 | AT91_UDP_RX_DATA_BK1)/* * Endpoint FIFO CSR bits have a mix of bits, making it unsafe to just write * back most of the value you just read (because of side effects, including * bits that may change after reading and before writing). * * Except when changing a specific bit, always write values which: * - clear SET_FX bits (setting them could change something) * - set CLR_FX bits (clearing them could change something) * * There are also state bits like FORCESTALL, EPEDS, DIR, and EPTYPE * that shouldn't normally be changed. * * NOTE at91sam9260 docs mention synch between UDPCK and MCK clock domains, * implying a need to wait for one write to complete (test relevant bits) * before starting the next write. This shouldn't be an issue given how * infrequently we write, except maybe for write-then-read idioms. */#define SET_FX (AT91_UDP_TXPKTRDY)#define CLR_FX (RX_DATA_READY | AT91_UDP_RXSETUP \ | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)/* pull OUT packet data from the endpoint's fifo */static int read_fifo (struct at91_ep *ep, struct at91_request *req){ u32 __iomem *creg = ep->creg; u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0)); u32 csr; u8 *buf; unsigned int count, bufferspace, is_done; buf = req->req.buf + req->req.actual; bufferspace = req->req.length - req->req.actual; /* * there might be nothing to read if ep_queue() calls us, * or if we already emptied both pingpong buffers */rescan: csr = __raw_readl(creg); if ((csr & RX_DATA_READY) == 0) return 0; count = (csr & AT91_UDP_RXBYTECNT) >> 16; if (count > ep->ep.maxpacket) count = ep->ep.maxpacket; if (count > bufferspace) { DBG("%s buffer overflow\n", ep->ep.name); req->req.status = -EOVERFLOW; count = bufferspace; } __raw_readsb(dreg, buf, count); /* release and swap pingpong mem bank */ csr |= CLR_FX; if (ep->is_pingpong) { if (ep->fifo_bank == 0) { csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0); ep->fifo_bank = 1; } else { csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK1); ep->fifo_bank = 0; } } else csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0); __raw_writel(csr, creg); req->req.actual += count; is_done = (count < ep->ep.maxpacket); if (count == bufferspace) is_done = 1; PACKET("%s %p out/%d%s\n", ep->ep.name, &req->req, count, is_done ? " (done)" : ""); /* * avoid extra trips through IRQ logic for packets already in * the fifo ... maybe preventing an extra (expensive) OUT-NAK */ if (is_done) done(ep, req, 0); else if (ep->is_pingpong) { bufferspace -= count; buf += count; goto rescan; } return is_done;}/* load fifo for an IN packet */static int write_fifo(struct at91_ep *ep, struct at91_request *req){ u32 __iomem *creg = ep->creg; u32 csr = __raw_readl(creg); u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0)); unsigned total, count, is_last; /* * TODO: allow for writing two packets to the fifo ... that'll * reduce the amount of IN-NAKing, but probably won't affect * throughput much. (Unlike preventing OUT-NAKing!) */ /* * If ep_queue() calls us, the queue is empty and possibly in * odd states like TXCOMP not yet cleared (we do it, saving at * least one IRQ) or the fifo not yet being free. Those aren't * issues normally (IRQ handler fast path). */ if (unlikely(csr & (AT91_UDP_TXCOMP | AT91_UDP_TXPKTRDY))) { if (csr & AT91_UDP_TXCOMP) { csr |= CLR_FX; csr &= ~(SET_FX | AT91_UDP_TXCOMP); __raw_writel(csr, creg); csr = __raw_readl(creg); } if (csr & AT91_UDP_TXPKTRDY) return 0; } total = req->req.length - req->req.actual; if (ep->ep.maxpacket < total) { count = ep->ep.maxpacket; is_last = 0; } else { count = total; is_last = (count < ep->ep.maxpacket) || !req->req.zero; } /* * Write the packet, maybe it's a ZLP. * * NOTE: incrementing req->actual before we receive the ACK means * gadget driver IN bytecounts can be wrong in fault cases. That's * fixable with PIO drivers like this one (save "count" here, and * do the increment later on TX irq), but not for most DMA hardware. * * So all gadget drivers must accept that potential error. Some * hardware supports precise fifo status reporting, letting them * recover when the actual bytecount matters (e.g. for USB Test * and Measurement Class devices). */ __raw_writesb(dreg, req->req.buf + req->req.actual, count); csr &= ~SET_FX; csr |= CLR_FX | AT91_UDP_TXPKTRDY; __raw_writel(csr, creg); req->req.actual += count; PACKET("%s %p in/%d%s\n", ep->ep.name, &req->req, count, is_last ? " (done)" : ""); if (is_last) done(ep, req, 0); return is_last;}static void nuke(struct at91_ep *ep, int status){ struct at91_request *req; // terminer chaque requete dans la queue ep->stopped = 1; if (list_empty(&ep->queue)) return; VDBG("%s %s\n", __FUNCTION__, ep->ep.name);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -