Skip to content

Commit

Permalink
Merge branch 'for-gadget/next' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/balbi/usb into usb-next

* 'for-gadget/next' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (24 commits)
  usb: dwc3: gadget: add support for SG lists
  usb: dwc3: gadget: don't force 'LST' always
  usb: dwc3: gadget: don't return anything on prepare trbs
  usb: dwc3: gadget: re-factor dwc3_prepare_trbs()
  usb: gadget: introduce support for sg lists
  usb: renesas: pipe: convert a long if into a XOR operation
  usb: gadget: remove useless depends on Kconfig
  usb: gadget: s3c-hsudc: remove the_controller global
  usb: gadget: s3c-hsudc: use release_mem_region instead of release_resource
  usb: gadget: s3c-hsudc: Add regulator handling
  usb: gadget: s3c-hsudc: use udc_start and udc_stop functions
  usb: gadget: s3c-hsudc: move device registration to probe
  usb: gadget: s3c-hsudc: add missing otg_put_transceiver in probe
  usb: gadget: s3c-hsudc: add __devinit to probe function
  usb: gadget: s3c-hsudc: move platform_data struct to global header
  USB: EHCI: Add Marvell Host Controller driver
  USB: OTG: add Marvell usb OTG driver support
  usb: gadget: mv_udc: drop ARCH dependency
  usb: gadget: mv_udc: fix bug in ep_dequeue
  usb: gadget: enlarge maxburst bit width.
  ...
  • Loading branch information
gregkh committed Dec 22, 2011
2 parents eea9fc7 + eeb720f commit ee0db58
Show file tree
Hide file tree
Showing 22 changed files with 1,911 additions and 280 deletions.
1 change: 1 addition & 0 deletions arch/arm/mach-s3c2416/mach-smdk2416.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <plat/nand.h>
#include <plat/sdhci.h>
#include <plat/udc.h>
#include <linux/platform_data/s3c-hsudc.h>

#include <plat/regs-fb-v4.h>
#include <plat/fb.h>
Expand Down
1 change: 1 addition & 0 deletions arch/arm/plat-samsung/devs.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <linux/mtd/partitions.h>
#include <linux/mmc/host.h>
#include <linux/ioport.h>
#include <linux/platform_data/s3c-hsudc.h>

#include <asm/irq.h>
#include <asm/pmu.h>
Expand Down
15 changes: 1 addition & 14 deletions arch/arm/plat-samsung/include/plat/udc.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,7 @@ struct s3c2410_udc_mach_info {

extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);

/**
* s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller.
* @epnum: Number of endpoints to be instantiated by the controller driver.
* @gpio_init: Platform specific USB related GPIO initialization.
* @gpio_uninit: Platform specific USB releted GPIO uninitialzation.
*
* Representation of platform data for the S3C24XX USB 2.0 High Speed gadget
* controllers.
*/
struct s3c24xx_hsudc_platdata {
unsigned int epnum;
void (*gpio_init)(void);
void (*gpio_uninit)(void);
};
struct s3c24xx_hsudc_platdata;

extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd);

Expand Down
239 changes: 160 additions & 79 deletions drivers/usb/dwc3/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ void dwc3_map_buffer_to_dma(struct dwc3_request *req)
return;
}

if (req->request.num_sgs) {
int mapped;

mapped = dma_map_sg(dwc->dev, req->request.sg,
req->request.num_sgs,
req->direction ? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
if (mapped < 0) {
dev_err(dwc->dev, "failed to map SGs\n");
return;
}

req->request.num_mapped_sgs = mapped;
return;
}

if (req->request.dma == DMA_ADDR_INVALID) {
req->request.dma = dma_map_single(dwc->dev, req->request.buf,
req->request.length, req->direction
Expand All @@ -82,6 +98,17 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req)
return;
}

if (req->request.num_mapped_sgs) {
req->request.dma = DMA_ADDR_INVALID;
dma_unmap_sg(dwc->dev, req->request.sg,
req->request.num_sgs,
req->direction ? DMA_TO_DEVICE
: DMA_FROM_DEVICE);

req->request.num_mapped_sgs = 0;
return;
}

if (req->mapped) {
dma_unmap_single(dwc->dev, req->request.dma,
req->request.length, req->direction
Expand All @@ -97,7 +124,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
struct dwc3 *dwc = dep->dwc;

if (req->queued) {
dep->busy_slot++;
if (req->request.num_mapped_sgs)
dep->busy_slot += req->request.num_mapped_sgs;
else
dep->busy_slot++;

/*
* Skip LINK TRB. We can't use req->trb and check for
* DWC3_TRBCTL_LINK_TRB because it points the TRB we just
Expand All @@ -108,6 +139,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
dep->busy_slot++;
}
list_del(&req->list);
req->trb = NULL;

if (req->request.status == -EINPROGRESS)
req->request.status = status;
Expand Down Expand Up @@ -544,6 +576,85 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
kfree(req);
}

/**
* dwc3_prepare_one_trb - setup one TRB from one request
* @dep: endpoint for which this request is prepared
* @req: dwc3_request pointer
*/
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, dma_addr_t dma,
unsigned length, unsigned last, unsigned chain)
{
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb_hw *trb_hw;
struct dwc3_trb trb;

unsigned int cur_slot;

dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
dep->name, req, (unsigned long long) dma,
length, last ? " last" : "",
chain ? " chain" : "");

trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
cur_slot = dep->free_slot;
dep->free_slot++;

/* Skip the LINK-TRB on ISOC */
if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
usb_endpoint_xfer_isoc(dep->desc))
return;

memset(&trb, 0, sizeof(trb));
if (!req->trb) {
dwc3_gadget_move_request_queued(req);
req->trb = trb_hw;
req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw);
}

if (usb_endpoint_xfer_isoc(dep->desc)) {
trb.isp_imi = true;
trb.csp = true;
} else {
trb.chn = chain;
trb.lst = last;
}

if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
trb.sid_sofn = req->request.stream_id;

switch (usb_endpoint_type(dep->desc)) {
case USB_ENDPOINT_XFER_CONTROL:
trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP;
break;

case USB_ENDPOINT_XFER_ISOC:
trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;

/* IOC every DWC3_TRB_NUM / 4 so we can refill */
if (!(cur_slot % (DWC3_TRB_NUM / 4)))
trb.ioc = last;
break;

case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
trb.trbctl = DWC3_TRBCTL_NORMAL;
break;
default:
/*
* This is only possible with faulty memory because we
* checked it already :)
*/
BUG();
}

trb.length = length;
trb.bplh = dma;
trb.hwo = true;

dwc3_trb_to_hw(&trb, trb_hw);
}

/*
* dwc3_prepare_trbs - setup TRBs from requests
* @dep: endpoint for which requests are being prepared
Expand All @@ -553,26 +664,25 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
* transfers. The functions returns once there are not more TRBs available or
* it run out of requests.
*/
static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep,
bool starting)
static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
{
struct dwc3_request *req, *n, *ret = NULL;
struct dwc3_trb_hw *trb_hw;
struct dwc3_trb trb;
struct dwc3_request *req, *n;
u32 trbs_left;
unsigned int last_one = 0;

BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);

/* the first request must not be queued */
trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;

/*
* if busy & slot are equal than it is either full or empty. If we are
* starting to proceed requests then we are empty. Otherwise we ar
* full and don't do anything
*/
if (!trbs_left) {
if (!starting)
return NULL;
return;
trbs_left = DWC3_TRB_NUM;
/*
* In case we start from scratch, we queue the ISOC requests
Expand All @@ -596,94 +706,62 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep,

/* The last TRB is a link TRB, not used for xfer */
if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc))
return NULL;
return;

list_for_each_entry_safe(req, n, &dep->request_list, list) {
unsigned int last_one = 0;
unsigned int cur_slot;
unsigned length;
dma_addr_t dma;

trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
cur_slot = dep->free_slot;
dep->free_slot++;
if (req->request.num_mapped_sgs > 0) {
struct usb_request *request = &req->request;
struct scatterlist *sg = request->sg;
struct scatterlist *s;
int i;

/* Skip the LINK-TRB on ISOC */
if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
usb_endpoint_xfer_isoc(dep->desc))
continue;
for_each_sg(sg, s, request->num_mapped_sgs, i) {
unsigned chain = true;

dwc3_gadget_move_request_queued(req);
memset(&trb, 0, sizeof(trb));
trbs_left--;
length = sg_dma_len(s);
dma = sg_dma_address(s);

/* Is our TRB pool empty? */
if (!trbs_left)
last_one = 1;
/* Is this the last request? */
if (list_empty(&dep->request_list))
last_one = 1;
if (i == (request->num_mapped_sgs - 1)
|| sg_is_last(s)) {
last_one = true;
chain = false;
}

/*
* FIXME we shouldn't need to set LST bit always but we are
* facing some weird problem with the Hardware where it doesn't
* complete even though it has been previously started.
*
* While we're debugging the problem, as a workaround to
* multiple TRBs handling, use only one TRB at a time.
*/
last_one = 1;
trbs_left--;
if (!trbs_left)
last_one = true;

req->trb = trb_hw;
if (!ret)
ret = req;
if (last_one)
chain = false;

trb.bplh = req->request.dma;
dwc3_prepare_one_trb(dep, req, dma, length,
last_one, chain);

if (usb_endpoint_xfer_isoc(dep->desc)) {
trb.isp_imi = true;
trb.csp = true;
if (last_one)
break;
}
} else {
trb.lst = last_one;
}
dma = req->request.dma;
length = req->request.length;
trbs_left--;

if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
trb.sid_sofn = req->request.stream_id;
if (!trbs_left)
last_one = 1;

switch (usb_endpoint_type(dep->desc)) {
case USB_ENDPOINT_XFER_CONTROL:
trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP;
break;
/* Is this the last request? */
if (list_is_last(&req->list, &dep->request_list))
last_one = 1;

case USB_ENDPOINT_XFER_ISOC:
trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
dwc3_prepare_one_trb(dep, req, dma, length,
last_one, false);

/* IOC every DWC3_TRB_NUM / 4 so we can refill */
if (!(cur_slot % (DWC3_TRB_NUM / 4)))
trb.ioc = last_one;
break;

case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
trb.trbctl = DWC3_TRBCTL_NORMAL;
break;
default:
/*
* This is only possible with faulty memory because we
* checked it already :)
*/
BUG();
if (last_one)
break;
}

trb.length = req->request.length;
trb.hwo = true;

dwc3_trb_to_hw(&trb, trb_hw);
req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw);

if (last_one)
break;
}

return ret;
}

static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
Expand Down Expand Up @@ -712,11 +790,13 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
/* req points to the first request which will be sent */
req = next_request(&dep->req_queued);
} else {
dwc3_prepare_trbs(dep, start_new);

/*
* req points to the first request where HWO changed
* from 0 to 1
*/
req = dwc3_prepare_trbs(dep, start_new);
req = next_request(&dep->req_queued);
}
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
Expand Down Expand Up @@ -2093,6 +2173,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.max_speed = USB_SPEED_SUPER;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.dev.parent = dwc->dev;
dwc->gadget.sg_supported = true;

dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask);

Expand Down

0 comments on commit ee0db58

Please sign in to comment.