Merge branch 'for-gadget/next' of git://git.kernel.org/pub/scm/linux/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.
  ...
This commit is contained in:
Greg Kroah-Hartman 2011-12-22 14:05:01 -08:00
commit ee0db58ade
22 changed files with 1919 additions and 288 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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
@ -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
@ -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
@ -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;
@ -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
@ -553,18 +664,17 @@ 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
@ -572,7 +682,7 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep,
*/
if (!trbs_left) {
if (!starting)
return NULL;
return;
trbs_left = DWC3_TRB_NUM;
/*
* In case we start from scratch, we queue the ISOC requests
@ -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 (!trbs_left)
last_one = 1;
/* Is this the last request? */
if (list_is_last(&req->list, &dep->request_list))
last_one = 1;
dwc3_prepare_one_trb(dep, req, dma, length,
last_one, false);
if (last_one)
break;
}
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_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();
}
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,
@ -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;
@ -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);

View File

@ -125,7 +125,6 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
#
choice
prompt "USB Peripheral Controller"
depends on USB_GADGET
help
A USB device uses a controller to talk to its host.
Systems should have only one such upstream link.
@ -310,13 +309,13 @@ config USB_S3C_HSUDC
This driver has been tested on S3C2416 and S3C2450 processors.
config USB_PXA_U2O
tristate "PXA9xx Processor USB2.0 controller"
depends on ARCH_MMP
config USB_MV_UDC
tristate "Marvell USB2.0 Device Controller"
select USB_GADGET_DUALSPEED
help
PXA9xx Processor series include a high speed USB2.0 device
controller, which support high speed and full speed USB peripheral.
Marvell Socs (including PXA and MMP series) include a high speed
USB2.0 OTG controller, which can be configured as high speed or
full speed USB peripheral.
#
# Controllers available in both integrated and discrete versions
@ -532,12 +531,10 @@ endchoice
# Selected by UDC drivers that support high-speed operation.
config USB_GADGET_DUALSPEED
bool
depends on USB_GADGET
# Selected by UDC drivers that support super-speed opperation
config USB_GADGET_SUPERSPEED
bool
depends on USB_GADGET
depends on USB_GADGET_DUALSPEED
#
@ -545,7 +542,6 @@ config USB_GADGET_SUPERSPEED
#
choice
tristate "USB Gadget Drivers"
depends on USB_GADGET
default USB_ETH
help
A Linux "Gadget Driver" talks to the USB Peripheral Controller

View File

@ -27,7 +27,7 @@ obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
obj-$(CONFIG_USB_EG20T) += pch_udc.o
obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
mv_udc-y := mv_udc_core.o
obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o

View File

@ -180,7 +180,7 @@ struct mv_udc {
struct mv_cap_regs __iomem *cap_regs;
struct mv_op_regs __iomem *op_regs;
unsigned int phy_regs;
void __iomem *phy_regs;
unsigned int max_eps;
struct mv_dqh *ep_dqh;
size_t ep_dqh_size;

View File

@ -276,11 +276,12 @@ static void done(struct mv_ep *ep, struct mv_req *req, int status)
static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
{
u32 tmp, epstatus, bit_pos, direction;
struct mv_udc *udc;
struct mv_dqh *dqh;
u32 bit_pos, direction;
u32 usbcmd, epstatus;
unsigned int loops;
int readsafe, retval = 0;
int retval = 0;
udc = ep->udc;
direction = ep_dir(ep);
@ -293,30 +294,18 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
lastreq = list_entry(ep->queue.prev, struct mv_req, queue);
lastreq->tail->dtd_next =
req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
if (readl(&udc->op_regs->epprime) & bit_pos) {
loops = LOOPS(PRIME_TIMEOUT);
while (readl(&udc->op_regs->epprime) & bit_pos) {
if (loops == 0) {
retval = -ETIME;
goto done;
}
udelay(LOOPS_USEC);
loops--;
}
if (readl(&udc->op_regs->epstatus) & bit_pos)
goto done;
}
readsafe = 0;
wmb();
if (readl(&udc->op_regs->epprime) & bit_pos)
goto done;
loops = LOOPS(READSAFE_TIMEOUT);
while (readsafe == 0) {
if (loops == 0) {
retval = -ETIME;
goto done;
}
while (1) {
/* start with setting the semaphores */
tmp = readl(&udc->op_regs->usbcmd);
tmp |= USBCMD_ATDTW_TRIPWIRE_SET;
writel(tmp, &udc->op_regs->usbcmd);
usbcmd = readl(&udc->op_regs->usbcmd);
usbcmd |= USBCMD_ATDTW_TRIPWIRE_SET;
writel(usbcmd, &udc->op_regs->usbcmd);
/* read the endpoint status */
epstatus = readl(&udc->op_regs->epstatus) & bit_pos;
@ -329,98 +318,46 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
* primed.
*/
if (readl(&udc->op_regs->usbcmd)
& USBCMD_ATDTW_TRIPWIRE_SET) {
readsafe = 1;
}
& USBCMD_ATDTW_TRIPWIRE_SET)
break;
loops--;
if (loops == 0) {
dev_err(&udc->dev->dev,
"Timeout for ATDTW_TRIPWIRE...\n");
retval = -ETIME;
goto done;
}
udelay(LOOPS_USEC);
}
/* Clear the semaphore */
tmp = readl(&udc->op_regs->usbcmd);
tmp &= USBCMD_ATDTW_TRIPWIRE_CLEAR;
writel(tmp, &udc->op_regs->usbcmd);
usbcmd = readl(&udc->op_regs->usbcmd);
usbcmd &= USBCMD_ATDTW_TRIPWIRE_CLEAR;
writel(usbcmd, &udc->op_regs->usbcmd);
/* If endpoint is not active, we activate it now. */
if (!epstatus) {
if (direction == EP_DIR_IN) {
struct mv_dtd *curr_dtd = dma_to_virt(
&udc->dev->dev, dqh->curr_dtd_ptr);
loops = LOOPS(DTD_TIMEOUT);
while (curr_dtd->size_ioc_sts
& DTD_STATUS_ACTIVE) {
if (loops == 0) {
retval = -ETIME;
goto done;
}
loops--;
udelay(LOOPS_USEC);
}
}
/* No other transfers on the queue */
/* Write dQH next pointer and terminate bit to 0 */
dqh->next_dtd_ptr = req->head->td_dma
& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
dqh->size_ioc_int_sts = 0;
/*
* Ensure that updates to the QH will
* occur before priming.
*/
wmb();
/* Prime the Endpoint */
writel(bit_pos, &udc->op_regs->epprime);
}
} else {
/* Write dQH next pointer and terminate bit to 0 */
dqh->next_dtd_ptr = req->head->td_dma
& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
dqh->size_ioc_int_sts = 0;
/* Ensure that updates to the QH will occur before priming. */
wmb();
/* Prime the Endpoint */
writel(bit_pos, &udc->op_regs->epprime);
if (direction == EP_DIR_IN) {
/* FIXME add status check after prime the IN ep */
int prime_again;
u32 curr_dtd_ptr = dqh->curr_dtd_ptr;
loops = LOOPS(DTD_TIMEOUT);
prime_again = 0;
while ((curr_dtd_ptr != req->head->td_dma)) {
curr_dtd_ptr = dqh->curr_dtd_ptr;
if (loops == 0) {
dev_err(&udc->dev->dev,
"failed to prime %s\n",
ep->name);
retval = -ETIME;
goto done;
}
loops--;
udelay(LOOPS_USEC);
if (loops == (LOOPS(DTD_TIMEOUT) >> 2)) {
if (prime_again)
goto done;
dev_info(&udc->dev->dev,
"prime again\n");
writel(bit_pos,
&udc->op_regs->epprime);
prime_again = 1;
}
}
}
if (epstatus)
goto done;
}
/* Write dQH next pointer and terminate bit to 0 */
dqh->next_dtd_ptr = req->head->td_dma
& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
/* clear active and halt bit, in case set from a previous error */
dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED);
/* Ensure that updates to the QH will occure before priming. */
wmb();
/* Prime the Endpoint */
writel(bit_pos, &udc->op_regs->epprime);
done:
return retval;
}
static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
dma_addr_t *dma, int *is_last)
{
@ -841,6 +778,27 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
return 0;
}
static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
{
struct mv_dqh *dqh = ep->dqh;
u32 bit_pos;
/* Write dQH next pointer and terminate bit to 0 */
dqh->next_dtd_ptr = req->head->td_dma
& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
/* clear active and halt bit, in case set from a previous error */
dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED);
/* Ensure that updates to the QH will occure before priming. */
wmb();
bit_pos = 1 << (((ep_dir(ep) == EP_DIR_OUT) ? 0 : 16) + ep->ep_num);
/* Prime the Endpoint */
writel(bit_pos, &ep->udc->op_regs->epprime);
}
/* dequeues (cancels, unlinks) an I/O request from an endpoint */
static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
@ -883,15 +841,13 @@ static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
/* The request isn't the last request in this ep queue */
if (req->queue.next != &ep->queue) {
struct mv_dqh *qh;
struct mv_req *next_req;
qh = ep->dqh;
next_req = list_entry(req->queue.next, struct mv_req,
queue);
next_req = list_entry(req->queue.next,
struct mv_req, queue);
/* Point the QH to the first TD of next request */
writel((u32) next_req->head, &qh->curr_dtd_ptr);
mv_prime_ep(ep, next_req);
} else {
struct mv_dqh *qh;
@ -1196,7 +1152,7 @@ static int mv_udc_get_frame(struct usb_gadget *gadget)
udc = container_of(gadget, struct mv_udc, gadget);
retval = readl(udc->op_regs->frindex) & USB_FRINDEX_MASKS;
retval = readl(&udc->op_regs->frindex) & USB_FRINDEX_MASKS;
return retval;
}
@ -2172,11 +2128,9 @@ static int __devexit mv_udc_remove(struct platform_device *dev)
if (udc->cap_regs)
iounmap(udc->cap_regs);
udc->cap_regs = NULL;
if (udc->phy_regs)
iounmap((void *)udc->phy_regs);
udc->phy_regs = 0;
iounmap(udc->phy_regs);
if (udc->status_req) {
kfree(udc->status_req->req.buf);
@ -2261,8 +2215,8 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
goto err_iounmap_capreg;
}
udc->phy_regs = (unsigned int)ioremap(r->start, resource_size(r));
if (udc->phy_regs == 0) {
udc->phy_regs = ioremap(r->start, resource_size(r));
if (udc->phy_regs == NULL) {
dev_err(&dev->dev, "failed to map phy I/O memory\n");
retval = -EBUSY;
goto err_iounmap_capreg;
@ -2273,7 +2227,8 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
if (retval)
goto err_iounmap_phyreg;
udc->op_regs = (struct mv_op_regs __iomem *)((u32)udc->cap_regs
udc->op_regs =
(struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs
+ (readl(&udc->cap_regs->caplength_hciversion)
& CAPLENGTH_MASK));
udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK;
@ -2433,7 +2388,7 @@ err_free_dma:
err_disable_clock:
mv_udc_disable_internal(udc);
err_iounmap_phyreg:
iounmap((void *)udc->phy_regs);
iounmap(udc->phy_regs);
err_iounmap_capreg:
iounmap(udc->cap_regs);
err_put_clk:
@ -2524,7 +2479,7 @@ static struct platform_driver udc_driver = {
.shutdown = mv_udc_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "pxa-u2o",
.name = "mv-udc",
#ifdef CONFIG_PM
.pm = &mv_udc_pm_ops,
#endif
@ -2532,9 +2487,8 @@ static struct platform_driver udc_driver = {
};
module_platform_driver(udc_driver);
MODULE_ALIAS("platform:mv-udc");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pxa-u2o");

View File

@ -28,9 +28,10 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/prefetch.h>
#include <linux/platform_data/s3c-hsudc.h>
#include <linux/regulator/consumer.h>
#include <mach/regs-s3c2443-clock.h>
#include <plat/udc.h>
#define S3C_HSUDC_REG(x) (x)
@ -87,6 +88,12 @@
#define DATA_STATE_XMIT (1)
#define DATA_STATE_RECV (2)
static const char * const s3c_hsudc_supply_names[] = {
"vdda", /* analog phy supply, 3.3V */
"vddi", /* digital phy supply, 1.2V */
"vddosc", /* oscillator supply, 1.8V - 3.3V */
};
/**
* struct s3c_hsudc_ep - Endpoint representation used by driver.
* @ep: USB gadget layer representation of device endpoint.
@ -139,6 +146,7 @@ struct s3c_hsudc {
struct device *dev;
struct s3c24xx_hsudc_platdata *pd;
struct otg_transceiver *transceiver;
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)];
spinlock_t lock;
void __iomem *regs;
struct resource *mem_rsrc;
@ -153,7 +161,6 @@ struct s3c_hsudc {
#define ep_index(_ep) ((_ep)->bEndpointAddress & \
USB_ENDPOINT_NUMBER_MASK)
static struct s3c_hsudc *the_controller;
static const char driver_name[] = "s3c-udc";
static const char ep0name[] = "ep0-control";
@ -282,8 +289,7 @@ static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
* All the endpoints are stopped and any pending transfer requests if any on
* the endpoint are terminated.
*/
static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
struct usb_gadget_driver *driver)
static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc)
{
struct s3c_hsudc_ep *hsep;
int epnum;
@ -295,10 +301,6 @@ static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
hsep->stopped = 1;
s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
}
spin_unlock(&hsudc->lock);
driver->disconnect(&hsudc->gadget);
spin_lock(&hsudc->lock);
}
/**
@ -1135,16 +1137,15 @@ static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
return IRQ_HANDLED;
}
static int s3c_hsudc_start(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
static int s3c_hsudc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct s3c_hsudc *hsudc = the_controller;
struct s3c_hsudc *hsudc = to_hsudc(gadget);
int ret;
if (!driver
|| driver->max_speed < USB_SPEED_FULL
|| !bind
|| !driver->unbind || !driver->disconnect || !driver->setup)
|| !driver->setup)
return -EINVAL;
if (!hsudc)
@ -1155,21 +1156,12 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver,
hsudc->driver = driver;
hsudc->gadget.dev.driver = &driver->driver;
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
ret = device_add(&hsudc->gadget.dev);
if (ret) {
dev_err(hsudc->dev, "failed to probe gadget device");
return ret;
}
ret = bind(&hsudc->gadget);
if (ret) {
dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name);
device_del(&hsudc->gadget.dev);
hsudc->driver = NULL;
hsudc->gadget.dev.driver = NULL;
return ret;
ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies),
hsudc->supplies);
if (ret != 0) {
dev_err(hsudc->dev, "failed to enable supplies: %d\n", ret);
goto err_supplies;
}
/* connect to bus through transceiver */
@ -1178,13 +1170,7 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver,
if (ret) {
dev_err(hsudc->dev, "%s: can't bind to transceiver\n",
hsudc->gadget.name);
driver->unbind(&hsudc->gadget);
device_del(&hsudc->gadget.dev);
hsudc->driver = NULL;
hsudc->gadget.dev.driver = NULL;
return ret;
goto err_otg;
}
}
@ -1197,34 +1183,43 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver,
hsudc->pd->gpio_init();
return 0;
err_otg:
regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
err_supplies:
hsudc->driver = NULL;
hsudc->gadget.dev.driver = NULL;
return ret;
}
static int s3c_hsudc_stop(struct usb_gadget_driver *driver)
static int s3c_hsudc_stop(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct s3c_hsudc *hsudc = the_controller;
struct s3c_hsudc *hsudc = to_hsudc(gadget);
unsigned long flags;
if (!hsudc)
return -ENODEV;
if (!driver || driver != hsudc->driver || !driver->unbind)
if (!driver || driver != hsudc->driver)
return -EINVAL;
spin_lock_irqsave(&hsudc->lock, flags);
hsudc->driver = 0;
hsudc->driver = NULL;
hsudc->gadget.dev.driver = NULL;
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
s3c_hsudc_uninit_phy();
if (hsudc->pd->gpio_uninit)
hsudc->pd->gpio_uninit();
s3c_hsudc_stop_activity(hsudc, driver);
s3c_hsudc_stop_activity(hsudc);
spin_unlock_irqrestore(&hsudc->lock, flags);
if (hsudc->transceiver)
(void) otg_set_peripheral(hsudc->transceiver, NULL);
driver->unbind(&hsudc->gadget);
device_del(&hsudc->gadget.dev);
disable_irq(hsudc->irq);
regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
driver->driver.name);
return 0;
@ -1242,7 +1237,7 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
{
struct s3c_hsudc *hsudc = the_controller;
struct s3c_hsudc *hsudc = to_hsudc(gadget);
if (!hsudc)
return -ENODEV;
@ -1255,18 +1250,18 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
.get_frame = s3c_hsudc_gadget_getframe,
.start = s3c_hsudc_start,
.stop = s3c_hsudc_stop,
.udc_start = s3c_hsudc_start,
.udc_stop = s3c_hsudc_stop,
.vbus_draw = s3c_hsudc_vbus_draw,
};
static int s3c_hsudc_probe(struct platform_device *pdev)
static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct s3c_hsudc *hsudc;
struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
int ret;
int ret, i;
hsudc = kzalloc(sizeof(struct s3c_hsudc) +
sizeof(struct s3c_hsudc_ep) * pd->epnum,
@ -1276,13 +1271,22 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
return -ENOMEM;
}
the_controller = hsudc;
platform_set_drvdata(pdev, dev);
hsudc->dev = dev;
hsudc->pd = pdev->dev.platform_data;
hsudc->transceiver = otg_get_transceiver();
for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
ret = regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
hsudc->supplies);
if (ret != 0) {
dev_err(dev, "failed to request supplies: %d\n", ret);
goto err_supplies;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "unable to obtain driver resource data\n");
@ -1307,7 +1311,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
spin_lock_init(&hsudc->lock);
device_initialize(&hsudc->gadget.dev);
dev_set_name(&hsudc->gadget.dev, "gadget");
hsudc->gadget.max_speed = USB_SPEED_HIGH;
@ -1319,6 +1322,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
hsudc->gadget.is_otg = 0;
hsudc->gadget.is_a_peripheral = 0;
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
s3c_hsudc_setup_ep(hsudc);
@ -1348,12 +1352,20 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
disable_irq(hsudc->irq);
local_irq_enable();
ret = device_register(&hsudc->gadget.dev);
if (ret) {
put_device(&hsudc->gadget.dev);
goto err_add_device;
}
ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget);
if (ret)
goto err_add_udc;
return 0;
err_add_udc:
device_unregister(&hsudc->gadget.dev);
err_add_device:
clk_disable(hsudc->uclk);
clk_put(hsudc->uclk);
err_clk:
@ -1362,10 +1374,13 @@ err_irq:
iounmap(hsudc->regs);
err_remap:
release_resource(hsudc->mem_rsrc);
kfree(hsudc->mem_rsrc);
release_mem_region(res->start, resource_size(res));
err_res:
if (hsudc->transceiver)
otg_put_transceiver(hsudc->transceiver);
regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
err_supplies:
kfree(hsudc);
return ret;
}

View File

@ -194,6 +194,15 @@ config USB_EHCI_S5P
help
Enable support for the S5P SOC's on-chip EHCI controller.
config USB_EHCI_MV
bool "EHCI support for Marvell on-chip controller"
depends on USB_EHCI_HCD
select USB_EHCI_ROOT_HUB_TT
---help---
Enables support for Marvell (including PXA and MMP series) on-chip
USB SPH and OTG controller. SPH is a single port host, and it can
only be EHCI host. OTG is controller that can switch to host mode.
config USB_W90X900_EHCI
bool "W90X900(W90P910) EHCI support"
depends on USB_EHCI_HCD && ARCH_W90X900

View File

@ -1371,6 +1371,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_xls_driver
#endif
#ifdef CONFIG_USB_EHCI_MV
#include "ehci-mv.c"
#define PLATFORM_DRIVER ehci_mv_driver
#endif
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
!defined(XILINX_OF_PLATFORM_DRIVER)

391
drivers/usb/host/ehci-mv.c Normal file
View File

@ -0,0 +1,391 @@
/*
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
* Author: Chao Xie <chao.xie@marvell.com>
* Neil Zhang <zhangwm@marvell.com>
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/usb/otg.h>
#include <linux/platform_data/mv_usb.h>
#define CAPLENGTH_MASK (0xff)
struct ehci_hcd_mv {
struct usb_hcd *hcd;
/* Which mode does this ehci running OTG/Host ? */
int mode;
void __iomem *phy_regs;
void __iomem *cap_regs;
void __iomem *op_regs;
struct otg_transceiver *otg;
struct mv_usb_platform_data *pdata;
/* clock source and total clock number */
unsigned int clknum;
struct clk *clk[0];
};
static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
{
unsigned int i;
for (i = 0; i < ehci_mv->clknum; i++)
clk_enable(ehci_mv->clk[i]);
}
static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
{
unsigned int i;
for (i = 0; i < ehci_mv->clknum; i++)
clk_disable(ehci_mv->clk[i]);
}
static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
{
int retval;
ehci_clock_enable(ehci_mv);
if (ehci_mv->pdata->phy_init) {
retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs);
if (retval)
return retval;
}
return 0;
}
static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
{
if (ehci_mv->pdata->phy_deinit)
ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs);
ehci_clock_disable(ehci_mv);
}
static int mv_ehci_reset(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct device *dev = hcd->self.controller;
struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
int retval;
if (ehci_mv == NULL) {
dev_err(dev, "Can not find private ehci data\n");
return -ENODEV;
}
/*
* data structure init
*/
retval = ehci_init(hcd);
if (retval) {
dev_err(dev, "ehci_init failed %d\n", retval);
return retval;
}
hcd->has_tt = 1;
ehci->sbrn = 0x20;
retval = ehci_reset(ehci);
if (retval) {
dev_err(dev, "ehci_reset failed %d\n", retval);
return retval;
}
return 0;
}
static const struct hc_driver mv_ehci_hc_driver = {
.description = hcd_name,
.product_desc = "Marvell EHCI",
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
/*
* basic lifecycle operations
*/
.reset = mv_ehci_reset,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
/*
* scheduling support
*/
.get_frame_number = ehci_get_frame,
/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
};
static int mv_ehci_probe(struct platform_device *pdev)
{
struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct ehci_hcd_mv *ehci_mv;
struct resource *r;
int clk_i, retval = -ENODEV;
u32 offset;
size_t size;
if (!pdata) {
dev_err(&pdev->dev, "missing platform_data\n");
return -ENODEV;
}
if (usb_disabled())
return -ENODEV;
hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci");
if (!hcd)
return -ENOMEM;
size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum;
ehci_mv = kzalloc(size, GFP_KERNEL);
if (ehci_mv == NULL) {
dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n");
retval = -ENOMEM;
goto err_put_hcd;
}
platform_set_drvdata(pdev, ehci_mv);
ehci_mv->pdata = pdata;
ehci_mv->hcd = hcd;
ehci_mv->clknum = pdata->clknum;
for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) {
ehci_mv->clk[clk_i] =
clk_get(&pdev->dev, pdata->clkname[clk_i]);
if (IS_ERR(ehci_mv->clk[clk_i])) {
dev_err(&pdev->dev, "error get clck \"%s\"\n",
pdata->clkname[clk_i]);
retval = PTR_ERR(ehci_mv->clk[clk_i]);
goto err_put_clk;
}
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs");
if (r == NULL) {
dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
retval = -ENODEV;
goto err_put_clk;
}
ehci_mv->phy_regs = ioremap(r->start, resource_size(r));
if (ehci_mv->phy_regs == 0) {
dev_err(&pdev->dev, "failed to map phy I/O memory\n");
retval = -EFAULT;
goto err_put_clk;
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
if (!r) {
dev_err(&pdev->dev, "no I/O memory resource defined\n");
retval = -ENODEV;
goto err_iounmap_phyreg;
}
ehci_mv->cap_regs = ioremap(r->start, resource_size(r));
if (ehci_mv->cap_regs == NULL) {
dev_err(&pdev->dev, "failed to map I/O memory\n");
retval = -EFAULT;
goto err_iounmap_phyreg;
}
retval = mv_ehci_enable(ehci_mv);
if (retval) {
dev_err(&pdev->dev, "init phy error %d\n", retval);
goto err_iounmap_capreg;
}
offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
ehci_mv->op_regs =
(void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
hcd->rsrc_start = r->start;
hcd->rsrc_len = r->end - r->start + 1;
hcd->regs = ehci_mv->op_regs;
hcd->irq = platform_get_irq(pdev, 0);
if (!hcd->irq) {
dev_err(&pdev->dev, "Cannot get irq.");
retval = -ENODEV;
goto err_disable_clk;
}
ehci = hcd_to_ehci(hcd);
ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
ehci->regs = (struct ehci_regs *) ehci_mv->op_regs;
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
ehci_mv->mode = pdata->mode;
if (ehci_mv->mode == MV_USB_MODE_OTG) {
#ifdef CONFIG_USB_OTG_UTILS
ehci_mv->otg = otg_get_transceiver();
if (!ehci_mv->otg) {
dev_err(&pdev->dev,
"unable to find transceiver\n");
retval = -ENODEV;
goto err_disable_clk;
}
retval = otg_set_host(ehci_mv->otg, &hcd->self);
if (retval < 0) {
dev_err(&pdev->dev,
"unable to register with transceiver\n");
retval = -ENODEV;
goto err_put_transceiver;
}
/* otg will enable clock before use as host */
mv_ehci_disable(ehci_mv);
#else
dev_info(&pdev->dev, "MV_USB_MODE_OTG "
"must have CONFIG_USB_OTG_UTILS enabled\n");
goto err_disable_clk;
#endif
} else {
if (pdata->set_vbus)
pdata->set_vbus(1);
retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
if (retval) {
dev_err(&pdev->dev,
"failed to add hcd with err %d\n", retval);
goto err_set_vbus;
}
}
if (pdata->private_init)
pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs);
dev_info(&pdev->dev,
"successful find EHCI device with regs 0x%p irq %d"
" working in %s mode\n", hcd->regs, hcd->irq,
ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host");
return 0;
err_set_vbus:
if (pdata->set_vbus)
pdata->set_vbus(0);
#ifdef CONFIG_USB_OTG_UTILS
err_put_transceiver:
if (ehci_mv->otg)
otg_put_transceiver(ehci_mv->otg);
#endif
err_disable_clk:
mv_ehci_disable(ehci_mv);
err_iounmap_capreg:
iounmap(ehci_mv->cap_regs);
err_iounmap_phyreg:
iounmap(ehci_mv->phy_regs);
err_put_clk:
for (clk_i--; clk_i >= 0; clk_i--)
clk_put(ehci_mv->clk[clk_i]);
platform_set_drvdata(pdev, NULL);
kfree(ehci_mv);
err_put_hcd:
usb_put_hcd(hcd);
return retval;
}
static int mv_ehci_remove(struct platform_device *pdev)
{
struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
struct usb_hcd *hcd = ehci_mv->hcd;
int clk_i;
if (hcd->rh_registered)
usb_remove_hcd(hcd);
if (ehci_mv->otg) {
otg_set_host(ehci_mv->otg, NULL);
otg_put_transceiver(ehci_mv->otg);
}
if (ehci_mv->mode == MV_USB_MODE_HOST) {
if (ehci_mv->pdata->set_vbus)
ehci_mv->pdata->set_vbus(0);
mv_ehci_disable(ehci_mv);
}
iounmap(ehci_mv->cap_regs);
iounmap(ehci_mv->phy_regs);
for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++)
clk_put(ehci_mv->clk[clk_i]);
platform_set_drvdata(pdev, NULL);
kfree(ehci_mv);
usb_put_hcd(hcd);
return 0;
}
MODULE_ALIAS("mv-ehci");
static const struct platform_device_id ehci_id_table[] = {
{"pxa-u2oehci", PXA_U2OEHCI},
{"pxa-sph", PXA_SPH},
{"mmp3-hsic", MMP3_HSIC},
{"mmp3-fsic", MMP3_FSIC},
{},
};
static void mv_ehci_shutdown(struct platform_device *pdev)
{
struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
struct usb_hcd *hcd = ehci_mv->hcd;
if (!hcd->rh_registered)
return;
if (hcd->driver->shutdown)
hcd->driver->shutdown(hcd);
}
static struct platform_driver ehci_mv_driver = {
.probe = mv_ehci_probe,
.remove = mv_ehci_remove,
.shutdown = mv_ehci_shutdown,
.driver = {
.name = "mv-ehci",
.bus = &platform_bus_type,
},
.id_table = ehci_id_table,
};

View File

@ -130,4 +130,16 @@ config FSL_USB2_OTG
help
Enable this to support Freescale USB OTG transceiver.
config USB_MV_OTG
tristate "Marvell USB OTG support"
depends on USB_MV_UDC
select USB_OTG
select USB_OTG_UTILS
help
Say Y here if you want to build Marvell USB OTG transciever
driver in kernel (including PXA and MMP series). This driver
implements role switch between EHCI host driver and gadget driver.
To compile this driver as a module, choose M here.
endif # USB || OTG

View File

@ -21,3 +21,4 @@ obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o
obj-$(CONFIG_AB8500_USB) += ab8500-usb.o
fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o
obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o
obj-$(CONFIG_USB_MV_OTG) += mv_otg.o

957
drivers/usb/otg/mv_otg.c Normal file
View File

@ -0,0 +1,957 @@
/*
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
* Author: Chao Xie <chao.xie@marvell.com>
* Neil Zhang <zhangwm@marvell.com>
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/otg.h>
#include <linux/usb/gadget.h>
#include <linux/usb/hcd.h>
#include <linux/platform_data/mv_usb.h>
#include "mv_otg.h"
#define DRIVER_DESC "Marvell USB OTG transceiver driver"
#define DRIVER_VERSION "Jan 20, 2010"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
static const char driver_name[] = "mv-otg";
static char *state_string[] = {
"undefined",
"b_idle",
"b_srp_init",
"b_peripheral",
"b_wait_acon",
"b_host",
"a_idle",
"a_wait_vrise",
"a_wait_bcon",
"a_host",
"a_suspend",
"a_peripheral",
"a_wait_vfall",
"a_vbus_err"
};
static int mv_otg_set_vbus(struct otg_transceiver *otg, bool on)
{
struct mv_otg *mvotg = container_of(otg, struct mv_otg, otg);
if (mvotg->pdata->set_vbus == NULL)
return -ENODEV;
return mvotg->pdata->set_vbus(on);
}
static int mv_otg_set_host(struct otg_transceiver *otg,
struct usb_bus *host)
{
otg->host = host;
return 0;
}
static int mv_otg_set_peripheral(struct otg_transceiver *otg,
struct usb_gadget *gadget)
{
otg->gadget = gadget;
return 0;
}
static void mv_otg_run_state_machine(struct mv_otg *mvotg,
unsigned long delay)
{
dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n");
if (!mvotg->qwork)
return;
queue_delayed_work(mvotg->qwork, &mvotg->work, delay);
}
static void mv_otg_timer_await_bcon(unsigned long data)
{
struct mv_otg *mvotg = (struct mv_otg *) data;
mvotg->otg_ctrl.a_wait_bcon_timeout = 1;
dev_info(&mvotg->pdev->dev, "B Device No Response!\n");
if (spin_trylock(&mvotg->wq_lock)) {
mv_otg_run_state_machine(mvotg, 0);
spin_unlock(&mvotg->wq_lock);
}
}
static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
{
struct timer_list *timer;
if (id >= OTG_TIMER_NUM)
return -EINVAL;
timer = &mvotg->otg_ctrl.timer[id];
if (timer_pending(timer))
del_timer(timer);
return 0;
}
static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
unsigned long interval,
void (*callback) (unsigned long))
{
struct timer_list *timer;
if (id >= OTG_TIMER_NUM)
return -EINVAL;
timer = &mvotg->otg_ctrl.timer[id];
if (timer_pending(timer)) {
dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id);
return -EBUSY;
}
init_timer(timer);
timer->data = (unsigned long) mvotg;
timer->function = callback;
timer->expires = jiffies + interval;
add_timer(timer);
return 0;
}
static int mv_otg_reset(struct mv_otg *mvotg)
{
unsigned int loops;
u32 tmp;
/* Stop the controller */
tmp = readl(&mvotg->op_regs->usbcmd);
tmp &= ~USBCMD_RUN_STOP;
writel(tmp, &mvotg->op_regs->usbcmd);
/* Reset the controller to get default values */
writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd);
loops = 500;
while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) {
if (loops == 0) {
dev_err(&mvotg->pdev->dev,
"Wait for RESET completed TIMEOUT\n");
return -ETIMEDOUT;
}
loops--;
udelay(20);
}
writel(0x0, &mvotg->op_regs->usbintr);
tmp = readl(&mvotg->op_regs->usbsts);
writel(tmp, &mvotg->op_regs->usbsts);
return 0;
}
static void mv_otg_init_irq(struct mv_otg *mvotg)
{
u32 otgsc;
mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID
| OTGSC_INTR_A_VBUS_VALID;
mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID
| OTGSC_INTSTS_A_VBUS_VALID;
if (mvotg->pdata->vbus == NULL) {
mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID
| OTGSC_INTR_B_SESSION_END;
mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID
| OTGSC_INTSTS_B_SESSION_END;
}
if (mvotg->pdata->id == NULL) {
mvotg->irq_en |= OTGSC_INTR_USB_ID;
mvotg->irq_status |= OTGSC_INTSTS_USB_ID;
}
otgsc = readl(&mvotg->op_regs->otgsc);
otgsc |= mvotg->irq_en;
writel(otgsc, &mvotg->op_regs->otgsc);
}
static void mv_otg_start_host(struct mv_otg *mvotg, int on)
{
struct otg_transceiver *otg = &mvotg->otg;
struct usb_hcd *hcd;
if (!otg->host)
return;
dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop");
hcd = bus_to_hcd(otg->host);
if (on)
usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
else
usb_remove_hcd(hcd);
}
static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
{
struct otg_transceiver *otg = &mvotg->otg;
if (!otg->gadget)
return;
dev_info(otg->dev, "gadget %s\n", on ? "on" : "off");
if (on)
usb_gadget_vbus_connect(otg->gadget);
else
usb_gadget_vbus_disconnect(otg->gadget);
}
static void otg_clock_enable(struct mv_otg *mvotg)
{
unsigned int i;
for (i = 0; i < mvotg->clknum; i++)
clk_enable(mvotg->clk[i]);
}
static void otg_clock_disable(struct mv_otg *mvotg)
{
unsigned int i;
for (i = 0; i < mvotg->clknum; i++)
clk_disable(mvotg->clk[i]);
}
static int mv_otg_enable_internal(struct mv_otg *mvotg)
{
int retval = 0;
if (mvotg->active)
return 0;
dev_dbg(&mvotg->pdev->dev, "otg enabled\n");
otg_clock_enable(mvotg);
if (mvotg->pdata->phy_init) {
retval = mvotg->pdata->phy_init(mvotg->phy_regs);
if (retval) {
dev_err(&mvotg->pdev->dev,
"init phy error %d\n", retval);
otg_clock_disable(mvotg);
return retval;
}
}
mvotg->active = 1;
return 0;
}
static int mv_otg_enable(struct mv_otg *mvotg)
{
if (mvotg->clock_gating)
return mv_otg_enable_internal(mvotg);
return 0;
}
static void mv_otg_disable_internal(struct mv_otg *mvotg)
{
if (mvotg->active) {
dev_dbg(&mvotg->pdev->dev, "otg disabled\n");
if (mvotg->pdata->phy_deinit)
mvotg->pdata->phy_deinit(mvotg->phy_regs);
otg_clock_disable(mvotg);
mvotg->active = 0;
}
}
static void mv_otg_disable(struct mv_otg *mvotg)
{
if (mvotg->clock_gating)
mv_otg_disable_internal(mvotg);
}
static void mv_otg_update_inputs(struct mv_otg *mvotg)
{
struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
u32 otgsc;
otgsc = readl(&mvotg->op_regs->otgsc);
if (mvotg->pdata->vbus) {
if (mvotg->pdata->vbus->poll() == VBUS_HIGH) {
otg_ctrl->b_sess_vld = 1;
otg_ctrl->b_sess_end = 0;
} else {
otg_ctrl->b_sess_vld = 0;
otg_ctrl->b_sess_end = 1;
}
} else {
otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID);
otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END);
}
if (mvotg->pdata->id)
otg_ctrl->id = !!mvotg->pdata->id->poll();
else
otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
otg_ctrl->a_bus_req = 1;
otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID);
otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID);
dev_dbg(&mvotg->pdev->dev, "%s: ", __func__);
dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id);
dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld);
dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end);
dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld);
dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld);
}
static void mv_otg_update_state(struct mv_otg *mvotg)
{
struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
struct otg_transceiver *otg = &mvotg->otg;
int old_state = otg->state;
switch (old_state) {
case OTG_STATE_UNDEFINED:
otg->state = OTG_STATE_B_IDLE;
/* FALL THROUGH */
case OTG_STATE_B_IDLE:
if (otg_ctrl->id == 0)
otg->state = OTG_STATE_A_IDLE;
else if (otg_ctrl->b_sess_vld)
otg->state = OTG_STATE_B_PERIPHERAL;
break;
case OTG_STATE_B_PERIPHERAL:
if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0)
otg->state = OTG_STATE_B_IDLE;
break;
case OTG_STATE_A_IDLE:
if (otg_ctrl->id)
otg->state = OTG_STATE_B_IDLE;
else if (!(otg_ctrl->a_bus_drop) &&
(otg_ctrl->a_bus_req || otg_ctrl->a_srp_det))
otg->state = OTG_STATE_A_WAIT_VRISE;
break;
case OTG_STATE_A_WAIT_VRISE:
if (otg_ctrl->a_vbus_vld)
otg->state = OTG_STATE_A_WAIT_BCON;
break;
case OTG_STATE_A_WAIT_BCON:
if (otg_ctrl->id || otg_ctrl->a_bus_drop
|| otg_ctrl->a_wait_bcon_timeout) {
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
otg->state = OTG_STATE_A_WAIT_VFALL;
otg_ctrl->a_bus_req = 0;
} else if (!otg_ctrl->a_vbus_vld) {
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
otg->state = OTG_STATE_A_VBUS_ERR;
} else if (otg_ctrl->b_conn) {
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
otg->state = OTG_STATE_A_HOST;
}
break;
case OTG_STATE_A_HOST:
if (otg_ctrl->id || !otg_ctrl->b_conn
|| otg_ctrl->a_bus_drop)
otg->state = OTG_STATE_A_WAIT_BCON;
else if (!otg_ctrl->a_vbus_vld)
otg->state = OTG_STATE_A_VBUS_ERR;
break;
case OTG_STATE_A_WAIT_VFALL:
if (otg_ctrl->id
|| (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld)
|| otg_ctrl->a_bus_req)
otg->state = OTG_STATE_A_IDLE;
break;
case OTG_STATE_A_VBUS_ERR:
if (otg_ctrl->id || otg_ctrl->a_clr_err
|| otg_ctrl->a_bus_drop) {
otg_ctrl->a_clr_err = 0;
otg->state = OTG_STATE_A_WAIT_VFALL;
}
break;
default:
break;
}
}
static void mv_otg_work(struct work_struct *work)
{
struct mv_otg *mvotg;
struct otg_transceiver *otg;
int old_state;
mvotg = container_of((struct delayed_work *)work, struct mv_otg, work);
run:
/* work queue is single thread, or we need spin_lock to protect */
otg = &mvotg->otg;
old_state = otg->state;
if (!mvotg->active)
return;
mv_otg_update_inputs(mvotg);
mv_otg_update_state(mvotg);
if (old_state != otg->state) {
dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
state_string[old_state],
state_string[otg->state]);
switch (otg->state) {
case OTG_STATE_B_IDLE:
mvotg->otg.default_a = 0;
if (old_state == OTG_STATE_B_PERIPHERAL)
mv_otg_start_periphrals(mvotg, 0);
mv_otg_reset(mvotg);
mv_otg_disable(mvotg);
break;
case OTG_STATE_B_PERIPHERAL:
mv_otg_enable(mvotg);
mv_otg_start_periphrals(mvotg, 1);
break;
case OTG_STATE_A_IDLE:
mvotg->otg.default_a = 1;
mv_otg_enable(mvotg);
if (old_state == OTG_STATE_A_WAIT_VFALL)
mv_otg_start_host(mvotg, 0);
mv_otg_reset(mvotg);
break;
case OTG_STATE_A_WAIT_VRISE:
mv_otg_set_vbus(&mvotg->otg, 1);
break;
case OTG_STATE_A_WAIT_BCON:
if (old_state != OTG_STATE_A_HOST)
mv_otg_start_host(mvotg, 1);
mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
T_A_WAIT_BCON,
mv_otg_timer_await_bcon);
/*
* Now, we directly enter A_HOST. So set b_conn = 1
* here. In fact, it need host driver to notify us.
*/
mvotg->otg_ctrl.b_conn = 1;
break;
case OTG_STATE_A_HOST:
break;
case OTG_STATE_A_WAIT_VFALL:
/*
* Now, we has exited A_HOST. So set b_conn = 0
* here. In fact, it need host driver to notify us.
*/
mvotg->otg_ctrl.b_conn = 0;
mv_otg_set_vbus(&mvotg->otg, 0);
break;
case OTG_STATE_A_VBUS_ERR:
break;
default:
break;
}
goto run;
}
}
static irqreturn_t mv_otg_irq(int irq, void *dev)
{
struct mv_otg *mvotg = dev;
u32 otgsc;
otgsc = readl(&mvotg->op_regs->otgsc);
writel(otgsc, &mvotg->op_regs->otgsc);
/*
* if we have vbus, then the vbus detection for B-device
* will be done by mv_otg_inputs_irq().
*/
if (mvotg->pdata->vbus)
if ((otgsc & OTGSC_STS_USB_ID) &&
!(otgsc & OTGSC_INTSTS_USB_ID))
return IRQ_NONE;
if ((otgsc & mvotg->irq_status) == 0)
return IRQ_NONE;
mv_otg_run_state_machine(mvotg, 0);
return IRQ_HANDLED;
}
static irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
{
struct mv_otg *mvotg = dev;
/* The clock may disabled at this time */
if (!mvotg->active) {
mv_otg_enable(mvotg);
mv_otg_init_irq(mvotg);
}
mv_otg_run_state_machine(mvotg, 0);
return IRQ_HANDLED;
}
static ssize_t
get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
{
struct mv_otg *mvotg = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n",
mvotg->otg_ctrl.a_bus_req);
}
static ssize_t
set_a_bus_req(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct mv_otg *mvotg = dev_get_drvdata(dev);
if (count > 2)
return -1;
/* We will use this interface to change to A device */
if (mvotg->otg.state != OTG_STATE_B_IDLE
&& mvotg->otg.state != OTG_STATE_A_IDLE)
return -1;
/* The clock may disabled and we need to set irq for ID detected */
mv_otg_enable(mvotg);
mv_otg_init_irq(mvotg);
if (buf[0] == '1') {
mvotg->otg_ctrl.a_bus_req = 1;
mvotg->otg_ctrl.a_bus_drop = 0;
dev_dbg(&mvotg->pdev->dev,
"User request: a_bus_req = 1\n");
if (spin_trylock(&mvotg->wq_lock)) {
mv_otg_run_state_machine(mvotg, 0);
spin_unlock(&mvotg->wq_lock);
}
}
return count;
}
static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req,
set_a_bus_req);
static ssize_t
set_a_clr_err(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct mv_otg *mvotg = dev_get_drvdata(dev);
if (!mvotg->otg.default_a)
return -1;
if (count > 2)
return -1;
if (buf[0] == '1') {
mvotg->otg_ctrl.a_clr_err = 1;
dev_dbg(&mvotg->pdev->dev,
"User request: a_clr_err = 1\n");
}
if (spin_trylock(&mvotg->wq_lock)) {
mv_otg_run_state_machine(mvotg, 0);
spin_unlock(&mvotg->wq_lock);
}
return count;
}
static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err);
static ssize_t
get_a_bus_drop(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mv_otg *mvotg = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n",
mvotg->otg_ctrl.a_bus_drop);
}
static ssize_t
set_a_bus_drop(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct mv_otg *mvotg = dev_get_drvdata(dev);
if (!mvotg->otg.default_a)
return -1;
if (count > 2)
return -1;
if (buf[0] == '0') {
mvotg->otg_ctrl.a_bus_drop = 0;
dev_dbg(&mvotg->pdev->dev,
"User request: a_bus_drop = 0\n");
} else if (buf[0] == '1') {
mvotg->otg_ctrl.a_bus_drop = 1;
mvotg->otg_ctrl.a_bus_req = 0;
dev_dbg(&mvotg->pdev->dev,
"User request: a_bus_drop = 1\n");
dev_dbg(&mvotg->pdev->dev,
"User request: and a_bus_req = 0\n");
}
if (spin_trylock(&mvotg->wq_lock)) {
mv_otg_run_state_machine(mvotg, 0);
spin_unlock(&mvotg->wq_lock);
}
return count;
}
static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR,
get_a_bus_drop, set_a_bus_drop);
static struct attribute *inputs_attrs[] = {
&dev_attr_a_bus_req.attr,
&dev_attr_a_clr_err.attr,
&dev_attr_a_bus_drop.attr,
NULL,
};
static struct attribute_group inputs_attr_group = {
.name = "inputs",
.attrs = inputs_attrs,
};
int mv_otg_remove(struct platform_device *pdev)
{
struct mv_otg *mvotg = platform_get_drvdata(pdev);
int clk_i;
sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group);
if (mvotg->irq)
free_irq(mvotg->irq, mvotg);
if (mvotg->pdata->vbus)
free_irq(mvotg->pdata->vbus->irq, mvotg);
if (mvotg->pdata->id)
free_irq(mvotg->pdata->id->irq, mvotg);
if (mvotg->qwork) {
flush_workqueue(mvotg->qwork);
destroy_workqueue(mvotg->qwork);
}
mv_otg_disable(mvotg);
if (mvotg->cap_regs)
iounmap(mvotg->cap_regs);
if (mvotg->phy_regs)
iounmap(mvotg->phy_regs);
for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++)
clk_put(mvotg->clk[clk_i]);
otg_set_transceiver(NULL);
platform_set_drvdata(pdev, NULL);
kfree(mvotg);
return 0;
}
static int mv_otg_probe(struct platform_device *pdev)
{
struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
struct mv_otg *mvotg;
struct resource *r;
int retval = 0, clk_i, i;
size_t size;
if (pdata == NULL) {
dev_err(&pdev->dev, "failed to get platform data\n");
return -ENODEV;
}
size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum;
mvotg = kzalloc(size, GFP_KERNEL);
if (!mvotg) {
dev_err(&pdev->dev, "failed to allocate memory!\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, mvotg);
mvotg->pdev = pdev;
mvotg->pdata = pdata;
mvotg->clknum = pdata->clknum;
for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) {
mvotg->clk[clk_i] = clk_get(&pdev->dev, pdata->clkname[clk_i]);
if (IS_ERR(mvotg->clk[clk_i])) {
retval = PTR_ERR(mvotg->clk[clk_i]);
goto err_put_clk;
}
}
mvotg->qwork = create_singlethread_workqueue("mv_otg_queue");
if (!mvotg->qwork) {
dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n");
retval = -ENOMEM;
goto err_put_clk;
}
INIT_DELAYED_WORK(&mvotg->work, mv_otg_work);
/* OTG common part */
mvotg->pdev = pdev;
mvotg->otg.dev = &pdev->dev;
mvotg->otg.label = driver_name;
mvotg->otg.set_host = mv_otg_set_host;
mvotg->otg.set_peripheral = mv_otg_set_peripheral;
mvotg->otg.set_vbus = mv_otg_set_vbus;
mvotg->otg.state = OTG_STATE_UNDEFINED;
for (i = 0; i < OTG_TIMER_NUM; i++)
init_timer(&mvotg->otg_ctrl.timer[i]);
r = platform_get_resource_byname(mvotg->pdev,
IORESOURCE_MEM, "phyregs");
if (r == NULL) {
dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
retval = -ENODEV;
goto err_destroy_workqueue;
}
mvotg->phy_regs = ioremap(r->start, resource_size(r));
if (mvotg->phy_regs == NULL) {
dev_err(&pdev->dev, "failed to map phy I/O memory\n");
retval = -EFAULT;
goto err_destroy_workqueue;
}
r = platform_get_resource_byname(mvotg->pdev,
IORESOURCE_MEM, "capregs");
if (r == NULL) {
dev_err(&pdev->dev, "no I/O memory resource defined\n");
retval = -ENODEV;
goto err_unmap_phyreg;
}
mvotg->cap_regs = ioremap(r->start, resource_size(r));
if (mvotg->cap_regs == NULL) {
dev_err(&pdev->dev, "failed to map I/O memory\n");
retval = -EFAULT;
goto err_unmap_phyreg;
}
/* we will acces controller register, so enable the udc controller */
retval = mv_otg_enable_internal(mvotg);
if (retval) {
dev_err(&pdev->dev, "mv otg enable error %d\n", retval);
goto err_unmap_capreg;
}
mvotg->op_regs =
(struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs
+ (readl(mvotg->cap_regs) & CAPLENGTH_MASK));
if (pdata->id) {
retval = request_threaded_irq(pdata->id->irq, NULL,
mv_otg_inputs_irq,
IRQF_ONESHOT, "id", mvotg);
if (retval) {
dev_info(&pdev->dev,
"Failed to request irq for ID\n");
pdata->id = NULL;
}
}
if (pdata->vbus) {
mvotg->clock_gating = 1;
retval = request_threaded_irq(pdata->vbus->irq, NULL,
mv_otg_inputs_irq,
IRQF_ONESHOT, "vbus", mvotg);
if (retval) {
dev_info(&pdev->dev,
"Failed to request irq for VBUS, "
"disable clock gating\n");
mvotg->clock_gating = 0;
pdata->vbus = NULL;
}
}
if (pdata->disable_otg_clock_gating)
mvotg->clock_gating = 0;
mv_otg_reset(mvotg);
mv_otg_init_irq(mvotg);
r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0);
if (r == NULL) {
dev_err(&pdev->dev, "no IRQ resource defined\n");
retval = -ENODEV;
goto err_disable_clk;
}
mvotg->irq = r->start;
if (request_irq(mvotg->irq, mv_otg_irq, IRQF_SHARED,
driver_name, mvotg)) {
dev_err(&pdev->dev, "Request irq %d for OTG failed\n",
mvotg->irq);
mvotg->irq = 0;
retval = -ENODEV;
goto err_disable_clk;
}
retval = otg_set_transceiver(&mvotg->otg);
if (retval < 0) {
dev_err(&pdev->dev, "can't register transceiver, %d\n",
retval);
goto err_free_irq;
}
retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group);
if (retval < 0) {
dev_dbg(&pdev->dev,
"Can't register sysfs attr group: %d\n", retval);
goto err_set_transceiver;
}
spin_lock_init(&mvotg->wq_lock);
if (spin_trylock(&mvotg->wq_lock)) {
mv_otg_run_state_machine(mvotg, 2 * HZ);
spin_unlock(&mvotg->wq_lock);
}
dev_info(&pdev->dev,
"successful probe OTG device %s clock gating.\n",
mvotg->clock_gating ? "with" : "without");
return 0;
err_set_transceiver:
otg_set_transceiver(NULL);
err_free_irq:
free_irq(mvotg->irq, mvotg);
err_disable_clk:
if (pdata->vbus)
free_irq(pdata->vbus->irq, mvotg);
if (pdata->id)
free_irq(pdata->id->irq, mvotg);
mv_otg_disable_internal(mvotg);
err_unmap_capreg:
iounmap(mvotg->cap_regs);
err_unmap_phyreg:
iounmap(mvotg->phy_regs);
err_destroy_workqueue:
flush_workqueue(mvotg->qwork);
destroy_workqueue(mvotg->qwork);
err_put_clk:
for (clk_i--; clk_i >= 0; clk_i--)
clk_put(mvotg->clk[clk_i]);
platform_set_drvdata(pdev, NULL);
kfree(mvotg);
return retval;
}
#ifdef CONFIG_PM
static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state)
{
struct mv_otg *mvotg = platform_get_drvdata(pdev);
if (mvotg->otg.state != OTG_STATE_B_IDLE) {
dev_info(&pdev->dev,
"OTG state is not B_IDLE, it is %d!\n",
mvotg->otg.state);
return -EAGAIN;
}
if (!mvotg->clock_gating)
mv_otg_disable_internal(mvotg);
return 0;
}
static int mv_otg_resume(struct platform_device *pdev)
{
struct mv_otg *mvotg = platform_get_drvdata(pdev);
u32 otgsc;
if (!mvotg->clock_gating) {
mv_otg_enable_internal(mvotg);
otgsc = readl(&mvotg->op_regs->otgsc);
otgsc |= mvotg->irq_en;
writel(otgsc, &mvotg->op_regs->otgsc);
if (spin_trylock(&mvotg->wq_lock)) {
mv_otg_run_state_machine(mvotg, 0);
spin_unlock(&mvotg->wq_lock);
}
}
return 0;
}
#endif
static struct platform_driver mv_otg_driver = {
.probe = mv_otg_probe,
.remove = __exit_p(mv_otg_remove),
.driver = {
.owner = THIS_MODULE,
.name = driver_name,
},
#ifdef CONFIG_PM
.suspend = mv_otg_suspend,
.resume = mv_otg_resume,
#endif
};
static int __init mv_otg_init(void)
{
return platform_driver_register(&mv_otg_driver);
}
static void __exit mv_otg_exit(void)
{
platform_driver_unregister(&mv_otg_driver);
}
module_init(mv_otg_init);
module_exit(mv_otg_exit);

165
drivers/usb/otg/mv_otg.h Normal file
View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
*
* 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.
*/
#ifndef __MV_USB_OTG_CONTROLLER__
#define __MV_USB_OTG_CONTROLLER__
#include <linux/types.h>
/* Command Register Bit Masks */
#define USBCMD_RUN_STOP (0x00000001)
#define USBCMD_CTRL_RESET (0x00000002)
/* otgsc Register Bit Masks */
#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001
#define OTGSC_CTRL_VUSB_CHARGE 0x00000002
#define OTGSC_CTRL_OTG_TERM 0x00000008
#define OTGSC_CTRL_DATA_PULSING 0x00000010
#define OTGSC_STS_USB_ID 0x00000100
#define OTGSC_STS_A_VBUS_VALID 0x00000200
#define OTGSC_STS_A_SESSION_VALID 0x00000400
#define OTGSC_STS_B_SESSION_VALID 0x00000800
#define OTGSC_STS_B_SESSION_END 0x00001000
#define OTGSC_STS_1MS_TOGGLE 0x00002000
#define OTGSC_STS_DATA_PULSING 0x00004000
#define OTGSC_INTSTS_USB_ID 0x00010000
#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000
#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000
#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000
#define OTGSC_INTSTS_B_SESSION_END 0x00100000
#define OTGSC_INTSTS_1MS 0x00200000
#define OTGSC_INTSTS_DATA_PULSING 0x00400000
#define OTGSC_INTR_USB_ID 0x01000000
#define OTGSC_INTR_A_VBUS_VALID 0x02000000
#define OTGSC_INTR_A_SESSION_VALID 0x04000000
#define OTGSC_INTR_B_SESSION_VALID 0x08000000
#define OTGSC_INTR_B_SESSION_END 0x10000000
#define OTGSC_INTR_1MS_TIMER 0x20000000
#define OTGSC_INTR_DATA_PULSING 0x40000000
#define CAPLENGTH_MASK (0xff)
/* Timer's interval, unit 10ms */
#define T_A_WAIT_VRISE 100
#define T_A_WAIT_BCON 2000
#define T_A_AIDL_BDIS 100
#define T_A_BIDL_ADIS 20
#define T_B_ASE0_BRST 400
#define T_B_SE0_SRP 300
#define T_B_SRP_FAIL 2000
#define T_B_DATA_PLS 10
#define T_B_SRP_INIT 100
#define T_A_SRP_RSPNS 10
#define T_A_DRV_RSM 5
enum otg_function {
OTG_B_DEVICE = 0,
OTG_A_DEVICE
};
enum mv_otg_timer {
A_WAIT_BCON_TIMER = 0,
OTG_TIMER_NUM
};
/* PXA OTG state machine */
struct mv_otg_ctrl {
/* internal variables */
u8 a_set_b_hnp_en; /* A-Device set b_hnp_en */
u8 b_srp_done;
u8 b_hnp_en;
/* OTG inputs */
u8 a_bus_drop;
u8 a_bus_req;
u8 a_clr_err;
u8 a_bus_resume;
u8 a_bus_suspend;
u8 a_conn;
u8 a_sess_vld;
u8 a_srp_det;
u8 a_vbus_vld;
u8 b_bus_req; /* B-Device Require Bus */
u8 b_bus_resume;
u8 b_bus_suspend;
u8 b_conn;
u8 b_se0_srp;
u8 b_sess_end;
u8 b_sess_vld;
u8 id;
u8 a_suspend_req;
/*Timer event */
u8 a_aidl_bdis_timeout;
u8 b_ase0_brst_timeout;
u8 a_bidl_adis_timeout;
u8 a_wait_bcon_timeout;
struct timer_list timer[OTG_TIMER_NUM];
};
#define VUSBHS_MAX_PORTS 8
struct mv_otg_regs {
u32 usbcmd; /* Command register */
u32 usbsts; /* Status register */
u32 usbintr; /* Interrupt enable */
u32 frindex; /* Frame index */
u32 reserved1[1];
u32 deviceaddr; /* Device Address */
u32 eplistaddr; /* Endpoint List Address */
u32 ttctrl; /* HOST TT status and control */
u32 burstsize; /* Programmable Burst Size */
u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */
u32 reserved[4];
u32 epnak; /* Endpoint NAK */
u32 epnaken; /* Endpoint NAK Enable */
u32 configflag; /* Configured Flag register */
u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */
u32 otgsc;
u32 usbmode; /* USB Host/Device mode */
u32 epsetupstat; /* Endpoint Setup Status */
u32 epprime; /* Endpoint Initialize */
u32 epflush; /* Endpoint De-initialize */
u32 epstatus; /* Endpoint Status */
u32 epcomplete; /* Endpoint Interrupt On Complete */
u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */
u32 mcr; /* Mux Control */
u32 isr; /* Interrupt Status */
u32 ier; /* Interrupt Enable */
};
struct mv_otg {
struct otg_transceiver otg;
struct mv_otg_ctrl otg_ctrl;
/* base address */
void __iomem *phy_regs;
void __iomem *cap_regs;
struct mv_otg_regs __iomem *op_regs;
struct platform_device *pdev;
int irq;
u32 irq_status;
u32 irq_en;
struct delayed_work work;
struct workqueue_struct *qwork;
spinlock_t wq_lock;
struct mv_usb_platform_data *pdata;
unsigned int active;
unsigned int clock_gating;
unsigned int clknum;
struct clk *clk[0];
};
#endif

View File

@ -185,7 +185,7 @@ static int usbhsg_dma_map(struct device *dev,
}
if (dma_mapping_error(dev, pkt->dma)) {
dev_err(dev, "dma mapping error %x\n", pkt->dma);
dev_err(dev, "dma mapping error %llx\n", (u64)pkt->dma);
return -EIO;
}

View File

@ -633,7 +633,6 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
struct urb *urb = ureq->urb;
struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
struct device *dev = usbhs_priv_to_dev(priv);
int status = 0;
@ -651,7 +650,7 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
usbhsh_ureq_free(hpriv, ureq);
usbhsh_endpoint_sequence_save(hpriv, urb, pkt);
usbhsh_pipe_detach(hpriv, uep);
usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(urb->ep));
usb_hcd_unlink_urb_from_ep(hcd, urb);
usb_hcd_giveback_urb(hcd, urb, status);

View File

@ -330,8 +330,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
if (dir_in)
usbhsp_flags_set(pipe, IS_DIR_HOST);
if ((is_host && !dir_in) ||
(!is_host && dir_in))
if (!!is_host ^ !!dir_in)
dir |= DIR_OUT;
if (!dir)

View File

@ -42,9 +42,23 @@ struct mv_usb_platform_data {
/* only valid for HCD. OTG or Host only*/
unsigned int mode;
int (*phy_init)(unsigned int regbase);
void (*phy_deinit)(unsigned int regbase);
/* This flag is used for that needs id pin checked by otg */
unsigned int disable_otg_clock_gating:1;
/* Force a_bus_req to be asserted */
unsigned int otg_force_a_bus_req:1;
int (*phy_init)(void __iomem *regbase);
void (*phy_deinit)(void __iomem *regbase);
int (*set_vbus)(unsigned int vbus);
int (*private_init)(void __iomem *opregs, void __iomem *phyregs);
};
#ifndef CONFIG_HAVE_CLK
/* Dummy stub for clk framework */
#define clk_get(dev, id) NULL
#define clk_put(clock) do {} while (0)
#define clk_enable(clock) do {} while (0)
#define clk_disable(clock) do {} while (0)
#endif
#endif

View File

@ -0,0 +1,34 @@
/*
* S3C24XX USB 2.0 High-speed USB controller gadget driver
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
* Each endpoint can be configured as either in or out endpoint. Endpoints
* can be configured for Bulk or Interrupt transfer mode.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __LINUX_USB_S3C_HSUDC_H
#define __LINUX_USB_S3C_HSUDC_H
/**
* 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);
};
#endif /* __LINUX_USB_S3C_HSUDC_H */

View File

@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/types.h>
#include <linux/usb/ch9.h>
@ -32,6 +33,9 @@ struct usb_ep;
* @dma: DMA address corresponding to 'buf'. If you don't set this
* field, and the usb controller needs one, it is responsible
* for mapping and unmapping the buffer.
* @sg: a scatterlist for SG-capable controllers.
* @num_sgs: number of SG entries
* @num_mapped_sgs: number of SG entries mapped to DMA (internal)
* @length: Length of that data
* @stream_id: The stream id, when USB3.0 bulk streams are being used
* @no_interrupt: If true, hints that no completion irq is needed.
@ -88,6 +92,10 @@ struct usb_request {
unsigned length;
dma_addr_t dma;
struct scatterlist *sg;
unsigned num_sgs;
unsigned num_mapped_sgs;
unsigned stream_id:16;
unsigned no_interrupt:1;
unsigned zero:1;
@ -164,7 +172,7 @@ struct usb_ep {
unsigned maxpacket:16;
unsigned max_streams:16;
unsigned mult:2;
unsigned maxburst:4;
unsigned maxburst:5;
u8 address;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
@ -479,6 +487,7 @@ struct usb_gadget_ops {
* @speed: Speed of current connection to USB host.
* @max_speed: Maximal speed the UDC can handle. UDC must support this
* and all slower speeds.
* @sg_supported: true if we can handle scatter-gather
* @is_otg: True if the USB device port uses a Mini-AB jack, so that the
* gadget driver must provide a USB OTG descriptor.
* @is_a_peripheral: False unless is_otg, the "A" end of a USB cable
@ -519,6 +528,7 @@ struct usb_gadget {
struct list_head ep_list; /* of usb_ep */
enum usb_device_speed speed;
enum usb_device_speed max_speed;
unsigned sg_supported:1;
unsigned is_otg:1;
unsigned is_a_peripheral:1;
unsigned b_hnp_enable:1;