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

📄 s3c2410_udc.c

📁 S3C2410 USBD驱动程序 s3c2410_udc
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * linux/drivers/usb/gadget/s3c2410_udc.c
 */

#include <linux/module.h>
#include <linux/kernel.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/platform_device.h>
#include <linux/version.h>
#include <linux/clk.h>

#include <linux/debugfs.h>
#include <linux/seq_file.h>

#include <linux/usb.h>
#include <linux/usb_gadget.h>

#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/arch/irqs.h>

#include <asm/arch/hardware.h>
#include <asm/arch/regs-clock.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-udc.h>
#include <asm/arch/udc.h>

#include <asm/mach-types.h>

#include "s3c2410_udc.h"

#define DRIVER_DESC   "S3C2410 USB Device Controller Gadget"
#define DRIVER_VERSION   "29 Apr 2007"
#define DRIVER_AUTHOR   "Herbert P?tzl <herbert@13thfloor.at>, " \
         "Arnaud Patard <arnaud.patard@rtp-net.org>"
static const char      gadget_name[] = "s3c2410_udc";
static const char      driver_desc[] = DRIVER_DESC;

static struct s3c2410_udc   *the_controller;
static struct clk      *udc_clock;
static struct clk      *usb_bus_clock;
static void __iomem      *base_addr;
static u64         rsrc_start;
static u64         rsrc_len;
static struct dentry      *s3c2410_udc_debugfs_root;

static inline u32 udc_read(u32 reg)
{
   return readb(base_addr + reg);
}

static inline void udc_write(u32 value, u32 reg)
{
   writeb(value, base_addr + reg);
}

static inline void udc_writeb(void __iomem *base, u32 value, u32 reg)
{
   writeb(value, base + reg);
}

static struct s3c2410_udc_mach_info *udc_info;

/*************************** DEBUG FUNCTION ***************************/
#define DEBUG_NORMAL   1
#define DEBUG_VERBOSE   2

#ifdef CONFIG_USB_S3C2410_DEBUG
#define USB_S3C2410_DEBUG_LEVEL 0

static uint32_t s3c2410_ticks = 0;

static int dprintk(int level, const char *fmt, ...)
{
   static char printk_buf[1024];
   static long prevticks;
   static int invocation;
   va_list args;
   int len;

   if (level > USB_S3C2410_DEBUG_LEVEL)
      return 0;

   if (s3c2410_ticks != prevticks) {
      prevticks = s3c2410_ticks;
      invocation = 0;
   }

   len = scnprintf(printk_buf,
        sizeof(printk_buf), "%1lu.%02d USB: ",
         prevticks, invocation++);

   va_start(args, fmt);
   len = vscnprintf(printk_buf+len,
         sizeof(printk_buf)-len, fmt, args);
   va_end(args);

   return printk(KERN_DEBUG "%s", printk_buf);
}
#else
static int dprintk(int level, const char *fmt, ...)
{
   return 0;

#endif
static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
{
   u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
   u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
   u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
   u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;

  addr_reg       = udc_read(S3C2410_UDC_FUNC_ADDR_REG);
   pwr_reg        = udc_read(S3C2410_UDC_PWR_REG);
   ep_int_reg     = udc_read(S3C2410_UDC_EP_INT_REG);
   usb_int_reg    = udc_read(S3C2410_UDC_USB_INT_REG);
   ep_int_en_reg  = udc_read(S3C2410_UDC_EP_INT_EN_REG);
   usb_int_en_reg = udc_read(S3C2410_UDC_USB_INT_EN_REG);
   udc_write(0, S3C2410_UDC_INDEX_REG);
   ep0_csr        = udc_read(S3C2410_UDC_IN_CSR1_REG);
  udc_write(1, S3C2410_UDC_INDEX_REG);
   ep1_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
   ep1_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
   ep1_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
   ep1_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
   udc_write(2, S3C2410_UDC_INDEX_REG);
  ep2_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
   ep2_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
   ep2_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
   ep2_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);

   seq_printf(m, "FUNC_ADDR_REG  : 0x%04X\n"
      "PWR_REG        : 0x%04X\n"
       "EP_INT_REG     : 0x%04X\n"
       "USB_INT_REG    : 0x%04X\n"
       "EP_INT_EN_REG  : 0x%04X\n"
       "USB_INT_EN_REG : 0x%04X\n"
       "EP0_CSR        : 0x%04X\n"
       "EP1_I_CSR1     : 0x%04X\n"
      "EP1_I_CSR2     : 0x%04X\n"
       "EP1_O_CSR1     : 0x%04X\n"
       "EP1_O_CSR2     : 0x%04X\n"
       "EP2_I_CSR1     : 0x%04X\n"
      "EP2_I_CSR2     : 0x%04X\n"
       "EP2_O_CSR1     : 0x%04X\n"
       "EP2_O_CSR2     : 0x%04X\n",
         addr_reg,pwr_reg,ep_int_reg,usb_int_reg,
        ep_int_en_reg, usb_int_en_reg, ep0_csr,
         ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2,
         ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2
      );

  return 0;
}

static int s3c2410_udc_debugfs_fops_open(struct inode *inode,
               struct file *file)
{
   return single_open(file, s3c2410_udc_debugfs_seq_show, NULL);
}

static const struct file_operations s3c2410_udc_debugfs_fops = {
  .open      = s3c2410_udc_debugfs_fops_open,
   .read      = seq_read,
   .llseek      = seq_lseek,
  .release   = single_release,
   .owner      = THIS_MODULE,
};

/* io macros */

static inline void s3c2410_udc_clear_ep0_opr(void __iomem *base)
{
   udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
  udc_writeb(base, S3C2410_UDC_EP0_CSR_SOPKTRDY,
         S3C2410_UDC_EP0_CSR_REG);
}

static inline void s3c2410_udc_clear_ep0_sst(void __iomem *base)
{
   udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
   writeb(0x00, base + S3C2410_UDC_EP0_CSR_REG);
}

static inline void s3c2410_udc_clear_ep0_se(void __iomem *base)
{
   udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
   udc_writeb(base, S3C2410_UDC_EP0_CSR_SSE, S3C2410_UDC_EP0_CSR_REG);
}

static inline void s3c2410_udc_set_ep0_ipr(void __iomem *base)
{
   udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
   udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG);
}

static inline void s3c2410_udc_set_ep0_de(void __iomem *base)
{
   udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
   udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG);
}

inline void s3c2410_udc_set_ep0_ss(void __iomem *b)
{
   udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
   udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG);
}

static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
{
   udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);

   udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY
           | S3C2410_UDC_EP0_CSR_DE),
         S3C2410_UDC_EP0_CSR_REG);
}

static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base)
{
   udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
   udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
            | S3C2410_UDC_EP0_CSR_SSE),
         S3C2410_UDC_EP0_CSR_REG);
}

static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
{
   udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
   udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY
         | S3C2410_UDC_EP0_CSR_DE),
      S3C2410_UDC_EP0_CSR_REG);
}

/*------------------------- I/O ----------------------------------*/

/*
 *   s3c2410_udc_done
 */
static void s3c2410_udc_done(struct s3c2410_ep *ep,
      struct s3c2410_request *req, int status)
{
   unsigned halted = ep->halted;
   list_del_init(&req->queue);

   if (likely (req->req.status == -EINPROGRESS))
      req->req.status = status;
   else
      status = req->req.status;

  ep->halted = 1;
   req->req.complete(&ep->ep, &req->req);
   ep->halted = halted;
}

static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
      struct s3c2410_ep *ep, int status)
{
   /* Sanity check */
   if (&ep->queue == NULL)
     return;

   while (!list_empty (&ep->queue)) {
      struct s3c2410_request *req;
      req = list_entry (ep->queue.next, struct s3c2410_request,
           queue);
      s3c2410_udc_done(ep, req, status);
   }
}

static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev)
{
   unsigned i;

  /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
    * fifos, and pending transactions mustn't be continued in any case.
    */

   for (i = 1; i < S3C2410_ENDPOINTS; i++)
      s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED);
}

static inline int s3c2410_udc_fifo_count_out(void)
{
   int tmp;
   tmp = udc_read(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
   tmp |= udc_read(S3C2410_UDC_OUT_FIFO_CNT1_REG);
   return tmp;
}

/*
 *   s3c2410_udc_write_packet
 */
static inline int s3c2410_udc_write_packet(int fifo,
      struct s3c2410_request *req,
      unsigned max)
{
   unsigned len = min(req->req.length - req->req.actual, max);
   u8 *buf = req->req.buf + req->req.actual;

   prefetch(buf);

   dprintk(DEBUG_VERBOSE, "%s %d %d %d %d\n", __func__,
      req->req.actual, req->req.length, len, req->req.actual + len);

   req->req.actual += len;

   udelay(5);
   writesb(base_addr + fifo, buf, len);
   return len;
}

/*
 *   s3c2410_udc_write_fifo
 *
 * return:  0 = still running, 1 = completed, negative = errno
 */
static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
      struct s3c2410_request *req)
{
   unsigned   count;
   int      is_last;
   u32      idx;
   int      fifo_reg;
   u32      ep_csr;

   idx = ep->bEndpointAddress & 0x7F;
   switch (idx) {
   default:
     idx = 0;
   case 0:
      fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
      break;
   case 1:
      fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
      break;
   case 2:
      fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
      break;
   case 3:
      fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
      break;
   case 4:
      fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
      break;
   }
   count = s3c2410_udc_write_packet(fifo_reg, req, ep->ep.maxpacket);

   /* last packet is often short (sometimes a zlp) */
   if (count != ep->ep.maxpacket)
      is_last = 1;
   else if (req->req.length != req->req.actual || req->req.zero)
     is_last = 0;
   else
      is_last = 2;

   /* Only ep0 debug messages are interesting */
   if (idx == 0)
     dprintk(DEBUG_NORMAL,
         "Written ep%d %d.%d of %d b [last %d,z %d]\n",
         idx, count, req->req.actual, req->req.length,
         is_last, req->req.zero);

   if (is_last) {
     /* The order is important. It prevents sending 2 packets
       * at the same time */

      if (idx == 0) {
        /* Reset signal => no need to say 'data sent' */
         if (! (udc_read(S3C2410_UDC_USB_INT_REG)
               & S3C2410_UDC_USBINT_RESET))
            s3c2410_udc_set_ep0_de_in(base_addr);
        ep->dev->ep0state=EP0_IDLE;
      } else {
         udc_write(idx, S3C2410_UDC_INDEX_REG);
         ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
        udc_write(idx, S3C2410_UDC_INDEX_REG);
         udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
               S3C2410_UDC_IN_CSR1_REG);
      }
      s3c2410_udc_done(ep, req, 0);
      is_last = 1;
   } else {
      if (idx == 0) {
        /* Reset signal => no need to say 'data sent' */
         if (! (udc_read(S3C2410_UDC_USB_INT_REG)
               & S3C2410_UDC_USBINT_RESET))
           s3c2410_udc_set_ep0_ipr(base_addr);
      } else {
         udc_write(idx, S3C2410_UDC_INDEX_REG);
        ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
         udc_write(idx, S3C2410_UDC_INDEX_REG);
         udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
              S3C2410_UDC_IN_CSR1_REG);
      }
  }

   return is_last;
}

static inline int s3c2410_udc_read_packet(int fifo, u8 *buf,
      struct s3c2410_request *req, unsigned avail)
{
   unsigned len;
   len = min(req->req.length - req->req.actual, avail);
   req->req.actual += len;
   readsb(fifo + base_addr, buf, len);
  return len;
}

/*
 * return:  0 = still running, 1 = queue empty, negative = errno
 */
static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
             struct s3c2410_request *req)
{
   u8      *buf;
   u32      ep_csr;
   unsigned   bufferspace;
   int      is_last=1;
   unsigned   avail;
   int      fifo_count = 0;
   u32      idx;
   int      fifo_reg;

   idx = ep->bEndpointAddress & 0x7F;

   switch (idx) {
   default:
      idx = 0;
   case 0:
      fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
      break;
   case 1:
      fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
     break;
   case 2:
      fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
      break;
   case 3:
      fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
      break;
   case 4:
      fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
      break;
   }

   if (!req->req.length)
      return 1;
   buf = req->req.buf + req->req.actual;
   bufferspace = req->req.length - req->req.actual;
   if (!bufferspace) {
      dprintk(DEBUG_NORMAL, "%s: buffer full!\n", __func__);
      return -1;
   }
   udc_write(idx, S3C2410_UDC_INDEX_REG);

   fifo_count = s3c2410_udc_fifo_count_out();
   dprintk(DEBUG_NORMAL, "%s fifo count : %d\n", __func__, fifo_count);

   if (fifo_count > ep->ep.maxpacket)
     avail = ep->ep.maxpacket;
   else
      avail = fifo_count;

   fifo_count = s3c2410_udc_read_packet(fifo_reg, buf, req, avail);

   /* checking this with ep0 is not accurate as we already
   * read a control request
    **/
   if (idx != 0 && fifo_count < ep->ep.maxpacket) {
     is_last = 1;
      /* overflowed this request?  flush extra data */
      if (fifo_count != avail)
         req->req.status = -EOVERFLOW;
  } else {
      is_last = (req->req.length <= req->req.actual) ? 1 : 0;
   }

  udc_write(idx, S3C2410_UDC_INDEX_REG);
   fifo_count = s3c2410_udc_fifo_count_out();

⌨️ 快捷键说明

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