📄 amd5536udc.c
字号:
/* * amd5536.c -- AMD 5536 UDC high/full speed USB device controller * * Copyright (C) 2005-2007 AMD (http://www.amd.com) * Author: Thomas Dahlmann * * 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 *//* * The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536. * It is a USB Highspeed DMA capable USB device controller. Beside ep0 it * provides 4 IN and 4 OUT endpoints (bulk or interrupt type). * * Make sure that UDC is assigned to port 4 by BIOS settings (port can also * be used as host port) and UOC bits PAD_EN and APU are set (should be done * by BIOS init). * * UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not * work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0") * can be used with gadget ether. *//* debug control *//* #define UDC_VERBOSE *//* Driver strings */#define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller"#define UDC_DRIVER_VERSION_STRING "01.00.0206 - $Revision: #3 $"/* system */#include <linux/module.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/version.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/ioctl.h>#include <linux/fs.h>#include <linux/dmapool.h>#include <linux/moduleparam.h>#include <linux/device.h>#include <linux/io.h>#include <linux/irq.h>#include <asm/byteorder.h>#include <asm/system.h>#include <asm/unaligned.h>/* gadget stack */#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>/* udc specific */#include "amd5536udc.h"static void udc_tasklet_disconnect(unsigned long);static void empty_req_queue(struct udc_ep *);static int udc_probe(struct udc *dev);static void udc_basic_init(struct udc *dev);static void udc_setup_endpoints(struct udc *dev);static void udc_soft_reset(struct udc *dev);static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);static int udc_free_dma_chain(struct udc *dev, struct udc_request *req);static int udc_create_dma_chain(struct udc_ep *ep, struct udc_request *req, unsigned long buf_len, gfp_t gfp_flags);static int udc_remote_wakeup(struct udc *dev);static int udc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id);static void udc_pci_remove(struct pci_dev *pdev);/* description */static const char mod_desc[] = UDC_MOD_DESCRIPTION;static const char name[] = "amd5536udc";/* structure to hold endpoint function pointers */static const struct usb_ep_ops udc_ep_ops;/* received setup data */static union udc_setup_data setup_data;/* pointer to device object */static struct udc *udc;/* irq spin lock for soft reset */static DEFINE_SPINLOCK(udc_irq_spinlock);/* stall spin lock */static DEFINE_SPINLOCK(udc_stall_spinlock);/** slave mode: pending bytes in rx fifo after nyet,* used if EPIN irq came but no req was available*/static unsigned int udc_rxfifo_pending;/* count soft resets after suspend to avoid loop */static int soft_reset_occured;static int soft_reset_after_usbreset_occured;/* timer */static struct timer_list udc_timer;static int stop_timer;/* set_rde -- Is used to control enabling of RX DMA. Problem is * that UDC has only one bit (RDE) to enable/disable RX DMA for * all OUT endpoints. So we have to handle race conditions like * when OUT data reaches the fifo but no request was queued yet. * This cannot be solved by letting the RX DMA disabled until a * request gets queued because there may be other OUT packets * in the FIFO (important for not blocking control traffic). * The value of set_rde controls the correspondig timer. * * set_rde -1 == not used, means it is alloed to be set to 0 or 1 * set_rde 0 == do not touch RDE, do no start the RDE timer * set_rde 1 == timer function will look whether FIFO has data * set_rde 2 == set by timer function to enable RX DMA on next call */static int set_rde = -1;static DECLARE_COMPLETION(on_exit);static struct timer_list udc_pollstall_timer;static int stop_pollstall_timer;static DECLARE_COMPLETION(on_pollstall_exit);/* tasklet for usb disconnect */static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect, (unsigned long) &udc);/* endpoint names used for print */static const char ep0_string[] = "ep0in";static const char *ep_string[] = { ep0_string, "ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk", "ep6in-bulk", "ep7in-bulk", "ep8in-bulk", "ep9in-bulk", "ep10in-bulk", "ep11in-bulk", "ep12in-bulk", "ep13in-bulk", "ep14in-bulk", "ep15in-bulk", "ep0out", "ep1out-bulk", "ep2out-bulk", "ep3out-bulk", "ep4out-bulk", "ep5out-bulk", "ep6out-bulk", "ep7out-bulk", "ep8out-bulk", "ep9out-bulk", "ep10out-bulk", "ep11out-bulk", "ep12out-bulk", "ep13out-bulk", "ep14out-bulk", "ep15out-bulk"};/* DMA usage flag */static int use_dma = 1;/* packet per buffer dma */static int use_dma_ppb = 1;/* with per descr. update */static int use_dma_ppb_du;/* buffer fill mode */static int use_dma_bufferfill_mode;/* full speed only mode */static int use_fullspeed;/* tx buffer size for high speed */static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;/* module parameters */module_param(use_dma, bool, S_IRUGO);MODULE_PARM_DESC(use_dma, "true for DMA");module_param(use_dma_ppb, bool, S_IRUGO);MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");module_param(use_dma_ppb_du, bool, S_IRUGO);MODULE_PARM_DESC(use_dma_ppb_du, "true for DMA in packet per buffer mode with descriptor update");module_param(use_fullspeed, bool, S_IRUGO);MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");/*---------------------------------------------------------------------------*//* Prints UDC device registers and endpoint irq registers */static void print_regs(struct udc *dev){ DBG(dev, "------- Device registers -------\n"); DBG(dev, "dev config = %08x\n", readl(&dev->regs->cfg)); DBG(dev, "dev control = %08x\n", readl(&dev->regs->ctl)); DBG(dev, "dev status = %08x\n", readl(&dev->regs->sts)); DBG(dev, "\n"); DBG(dev, "dev int's = %08x\n", readl(&dev->regs->irqsts)); DBG(dev, "dev intmask = %08x\n", readl(&dev->regs->irqmsk)); DBG(dev, "\n"); DBG(dev, "dev ep int's = %08x\n", readl(&dev->regs->ep_irqsts)); DBG(dev, "dev ep intmask = %08x\n", readl(&dev->regs->ep_irqmsk)); DBG(dev, "\n"); DBG(dev, "USE DMA = %d\n", use_dma); if (use_dma && use_dma_ppb && !use_dma_ppb_du) { DBG(dev, "DMA mode = PPBNDU (packet per buffer " "WITHOUT desc. update)\n"); dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU"); } else if (use_dma && use_dma_ppb_du && use_dma_ppb_du) { DBG(dev, "DMA mode = PPBDU (packet per buffer " "WITH desc. update)\n"); dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU"); } if (use_dma && use_dma_bufferfill_mode) { DBG(dev, "DMA mode = BF (buffer fill mode)\n"); dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF"); } if (!use_dma) { dev_info(&dev->pdev->dev, "FIFO mode\n"); } DBG(dev, "-------------------------------------------------------\n");}/* Masks unused interrupts */static int udc_mask_unused_interrupts(struct udc *dev){ u32 tmp; /* mask all dev interrupts */ tmp = AMD_BIT(UDC_DEVINT_SVC) | AMD_BIT(UDC_DEVINT_ENUM) | AMD_BIT(UDC_DEVINT_US) | AMD_BIT(UDC_DEVINT_UR) | AMD_BIT(UDC_DEVINT_ES) | AMD_BIT(UDC_DEVINT_SI) | AMD_BIT(UDC_DEVINT_SOF)| AMD_BIT(UDC_DEVINT_SC); writel(tmp, &dev->regs->irqmsk); /* mask all ep interrupts */ writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk); return 0;}/* Enables endpoint 0 interrupts */static int udc_enable_ep0_interrupts(struct udc *dev){ u32 tmp; DBG(dev, "udc_enable_ep0_interrupts()\n"); /* read irq mask */ tmp = readl(&dev->regs->ep_irqmsk); /* enable ep0 irq's */ tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0) & AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0); writel(tmp, &dev->regs->ep_irqmsk); return 0;}/* Enables device interrupts for SET_INTF and SET_CONFIG */static int udc_enable_dev_setup_interrupts(struct udc *dev){ u32 tmp; DBG(dev, "enable device interrupts for setup data\n"); /* read irq mask */ tmp = readl(&dev->regs->irqmsk); /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */ tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI) & AMD_UNMASK_BIT(UDC_DEVINT_SC) & AMD_UNMASK_BIT(UDC_DEVINT_UR) & AMD_UNMASK_BIT(UDC_DEVINT_SVC) & AMD_UNMASK_BIT(UDC_DEVINT_ENUM); writel(tmp, &dev->regs->irqmsk); return 0;}/* Calculates fifo start of endpoint based on preceeding endpoints */static int udc_set_txfifo_addr(struct udc_ep *ep){ struct udc *dev; u32 tmp; int i; if (!ep || !(ep->in)) return -EINVAL; dev = ep->dev; ep->txfifo = dev->txfifo; /* traverse ep's */ for (i = 0; i < ep->num; i++) { if (dev->ep[i].regs) { /* read fifo size */ tmp = readl(&dev->ep[i].regs->bufin_framenum); tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE); ep->txfifo += tmp; } } return 0;}/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */static u32 cnak_pending;static void UDC_QUEUE_CNAK(struct udc_ep *ep, unsigned num){ if (readl(&ep->regs->ctl) & AMD_BIT(UDC_EPCTL_NAK)) { DBG(ep->dev, "NAK could not be cleared for ep%d\n", num); cnak_pending |= 1 << (num); ep->naking = 1; } else cnak_pending = cnak_pending & (~(1 << (num)));}/* Enables endpoint, is called by gadget driver */static intudc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc){ struct udc_ep *ep; struct udc *dev; u32 tmp; unsigned long iflags; u8 udc_csr_epix; if (!usbep || usbep->name == ep0_string || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; ep = container_of(usbep, struct udc_ep, ep); dev = ep->dev; DBG(dev, "udc_ep_enable() ep %d\n", ep->num); if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; spin_lock_irqsave(&dev->lock, iflags); ep->desc = desc; ep->halted = 0; /* set traffic type */ tmp = readl(&dev->ep[ep->num].regs->ctl); tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET); writel(tmp, &dev->ep[ep->num].regs->ctl); /* set max packet size */ tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt); tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_EP_MAX_PKT_SIZE); ep->ep.maxpacket = desc->wMaxPacketSize; writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt); /* IN ep */ if (ep->in) { /* ep ix in UDC CSR register space */ udc_csr_epix = ep->num; /* set buffer size (tx fifo entries) */ tmp = readl(&dev->ep[ep->num].regs->bufin_framenum); /* double buffering: fifo size = 2 x max packet size */ tmp = AMD_ADDBITS( tmp, desc->wMaxPacketSize * UDC_EPIN_BUFF_SIZE_MULT / UDC_DWORD_BYTES, UDC_EPIN_BUFF_SIZE); writel(tmp, &dev->ep[ep->num].regs->bufin_framenum); /* calc. tx fifo base addr */ udc_set_txfifo_addr(ep); /* flush fifo */ tmp = readl(&ep->regs->ctl); tmp |= AMD_BIT(UDC_EPCTL_F); writel(tmp, &ep->regs->ctl); /* OUT ep */ } else { /* ep ix in UDC CSR register space */ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS; /* set max packet size UDC CSR */ tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]); tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT); writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]); if (use_dma && !ep->in) { /* alloc and init BNA dummy request */ ep->bna_dummy_req = udc_alloc_bna_dummy(ep); ep->bna_occurred = 0; } if (ep->num != UDC_EP0OUT_IX) dev->data_ep_enabled = 1; } /* set ep values */ tmp = readl(&dev->csr->ne[udc_csr_epix]); /* max packet */ tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT); /* ep number */ tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM); /* ep direction */ tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR); /* ep type */ tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE); /* ep config */ tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG); /* ep interface */ tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF); /* ep alt */ tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT); /* write reg */ writel(tmp, &dev->csr->ne[udc_csr_epix]); /* enable ep irq */ tmp = readl(&dev->regs->ep_irqmsk); tmp &= AMD_UNMASK_BIT(ep->num); writel(tmp, &dev->regs->ep_irqmsk); /* * clear NAK by writing CNAK * avoid BNA for OUT DMA, don't clear NAK until DMA desc. written */ if (!use_dma || ep->in) { tmp = readl(&ep->regs->ctl); tmp |= AMD_BIT(UDC_EPCTL_CNAK); writel(tmp, &ep->regs->ctl); ep->naking = 0; UDC_QUEUE_CNAK(ep, ep->num); } tmp = desc->bEndpointAddress; DBG(dev, "%s enabled\n", usbep->name); spin_unlock_irqrestore(&dev->lock, iflags); return 0;}/* Resets endpoint */static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep){ u32 tmp; VDBG(ep->dev, "ep-%d reset\n", ep->num); ep->desc = NULL; ep->ep.ops = &udc_ep_ops; INIT_LIST_HEAD(&ep->queue); ep->ep.maxpacket = (u16) ~0; /* set NAK */ tmp = readl(&ep->regs->ctl); tmp |= AMD_BIT(UDC_EPCTL_SNAK); writel(tmp, &ep->regs->ctl); ep->naking = 1; /* disable interrupt */ tmp = readl(®s->ep_irqmsk); tmp |= AMD_BIT(ep->num); writel(tmp, ®s->ep_irqmsk); if (ep->in) { /* unset P and IN bit of potential former DMA */ tmp = readl(&ep->regs->ctl); tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P); writel(tmp, &ep->regs->ctl); tmp = readl(&ep->regs->sts); tmp |= AMD_BIT(UDC_EPSTS_IN); writel(tmp, &ep->regs->sts); /* flush the fifo */ tmp = readl(&ep->regs->ctl); tmp |= AMD_BIT(UDC_EPCTL_F); writel(tmp, &ep->regs->ctl); } /* reset desc pointer */ writel(0, &ep->regs->desptr);}/* Disables endpoint, is called by gadget driver */static int udc_ep_disable(struct usb_ep *usbep){ struct udc_ep *ep = NULL; unsigned long iflags; if (!usbep) return -EINVAL; ep = container_of(usbep, struct udc_ep, ep); if (usbep->name == ep0_string || !ep->desc) return -EINVAL; DBG(ep->dev, "Disable ep-%d\n", ep->num); spin_lock_irqsave(&ep->dev->lock, iflags); udc_free_request(&ep->ep, &ep->bna_dummy_req->req); empty_req_queue(ep); ep_init(ep->dev->regs, ep); spin_unlock_irqrestore(&ep->dev->lock, iflags); return 0;}/* Allocates request packet, called by gadget driver */static struct usb_request *udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -