Merge tag 'gadget-for-v3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
USB: Gadget: changes for 3.4 This merge is rather big. Here's what it contains: For am5536udc we have just simple coding style fixes. Nothing that has any potential to cause any issues going forward. With mv_udc, there's only one single change removing an unneeded NULL check. at91_udc also only saw a single change this merge window, and that's only removing a duplicated header. The Renesas controller has a few more involved changes. Support for SUDMAC was added, there's now a special handling of IRQ resources for when the IRQ line is shared between Renesas controller and SUDMAC, we also had a bug fix where Renesas controller would sleep in atomic context while doing DMA transfers from a tasklet. There were also a set of minor cleanups. The FSL UDC also had a scheduling in atomic context bug fix, but that's all. Thanks to Sebastian, the dummy_hcd now works better than ever with support for scatterlists and streams. Sebastian also added SuperSpeed descriptors to the serial gadgets. The highlight on this merge is the addition of a generic API for mapping and unmapping usb_requests. This will avoid code duplication on all UDC controllers and also kills all the defines for DMA_ADDR_INVALID which UDC controllers sprinkled around. A few of the UDC controllers were already converted to use this new API. Conflicts: drivers/usb/dwc3/gadget.c
This commit is contained in:
@@ -413,8 +413,7 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
struct renesas_usbhs_platform_info *info = pdev->dev.platform_data;
|
||||
struct renesas_usbhs_driver_callback *dfunc;
|
||||
struct usbhs_priv *priv;
|
||||
struct resource *res;
|
||||
unsigned int irq;
|
||||
struct resource *res, *irq_res;
|
||||
int ret;
|
||||
|
||||
/* check platform information */
|
||||
@@ -426,8 +425,8 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
|
||||
/* platform data */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!res || (int)irq <= 0) {
|
||||
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res || !irq_res) {
|
||||
dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -476,7 +475,9 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
/*
|
||||
* priv settings
|
||||
*/
|
||||
priv->irq = irq;
|
||||
priv->irq = irq_res->start;
|
||||
if (irq_res->flags & IORESOURCE_IRQ_SHAREABLE)
|
||||
priv->irqflags = IRQF_SHARED;
|
||||
priv->pdev = pdev;
|
||||
INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
|
||||
spin_lock_init(usbhs_priv_to_lock(priv));
|
||||
|
@@ -242,6 +242,7 @@ struct usbhs_priv {
|
||||
|
||||
void __iomem *base;
|
||||
unsigned int irq;
|
||||
unsigned long irqflags;
|
||||
|
||||
struct renesas_usbhs_platform_callback pfunc;
|
||||
struct renesas_usbhs_driver_param dparam;
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#define usbhsf_get_cfifo(p) (&((p)->fifo_info.cfifo))
|
||||
#define usbhsf_get_d0fifo(p) (&((p)->fifo_info.d0fifo))
|
||||
#define usbhsf_get_d1fifo(p) (&((p)->fifo_info.d1fifo))
|
||||
#define usbhsf_is_cfifo(p, f) (usbhsf_get_cfifo(p) == f)
|
||||
|
||||
#define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */
|
||||
|
||||
@@ -75,8 +76,7 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
|
||||
pipe->handler = &usbhsf_null_handler;
|
||||
}
|
||||
|
||||
list_del_init(&pkt->node);
|
||||
list_add_tail(&pkt->node, &pipe->list);
|
||||
list_move_tail(&pkt->node, &pipe->list);
|
||||
|
||||
/*
|
||||
* each pkt must hold own handler.
|
||||
@@ -106,7 +106,7 @@ static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
|
||||
if (list_empty(&pipe->list))
|
||||
return NULL;
|
||||
|
||||
return list_entry(pipe->list.next, struct usbhs_pkt, node);
|
||||
return list_first_entry(&pipe->list, struct usbhs_pkt, node);
|
||||
}
|
||||
|
||||
struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
|
||||
@@ -305,7 +305,10 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe,
|
||||
}
|
||||
|
||||
/* "base" will be used below */
|
||||
usbhs_write(priv, fifo->sel, base | MBW_32);
|
||||
if (usbhs_get_dparam(priv, has_sudmac) && !usbhsf_is_cfifo(priv, fifo))
|
||||
usbhs_write(priv, fifo->sel, base);
|
||||
else
|
||||
usbhs_write(priv, fifo->sel, base | MBW_32);
|
||||
|
||||
/* check ISEL and CURPIPE value */
|
||||
while (timeout--) {
|
||||
@@ -762,9 +765,9 @@ static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
|
||||
}
|
||||
|
||||
static void usbhsf_dma_complete(void *arg);
|
||||
static void usbhsf_dma_prepare_tasklet(unsigned long data)
|
||||
static void xfer_work(struct work_struct *work)
|
||||
{
|
||||
struct usbhs_pkt *pkt = (struct usbhs_pkt *)data;
|
||||
struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
@@ -844,11 +847,8 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
||||
|
||||
pkt->trans = len;
|
||||
|
||||
tasklet_init(&fifo->tasklet,
|
||||
usbhsf_dma_prepare_tasklet,
|
||||
(unsigned long)pkt);
|
||||
|
||||
tasklet_schedule(&fifo->tasklet);
|
||||
INIT_WORK(&pkt->work, xfer_work);
|
||||
schedule_work(&pkt->work);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -938,11 +938,8 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
|
||||
|
||||
pkt->trans = len;
|
||||
|
||||
tasklet_init(&fifo->tasklet,
|
||||
usbhsf_dma_prepare_tasklet,
|
||||
(unsigned long)pkt);
|
||||
|
||||
tasklet_schedule(&fifo->tasklet);
|
||||
INIT_WORK(&pkt->work, xfer_work);
|
||||
schedule_work(&pkt->work);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sh_dma.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <asm/dma.h>
|
||||
#include "pipe.h"
|
||||
|
||||
@@ -31,7 +32,6 @@ struct usbhs_fifo {
|
||||
u32 ctr; /* xFIFOCTR */
|
||||
|
||||
struct usbhs_pipe *pipe;
|
||||
struct tasklet_struct tasklet;
|
||||
|
||||
struct dma_chan *tx_chan;
|
||||
struct dma_chan *rx_chan;
|
||||
@@ -53,6 +53,7 @@ struct usbhs_pkt {
|
||||
struct usbhs_pkt_handle *handler;
|
||||
void (*done)(struct usbhs_priv *priv,
|
||||
struct usbhs_pkt *pkt);
|
||||
struct work_struct work;
|
||||
dma_addr_t dma;
|
||||
void *buf;
|
||||
int length;
|
||||
|
@@ -152,7 +152,7 @@ int usbhs_mod_probe(struct usbhs_priv *priv)
|
||||
|
||||
/* irq settings */
|
||||
ret = request_irq(priv->irq, usbhs_interrupt,
|
||||
0, dev_name(dev), priv);
|
||||
priv->irqflags, dev_name(dev), priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "irq request err\n");
|
||||
goto mod_init_gadget_err;
|
||||
|
@@ -165,69 +165,32 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep,
|
||||
/*
|
||||
* dma map/unmap
|
||||
*/
|
||||
static int usbhsg_dma_map(struct device *dev,
|
||||
struct usbhs_pkt *pkt,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
|
||||
struct usb_request *req = &ureq->req;
|
||||
|
||||
if (pkt->dma != DMA_ADDR_INVALID) {
|
||||
dev_err(dev, "dma is already mapped\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (req->dma == DMA_ADDR_INVALID) {
|
||||
pkt->dma = dma_map_single(dev, pkt->buf, pkt->length, dir);
|
||||
} else {
|
||||
dma_sync_single_for_device(dev, req->dma, req->length, dir);
|
||||
pkt->dma = req->dma;
|
||||
}
|
||||
|
||||
if (dma_mapping_error(dev, pkt->dma)) {
|
||||
dev_err(dev, "dma mapping error %llx\n", (u64)pkt->dma);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsg_dma_unmap(struct device *dev,
|
||||
struct usbhs_pkt *pkt,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
|
||||
struct usb_request *req = &ureq->req;
|
||||
|
||||
if (pkt->dma == DMA_ADDR_INVALID) {
|
||||
dev_err(dev, "dma is not mapped\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (req->dma == DMA_ADDR_INVALID)
|
||||
dma_unmap_single(dev, pkt->dma, pkt->length, dir);
|
||||
else
|
||||
dma_sync_single_for_cpu(dev, req->dma, req->length, dir);
|
||||
|
||||
pkt->dma = DMA_ADDR_INVALID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsg_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
|
||||
{
|
||||
struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
|
||||
struct usb_request *req = &ureq->req;
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
|
||||
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
|
||||
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
|
||||
enum dma_data_direction dir;
|
||||
int ret = 0;
|
||||
|
||||
dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
dir = usbhs_pipe_is_dir_host(pipe);
|
||||
|
||||
if (map)
|
||||
return usbhsg_dma_map(dev, pkt, dir);
|
||||
else
|
||||
return usbhsg_dma_unmap(dev, pkt, dir);
|
||||
if (map) {
|
||||
/* it can not use scatter/gather */
|
||||
WARN_ON(req->num_sgs);
|
||||
|
||||
ret = usb_gadget_map_request(&gpriv->gadget, req, dir);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pkt->dma = req->dma;
|
||||
} else {
|
||||
usb_gadget_unmap_request(&gpriv->gadget, req, dir);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -657,8 +620,6 @@ static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
|
||||
|
||||
usbhs_pkt_init(usbhsg_ureq_to_pkt(ureq));
|
||||
|
||||
ureq->req.dma = DMA_ADDR_INVALID;
|
||||
|
||||
return &ureq->req;
|
||||
}
|
||||
|
||||
@@ -941,6 +902,11 @@ static int usbhsg_stop(struct usbhs_priv *priv)
|
||||
return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED);
|
||||
}
|
||||
|
||||
static void usbhs_mod_gadget_release(struct device *pdev)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
|
||||
{
|
||||
struct usbhsg_gpriv *gpriv;
|
||||
@@ -989,6 +955,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
|
||||
*/
|
||||
dev_set_name(&gpriv->gadget.dev, "gadget");
|
||||
gpriv->gadget.dev.parent = dev;
|
||||
gpriv->gadget.dev.release = usbhs_mod_gadget_release;
|
||||
gpriv->gadget.name = "renesas_usbhs_udc";
|
||||
gpriv->gadget.ops = &usbhsg_gadget_ops;
|
||||
gpriv->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
Referens i nytt ärende
Block a user