Commit dde34cc5 authored by Neil Zhang's avatar Neil Zhang Committed by Felipe Balbi

usb: gadget: mv_udc: refine the driver structure

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: default avatarNeil Zhang <zhangwm@marvell.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 8a9775ab
......@@ -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
......
/*
* 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
......@@ -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 */
......@@ -289,6 +302,4 @@ struct mv_dtd {
struct mv_dtd *next_dtd_virt;
};
extern int mv_udc_phy_init(unsigned int base);
#endif
/*
* 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>
......@@ -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>
......@@ -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;
......@@ -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;
......@@ -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);
......@@ -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;
......@@ -1927,33 +1958,46 @@ 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 *)
......@@ -1961,29 +2005,31 @@ int mv_udc_probe(struct platform_device *dev)
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);
udc_clock_enable(udc);
if (pdata->phy_init) {
retval = pdata->phy_init(udc->phy_regs);
if (retval) {
dev_err(&dev->dev, "phy initialization error %d\n", retval);
goto error;
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
......@@ -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;
......@@ -2012,7 +2058,7 @@ 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;
......@@ -2020,7 +2066,7 @@ int mv_udc_probe(struct platform_device *dev)
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 */
......@@ -2028,7 +2074,7 @@ int mv_udc_probe(struct platform_device *dev)
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);
......@@ -2045,7 +2091,7 @@ 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,
......@@ -2053,7 +2099,7 @@ int mv_udc_probe(struct platform_device *dev)
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 */
......@@ -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;
}
......@@ -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 (udc->pdata->phy_init) {
retval = udc->pdata->phy_init(udc->phy_regs);
if (retval) {
dev_err(_dev, "phy initialization error %d\n", 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);
......
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <mach/cputype.h>
#ifdef CONFIG_ARCH_MMP
#define UTMI_REVISION 0x0
#define UTMI_CTRL 0x4
#define UTMI_PLL 0x8
#define UTMI_TX 0xc
#define UTMI_RX 0x10
#define UTMI_IVREF 0x14
#define UTMI_T0 0x18
#define UTMI_T1 0x1c
#define UTMI_T2 0x20
#define UTMI_T3 0x24
#define UTMI_T4 0x28
#define UTMI_T5 0x2c
#define UTMI_RESERVE 0x30
#define UTMI_USB_INT 0x34
#define UTMI_DBG_CTL 0x38
#define UTMI_OTG_ADDON 0x3c
/* For UTMICTRL Register */
#define UTMI_CTRL_USB_CLK_EN (1 << 31)
/* pxa168 */
#define UTMI_CTRL_SUSPEND_SET1 (1 << 30)
#define UTMI_CTRL_SUSPEND_SET2 (1 << 29)
#define UTMI_CTRL_RXBUF_PDWN (1 << 24)
#define UTMI_CTRL_TXBUF_PDWN (1 << 11)
#define UTMI_CTRL_INPKT_DELAY_SHIFT 30
#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28
#define UTMI_CTRL_PU_REF_SHIFT 20
#define UTMI_CTRL_ARC_PULLDN_SHIFT 12
#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1
#define UTMI_CTRL_PWR_UP_SHIFT 0
/* For UTMI_PLL Register */
#define UTMI_PLL_CLK_BLK_EN_SHIFT 24
#define UTMI_PLL_FBDIV_SHIFT 4
#define UTMI_PLL_REFDIV_SHIFT 0
#define UTMI_PLL_FBDIV_MASK 0x00000FF0
#define UTMI_PLL_REFDIV_MASK 0x0000000F
#define UTMI_PLL_ICP_MASK 0x00007000
#define UTMI_PLL_KVCO_MASK 0x00031000
#define UTMI_PLL_PLLCALI12_SHIFT 29
#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29)
#define UTMI_PLL_PLLVDD18_SHIFT 27
#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27)
#define UTMI_PLL_PLLVDD12_SHIFT 25
#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25)
#define UTMI_PLL_KVCO_SHIFT 15
#define UTMI_PLL_ICP_SHIFT 12
/* For UTMI_TX Register */
#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27
#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27)
#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK 26
#define UTMI_TX_REG_EXT_FS_RCAL_EN (0x1 << 26)
#define UTMI_TX_LOW_VDD_EN_SHIFT 11
#define UTMI_TX_IMPCAL_VTH_SHIFT 14
#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14)
#define UTMI_TX_CK60_PHSEL_SHIFT 17
#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17)
#define UTMI_TX_TXVDD12_SHIFT 22
#define UTMI_TX_TXVDD12_MASK (0x3 << 22)
#define UTMI_TX_AMP_SHIFT 0
#define UTMI_TX_AMP_MASK (0x7 << 0)
/* For UTMI_RX Register */
#define UTMI_RX_SQ_THRESH_SHIFT 4
#define UTMI_RX_SQ_THRESH_MASK (0xf << 4)
#define UTMI_REG_SQ_LENGTH_SHIFT 15
#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15)
#define REG_RCAL_START 0x00001000
#define VCOCAL_START 0x00200000
#define KVCO_EXT 0x00400000
#define PLL_READY 0x00800000
#define CLK_BLK_EN 0x01000000
#endif
static unsigned int u2o_read(unsigned int base, unsigned int offset)
{
return readl(base + offset);
}
static void u2o_set(unsigned int base, unsigned int offset, unsigned int value)
{
unsigned int reg;
reg = readl(base + offset);
reg |= value;
writel(reg, base + offset);
readl(base + offset);
}
static void u2o_clear(unsigned int base, unsigned int offset,
unsigned int value)
{
unsigned int reg;
reg = readl(base + offset);
reg &= ~value;
writel(reg, base + offset);
readl(base + offset);
}
static void u2o_write(unsigned int base, unsigned int offset,
unsigned int value)
{
writel(value, base + offset);
readl(base + offset);
}
#ifdef CONFIG_ARCH_MMP
int mv_udc_phy_init(unsigned int base)
{
unsigned long timeout;
/* Initialize the USB PHY power */
if (cpu_is_pxa910()) {
u2o_set(base, UTMI_CTRL, (1 << UTMI_CTRL_INPKT_DELAY_SOF_SHIFT)
| (1 << UTMI_CTRL_PU_REF_SHIFT));
}
u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PLL_PWR_UP_SHIFT);
u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PWR_UP_SHIFT);
/* UTMI_PLL settings */
u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK
| UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK
| UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK
| UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK);
u2o_set(base, UTMI_PLL, (0xee << UTMI_PLL_FBDIV_SHIFT)
| (0xb << UTMI_PLL_REFDIV_SHIFT)
| (3 << UTMI_PLL_PLLVDD18_SHIFT)
| (3 << UTMI_PLL_PLLVDD12_SHIFT)
| (3 << UTMI_PLL_PLLCALI12_SHIFT)
| (1 << UTMI_PLL_ICP_SHIFT) | (3 << UTMI_PLL_KVCO_SHIFT));
/* UTMI_TX */
u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK
| UTMI_TX_TXVDD12_MASK
| UTMI_TX_CK60_PHSEL_MASK | UTMI_TX_IMPCAL_VTH_MASK
| UTMI_TX_REG_EXT_FS_RCAL_MASK | UTMI_TX_AMP_MASK);
u2o_set(base, UTMI_TX, (3 << UTMI_TX_TXVDD12_SHIFT)
| (4 << UTMI_TX_CK60_PHSEL_SHIFT)
| (4 << UTMI_TX_IMPCAL_VTH_SHIFT)
| (8 << UTMI_TX_REG_EXT_FS_RCAL_SHIFT)
| (3 << UTMI_TX_AMP_SHIFT));
/* UTMI_RX */
u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK
| UTMI_REG_SQ_LENGTH_MASK);
if (cpu_is_pxa168())
u2o_set(base, UTMI_RX, (7 << UTMI_RX_SQ_THRESH_SHIFT)
| (2 << UTMI_REG_SQ_LENGTH_SHIFT));
else
u2o_set(base, UTMI_RX, (0x7 << UTMI_RX_SQ_THRESH_SHIFT)
| (2 << UTMI_REG_SQ_LENGTH_SHIFT));
/* UTMI_IVREF */
if (cpu_is_pxa168())
/*
* fixing Microsoft Altair board interface with NEC hub issue -
* Set UTMI_IVREF from 0x4a3 to 0x4bf
*/
u2o_write(base, UTMI_IVREF, 0x4bf);
/* calibrate */
timeout = jiffies + 100;
while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
if (time_after(jiffies, timeout))
return -ETIME;
cpu_relax();
}
/* toggle VCOCAL_START bit of UTMI_PLL */
udelay(200);
u2o_set(base, UTMI_PLL, VCOCAL_START);
udelay(40);
u2o_clear(base, UTMI_PLL, VCOCAL_START);
/* toggle REG_RCAL_START bit of UTMI_TX */
udelay(200);
u2o_set(base, UTMI_TX, REG_RCAL_START);
udelay(40);
u2o_clear(base, UTMI_TX, REG_RCAL_START);
udelay(200);
/* make sure phy is ready */
timeout = jiffies + 100;
while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
if (time_after(jiffies, timeout))
return -ETIME;
cpu_relax();
}
if (cpu_is_pxa168()) {
u2o_set(base, UTMI_RESERVE, 1 << 5);
/* Turn on UTMI PHY OTG extension */
u2o_write(base, UTMI_OTG_ADDON, 1);
}
return 0;
}
#else
int mv_udc_phy_init(unsigned int base)
{
return 0;
}
#endif
/*
* 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_PLATFORM_USB_H
#define __MV_PLATFORM_USB_H
enum pxa_ehci_type {
EHCI_UNDEFINED = 0,
PXA_U2OEHCI, /* pxa 168, 9xx */
PXA_SPH, /* pxa 168, 9xx SPH */
MMP3_HSIC, /* mmp3 hsic */
MMP3_FSIC, /* mmp3 fsic */
};
enum {
MV_USB_MODE_OTG,
MV_USB_MODE_HOST,
};
enum {
VBUS_LOW = 0,
VBUS_HIGH = 1 << 0,
};
struct mv_usb_addon_irq {
unsigned int irq;
int (*poll)(void);
};
struct mv_usb_platform_data {
unsigned int clknum;
char **clkname;
struct mv_usb_addon_irq *id; /* Only valid for OTG. ID pin change*/
struct mv_usb_addon_irq *vbus; /* valid for OTG/UDC. VBUS change*/
/* only valid for HCD. OTG or Host only*/
unsigned int mode;
int (*phy_init)(unsigned int regbase);
void (*phy_deinit)(unsigned int regbase);
int (*set_vbus)(unsigned int vbus);
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment