cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/gadget/udc/gr_udc.c

/*
 * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC.
 *
 * 2013 (c) Aeroflex Gaisler AB
 *
 * This driver supports GRUSBDC USB Device Controller cores available in the
 * GRLIB VHDL IP core library.
 *
 * Full documentation of the GRUSBDC core can be found here:
 * http://www.gaisler.com/products/grlib/grip.pdf
 *
 * 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.
 *
 * Contributors:
 * - Andreas Larsson <andreas@gaisler.com>
 * - Marko Isomaki
 */

/*
 * A GRUSBDC core can have up to 16 IN endpoints and 16 OUT endpoints each
 * individually configurable to any of the four USB transfer types. This driver
 * only supports cores in DMA mode.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>

#include <asm/byteorder.h>

#include "gr_udc.h"


#define	DRIVER_NAME	"gr_udc"

#define	DRIVER_DESC	"Aeroflex Gaisler GRUSBDC USB Peripheral Controller"


static const char driver_name[] = DRIVER_NAME;

static const char driver_desc[] = DRIVER_DESC;


#define gr_read32(x) (ioread32be((x)))

#define gr_write32(x, v) (iowrite32be((v), (x)))

/* USB speed and corresponding string calculated from status register value */

#define GR_SPEED(status) \
	((status & GR_STATUS_SP) ? USB_SPEED_FULL : USB_SPEED_HIGH)

#define GR_SPEED_STR(status) usb_speed_string(GR_SPEED(status))

/* Size of hardware buffer calculated from epctrl register value */

#define GR_BUFFER_SIZE(epctrl)					      \
	((((epctrl) & GR_EPCTRL_BUFSZ_MASK) >> GR_EPCTRL_BUFSZ_POS) * \
         GR_EPCTRL_BUFSZ_SCALER)

/* ---------------------------------------------------------------------- */
/* Debug printout functionality */


static const char * const gr_modestring[] = {"control", "iso", "bulk", "int"};


static const char *gr_ep0state_string(enum gr_ep0state state) { static const char *const names[] = { [GR_EP0_DISCONNECT] = "disconnect", [GR_EP0_SETUP] = "setup", [GR_EP0_IDATA] = "idata", [GR_EP0_ODATA] = "odata", [GR_EP0_ISTATUS] = "istatus", [GR_EP0_OSTATUS] = "ostatus", [GR_EP0_STALL] = "stall", [GR_EP0_SUSPEND] = "suspend", }; if (state < 0 || state >= ARRAY_SIZE(names)) return "UNKNOWN"; return names[state]; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson92100.00%1100.00%
Total92100.00%1100.00%

#ifdef VERBOSE_DEBUG
static void gr_dbgprint_request(const char *str, struct gr_ep *ep, struct gr_request *req) { int buflen = ep->is_in ? req->req.length : req->req.actual; int rowlen = 32; int plen = min(rowlen, buflen); dev_dbg(ep->dev->dev, "%s: 0x%p, %d bytes data%s:\n", str, req, buflen, (buflen > plen ? " (truncated)" : "")); print_hex_dump_debug(" ", DUMP_PREFIX_NONE, rowlen, 4, req->req.buf, plen, false); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson103100.00%1100.00%
Total103100.00%1100.00%


static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request, u16 value, u16 index, u16 length) { dev_vdbg(dev->dev, "REQ: %02x.%02x v%04x i%04x l%04x\n", type, request, value, index, length); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson45100.00%1100.00%
Total45100.00%1100.00%

#else /* !VERBOSE_DEBUG */
static void gr_dbgprint_request(const char *str, struct gr_ep *ep, struct gr_request *req) {}

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson20100.00%1100.00%
Total20100.00%1100.00%


static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request, u16 value, u16 index, u16 length) {}

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson25100.00%1100.00%
Total25100.00%1100.00%

#endif /* VERBOSE_DEBUG */ /* ---------------------------------------------------------------------- */ /* Debugfs functionality */ #ifdef CONFIG_USB_GADGET_DEBUG_FS
static void gr_seq_ep_show(struct seq_file *seq, struct gr_ep *ep) { u32 epctrl = gr_read32(&ep->regs->epctrl); u32 epstat = gr_read32(&ep->regs->epstat); int mode = (epctrl & GR_EPCTRL_TT_MASK) >> GR_EPCTRL_TT_POS; struct gr_request *req; seq_printf(seq, "%s:\n", ep->ep.name); seq_printf(seq, " mode = %s\n", gr_modestring[mode]); seq_printf(seq, " halted: %d\n", !!(epctrl & GR_EPCTRL_EH)); seq_printf(seq, " disabled: %d\n", !!(epctrl & GR_EPCTRL_ED)); seq_printf(seq, " valid: %d\n", !!(epctrl & GR_EPCTRL_EV)); seq_printf(seq, " dma_start = %d\n", ep->dma_start); seq_printf(seq, " stopped = %d\n", ep->stopped); seq_printf(seq, " wedged = %d\n", ep->wedged); seq_printf(seq, " callback = %d\n", ep->callback); seq_printf(seq, " maxpacket = %d\n", ep->ep.maxpacket); seq_printf(seq, " maxpacket_limit = %d\n", ep->ep.maxpacket_limit); seq_printf(seq, " bytes_per_buffer = %d\n", ep->bytes_per_buffer); if (mode == 1 || mode == 3) seq_printf(seq, " nt = %d\n", (epctrl & GR_EPCTRL_NT_MASK) >> GR_EPCTRL_NT_POS); seq_printf(seq, " Buffer 0: %s %s%d\n", epstat & GR_EPSTAT_B0 ? "valid" : "invalid", epstat & GR_EPSTAT_BS ? " " : "selected ", (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS); seq_printf(seq, " Buffer 1: %s %s%d\n", epstat & GR_EPSTAT_B1 ? "valid" : "invalid", epstat & GR_EPSTAT_BS ? "selected " : " ", (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS); if (list_empty(&ep->queue)) { seq_puts(seq, " Queue: empty\n\n"); return; } seq_puts(seq, " Queue:\n"); list_for_each_entry(req, &ep->queue, queue) { struct gr_dma_desc *desc; struct gr_dma_desc *next; seq_printf(seq, " 0x%p: 0x%p %d %d\n", req, &req->req.buf, req->req.actual, req->req.length); next = req->first_desc; do { desc = next; next = desc->next_desc; seq_printf(seq, " %c 0x%p (0x%08x): 0x%05x 0x%08x\n", desc == req->curr_desc ? 'c' : ' ', desc, desc->paddr, desc->ctrl, desc->data); } while (desc != req->last_desc); } seq_puts(seq, "\n"); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson437100.00%2100.00%
Total437100.00%2100.00%


static int gr_seq_show(struct seq_file *seq, void *v) { struct gr_udc *dev = seq->private; u32 control = gr_read32(&dev->regs->control); u32 status = gr_read32(&dev->regs->status); struct gr_ep *ep; seq_printf(seq, "usb state = %s\n", usb_state_string(dev->gadget.state)); seq_printf(seq, "address = %d\n", (control & GR_CONTROL_UA_MASK) >> GR_CONTROL_UA_POS); seq_printf(seq, "speed = %s\n", GR_SPEED_STR(status)); seq_printf(seq, "ep0state = %s\n", gr_ep0state_string(dev->ep0state)); seq_printf(seq, "irq_enabled = %d\n", dev->irq_enabled); seq_printf(seq, "remote_wakeup = %d\n", dev->remote_wakeup); seq_printf(seq, "test_mode = %d\n", dev->test_mode); seq_puts(seq, "\n"); list_for_each_entry(ep, &dev->ep_list, ep_list) gr_seq_ep_show(seq, ep); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson170100.00%1100.00%
Total170100.00%1100.00%


static int gr_dfs_open(struct inode *inode, struct file *file) { return single_open(file, gr_seq_show, inode->i_private); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson28100.00%1100.00%
Total28100.00%1100.00%

static const struct file_operations gr_dfs_fops = { .owner = THIS_MODULE, .open = gr_dfs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, };
static void gr_dfs_create(struct gr_udc *dev) { const char *name = "gr_udc_state"; dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL); dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev, &gr_dfs_fops); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson54100.00%1100.00%
Total54100.00%1100.00%


static void gr_dfs_delete(struct gr_udc *dev) { /* Handles NULL and ERR pointers internally */ debugfs_remove(dev->dfs_state); debugfs_remove(dev->dfs_root); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson26100.00%1100.00%
Total26100.00%1100.00%

#else /* !CONFIG_USB_GADGET_DEBUG_FS */
static void gr_dfs_create(struct gr_udc *dev) {}

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson10100.00%1100.00%
Total10100.00%1100.00%


static void gr_dfs_delete(struct gr_udc *dev) {}

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson10100.00%1100.00%
Total10100.00%1100.00%

#endif /* CONFIG_USB_GADGET_DEBUG_FS */ /* ---------------------------------------------------------------------- */ /* DMA and request handling */ /* Allocates a new struct gr_dma_desc, sets paddr and zeroes the rest */
static struct gr_dma_desc *gr_alloc_dma_desc(struct gr_ep *ep, gfp_t gfp_flags) { dma_addr_t paddr; struct gr_dma_desc *dma_desc; dma_desc = dma_pool_zalloc(ep->dev->desc_pool, gfp_flags, &paddr); if (!dma_desc) { dev_err(ep->dev->dev, "Could not allocate from DMA pool\n"); return NULL; } dma_desc->paddr = paddr; return dma_desc; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson6998.57%150.00%
saurabh sengarsaurabh sengar11.43%150.00%
Total70100.00%2100.00%


static inline void gr_free_dma_desc(struct gr_udc *dev, struct gr_dma_desc *desc) { dma_pool_free(dev->desc_pool, desc, (dma_addr_t)desc->paddr); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson33100.00%1100.00%
Total33100.00%1100.00%

/* Frees the chain of struct gr_dma_desc for the given request */
static void gr_free_dma_desc_chain(struct gr_udc *dev, struct gr_request *req) { struct gr_dma_desc *desc; struct gr_dma_desc *next; next = req->first_desc; if (!next) return; do { desc = next; next = desc->next_desc; gr_free_dma_desc(dev, desc); } while (desc != req->last_desc); req->first_desc = NULL; req->curr_desc = NULL; req->last_desc = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson85100.00%1100.00%
Total85100.00%1100.00%

static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req); /* * Frees allocated resources and calls the appropriate completion function/setup * package handler for a finished request. * * Must be called with dev->lock held and irqs disabled. */
static void gr_finish_request(struct gr_ep *ep, struct gr_request *req, int status) __releases(&dev->lock

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson20100.00%1100.00%
Total20100.00%1100.00%

) __acquires(&dev->lock) { struct gr_udc *dev; list_del_init(&req->queue); if (likely(req->req.status == -EINPROGRESS)) req->req.status = status; else status = req->req.status; dev = ep->dev; usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); gr_free_dma_desc_chain(dev, req); if (ep->is_in) { /* For OUT, req->req.actual gets updated bit by bit */ req->req.actual = req->req.length; } else if (req->oddlen && req->req.actual > req->evenlen) { /* * Copy to user buffer in this case where length was not evenly * divisible by ep->ep.maxpacket and the last descriptor was * actually used. */ char *buftail = ((char *)req->req.buf + req->evenlen); memcpy(buftail, ep->tailbuf, req->oddlen); if (req->req.actual > req->req.length) { /* We got more data than was requested */ dev_dbg(ep->dev->dev, "Overflow for ep %s\n", ep->ep.name); gr_dbgprint_request("OVFL", ep, req); req->req.status = -EOVERFLOW; } } if (!status) { if (ep->is_in) gr_dbgprint_request("SENT", ep, req); else gr_dbgprint_request("RECV", ep, req); } /* Prevent changes to ep->queue during callback */ ep->callback = 1; if (req == dev->ep0reqo && !status) { if (req->setup) gr_ep0_setup(dev, req); else dev_err(dev->dev, "Unexpected non setup packet on ep0in\n"); } else if (req->req.complete) { spin_unlock(&dev->lock); usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dev->lock); } ep->callback = 0; }
static struct usb_request *gr_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct gr_request *req; req = kzalloc(sizeof(*req), gfp_flags); if (!req) return NULL; INIT_LIST_HEAD(&req->queue); return &req->req; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson56100.00%1100.00%
Total56100.00%1100.00%

/* * Starts DMA for endpoint ep if there are requests in the queue. * * Must be called with dev->lock held and with !ep->stopped. */
static void gr_start_dma(struct gr_ep *ep) { struct gr_request *req; u32 dmactrl; if (list_empty(&ep->queue)) { ep->dma_start = 0; return; } req = list_first_entry(&ep->queue, struct gr_request, queue); /* A descriptor should already have been allocated */ BUG_ON(!req->curr_desc); /* * The DMA controller can not handle smaller OUT buffers than * ep->ep.maxpacket. It could lead to buffer overruns if an unexpectedly * long packet are received. Therefore an internal bounce buffer gets * used when such a request gets enabled. */ if (!ep->is_in && req->oddlen) req->last_desc->data = ep->tailbuf_paddr; wmb(); /* Make sure all is settled before handing it over to DMA */ /* Set the descriptor pointer in the hardware */ gr_write32(&ep->regs->dmaaddr, req->curr_desc->paddr); /* Announce available descriptors */ dmactrl = gr_read32(&ep->regs->dmactrl); gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_DA); ep->dma_start = 1; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson138100.00%2100.00%
Total138100.00%2100.00%

/* * Finishes the first request in the ep's queue and, if available, starts the * next request in queue. * * Must be called with dev->lock held, irqs disabled and with !ep->stopped. */
static void gr_dma_advance(struct gr_ep *ep, int status) { struct gr_request *req; req = list_first_entry(&ep->queue, struct gr_request, queue); gr_finish_request(ep, req, status); gr_start_dma(ep); /* Regardless of ep->dma_start */ }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson49100.00%1100.00%
Total49100.00%1100.00%

/* * Abort DMA for an endpoint. Sets the abort DMA bit which causes an ongoing DMA * transfer to be canceled and clears GR_DMACTRL_DA. * * Must be called with dev->lock held. */
static void gr_abort_dma(struct gr_ep *ep) { u32 dmactrl; dmactrl = gr_read32(&ep->regs->dmactrl); gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_AD); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson40100.00%1100.00%
Total40100.00%1100.00%

/* * Allocates and sets up a struct gr_dma_desc and putting it on the descriptor * chain. * * Size is not used for OUT endpoints. Hardware can not be instructed to handle * smaller buffer than MAXPL in the OUT direction. */
static int gr_add_dma_desc(struct gr_ep *ep, struct gr_request *req, dma_addr_t data, unsigned size, gfp_t gfp_flags) { struct gr_dma_desc *desc; desc = gr_alloc_dma_desc(ep, gfp_flags); if (!desc) return -ENOMEM; desc->data = data; if (ep->is_in) desc->ctrl = (GR_DESC_IN_CTRL_LEN_MASK & size) | GR_DESC_IN_CTRL_EN; else desc->ctrl = GR_DESC_OUT_CTRL_IE; if (!req->first_desc) { req->first_desc = desc; req->curr_desc = desc; } else { req->last_desc->next_desc = desc; req->last_desc->next = desc->paddr; req->last_desc->ctrl |= GR_DESC_OUT_CTRL_NX; } req->last_desc = desc; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson138100.00%1100.00%
Total138100.00%1100.00%

/* * Sets up a chain of struct gr_dma_descriptors pointing to buffers that * together covers req->req.length bytes of the buffer at DMA address * req->req.dma for the OUT direction. * * The first descriptor in the chain is enabled, the rest disabled. The * interrupt handler will later enable them one by one when needed so we can * find out when the transfer is finished. For OUT endpoints, all descriptors * therefore generate interrutps. */
static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags) { u16 bytes_left; /* Bytes left to provide descriptors for */ u16 bytes_used; /* Bytes accommodated for */ int ret = 0; req->first_desc = NULL; /* Signals that no allocation is done yet */ bytes_left = req->req.length; bytes_used = 0; while (bytes_left > 0) { dma_addr_t start = req->req.dma + bytes_used; u16 size = min(bytes_left, ep->bytes_per_buffer); if (size < ep->bytes_per_buffer) { /* Prepare using bounce buffer */ req->evenlen = req->req.length - bytes_left; req->oddlen = size; } ret = gr_add_dma_desc(ep, req, start, size, gfp_flags); if (ret) goto alloc_err; bytes_left -= size; bytes_used += size; } req->first_desc->ctrl |= GR_DESC_OUT_CTRL_EN; return 0; alloc_err: gr_free_dma_desc_chain(ep->dev, req); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson166100.00%2100.00%
Total166100.00%2100.00%

/* * Sets up a chain of struct gr_dma_descriptors pointing to buffers that * together covers req->req.length bytes of the buffer at DMA address * req->req.dma for the IN direction. * * When more data is provided than the maximum payload size, the hardware splits * this up into several payloads automatically. Moreover, ep->bytes_per_buffer * is always set to a multiple of the maximum payload (restricted to the valid * number of maximum payloads during high bandwidth isochronous or interrupt * transfers) * * All descriptors are enabled from the beginning and we only generate an * interrupt for the last one indicating that the entire request has been pushed * to hardware. */
static int gr_setup_in_desc_list(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags) { u16 bytes_left; /* Bytes left in req to provide descriptors for */ u16 bytes_used; /* Bytes in req accommodated for */ int ret = 0; req->first_desc = NULL; /* Signals that no allocation is done yet */ bytes_left = req->req.length; bytes_used = 0; do { /* Allow for zero length packets */ dma_addr_t start = req->req.dma + bytes_used; u16 size = min(bytes_left, ep->bytes_per_buffer); ret = gr_add_dma_desc(ep, req, start, size, gfp_flags); if (ret) goto alloc_err; bytes_left -= size; bytes_used += size; } while (bytes_left > 0); /* * Send an extra zero length packet to indicate that no more data is * available when req->req.zero is set and the data length is even * multiples of ep->ep.maxpacket. */ if (req->req.zero && (req->req.length % ep->ep.maxpacket == 0)) { ret = gr_add_dma_desc(ep, req, 0, 0, gfp_flags); if (ret) goto alloc_err; } /* * For IN packets we only want to know when the last packet has been * transmitted (not just put into internal buffers). */ req->last_desc->ctrl |= GR_DESC_IN_CTRL_PI; return 0; alloc_err: gr_free_dma_desc_chain(ep->dev, req); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson190100.00%1100.00%
Total190100.00%1100.00%

/* Must be called with dev->lock held */
static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags) { struct gr_udc *dev = ep->dev; int ret; if (unlikely(!ep->ep.desc && ep->num != 0)) { dev_err(dev->dev, "No ep descriptor for %s\n", ep->ep.name); return -EINVAL; } if (unlikely(!req->req.buf || !list_empty(&req->queue))) { dev_err(dev->dev, "Invalid request for %s: buf=%p list_empty=%d\n", ep->ep.name, req->req.buf, list_empty(&req->queue)); return -EINVAL; } if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { dev_err(dev->dev, "-ESHUTDOWN"); return -ESHUTDOWN; } /* Can't touch registers when suspended */ if (dev->ep0state == GR_EP0_SUSPEND) { dev_err(dev->dev, "-EBUSY"); return -EBUSY; } /* Set up DMA mapping in case the caller didn't */ ret = usb_gadget_map_request(&dev->gadget, &req->req, ep->is_in); if (ret) { dev_err(dev->dev, "usb_gadget_map_request"); return ret; } if (ep->is_in) ret = gr_setup_in_desc_list(ep, req, gfp_flags); else ret = gr_setup_out_desc_list(ep, req, gfp_flags); if (ret) return ret; req->req.status = -EINPROGRESS; req->req.actual = 0; list_add_tail(&req->queue, &ep->queue); /* Start DMA if not started, otherwise interrupt handler handles it */ if (!ep->dma_start && likely(!ep->stopped)) gr_start_dma(ep); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson311100.00%1100.00%
Total311100.00%1100.00%

/* * Queue a request from within the driver. * * Must be called with dev->lock held. */
static inline int gr_queue_int(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags) { if (ep->is_in) gr_dbgprint_request("RESP", ep, req); return gr_queue(ep, req, gfp_flags); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson45100.00%1100.00%
Total45100.00%1100.00%

/* ---------------------------------------------------------------------- */ /* General helper functions */ /* * Dequeue ALL requests. * * Must be called with dev->lock held and irqs disabled. */
static void gr_ep_nuke(struct gr_ep *ep) { struct gr_request *req; ep->stopped = 1; ep->dma_start = 0; gr_abort_dma(ep); while (!list_empty(&ep->queue)) { req = list_first_entry(&ep->queue, struct gr_request, queue); gr_finish_request(ep, req, -ESHUTDOWN); } }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson71100.00%1100.00%
Total71100.00%1100.00%

/* * Reset the hardware state of this endpoint. * * Must be called with dev->lock held. */
static void gr_ep_reset(struct gr_ep *ep) { gr_write32(&ep->regs->epctrl, 0); gr_write32(&ep->regs->dmactrl, 0); ep->ep.maxpacket = MAX_CTRL_PL_SIZE; ep->ep.desc = NULL; ep->stopped = 1; ep->dma_start = 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson63100.00%1100.00%
Total63100.00%1100.00%

/* * Generate STALL on ep0in/out. * * Must be called with dev->lock held. */
static void gr_control_stall(struct gr_udc *dev) { u32 epctrl; epctrl = gr_read32(&dev->epo[0].regs->epctrl); gr_write32(&dev->epo[0].regs->epctrl, epctrl | GR_EPCTRL_CS); epctrl = gr_read32(&dev->epi[0].regs->epctrl); gr_write32(&dev->epi[0].regs->epctrl, epctrl | GR_EPCTRL_CS); dev->ep0state = GR_EP0_STALL; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson92100.00%1100.00%
Total92100.00%1100.00%

/* * Halts, halts and wedges, or clears halt for an endpoint. * * Must be called with dev->lock held. */
static int gr_ep_halt_wedge(struct gr_ep *ep, int halt, int wedge, int fromhost) { u32 epctrl; int retval = 0; if (ep->num && !ep->ep.desc) return -EINVAL; if (ep->num && ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) return -EOPNOTSUPP; /* Never actually halt ep0, and therefore never clear halt for ep0 */ if (!ep->num) { if (halt && !fromhost) { /* ep0 halt from gadget - generate protocol stall */ gr_control_stall(ep->dev); dev_dbg(ep->dev->dev, "EP: stall ep0\n"); return 0; } return -EINVAL; } dev_dbg(ep->dev->dev, "EP: %s halt %s\n", (halt ? (wedge ? "wedge" : "set") : "clear"), ep->ep.name); epctrl = gr_read32(&ep->regs->epctrl); if (halt) { /* Set HALT */ gr_write32(&ep->regs->epctrl, epctrl | GR_EPCTRL_EH); ep->stopped = 1; if (wedge) ep->wedged = 1; } else { gr_write32(&ep->regs->epctrl, epctrl & ~GR_EPCTRL_EH); ep->stopped = 0; ep->wedged = 0; /* Things might have been queued up in the meantime */ if (!ep->dma_start) gr_start_dma(ep); } return retval; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson236100.00%1100.00%
Total236100.00%1100.00%

/* Must be called with dev->lock held */
static inline void gr_set_ep0state(struct gr_udc *dev, enum gr_ep0state value) { if (dev->ep0state != value) dev_vdbg(dev->dev, "STATE: ep0state=%s\n", gr_ep0state_string(value)); dev->ep0state = value; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson44100.00%1100.00%
Total44100.00%1100.00%

/* * Should only be called when endpoints can not generate interrupts. * * Must be called with dev->lock held. */
static void gr_disable_interrupts_and_pullup(struct gr_udc *dev) { gr_write32(&dev->regs->control, 0); wmb(); /* Make sure that we do not deny one of our interrupts */ dev->irq_enabled = 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson33100.00%1100.00%
Total33100.00%1100.00%

/* * Stop all device activity and disable data line pullup. * * Must be called with dev->lock held and irqs disabled. */
static void gr_stop_activity(struct gr_udc *dev) { struct gr_ep *ep; list_for_each_entry(ep, &dev->ep_list, ep_list) gr_ep_nuke(ep); gr_disable_interrupts_and_pullup(dev); gr_set_ep0state(dev, GR_EP0_DISCONNECT); usb_gadget_set_state(&dev->gadget, USB_STATE_NOTATTACHED); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson51100.00%1100.00%
Total51100.00%1100.00%

/* ---------------------------------------------------------------------- */ /* ep0 setup packet handling */
static void gr_ep0_testmode_complete(struct usb_ep *_ep, struct usb_request *_req) { struct gr_ep *ep; struct gr_udc *dev; u32 control; ep = container_of(_ep, struct gr_ep, ep); dev = ep->dev; spin_lock(&dev->lock); control = gr_read32(&dev->regs->control); control |= GR_CONTROL_TM | (dev->test_mode << GR_CONTROL_TS_POS); gr_write32(&dev->regs->control, control); spin_unlock(&dev->lock); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson99100.00%1100.00%
Total99100.00%1100.00%


static void gr_ep0_dummy_complete(struct usb_ep *_ep, struct usb_request *_req) { /* Nothing needs to be done here */ }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson17100.00%1100.00%
Total17100.00%1100.00%

/* * Queue a response on ep0in. * * Must be called with dev->lock held. */
static int gr_ep0_respond(struct gr_udc *dev, u8 *buf, int length, void (*complete)(struct usb_ep *ep, struct usb_request *req)) { u8 *reqbuf = dev->ep0reqi->req.buf; int status; int i; for (i = 0; i < length; i++) reqbuf[i] = buf[i]; dev->ep0reqi->req.length = length; dev->ep0reqi->req.complete = complete; status = gr_queue_int(&dev->epi[0], dev->ep0reqi, GFP_ATOMIC); if (status < 0) dev_err(dev->dev, "Could not queue ep0in setup response: %d\n", status); return status; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson135100.00%1100.00%
Total135100.00%1100.00%

/* * Queue a 2 byte response on ep0in. * * Must be called with dev->lock held. */
static inline int gr_ep0_respond_u16(struct gr_udc *dev, u16 response) { __le16 le_response = cpu_to_le16(response); return gr_ep0_respond(dev, (u8 *)&le_response, 2, gr_ep0_dummy_complete); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson40100.00%1100.00%
Total40100.00%1100.00%

/* * Queue a ZLP response on ep0in. * * Must be called with dev->lock held. */
static inline int gr_ep0_respond_empty(struct gr_udc *dev) { return gr_ep0_respond(dev, NULL, 0, gr_ep0_dummy_complete); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson24100.00%1100.00%
Total24100.00%1100.00%

/* * This is run when a SET_ADDRESS request is received. First writes * the new address to the control register which is updated internally * when the next IN packet is ACKED. * * Must be called with dev->lock held. */
static void gr_set_address(struct gr_udc *dev, u8 address) { u32 control; control = gr_read32(&dev->regs->control) & ~GR_CONTROL_UA_MASK; control |= (address << GR_CONTROL_UA_POS) & GR_CONTROL_UA_MASK; control |= GR_CONTROL_SU; gr_write32(&dev->regs->control, control); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson58100.00%1100.00%
Total58100.00%1100.00%

/* * Returns negative for STALL, 0 for successful handling and positive for * delegation. * * Must be called with dev->lock held. */
static int gr_device_request(struct gr_udc *dev, u8 type, u8 request, u16 value, u16 index) { u16 response; u8 test; switch (request) { case USB_REQ_SET_ADDRESS: dev_dbg(dev->dev, "STATUS: address %d\n", value & 0xff); gr_set_address(dev, value & 0xff); if (value) usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS); else usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT); return gr_ep0_respond_empty(dev); case USB_REQ_GET_STATUS: /* Self powered | remote wakeup */ response = 0x0001 | (dev->remote_wakeup ? 0x0002 : 0); return gr_ep0_respond_u16(dev, response); case USB_REQ_SET_FEATURE: switch (value) { case USB_DEVICE_REMOTE_WAKEUP: /* Allow remote wakeup */ dev->remote_wakeup = 1; return gr_ep0_respond_empty(dev); case USB_DEVICE_TEST_MODE: /* The hardware does not support TEST_FORCE_EN */ test = index >> 8; if (test >= TEST_J && test <= TEST_PACKET) { dev->test_mode = test; return gr_ep0_respond(dev, NULL, 0, gr_ep0_testmode_complete); } } break; case USB_REQ_CLEAR_FEATURE: switch (value) { case USB_DEVICE_REMOTE_WAKEUP: /* Disallow remote wakeup */ dev->remote_wakeup = 0; return gr_ep0_respond_empty(dev); } break; } return 1; /* Delegate the rest */ }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson213100.00%1100.00%
Total213100.00%1100.00%

/* * Returns negative for STALL, 0 for successful handling and positive for * delegation. * * Must be called with dev->lock held. */
static int gr_interface_request(struct gr_udc *dev, u8 type, u8 request, u16 value, u16 index) { if (dev->gadget.state != USB_STATE_CONFIGURED) return -1; /* * Should return STALL for invalid interfaces, but udc driver does not * know anything about that. However, many gadget drivers do not handle * GET_STATUS so we need to take care of that. */ switch (request) { case USB_REQ_GET_STATUS: return gr_ep0_respond_u16(dev, 0x0000); case USB_REQ_SET_FEATURE: case USB_REQ_CLEAR_FEATURE: /* * No possible valid standard requests. Still let gadget drivers * have a go at it. */ break; } return 1; /* Delegate the rest */ }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson67100.00%1100.00%
Total67100.00%1100.00%

/* * Returns negative for STALL, 0 for successful handling and positive for * delegation. * * Must be called with dev->lock held. */
static int gr_endpoint_request(struct gr_udc *dev, u8 type, u8 request, u16 value, u16 index) { struct gr_ep *ep; int status; int halted; u8 epnum = index & USB_ENDPOINT_NUMBER_MASK; u8 is_in = index & USB_ENDPOINT_DIR_MASK; if ((is_in && epnum >= dev->nepi) || (!is_in && epnum >= dev->nepo)) return -1; if (dev->gadget.state != USB_STATE_CONFIGURED && epnum != 0) return -1; ep = (is_in ? &dev->epi[epnum] : &dev->epo[epnum]); switch (request) { case USB_REQ_GET_STATUS: halted = gr_read32(&ep->regs->epctrl) & GR_EPCTRL_EH; return gr_ep0_respond_u16(dev, halted ? 0x0001 : 0); case USB_REQ_SET_FEATURE: switch (value) { case USB_ENDPOINT_HALT: status = gr_ep_halt_wedge(ep, 1, 0, 1); if (status >= 0) status = gr_ep0_respond_empty(dev); return status; } break; case USB_REQ_CLEAR_FEATURE: switch (value) { case USB_ENDPOINT_HALT: if (ep->wedged) return -1; status = gr_ep_halt_wedge(ep, 0, 0, 1); if (status >= 0) status = gr_ep0_respond_empty(dev); return status; } break; } return 1; /* Delegate the rest */ }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson248100.00%1100.00%
Total248100.00%1100.00%

/* Must be called with dev->lock held */
static void gr_ep0out_requeue(struct gr_udc *dev) { int ret = gr_queue_int(&dev->epo[0], dev->ep0reqo, GFP_ATOMIC); if (ret) dev_err(dev->dev, "Could not queue ep0out setup request: %d\n", ret); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson46100.00%1100.00%
Total46100.00%1100.00%

/* * The main function dealing with setup requests on ep0. * * Must be called with dev->lock held and irqs disabled */
static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req) __releases(&dev->lock

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson17100.00%1100.00%
Total17100.00%1100.00%

) __acquires(&dev->lock) { union { struct usb_ctrlrequest ctrl; u8 raw[8]; u32 word[2]; } u; u8 type; u8 request; u16 value; u16 index; u16 length; int i; int status; /* Restore from ep0 halt */ if (dev->ep0state == GR_EP0_STALL) { gr_set_ep0state(dev, GR_EP0_SETUP); if (!req->req.actual) goto out; } if (dev->ep0state == GR_EP0_ISTATUS) { gr_set_ep0state(dev, GR_EP0_SETUP); if (req->req.actual > 0) dev_dbg(dev->dev, "Unexpected setup packet at state %s\n", gr_ep0state_string(GR_EP0_ISTATUS)); else goto out; /* Got expected ZLP */ } else if (dev->ep0state != GR_EP0_SETUP) { dev_info(dev->dev, "Unexpected ep0out request at state %s - stalling\n", gr_ep0state_string(dev->ep0state)); gr_control_stall(dev); gr_set_ep0state(dev, GR_EP0_SETUP); goto out; } else if (!req->req.actual) { dev_dbg(dev->dev, "Unexpected ZLP at state %s\n", gr_ep0state_string(dev->ep0state)); goto out; } /* Handle SETUP packet */ for (i = 0; i < req->req.actual; i++) u.raw[i] = ((u8 *)req->req.buf)[i]; type = u.ctrl.bRequestType; request = u.ctrl.bRequest; value = le16_to_cpu(u.ctrl.wValue); index = le16_to_cpu(u.ctrl.wIndex); length = le16_to_cpu(u.ctrl.wLength); gr_dbgprint_devreq(dev, type, request, value, index, length); /* Check for data stage */ if (length) { if (type & USB_DIR_IN) gr_set_ep0state(dev, GR_EP0_IDATA); else gr_set_ep0state(dev, GR_EP0_ODATA); } status = 1; /* Positive status flags delegation */ if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (type & USB_RECIP_MASK) { case USB_RECIP_DEVICE: status = gr_device_request(dev, type, request, value, index); break; case USB_RECIP_ENDPOINT: status = gr_endpoint_request(dev, type, request, value, index); break; case USB_RECIP_INTERFACE: status = gr_interface_request(dev, type, request, value, index); break; } } if (status > 0) { spin_unlock(&dev->lock); dev_vdbg(dev->dev, "DELEGATE\n"); status = dev->driver->setup(&dev->gadget, &u.ctrl); spin_lock(&dev->lock); } /* Generate STALL on both ep0out and ep0in if requested */ if (unlikely(status < 0)) { dev_vdbg(dev->dev, "STALL\n"); gr_control_stall(dev); } if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD && request == USB_REQ_SET_CONFIGURATION) { if (!value) { dev_dbg(dev->dev, "STATUS: deconfigured\n"); usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS); } else if (status >= 0) { /* Not configured unless gadget OK:s it */ dev_dbg(dev->dev, "STATUS: configured: %d\n", value); usb_gadget_set_state(&dev->gadget, USB_STATE_CONFIGURED); } } /* Get ready for next stage */ if (dev->ep0state == GR_EP0_ODATA) gr_set_ep0state(dev, GR_EP0_OSTATUS); else if (dev->ep0state == GR_EP0_IDATA) gr_set_ep0state(dev, GR_EP0_ISTATUS); else gr_set_ep0state(dev, GR_EP0_SETUP); out: gr_ep0out_requeue(dev); } /* ---------------------------------------------------------------------- */ /* VBUS and USB reset handling */ /* Must be called with dev->lock held and irqs disabled */
static void gr_vbus_connected(struct gr_udc *dev, u32 status) { u32 control; dev->gadget.speed = GR_SPEED(status); usb_gadget_set_state(&dev->gadget, USB_STATE_POWERED); /* Turn on full interrupts and pullup */ control = (GR_CONTROL_SI | GR_CONTROL_UI | GR_CONTROL_VI | GR_CONTROL_SP | GR_CONTROL_EP); gr_write32(&dev->regs->control, control); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson65100.00%1100.00%
Total65100.00%1100.00%

/* Must be called with dev->lock held */
static void gr_enable_vbus_detect(struct gr_udc *dev) { u32 status; dev->irq_enabled = 1; wmb(); /* Make sure we do not ignore an interrupt */ gr_write32(&dev->regs->control, GR_CONTROL_VI); /* Take care of the case we are already plugged in at this point */ status = gr_read32(&dev->regs->status); if (status & GR_STATUS_VB) gr_vbus_connected(dev, status); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson62100.00%1100.00%
Total62100.00%1100.00%

/* Must be called with dev->lock held and irqs disabled */
static void gr_vbus_disconnected(struct gr_udc *dev) { gr_stop_activity(dev); /* Report disconnect */ if (dev->driver && dev->driver->disconnect) { spin_unlock(&dev->lock); dev->driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } gr_enable_vbus_detect(dev); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson64100.00%1100.00%
Total64100.00%1100.00%

/* Must be called with dev->lock held and irqs disabled */
static void gr_udc_usbreset(struct gr_udc *dev, u32 status) { gr_set_address(dev, 0); gr_set_ep0state(dev, GR_EP0_SETUP); usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT); dev->gadget.speed = GR_SPEED(status); gr_ep_nuke(&dev->epo[0]); gr_ep_nuke(&dev->epi[0]); dev->epo[0].stopped = 0; dev->epi[0].stopped = 0; gr_ep0out_requeue(dev); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson98100.00%1100.00%
Total98100.00%1100.00%

/* ---------------------------------------------------------------------- */ /* Irq handling */ /* * Handles interrupts from in endpoints. Returns whether something was handled. * * Must be called with dev->lock held, irqs disabled and with !ep->stopped. */
static int gr_handle_in_ep(struct gr_ep *ep) { struct gr_request *req; req = list_first_entry(&ep->queue, struct gr_request, queue); if (!req->last_desc) return 0; if (ACCESS_ONCE(req->last_desc->ctrl) & GR_DESC_IN_CTRL_EN) return 0; /* Not put in hardware buffers yet */ if (gr_read32(&ep->regs->epstat) & (GR_EPSTAT_B1 | GR_EPSTAT_B0)) return 0; /* Not transmitted yet, still in hardware buffers */ /* Write complete */ gr_dma_advance(ep, 0); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson91100.00%1100.00%
Total91100.00%1100.00%

/* * Handles interrupts from out endpoints. Returns whether something was handled. * * Must be called with dev->lock held, irqs disabled and with !ep->stopped. */
static int gr_handle_out_ep(struct gr_ep *ep) { u32 ep_dmactrl; u32 ctrl; u16 len; struct gr_request *req; struct gr_udc *dev = ep->dev; req = list_first_entry(&ep->queue, struct gr_request, queue); if (!req->curr_desc) return 0; ctrl = ACCESS_ONCE(req->curr_desc->ctrl); if (ctrl & GR_DESC_OUT_CTRL_EN) return 0; /* Not received yet */ /* Read complete */ len = ctrl & GR_DESC_OUT_CTRL_LEN_MASK; req->req.actual += len; if (ctrl & GR_DESC_OUT_CTRL_SE) req->setup = 1; if (len < ep->ep.maxpacket || req->req.actual >= req->req.length) { /* Short packet or >= expected size - we are done */ if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) { /* * Send a status stage ZLP to ack the DATA stage in the * OUT direction. This needs to be done before * gr_dma_advance as that can lead to a call to * ep0_setup that can change dev->ep0state. */ gr_ep0_respond_empty(dev); gr_set_ep0state(dev, GR_EP0_SETUP); } gr_dma_advance(ep, 0); } else { /* Not done yet. Enable the next descriptor to receive more. */ req->curr_desc = req->curr_desc->next_desc; req->curr_desc->ctrl |= GR_DESC_OUT_CTRL_EN; ep_dmactrl = gr_read32(&ep->regs->dmactrl); gr_write32(&ep->regs->dmactrl, ep_dmactrl | GR_DMACTRL_DA); } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson227100.00%2100.00%
Total227100.00%2100.00%

/* * Handle state changes. Returns whether something was handled. * * Must be called with dev->lock held and irqs disabled. */
static int gr_handle_state_changes(struct gr_udc *dev) { u32 status = gr_read32(&dev->regs->status); int handled = 0; int powstate = !(dev->gadget.state == USB_STATE_NOTATTACHED || dev->gadget.state == USB_STATE_ATTACHED); /* VBUS valid detected */ if (!powstate && (status & GR_STATUS_VB)) { dev_dbg(dev->dev, "STATUS: vbus valid detected\n"); gr_vbus_connected(dev, status); handled = 1; } /* Disconnect */ if (powstate && !(status & GR_STATUS_VB)) { dev_dbg(dev->dev, "STATUS: vbus invalid detected\n"); gr_vbus_disconnected(dev); handled = 1; } /* USB reset detected */ if (status & GR_STATUS_UR) { dev_dbg(dev->dev, "STATUS: USB reset - speed is %s\n", GR_SPEED_STR(status)); gr_write32(&dev->regs->status, GR_STATUS_UR); gr_udc_usbreset(dev, status); handled = 1; } /* Speed change */ if (dev->gadget.speed != GR_SPEED(status)) { dev_dbg(dev->dev, "STATUS: USB Speed change to %s\n", GR_SPEED_STR(status)); dev->gadget.speed = GR_SPEED(status); handled = 1; } /* Going into suspend */ if ((dev->ep0state != GR_EP0_SUSPEND) && !(status & GR_STATUS_SU)) { dev_dbg(dev->dev, "STATUS: USB suspend\n"); gr_set_ep0state(dev, GR_EP0_SUSPEND); dev->suspended_from = dev->gadget.state; usb_gadget_set_state(&dev->gadget, USB_STATE_SUSPENDED); if ((dev->gadget.speed != USB_SPEED_UNKNOWN) && dev->driver && dev->driver->suspend) { spin_unlock(&dev->lock); dev->driver->suspend(&dev->gadget); spin_lock(&dev->lock); } handled = 1; } /* Coming out of suspend */ if ((dev->ep0state == GR_EP0_SUSPEND) && (status & GR_STATUS_SU)) { dev_dbg(dev->dev, "STATUS: USB resume\n"); if (dev->suspended_from == USB_STATE_POWERED) gr_set_ep0state(dev, GR_EP0_DISCONNECT); else gr_set_ep0state(dev, GR_EP0_SETUP); usb_gadget_set_state(&dev->gadget, dev->suspended_from); if ((dev->gadget.speed != USB_SPEED_UNKNOWN) && dev->driver && dev->driver->resume) { spin_unlock(&dev->lock); dev->driver->resume(&dev->gadget); spin_lock(&dev->lock); } handled = 1; } return handled; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson442100.00%1100.00%
Total442100.00%1100.00%

/* Non-interrupt context irq handler */
static irqreturn_t gr_irq_handler(int irq, void *_dev) { struct gr_udc *dev = _dev; struct gr_ep *ep; int handled = 0; int i; unsigned long flags; spin_lock_irqsave(&dev->lock, flags); if (!dev->irq_enabled) goto out; /* * Check IN ep interrupts. We check these before the OUT eps because * some gadgets reuse the request that might already be currently * outstanding and needs to be completed (mainly setup requests). */ for (i = 0; i < dev->nepi; i++) { ep = &dev->epi[i]; if (!ep->stopped && !ep->callback && !list_empty(&ep->queue)) handled = gr_handle_in_ep(ep) || handled; } /* Check OUT ep interrupts */ for (i = 0; i < dev->nepo; i++) { ep = &dev->epo[i]; if (!ep->stopped && !ep->callback && !list_empty(&ep->queue)) handled = gr_handle_out_ep(ep) || handled; } /* Check status interrupts */ handled = gr_handle_state_changes(dev) || handled; /* * Check AMBA DMA errors. Only check if we didn't find anything else to * handle because this shouldn't happen if we did everything right. */ if (!handled) { list_for_each_entry(ep, &dev->ep_list, ep_list) { if (gr_read32(&ep->regs->dmactrl) & GR_DMACTRL_AE) { dev_err(dev->dev, "AMBA Error occurred for %s\n", ep->ep.name); handled = 1; } } } out: spin_unlock_irqrestore(&dev->lock, flags); return handled ? IRQ_HANDLED : IRQ_NONE; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson255100.00%1100.00%
Total255100.00%1100.00%

/* Interrupt context irq handler */
static irqreturn_t gr_irq(int irq, void *_dev) { struct gr_udc *dev = _dev; if (!dev->irq_enabled) return IRQ_NONE; return IRQ_WAKE_THREAD; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson33100.00%1100.00%
Total33100.00%1100.00%

/* ---------------------------------------------------------------------- */ /* USB ep ops */ /* Enable endpoint. Not for ep0in and ep0out that are handled separately. */
static int gr_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct gr_udc *dev; struct gr_ep *ep; u8 mode; u8 nt; u16 max; u16 buffer_size = 0; u32 epctrl; ep = container_of(_ep, struct gr_ep, ep); if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; dev = ep->dev; /* 'ep0' IN and OUT are reserved */ if (ep == &dev->epo[0] || ep == &dev->epi[0]) return -EINVAL; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; /* Make sure we are clear for enabling */ epctrl = gr_read32(&ep->regs->epctrl); if (epctrl & GR_EPCTRL_EV) return -EBUSY; /* Check that directions match */ if (!ep->is_in != !usb_endpoint_dir_in(desc)) return -EINVAL; /* Check ep num */ if ((!ep->is_in && ep->num >= dev->nepo) || (ep->is_in && ep->num >= dev->nepi)) return -EINVAL; if (usb_endpoint_xfer_control(desc)) { mode = 0; } else if (usb_endpoint_xfer_isoc(desc)) { mode = 1; } else if (usb_endpoint_xfer_bulk(desc)) { mode = 2; } else if (usb_endpoint_xfer_int(desc)) { mode = 3; } else { dev_err(dev->dev, "Unknown transfer type for %s\n", ep->ep.name); return -EINVAL; } /* * Bits 10-0 set the max payload. 12-11 set the number of * additional transactions. */ max = 0x7ff & usb_endpoint_maxp(desc); nt = 0x3 & (usb_endpoint_maxp(desc) >> 11); buffer_size = GR_BUFFER_SIZE(epctrl); if (nt && (mode == 0 || mode == 2)) { dev_err(dev->dev, "%s mode: multiple trans./microframe not valid\n", (mode == 2 ? "Bulk" : "Control")); return -EINVAL; } else if (nt == 0x3) { dev_err(dev->dev, "Invalid value 0x3 for additional trans./microframe\n"); return -EINVAL; } else if ((nt + 1) * max > buffer_size) { dev_err(dev->dev, "Hw buffer size %d < max payload %d * %d\n", buffer_size, (nt + 1), max); return -EINVAL; } else if (max == 0) { dev_err(dev->dev, "Max payload cannot be set to 0\n"); return -EINVAL; } else if (max > ep->ep.maxpacket_limit) { dev_err(dev->dev, "Requested max payload %d > limit %d\n", max, ep->ep.maxpacket_limit); return -EINVAL; } spin_lock(&ep->dev->lock); if (!ep->stopped) { spin_unlock(&ep->dev->lock); return -EBUSY; } ep->stopped = 0; ep->wedged = 0; ep->ep.desc = desc; ep->ep.maxpacket = max; ep->dma_start = 0; if (nt) { /* * Maximum possible size of all payloads in one microframe * regardless of direction when using high-bandwidth mode. */ ep->bytes_per_buffer = (nt + 1) * max; } else if (ep->is_in) { /* * The biggest multiple of maximum packet size that fits into * the buffer. The hardware will split up into many packets in * the IN direction. */ ep->bytes_per_buffer = (buffer_size / max) * max; } else { /* * Only single packets will be placed the buffers in the OUT * direction. */ ep->bytes_per_buffer = max; } epctrl = (max << GR_EPCTRL_MAXPL_POS) | (nt << GR_EPCTRL_NT_POS) | (mode << GR_EPCTRL_TT_POS) | GR_EPCTRL_EV; if (ep->is_in) epctrl |= GR_EPCTRL_PI; gr_write32(&ep->regs->epctrl, epctrl); gr_write32(&ep->regs->dmactrl, GR_DMACTRL_IE | GR_DMACTRL_AI); spin_unlock(&ep->dev->lock); dev_dbg(ep->dev->dev, "EP: %s enabled - %s with %d bytes/buffer\n", ep->ep.name, gr_modestring[mode], ep->bytes_per_buffer); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson680100.00%3100.00%
Total680100.00%3100.00%

/* Disable endpoint. Not for ep0in and ep0out that are handled separately. */
static int gr_ep_disable(struct usb_ep *_ep) { struct gr_ep *ep; struct gr_udc *dev; unsigned long flags; ep = container_of(_ep, struct gr_ep, ep); if (!_ep || !ep->ep.desc) return -ENODEV; dev = ep->dev; /* 'ep0' IN and OUT are reserved */ if (ep == &dev->epo[0] || ep == &dev->epi[0]) return -EINVAL; if (dev->ep0state == GR_EP0_SUSPEND) return -EBUSY; dev_dbg(ep->dev->dev, "EP: disable %s\n", ep->ep.name); spin_lock_irqsave(&dev->lock, flags); gr_ep_nuke(ep); gr_ep_reset(ep); ep->ep.desc = NULL; spin_unlock_irqrestore(&dev->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson156100.00%1100.00%
Total156100.00%1100.00%

/* * Frees a request, but not any DMA buffers associated with it * (gr_finish_request should already have taken care of that). */
static void gr_free_request(struct usb_ep *_ep, struct usb_request *_req) { struct gr_request *req; if (!_ep || !_req) return; req = container_of(_req, struct gr_request, req); /* Leads to memory leak */ WARN(!list_empty(&req->queue), "request not dequeued properly before freeing\n"); kfree(req); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson62100.00%1100.00%
Total62100.00%1100.00%

/* Queue a request from the gadget */
static int gr_queue_ext(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { struct gr_ep *ep; struct gr_request *req; struct gr_udc *dev; int ret; if (unlikely(!_ep || !_req)) return -EINVAL; ep = container_of(_ep, struct gr_ep, ep); req = container_of(_req, struct gr_request, req); dev = ep->dev; spin_lock(&ep->dev->lock); /* * The ep0 pointer in the gadget struct is used both for ep0in and * ep0out. In a data stage in the out direction ep0out needs to be used * instead of the default ep0in. Completion functions might use * driver_data, so that needs to be copied as well. */ if ((ep == &dev->epi[0]) && (dev->ep0state == GR_EP0_ODATA)) { ep = &dev->epo[0]; ep->ep.driver_data = dev->epi[0].ep.driver_data; } if (ep->is_in) gr_dbgprint_request("EXTERN", ep, req); ret = gr_queue(ep, req, GFP_ATOMIC); spin_unlock(&ep->dev->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson18299.45%150.00%
alexey khoroshilovalexey khoroshilov10.55%150.00%
Total183100.00%2100.00%

/* Dequeue JUST ONE request */
static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct gr_request *req; struct gr_ep *ep; struct gr_udc *dev; int ret = 0; unsigned long flags; ep = container_of(_ep, struct gr_ep, ep); if (!_ep || !_req || (!ep->ep.desc && ep->num != 0)) return -EINVAL; dev = ep->dev; if (!dev->driver) return -ESHUTDOWN; /* We can't touch (DMA) registers when suspended */ if (dev->ep0state == GR_EP0_SUSPEND) return -EBUSY; spin_lock_irqsave(&dev->lock, flags); /* Make sure it's actually queued on this endpoint */ list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) break; } if (&req->req != _req) { ret = -EINVAL; goto out; } if (list_first_entry(&ep->queue, struct gr_request, queue) == req) { /* This request is currently being processed */ gr_abort_dma(ep); if (ep->stopped) gr_finish_request(ep, req, -ECONNRESET); else gr_dma_advance(ep, -ECONNRESET); } else if (!list_empty(&req->queue)) { /* Not being processed - gr_finish_request dequeues it */ gr_finish_request(ep, req, -ECONNRESET); } else { ret = -EOPNOTSUPP; } out: spin_unlock_irqrestore(&dev->lock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson257100.00%1100.00%
Total257100.00%1100.00%

/* Helper for gr_set_halt and gr_set_wedge */
static int gr_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) { int ret; struct gr_ep *ep; if (!_ep) return -ENODEV; ep = container_of(_ep, struct gr_ep, ep); spin_lock(&ep->dev->lock); /* Halting an IN endpoint should fail if queue is not empty */ if (halt && ep->is_in && !list_empty(&ep->queue)) { ret = -EAGAIN; goto out; } ret = gr_ep_halt_wedge(ep, halt, wedge, 0); out: spin_unlock(&ep->dev->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson112100.00%1100.00%
Total112100.00%1100.00%

/* Halt endpoint */
static int gr_set_halt(struct usb_ep *_ep, int halt) { return gr_set_halt_wedge(_ep, halt, 0); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson24100.00%1100.00%
Total24100.00%1100.00%

/* Halt and wedge endpoint */
static int gr_set_wedge(struct usb_ep *_ep) { return gr_set_halt_wedge(_ep, 1, 1); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson21100.00%1100.00%
Total21100.00%1100.00%

/* * Return the total number of bytes currently stored in the internal buffers of * the endpoint. */
static int gr_fifo_status(struct usb_ep *_ep) { struct gr_ep *ep; u32 epstat; u32 bytes = 0; if (!_ep) return -ENODEV; ep = container_of(_ep, struct gr_ep, ep); epstat = gr_read32(&ep->regs->epstat); if (epstat & GR_EPSTAT_B0) bytes += (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS; if (epstat & GR_EPSTAT_B1) bytes += (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS; return bytes; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson92100.00%1100.00%
Total92100.00%1100.00%

/* Empty data from internal buffers of an endpoint. */
static void gr_fifo_flush(struct usb_ep *_ep) { struct gr_ep *ep; u32 epctrl; if (!_ep) return; ep = container_of(_ep, struct gr_ep, ep); dev_vdbg(ep->dev->dev, "EP: flush fifo %s\n", ep->ep.name); spin_lock(&ep->dev->lock); epctrl = gr_read32(&ep->regs->epctrl); epctrl |= GR_EPCTRL_CB; gr_write32(&ep->regs->epctrl, epctrl); spin_unlock(&ep->dev->lock); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson102100.00%1100.00%
Total102100.00%1100.00%

static struct usb_ep_ops gr_ep_ops = { .enable = gr_ep_enable, .disable = gr_ep_disable, .alloc_request = gr_alloc_request, .free_request = gr_free_request, .queue = gr_queue_ext, .dequeue = gr_dequeue, .set_halt = gr_set_halt, .set_wedge = gr_set_wedge, .fifo_status = gr_fifo_status, .fifo_flush = gr_fifo_flush, }; /* ---------------------------------------------------------------------- */ /* USB Gadget ops */
static int gr_get_frame(struct usb_gadget *_gadget) { struct gr_udc *dev; if (!_gadget) return -ENODEV; dev = container_of(_gadget, struct gr_udc, gadget); return gr_read32(&dev->regs->status) & GR_STATUS_FN_MASK; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson50100.00%1100.00%
Total50100.00%1100.00%


static int gr_wakeup(struct usb_gadget *_gadget) { struct gr_udc *dev; if (!_gadget) return -ENODEV; dev = container_of(_gadget, struct gr_udc, gadget); /* Remote wakeup feature not enabled by host*/ if (!dev->remote_wakeup) return -EINVAL; spin_lock(&dev->lock); gr_write32(&dev->regs->control, gr_read32(&dev->regs->control) | GR_CONTROL_RW); spin_unlock(&dev->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson90100.00%1100.00%
Total90100.00%1100.00%


static int gr_pullup(struct usb_gadget *_gadget, int is_on) { struct gr_udc *dev; u32 control; if (!_gadget) return -ENODEV; dev = container_of(_gadget, struct gr_udc, gadget); spin_lock(&dev->lock); control = gr_read32(&dev->regs->control); if (is_on) control |= GR_CONTROL_EP; else control &= ~GR_CONTROL_EP; gr_write32(&dev->regs->control, control); spin_unlock(&dev->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson100100.00%1100.00%
Total100100.00%1100.00%


static int gr_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { struct gr_udc *dev = to_gr_udc(gadget); spin_lock(&dev->lock); /* Hook up the driver */ driver->driver.bus = NULL; dev->driver = driver; /* Get ready for host detection */ gr_enable_vbus_detect(dev); spin_unlock(&dev->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson66100.00%1100.00%
Total66100.00%1100.00%


static int gr_udc_stop(struct usb_gadget *gadget) { struct gr_udc *dev = to_gr_udc(gadget); unsigned long flags; spin_lock_irqsave(&dev->lock, flags); dev->driver = NULL; gr_stop_activity(dev); spin_unlock_irqrestore(&dev->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson59100.00%1100.00%
Total59100.00%1100.00%

static const struct usb_gadget_ops gr_ops = { .get_frame = gr_get_frame, .wakeup = gr_wakeup, .pullup = gr_pullup, .udc_start = gr_udc_start, .udc_stop = gr_udc_stop, /* Other operations not supported */ }; /* ---------------------------------------------------------------------- */ /* Module probe, removal and of-matching */ static const char * const onames[] = { "ep0out", "ep1out", "ep2out", "ep3out", "ep4out", "ep5out", "ep6out", "ep7out", "ep8out", "ep9out", "ep10out", "ep11out", "ep12out", "ep13out", "ep14out", "ep15out" }; static const char * const inames[] = { "ep0in", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in", "ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in", "ep12in", "ep13in", "ep14in", "ep15in" }; /* Must be called with dev->lock held */
static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit) { struct gr_ep *ep; struct gr_request *req; struct usb_request *_req; void *buf; if (is_in) { ep = &dev->epi[num]; ep->ep.name = inames[num]; ep->regs = &dev->regs->epi[num]; } else { ep = &dev->epo[num]; ep->ep.name = onames[num]; ep->regs = &dev->regs->epo[num]; } gr_ep_reset(ep); ep->num = num; ep->is_in = is_in; ep->dev = dev; ep->ep.ops = &gr_ep_ops; INIT_LIST_HEAD(&ep->queue); if (num == 0) { _req = gr_alloc_request(&ep->ep, GFP_ATOMIC); buf = devm_kzalloc(dev->dev, PAGE_SIZE, GFP_DMA | GFP_ATOMIC); if (!_req || !buf) { /* possible _req freed by gr_probe via gr_remove */ return -ENOMEM; } req = container_of(_req, struct gr_request, req); req->req.buf = buf; req->req.length = MAX_CTRL_PL_SIZE; if (is_in) dev->ep0reqi = req; /* Complete gets set as used */ else dev->ep0reqo = req; /* Completion treated separately */ usb_ep_set_maxpacket_limit(&ep->ep, MAX_CTRL_PL_SIZE); ep->bytes_per_buffer = MAX_CTRL_PL_SIZE; ep->ep.caps.type_control = true; } else { usb_ep_set_maxpacket_limit(&ep->ep, (u16)maxplimit); list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); ep->ep.caps.type_iso = true; ep->ep.caps.type_bulk = true; ep->ep.caps.type_int = true; } list_add_tail(&ep->ep_list, &dev->ep_list); if (is_in) ep->ep.caps.dir_in = true; else ep->ep.caps.dir_out = true; ep->tailbuf = dma_alloc_coherent(dev->dev, ep->ep.maxpacket_limit, &ep->tailbuf_paddr, GFP_ATOMIC); if (!ep->tailbuf) return -ENOMEM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson35584.52%375.00%
robert baldygarobert baldyga6515.48%125.00%
Total420100.00%4100.00%

/* Must be called with dev->lock held */
static int gr_udc_init(struct gr_udc *dev) { struct device_node *np = dev->dev->of_node; u32 epctrl_val; u32 dmactrl_val; int i; int ret = 0; u32 bufsize; gr_set_address(dev, 0); INIT_LIST_HEAD(&dev->gadget.ep_list); dev->gadget.speed = USB_SPEED_UNKNOWN; dev->gadget.ep0 = &dev->epi[0].ep; INIT_LIST_HEAD(&dev->ep_list); gr_set_ep0state(dev, GR_EP0_DISCONNECT); for (i = 0; i < dev->nepo; i++) { if (of_property_read_u32_index(np, "epobufsizes", i, &bufsize)) bufsize = 1024; ret = gr_ep_init(dev, i, 0, bufsize); if (ret) return ret; } for (i = 0; i < dev->nepi; i++) { if (of_property_read_u32_index(np, "epibufsizes", i, &bufsize)) bufsize = 1024; ret = gr_ep_init(dev, i, 1, bufsize); if (ret) return ret; } /* Must be disabled by default */ dev->remote_wakeup = 0; /* Enable ep0out and ep0in */ epctrl_val = (MAX_CTRL_PL_SIZE << GR_EPCTRL_MAXPL_POS) | GR_EPCTRL_EV; dmactrl_val = GR_DMACTRL_IE | GR_DMACTRL_AI; gr_write32(&dev->epo[0].regs->epctrl, epctrl_val); gr_write32(&dev->epi[0].regs->epctrl, epctrl_val | GR_EPCTRL_PI); gr_write32(&dev->epo[0].regs->dmactrl, dmactrl_val); gr_write32(&dev->epi[0].regs->dmactrl, dmactrl_val); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson302100.00%2100.00%
Total302100.00%2100.00%


static void gr_ep_remove(struct gr_udc *dev, int num, int is_in) { struct gr_ep *ep; if (is_in) ep = &dev->epi[num]; else ep = &dev->epo[num]; if (ep->tailbuf) dma_free_coherent(dev->dev, ep->ep.maxpacket_limit, ep->tailbuf, ep->tailbuf_paddr); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson74100.00%1100.00%
Total74100.00%1100.00%


static int gr_remove(struct platform_device *pdev) { struct gr_udc *dev = platform_get_drvdata(pdev); int i; if (dev->added) usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */ if (dev->driver) return -EBUSY; gr_dfs_delete(dev); dma_pool_destroy(dev->desc_pool); platform_set_drvdata(pdev, NULL); gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req); gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req); for (i = 0; i < dev->nepo; i++) gr_ep_remove(dev, i, 0); for (i = 0; i < dev->nepi; i++) gr_ep_remove(dev, i, 1); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson159100.00%3100.00%
Total159100.00%3100.00%


static int gr_request_irq(struct gr_udc *dev, int irq) { return devm_request_threaded_irq(dev->dev, irq, gr_irq, gr_irq_handler, IRQF_SHARED, driver_name, dev); }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson34100.00%1100.00%
Total34100.00%1100.00%


static int gr_probe(struct platform_device *pdev) { struct gr_udc *dev; struct resource *res; struct gr_regs __iomem *regs; int retval; u32 status; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev->dev, res); if (IS_ERR(regs)) return PTR_ERR(regs); dev->irq = platform_get_irq(pdev, 0); if (dev->irq <= 0) { dev_err(dev->dev, "No irq found\n"); return -ENODEV; } /* Some core configurations has separate irqs for IN and OUT events */ dev->irqi = platform_get_irq(pdev, 1); if (dev->irqi > 0) { dev->irqo = platform_get_irq(pdev, 2); if (dev->irqo <= 0) { dev_err(dev->dev, "Found irqi but not irqo\n"); return -ENODEV; } } else { dev->irqi = 0; } dev->gadget.name = driver_name; dev->gadget.max_speed = USB_SPEED_HIGH; dev->gadget.ops = &gr_ops; spin_lock_init(&dev->lock); dev->regs = regs; platform_set_drvdata(pdev, dev); /* Determine number of endpoints and data interface mode */ status = gr_read32(&dev->regs->status); dev->nepi = ((status & GR_STATUS_NEPI_MASK) >> GR_STATUS_NEPI_POS) + 1; dev->nepo = ((status & GR_STATUS_NEPO_MASK) >> GR_STATUS_NEPO_POS) + 1; if (!(status & GR_STATUS_DM)) { dev_err(dev->dev, "Slave mode cores are not supported\n"); return -ENODEV; } /* --- Effects of the following calls might need explicit cleanup --- */ /* Create DMA pool for descriptors */ dev->desc_pool = dma_pool_create("desc_pool", dev->dev, sizeof(struct gr_dma_desc), 4, 0); if (!dev->desc_pool) { dev_err(dev->dev, "Could not allocate DMA pool"); return -ENOMEM; } spin_lock(&dev->lock); /* Inside lock so that no gadget can use this udc until probe is done */ retval = usb_add_gadget_udc(dev->dev, &dev->gadget); if (retval) { dev_err(dev->dev, "Could not add gadget udc"); goto out; } dev->added = 1; retval = gr_udc_init(dev); if (retval) goto out; gr_dfs_create(dev); /* Clear all interrupt enables that might be left on since last boot */ gr_disable_interrupts_and_pullup(dev); retval = gr_request_irq(dev, dev->irq); if (retval) { dev_err(dev->dev, "Failed to request irq %d\n", dev->irq); goto out; } if (dev->irqi) { retval = gr_request_irq(dev, dev->irqi); if (retval) { dev_err(dev->dev, "Failed to request irqi %d\n", dev->irqi); goto out; } retval = gr_request_irq(dev, dev->irqo); if (retval) { dev_err(dev->dev, "Failed to request irqo %d\n", dev->irqo); goto out; } } if (dev->irqi) dev_info(dev->dev, "regs: %p, irqs %d, %d, %d\n", dev->regs, dev->irq, dev->irqi, dev->irqo); else dev_info(dev->dev, "regs: %p, irq %d\n", dev->regs, dev->irq); out: spin_unlock(&dev->lock); if (retval) gr_remove(pdev); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson615100.00%3100.00%
Total615100.00%3100.00%

static const struct of_device_id gr_match[] = { {.name = "GAISLER_USBDC"}, {.name = "01_021"}, {}, }; MODULE_DEVICE_TABLE(of, gr_match); static struct platform_driver gr_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = gr_match, }, .probe = gr_probe, .remove = gr_remove, }; module_platform_driver(gr_driver); MODULE_AUTHOR("Aeroflex Gaisler AB."); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
andreas larssonandreas larsson1031599.34%964.29%
robert baldygarobert baldyga650.63%17.14%
michal sojkamichal sojka10.01%17.14%
jingoo hanjingoo han10.01%17.14%
alexey khoroshilovalexey khoroshilov10.01%17.14%
saurabh sengarsaurabh sengar10.01%17.14%
Total10384100.00%14100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}