USB: fsl_udc_core: support device mode of MPC5121E DR USB Controller

Extend the FSL UDC driver to support MPC5121E DR USB Controller
operation in device mode. Add MPC5121E specific init/uninit
at probe and remove and isolate system interface register accesses
when running on MPC5121E SoC, as these registers are not available
on this platform. This patch relies on previous patch for supporting
big endian registers and descriptors access in the FSL UDC driver.
Additionally support endpoint FIFO status operation by providing
appropriate callback in endpoint ops structure.

Also flush cache for the req buffer used for GetStatus reply.
Without this, the correct reply to an endpoint GetStatus
is written to 'req', but doesn't make it out to the USB bus
since the buffer hasn't been flushed. This would cause the
USBCV Halt Endpoint test to fail (according to changelog in
Freescale LTIB driver code).

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: Li Yang <leoli@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Anatolij Gustschin 2011-04-18 22:01:58 +02:00 committed by Greg Kroah-Hartman
parent 09ba0def9a
commit 2ea6698d7b
2 changed files with 112 additions and 31 deletions

View File

@ -6,7 +6,7 @@
* *
* Description: * Description:
* Freescale high-speed USB SOC DR module device controller driver. * Freescale high-speed USB SOC DR module device controller driver.
* This can be found on MPC8349E/MPC8313E cpus. * This can be found on MPC8349E/MPC8313E/MPC5121E cpus.
* The driver is previously named as mpc_udc. Based on bare board * The driver is previously named as mpc_udc. Based on bare board
* code from Dave Liu and Shlomi Gridish. * code from Dave Liu and Shlomi Gridish.
* *
@ -45,6 +45,7 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/cacheflush.h>
#include "fsl_usb2_udc.h" #include "fsl_usb2_udc.h"
@ -278,9 +279,12 @@ static int dr_controller_setup(struct fsl_udc *udc)
/* Set the controller as device mode */ /* Set the controller as device mode */
tmp = fsl_readl(&dr_regs->usbmode); tmp = fsl_readl(&dr_regs->usbmode);
tmp &= ~USB_MODE_CTRL_MODE_MASK; /* clear mode bits */
tmp |= USB_MODE_CTRL_MODE_DEVICE; tmp |= USB_MODE_CTRL_MODE_DEVICE;
/* Disable Setup Lockout */ /* Disable Setup Lockout */
tmp |= USB_MODE_SETUP_LOCK_OFF; tmp |= USB_MODE_SETUP_LOCK_OFF;
if (udc->pdata->es)
tmp |= USB_MODE_ES;
fsl_writel(tmp, &dr_regs->usbmode); fsl_writel(tmp, &dr_regs->usbmode);
/* Clear the setup status */ /* Clear the setup status */
@ -296,20 +300,24 @@ static int dr_controller_setup(struct fsl_udc *udc)
/* Config control enable i/o output, cpu endian register */ /* Config control enable i/o output, cpu endian register */
#ifndef CONFIG_ARCH_MXC #ifndef CONFIG_ARCH_MXC
ctrl = __raw_readl(&usb_sys_regs->control); if (udc->pdata->have_sysif_regs) {
ctrl |= USB_CTRL_IOENB; ctrl = __raw_readl(&usb_sys_regs->control);
__raw_writel(ctrl, &usb_sys_regs->control); ctrl |= USB_CTRL_IOENB;
__raw_writel(ctrl, &usb_sys_regs->control);
}
#endif #endif
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
/* Turn on cache snooping hardware, since some PowerPC platforms /* Turn on cache snooping hardware, since some PowerPC platforms
* wholly rely on hardware to deal with cache coherent. */ * wholly rely on hardware to deal with cache coherent. */
/* Setup Snooping for all the 4GB space */ if (udc->pdata->have_sysif_regs) {
tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ /* Setup Snooping for all the 4GB space */
__raw_writel(tmp, &usb_sys_regs->snoop1); tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */
tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ __raw_writel(tmp, &usb_sys_regs->snoop1);
__raw_writel(tmp, &usb_sys_regs->snoop2); tmp |= 0x80000000; /* starts from 0x8000000, size 2G */
__raw_writel(tmp, &usb_sys_regs->snoop2);
}
#endif #endif
return 0; return 0;
@ -1014,6 +1022,36 @@ out:
return status; return status;
} }
static int fsl_ep_fifo_status(struct usb_ep *_ep)
{
struct fsl_ep *ep;
struct fsl_udc *udc;
int size = 0;
u32 bitmask;
struct ep_queue_head *d_qh;
ep = container_of(_ep, struct fsl_ep, ep);
if (!_ep || (!ep->desc && ep_index(ep) != 0))
return -ENODEV;
udc = (struct fsl_udc *)ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)];
bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) :
(1 << (ep_index(ep)));
if (fsl_readl(&dr_regs->endptstatus) & bitmask)
size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE)
>> DTD_LENGTH_BIT_POS;
pr_debug("%s %u\n", __func__, size);
return size;
}
static void fsl_ep_fifo_flush(struct usb_ep *_ep) static void fsl_ep_fifo_flush(struct usb_ep *_ep)
{ {
struct fsl_ep *ep; struct fsl_ep *ep;
@ -1066,6 +1104,7 @@ static struct usb_ep_ops fsl_ep_ops = {
.dequeue = fsl_ep_dequeue, .dequeue = fsl_ep_dequeue,
.set_halt = fsl_ep_set_halt, .set_halt = fsl_ep_set_halt,
.fifo_status = fsl_ep_fifo_status,
.fifo_flush = fsl_ep_fifo_flush, /* flush fifo */ .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */
}; };
@ -1280,6 +1319,10 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
req = udc->status_req; req = udc->status_req;
/* Fill in the reqest structure */ /* Fill in the reqest structure */
*((u16 *) req->req.buf) = cpu_to_le16(tmp); *((u16 *) req->req.buf) = cpu_to_le16(tmp);
/* flush cache for the req buffer */
flush_dcache_range((u32)req->req.buf, (u32)req->req.buf + 8);
req->ep = ep; req->ep = ep;
req->req.length = 2; req->req.length = 2;
req->req.status = -EINPROGRESS; req->req.status = -EINPROGRESS;
@ -1332,6 +1375,7 @@ static void setup_received_irq(struct fsl_udc *udc,
/* Status phase from udc */ /* Status phase from udc */
{ {
int rc = -EOPNOTSUPP; int rc = -EOPNOTSUPP;
u16 ptc = 0;
if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
== (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
@ -1353,17 +1397,19 @@ static void setup_received_irq(struct fsl_udc *udc,
| USB_TYPE_STANDARD)) { | USB_TYPE_STANDARD)) {
/* Note: The driver has not include OTG support yet. /* Note: The driver has not include OTG support yet.
* This will be set when OTG support is added */ * This will be set when OTG support is added */
if (!gadget_is_otg(&udc->gadget)) if (wValue == USB_DEVICE_TEST_MODE)
break; ptc = wIndex >> 8;
else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) else if (gadget_is_otg(&udc->gadget)) {
udc->gadget.b_hnp_enable = 1; if (setup->bRequest ==
else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) USB_DEVICE_B_HNP_ENABLE)
udc->gadget.a_hnp_support = 1; udc->gadget.b_hnp_enable = 1;
else if (setup->bRequest == else if (setup->bRequest ==
USB_DEVICE_A_ALT_HNP_SUPPORT) USB_DEVICE_A_HNP_SUPPORT)
udc->gadget.a_alt_hnp_support = 1; udc->gadget.a_hnp_support = 1;
else else if (setup->bRequest ==
break; USB_DEVICE_A_ALT_HNP_SUPPORT)
udc->gadget.a_alt_hnp_support = 1;
}
rc = 0; rc = 0;
} else } else
break; break;
@ -1372,6 +1418,15 @@ static void setup_received_irq(struct fsl_udc *udc,
if (ep0_prime_status(udc, EP_DIR_IN)) if (ep0_prime_status(udc, EP_DIR_IN))
ep0stall(udc); ep0stall(udc);
} }
if (ptc) {
u32 tmp;
mdelay(10);
tmp = fsl_readl(&dr_regs->portsc1) | (ptc << 16);
fsl_writel(tmp, &dr_regs->portsc1);
printk(KERN_INFO "udc: switch to test mode %d.\n", ptc);
}
return; return;
} }
@ -2106,16 +2161,18 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
next += t; next += t;
#ifndef CONFIG_ARCH_MXC #ifndef CONFIG_ARCH_MXC
tmp_reg = usb_sys_regs->snoop1; if (udc->pdata->have_sysif_regs) {
t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); tmp_reg = usb_sys_regs->snoop1;
size -= t; t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
next += t; size -= t;
next += t;
tmp_reg = usb_sys_regs->control; tmp_reg = usb_sys_regs->control;
t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n", t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n",
tmp_reg); tmp_reg);
size -= t; size -= t;
next += t; next += t;
}
#endif #endif
/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */ /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
@ -2336,6 +2393,17 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
goto err_release_mem_region; goto err_release_mem_region;
} }
pdata->regs = (void *)dr_regs;
/*
* do platform specific init: check the clock, grab/config pins, etc.
*/
if (pdata->init && pdata->init(pdev)) {
ret = -ENODEV;
goto err_iounmap_noclk;
}
/* Set accessors only after pdata->init() ! */
if (pdata->big_endian_mmio) { if (pdata->big_endian_mmio) {
_fsl_readl = _fsl_readl_be; _fsl_readl = _fsl_readl_be;
_fsl_writel = _fsl_writel_be; _fsl_writel = _fsl_writel_be;
@ -2345,8 +2413,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
} }
#ifndef CONFIG_ARCH_MXC #ifndef CONFIG_ARCH_MXC
usb_sys_regs = (struct usb_sys_interface *) if (pdata->have_sysif_regs)
((u32)dr_regs + USB_DR_SYS_OFFSET); usb_sys_regs = (struct usb_sys_interface *)
((u32)dr_regs + USB_DR_SYS_OFFSET);
#endif #endif
/* Initialize USB clocks */ /* Initialize USB clocks */
@ -2446,6 +2515,8 @@ err_unregister:
err_free_irq: err_free_irq:
free_irq(udc_controller->irq, udc_controller); free_irq(udc_controller->irq, udc_controller);
err_iounmap: err_iounmap:
if (pdata->exit)
pdata->exit(pdev);
fsl_udc_clk_release(); fsl_udc_clk_release();
err_iounmap_noclk: err_iounmap_noclk:
iounmap(dr_regs); iounmap(dr_regs);
@ -2463,6 +2534,7 @@ err_kfree:
static int __exit fsl_udc_remove(struct platform_device *pdev) static int __exit fsl_udc_remove(struct platform_device *pdev)
{ {
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
DECLARE_COMPLETION(done); DECLARE_COMPLETION(done);
@ -2489,6 +2561,13 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
/* free udc --wait for the release() finished */ /* free udc --wait for the release() finished */
wait_for_completion(&done); wait_for_completion(&done);
/*
* do platform specific un-initialization:
* release iomux pins, etc.
*/
if (pdata->exit)
pdata->exit(pdev);
return 0; return 0;
} }

View File

@ -275,7 +275,9 @@ struct usb_sys_interface {
#define USB_MODE_CTRL_MODE_IDLE 0x00000000 #define USB_MODE_CTRL_MODE_IDLE 0x00000000
#define USB_MODE_CTRL_MODE_DEVICE 0x00000002 #define USB_MODE_CTRL_MODE_DEVICE 0x00000002
#define USB_MODE_CTRL_MODE_HOST 0x00000003 #define USB_MODE_CTRL_MODE_HOST 0x00000003
#define USB_MODE_CTRL_MODE_MASK 0x00000003
#define USB_MODE_CTRL_MODE_RSV 0x00000001 #define USB_MODE_CTRL_MODE_RSV 0x00000001
#define USB_MODE_ES 0x00000004 /* Endian Select */
#define USB_MODE_SETUP_LOCK_OFF 0x00000008 #define USB_MODE_SETUP_LOCK_OFF 0x00000008
#define USB_MODE_STREAM_DISABLE 0x00000010 #define USB_MODE_STREAM_DISABLE 0x00000010
/* Endpoint Flush Register */ /* Endpoint Flush Register */