-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
USB: chipidea: add imx usbmisc support
i.MX usb controllers share non-core registers, which may include SoC specific controls. We turn it into a usbmisc device and usbmisc driver set operations needed by ci13xxx_imx driver. For example, Sabrelite board has bad over-current design, we can usbmisc to disable over-current detection. Acked-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Richard Zhao <richard.zhao@freescale.com> Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
- Loading branch information
Richard Zhao
authored and
Greg Kroah-Hartman
committed
Sep 12, 2012
1 parent
08d9c74
commit d142d6b
Showing
6 changed files
with
274 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
* Freescale i.MX non-core registers | ||
|
||
Required properties: | ||
- #index-cells: Cells used to descibe usb controller index. Should be <1> | ||
- compatible: Should be one of below: | ||
"fsl,imx6q-usbmisc" for imx6q | ||
- reg: Should contain registers location and length | ||
|
||
Examples: | ||
usbmisc@02184800 { | ||
#index-cells = <1>; | ||
compatible = "fsl,imx6q-usbmisc"; | ||
reg = <0x02184800 0x200>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright 2012 Freescale Semiconductor, Inc. | ||
* | ||
* The code contained herein is licensed under the GNU General Public | ||
* License. You may obtain a copy of the GNU General Public License | ||
* Version 2 or later at the following locations: | ||
* | ||
* http://www.opensource.org/licenses/gpl-license.html | ||
* http://www.gnu.org/copyleft/gpl.html | ||
*/ | ||
|
||
/* Used to set SoC specific callbacks */ | ||
struct usbmisc_ops { | ||
/* It's called once when probe a usb device */ | ||
int (*init)(struct device *dev); | ||
}; | ||
|
||
struct usbmisc_usb_device { | ||
struct device *dev; /* usb controller device */ | ||
int index; | ||
|
||
int disable_oc:1; /* over current detect disabled */ | ||
}; | ||
|
||
int usbmisc_set_ops(const struct usbmisc_ops *ops); | ||
void usbmisc_unset_ops(const struct usbmisc_ops *ops); | ||
int | ||
usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* | ||
* Copyright 2012 Freescale Semiconductor, Inc. | ||
* | ||
* The code contained herein is licensed under the GNU General Public | ||
* License. You may obtain a copy of the GNU General Public License | ||
* Version 2 or later at the following locations: | ||
* | ||
* http://www.opensource.org/licenses/gpl-license.html | ||
* http://www.gnu.org/copyleft/gpl.html | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/of_platform.h> | ||
#include <linux/clk.h> | ||
#include <linux/err.h> | ||
#include <linux/io.h> | ||
|
||
#include "ci13xxx_imx.h" | ||
|
||
#define USB_DEV_MAX 4 | ||
|
||
#define BM_OVER_CUR_DIS BIT(7) | ||
|
||
struct imx6q_usbmisc { | ||
void __iomem *base; | ||
spinlock_t lock; | ||
struct clk *clk; | ||
struct usbmisc_usb_device usbdev[USB_DEV_MAX]; | ||
}; | ||
|
||
static struct imx6q_usbmisc *usbmisc; | ||
|
||
static struct usbmisc_usb_device *get_usbdev(struct device *dev) | ||
{ | ||
int i, ret; | ||
|
||
for (i = 0; i < USB_DEV_MAX; i++) { | ||
if (usbmisc->usbdev[i].dev == dev) | ||
return &usbmisc->usbdev[i]; | ||
else if (!usbmisc->usbdev[i].dev) | ||
break; | ||
} | ||
|
||
if (i >= USB_DEV_MAX) | ||
return ERR_PTR(-EBUSY); | ||
|
||
ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]); | ||
if (ret) | ||
return ERR_PTR(ret); | ||
|
||
return &usbmisc->usbdev[i]; | ||
} | ||
|
||
static int usbmisc_imx6q_init(struct device *dev) | ||
{ | ||
|
||
struct usbmisc_usb_device *usbdev; | ||
unsigned long flags; | ||
u32 reg; | ||
|
||
usbdev = get_usbdev(dev); | ||
if (IS_ERR(usbdev)) | ||
return PTR_ERR(usbdev); | ||
|
||
if (usbdev->disable_oc) { | ||
spin_lock_irqsave(&usbmisc->lock, flags); | ||
reg = readl(usbmisc->base + usbdev->index * 4); | ||
writel(reg | BM_OVER_CUR_DIS, | ||
usbmisc->base + usbdev->index * 4); | ||
spin_unlock_irqrestore(&usbmisc->lock, flags); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static const struct usbmisc_ops imx6q_usbmisc_ops = { | ||
.init = usbmisc_imx6q_init, | ||
}; | ||
|
||
static const struct of_device_id usbmisc_imx6q_dt_ids[] = { | ||
{ .compatible = "fsl,imx6q-usbmisc"}, | ||
{ /* sentinel */ } | ||
}; | ||
|
||
static int __devinit usbmisc_imx6q_probe(struct platform_device *pdev) | ||
{ | ||
struct resource *res; | ||
struct imx6q_usbmisc *data; | ||
int ret; | ||
|
||
if (usbmisc) | ||
return -EBUSY; | ||
|
||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
if (!data) | ||
return -ENOMEM; | ||
|
||
spin_lock_init(&data->lock); | ||
|
||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
data->base = devm_request_and_ioremap(&pdev->dev, res); | ||
if (!data->base) | ||
return -EADDRNOTAVAIL; | ||
|
||
data->clk = devm_clk_get(&pdev->dev, NULL); | ||
if (IS_ERR(data->clk)) { | ||
dev_err(&pdev->dev, | ||
"failed to get clock, err=%ld\n", PTR_ERR(data->clk)); | ||
return PTR_ERR(data->clk); | ||
} | ||
|
||
ret = clk_prepare_enable(data->clk); | ||
if (ret) { | ||
dev_err(&pdev->dev, | ||
"clk_prepare_enable failed, err=%d\n", ret); | ||
return ret; | ||
} | ||
|
||
ret = usbmisc_set_ops(&imx6q_usbmisc_ops); | ||
if (ret) { | ||
clk_disable_unprepare(data->clk); | ||
return ret; | ||
} | ||
|
||
usbmisc = data; | ||
|
||
return 0; | ||
} | ||
|
||
static int __devexit usbmisc_imx6q_remove(struct platform_device *pdev) | ||
{ | ||
usbmisc_unset_ops(&imx6q_usbmisc_ops); | ||
clk_disable_unprepare(usbmisc->clk); | ||
return 0; | ||
} | ||
|
||
static struct platform_driver usbmisc_imx6q_driver = { | ||
.probe = usbmisc_imx6q_probe, | ||
.remove = __devexit_p(usbmisc_imx6q_remove), | ||
.driver = { | ||
.name = "usbmisc_imx6q", | ||
.owner = THIS_MODULE, | ||
.of_match_table = usbmisc_imx6q_dt_ids, | ||
}, | ||
}; | ||
|
||
int __init usbmisc_imx6q_drv_init(void) | ||
{ | ||
return platform_driver_register(&usbmisc_imx6q_driver); | ||
} | ||
subsys_initcall(usbmisc_imx6q_drv_init); | ||
|
||
void __exit usbmisc_imx6q_drv_exit(void) | ||
{ | ||
platform_driver_unregister(&usbmisc_imx6q_driver); | ||
} | ||
module_exit(usbmisc_imx6q_drv_exit); | ||
|
||
MODULE_ALIAS("platform:usbmisc-imx6q"); | ||
MODULE_LICENSE("GPL v2"); | ||
MODULE_DESCRIPTION("driver for imx6q usb non-core registers"); | ||
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); |