Skip to content

Commit

Permalink
usb: gadget: mv_udc: refine the driver structure
Browse files Browse the repository at this point in the history
This patch do the following things:

1. Add header and Copyright for marvell usb driver.
2. Add mv_usb.h in include/linux/platform_data, make the driver
   fits all the marvell platform using the same ChipIdea usb ip.
3. Some SOC may has mutiple clock sources, so let me define it
   in mv_usb_platform_data and give two helper functions named
   udc_clock_enable/udc_clock_disable to deal with the clocks.
4. Different SOCs will have some difference in PHY initialization,
   so we will remove file mv_udc_phy.c and add two funtions in
   mv_usb_platform_data, let the platform relative driver to realize it.
5. Rewrite probe function according to the modification list above. Find
   it will kernel panic when probe failed. The root cause is as follows:
	When probe failed, the error handle may call device_unregister()
	which in return will call gadget_release.In current code,
	gadget_release have two issues:
		1: the_controller is a NULL pointer.
		2: if we free udc here, then the following code in probe
		   will access NULL pointer.

Signed-off-by: Neil Zhang <zhangwm@marvell.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
  • Loading branch information
Neil Zhang authored and Felipe Balbi committed Oct 13, 2011
1 parent 8a9775a commit dde34cc
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 259 deletions.
2 changes: 1 addition & 1 deletion drivers/usb/gadget/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ 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
mv_udc-y := mv_udc_core.o mv_udc_phy.o
mv_udc-y := mv_udc_core.o
obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o

Expand Down
17 changes: 14 additions & 3 deletions drivers/usb/gadget/mv_udc.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* 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_UDC_H
#define __MV_UDC_H
Expand Down Expand Up @@ -201,7 +209,12 @@ struct mv_udc {
remote_wakeup:1,
softconnected:1,
force_fs:1;
struct clk *clk;

struct mv_usb_platform_data *pdata;

/* some SOC has mutiple clock sources for USB*/
unsigned int clknum;
struct clk *clk[0];
};

/* endpoint data structure */
Expand Down Expand Up @@ -289,6 +302,4 @@ struct mv_dtd {
struct mv_dtd *next_dtd_virt;
};

extern int mv_udc_phy_init(unsigned int base);

#endif
158 changes: 117 additions & 41 deletions drivers/usb/gadget/mv_udc_core.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/*
* 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/pci.h>
#include <linux/dma-mapping.h>
Expand All @@ -22,6 +33,7 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/platform_data/mv_usb.h>
#include <asm/system.h>
#include <asm/unaligned.h>

Expand All @@ -45,6 +57,8 @@
#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)

static DECLARE_COMPLETION(release_done);

static const char driver_name[] = "mv_udc";
static const char driver_desc[] = DRIVER_DESC;

Expand Down Expand Up @@ -987,6 +1001,22 @@ static struct usb_ep_ops mv_ep_ops = {
.fifo_flush = mv_ep_fifo_flush, /* flush fifo */
};

static void udc_clock_enable(struct mv_udc *udc)
{
unsigned int i;

for (i = 0; i < udc->clknum; i++)
clk_enable(udc->clk[i]);
}

static void udc_clock_disable(struct mv_udc *udc)
{
unsigned int i;

for (i = 0; i < udc->clknum; i++)
clk_disable(udc->clk[i]);
}

static void udc_stop(struct mv_udc *udc)
{
u32 tmp;
Expand Down Expand Up @@ -1877,18 +1907,15 @@ static void gadget_release(struct device *_dev)
struct mv_udc *udc = the_controller;

complete(udc->done);
kfree(udc);
}

static int mv_udc_remove(struct platform_device *dev)
{
struct mv_udc *udc = the_controller;
DECLARE_COMPLETION(done);
int clk_i;

usb_del_gadget_udc(&udc->gadget);

udc->done = &done;

/* free memory allocated in probe */
if (udc->dtd_pool)
dma_pool_destroy(udc->dtd_pool);
Expand All @@ -1915,10 +1942,14 @@ static int mv_udc_remove(struct platform_device *dev)
kfree(udc->status_req);
}

for (clk_i = 0; clk_i <= udc->clknum; clk_i++)
clk_put(udc->clk[clk_i]);

device_unregister(&udc->gadget.dev);

/* free dev, wait for the release() finished */
wait_for_completion(&done);
wait_for_completion(udc->done);
kfree(udc);

the_controller = NULL;

Expand All @@ -1927,63 +1958,78 @@ static int mv_udc_remove(struct platform_device *dev)

int mv_udc_probe(struct platform_device *dev)
{
struct mv_usb_platform_data *pdata = dev->dev.platform_data;
struct mv_udc *udc;
int retval = 0;
int clk_i = 0;
struct resource *r;
size_t size;

udc = kzalloc(sizeof *udc, GFP_KERNEL);
if (pdata == NULL) {
dev_err(&dev->dev, "missing platform_data\n");
return -ENODEV;
}

size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum;
udc = kzalloc(size, GFP_KERNEL);
if (udc == NULL) {
dev_err(&dev->dev, "failed to allocate memory for udc\n");
retval = -ENOMEM;
goto error;
return -ENOMEM;
}

the_controller = udc;
udc->done = &release_done;
udc->pdata = dev->dev.platform_data;
spin_lock_init(&udc->lock);

udc->dev = dev;

udc->clk = clk_get(&dev->dev, "U2OCLK");
if (IS_ERR(udc->clk)) {
retval = PTR_ERR(udc->clk);
goto error;
udc->clknum = pdata->clknum;
for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]);
if (IS_ERR(udc->clk[clk_i])) {
retval = PTR_ERR(udc->clk[clk_i]);
goto err_put_clk;
}
}

r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2o");
r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs");
if (r == NULL) {
dev_err(&dev->dev, "no I/O memory resource defined\n");
retval = -ENODEV;
goto error;
goto err_put_clk;
}

udc->cap_regs = (struct mv_cap_regs __iomem *)
ioremap(r->start, resource_size(r));
if (udc->cap_regs == NULL) {
dev_err(&dev->dev, "failed to map I/O memory\n");
retval = -EBUSY;
goto error;
goto err_put_clk;
}

r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2ophy");
r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs");
if (r == NULL) {
dev_err(&dev->dev, "no phy I/O memory resource defined\n");
retval = -ENODEV;
goto error;
goto err_iounmap_capreg;
}

udc->phy_regs = (unsigned int)ioremap(r->start, resource_size(r));
if (udc->phy_regs == 0) {
dev_err(&dev->dev, "failed to map phy I/O memory\n");
retval = -EBUSY;
goto error;
goto err_iounmap_capreg;
}

/* we will acces controller register, so enable the clk */
clk_enable(udc->clk);
retval = mv_udc_phy_init(udc->phy_regs);
if (retval) {
dev_err(&dev->dev, "phy initialization error %d\n", retval);
goto error;
udc_clock_enable(udc);
if (pdata->phy_init) {
retval = pdata->phy_init(udc->phy_regs);
if (retval) {
dev_err(&dev->dev, "phy init error %d\n", retval);
goto err_iounmap_phyreg;
}
}

udc->op_regs = (struct mv_op_regs __iomem *)((u32)udc->cap_regs
Expand All @@ -1999,7 +2045,7 @@ int mv_udc_probe(struct platform_device *dev)
if (udc->ep_dqh == NULL) {
dev_err(&dev->dev, "allocate dQH memory failed\n");
retval = -ENOMEM;
goto error;
goto err_disable_clock;
}
udc->ep_dqh_size = size;

Expand All @@ -2012,23 +2058,23 @@ int mv_udc_probe(struct platform_device *dev)

if (!udc->dtd_pool) {
retval = -ENOMEM;
goto error;
goto err_free_dma;
}

size = udc->max_eps * sizeof(struct mv_ep) *2;
udc->eps = kzalloc(size, GFP_KERNEL);
if (udc->eps == NULL) {
dev_err(&dev->dev, "allocate ep memory failed\n");
retval = -ENOMEM;
goto error;
goto err_destroy_dma;
}

/* initialize ep0 status request structure */
udc->status_req = kzalloc(sizeof(struct mv_req), GFP_KERNEL);
if (!udc->status_req) {
dev_err(&dev->dev, "allocate status_req memory failed\n");
retval = -ENOMEM;
goto error;
goto err_free_eps;
}
INIT_LIST_HEAD(&udc->status_req->queue);

Expand All @@ -2045,15 +2091,15 @@ int mv_udc_probe(struct platform_device *dev)
if (r == NULL) {
dev_err(&dev->dev, "no IRQ resource defined\n");
retval = -ENODEV;
goto error;
goto err_free_status_req;
}
udc->irq = r->start;
if (request_irq(udc->irq, mv_udc_irq,
IRQF_SHARED, driver_name, udc)) {
dev_err(&dev->dev, "Request irq %d for UDC failed\n",
udc->irq);
retval = -ENODEV;
goto error;
goto err_free_status_req;
}

/* initialize gadget structure */
Expand All @@ -2072,18 +2118,43 @@ int mv_udc_probe(struct platform_device *dev)

retval = device_register(&udc->gadget.dev);
if (retval)
goto error;
goto err_free_irq;

eps_init(udc);

the_controller = udc;

retval = usb_add_gadget_udc(&dev->dev, &udc->gadget);
if (!retval)
return retval;
error:
if (udc)
mv_udc_remove(udc->dev);
if (retval)
goto err_unregister;

return 0;

err_unregister:
device_unregister(&udc->gadget.dev);
err_free_irq:
free_irq(udc->irq, &dev->dev);
err_free_status_req:
kfree(udc->status_req->req.buf);
kfree(udc->status_req);
err_free_eps:
kfree(udc->eps);
err_destroy_dma:
dma_pool_destroy(udc->dtd_pool);
err_free_dma:
dma_free_coherent(&dev->dev, udc->ep_dqh_size,
udc->ep_dqh, udc->ep_dqh_dma);
err_disable_clock:
if (udc->pdata->phy_deinit)
udc->pdata->phy_deinit(udc->phy_regs);
udc_clock_disable(udc);
err_iounmap_phyreg:
iounmap((void *)udc->phy_regs);
err_iounmap_capreg:
iounmap(udc->cap_regs);
err_put_clk:
for (clk_i--; clk_i >= 0; clk_i--)
clk_put(udc->clk[clk_i]);
the_controller = NULL;
kfree(udc);
return retval;
}

Expand All @@ -2102,11 +2173,16 @@ static int mv_udc_resume(struct device *_dev)
struct mv_udc *udc = the_controller;
int retval;

retval = mv_udc_phy_init(udc->phy_regs);
if (retval) {
dev_err(_dev, "phy initialization error %d\n", retval);
return retval;
if (udc->pdata->phy_init) {
retval = udc->pdata->phy_init(udc->phy_regs);
if (retval) {
dev_err(&udc->dev->dev,
"init phy error %d when resume back\n",
retval);
return retval;
}
}

udc_reset(udc);
ep0_reset(udc);
udc_start(udc);
Expand Down
Loading

0 comments on commit dde34cc

Please sign in to comment.