At former design, both ci13xxx_imx and usbmisc_imx are individual module, ci13xxx_imx is glue layer for imx usb driver. usbmisc_imx handles non-core registers which have different register layout for imx SoC serials, it usually supplies interface for wakeup setting, PHY setting, etc. usbmisc_imx uses symbols from ci_hdrc_imx, So, when we combile both of drivers as loadable module, usbmisc is needed to be unload first. However at ci_hdrc_imx, it needs to call usbmisc_imx's interface at its removal function once we enable wakeup/runtime pm function, so keeping two drivers together will not work at loadable module use case. To fix loadable module use case, we need to build usbmisc_imx and ci_hdrc_imx together as one module, the usbmisc_imx still handles misc setting for controllers. There is a short discussion for it: http://marc.info/?l=linux-usb&m=136861599423172&w=2 Signed-off-by: Peter Chen <peter.chen@xxxxxxxxxxxxx> --- drivers/usb/chipidea/ci_hdrc_imx.c | 113 +++++++++++----------- drivers/usb/chipidea/ci_hdrc_imx.h | 28 +++--- drivers/usb/chipidea/usbmisc_imx.c | 186 +++--------------------------------- drivers/usb/chipidea/usbmisc_imx.h | 14 +++ 4 files changed, 98 insertions(+), 243 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 30fdc2f..be7c86f 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -19,68 +19,68 @@ #include <linux/dma-mapping.h> #include <linux/usb/chipidea.h> #include <linux/clk.h> +#include <linux/mfd/syscon.h> #include "ci.h" #include "ci_hdrc_imx.h" +#include "usbmisc_imx.h" -struct ci_hdrc_imx_data { - struct usb_phy *phy; - struct platform_device *ci_pdev; - struct clk *clk; +static const struct of_device_id ci_hdrc_imx_dt_ids[] = { + { + .compatible = "fsl,imx25-usb", + .data = &imx25_usbmisc_data, + }, + { + .compatible = "fsl,imx53-usb", + .data = &imx53_usbmisc_data, + }, + { + .compatible = "fsl,imx6q-usb", + .data = &imx6q_usbmisc_data, + }, + { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); -static const struct usbmisc_ops *usbmisc_ops; - -/* Common functions shared by usbmisc drivers */ - -int usbmisc_set_ops(const struct usbmisc_ops *ops) -{ - if (usbmisc_ops) - return -EBUSY; - - usbmisc_ops = ops; - - return 0; -} -EXPORT_SYMBOL_GPL(usbmisc_set_ops); - -void usbmisc_unset_ops(const struct usbmisc_ops *ops) -{ - usbmisc_ops = NULL; -} -EXPORT_SYMBOL_GPL(usbmisc_unset_ops); - -int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev) +static int usbmisc_init(struct device *dev) { struct device_node *np = dev->of_node; - struct of_phandle_args args; + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); int ret; + const struct of_device_id *of_id = + of_match_device(ci_hdrc_imx_dt_ids, dev); - usbdev->dev = dev; + if (of_id->data) + data->misc_data = (struct usbmisc_data *)of_id->data; + else + dev_dbg(dev, "no usbmisc data\n"); - ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", - 0, &args); - if (ret) { - dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", - ret); - memset(usbdev, 0, sizeof(*usbdev)); + ret = of_alias_get_id(np, "usb"); + if (ret < 0) { + dev_err(dev, "failed to get alias id, errno %d\n", ret); return ret; } - usbdev->index = args.args[0]; - of_node_put(args.np); if (of_find_property(np, "disable-over-current", NULL)) - usbdev->disable_oc = 1; + data->disable_oc = 1; if (of_find_property(np, "external-vbus-divider", NULL)) - usbdev->evdo = 1; + data->evdo = 1; + + /* Currently, the usbmisc only handles non core register stuffs */ + if (!data->misc_data) + return 0; + + data->regmap = syscon_regmap_lookup_by_phandle + (np, "ci,noncore"); + if (IS_ERR(data->regmap)) { + dev_err(dev, "can't find non-core regmap: %ld\n", + PTR_ERR(data->regmap)); + return PTR_ERR(data->regmap); + } return 0; } -EXPORT_SYMBOL_GPL(usbmisc_get_init_data); - -/* End of common functions shared by usbmisc drivers*/ - static int ci_hdrc_imx_probe(struct platform_device *pdev) { struct ci_hdrc_imx_data *data; @@ -93,10 +93,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) }; int ret; - if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL) - && !usbmisc_ops) - return -EPROBE_DEFER; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { dev_err(&pdev->dev, "Failed to allocate ci_hdrc-imx data!\n"); @@ -136,8 +132,17 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - if (usbmisc_ops && usbmisc_ops->init) { - ret = usbmisc_ops->init(&pdev->dev); + platform_set_drvdata(pdev, data); + + /* Some platform specific initializations */ + ret = usbmisc_init(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "usbmisc_init failed, ret=%d\n", ret); + goto err_clk; + } + + if (data->misc_data && data->misc_data->init) { + ret = data->misc_data->init(data); if (ret) { dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); @@ -156,8 +161,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) goto err_clk; } - if (usbmisc_ops && usbmisc_ops->post) { - ret = usbmisc_ops->post(&pdev->dev); + if (data->misc_data && data->misc_data->post) { + ret = data->misc_data->post(data); if (ret) { dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); @@ -165,8 +170,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) } } - platform_set_drvdata(pdev, data); - pm_runtime_no_callbacks(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -194,12 +197,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id ci_hdrc_imx_dt_ids[] = { - { .compatible = "fsl,imx27-usb", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); - static struct platform_driver ci_hdrc_imx_driver = { .probe = ci_hdrc_imx_probe, .remove = ci_hdrc_imx_remove, diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 550bfa4..3e62fed 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -10,22 +10,24 @@ */ /* Used to set SoC specific callbacks */ -struct usbmisc_ops { +struct ci_hdrc_imx_data; + +struct usbmisc_data { /* It's called once when probe a usb device */ - int (*init)(struct device *dev); + int (*init)(struct ci_hdrc_imx_data *data); /* It's called once after adding a usb device */ - int (*post)(struct device *dev); + int (*post)(struct ci_hdrc_imx_data *data); }; -struct usbmisc_usb_device { - struct device *dev; /* usb controller device */ - int index; - - unsigned int disable_oc:1; /* over current detect disabled */ - unsigned int evdo:1; /* set external vbus divider option */ +struct ci_hdrc_imx_data { + struct usb_phy *phy; + struct platform_device *ci_pdev; + struct clk *clk; + struct usbmisc_data *misc_data; + /* non core register regmap, mx23/mx28 has no non core register */ + struct regmap *regmap; + int index; /* controller's index */ + int disable_oc:1; /* over current detect disabled */ + int evdo:1; /* set external vbus divider option */ }; -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); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 545efbf..11abee0 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -9,18 +9,11 @@ * 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 <linux/delay.h> #include <linux/regmap.h> #include "ci_hdrc_imx.h" -#define USB_DEV_MAX 4 - #define MX25_USB_PHY_CTRL_OFFSET 0x08 #define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) @@ -33,47 +26,10 @@ #define MX6_BM_OVER_CUR_DIS BIT(7) -struct imx_usbmisc { - void __iomem *base; - struct clk *clk; - struct usbmisc_usb_device usbdev[USB_DEV_MAX]; - const struct usbmisc_ops *ops; - struct regmap *regmap; -}; - -static struct imx_usbmisc *usbmisc; - -static struct usbmisc_usb_device *get_usbdev(struct device *dev) +static int usbmisc_imx25_post(struct ci_hdrc_imx_data *data) { - 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_imx25_post(struct device *dev) -{ - struct usbmisc_usb_device *usbdev; - - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); - - if (usbdev->evdo) { - regmap_update_bits(usbmisc->regmap, + if (data->evdo) { + regmap_update_bits(data->regmap, MX25_USB_PHY_CTRL_OFFSET, MX25_BM_EXTERNAL_VBUS_DIVIDER, MX25_BM_EXTERNAL_VBUS_DIVIDER); @@ -83,17 +39,12 @@ static int usbmisc_imx25_post(struct device *dev) return 0; } -static int usbmisc_imx53_init(struct device *dev) +static int usbmisc_imx53_init(struct ci_hdrc_imx_data *data) { - struct usbmisc_usb_device *usbdev; unsigned int reg = 0, val = 0; - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); - - if (usbdev->disable_oc) { - switch (usbdev->index) { + if (data->disable_oc) { + switch (data->index) { case 0: reg = MX53_USB_OTG_PHY_CTRL_0_OFFSET; val = MX53_BM_OVER_CUR_DIS_OTG; @@ -111,139 +62,30 @@ static int usbmisc_imx53_init(struct device *dev) val = MX53_BM_OVER_CUR_DIS_UHx; break; } - if (usbdev->index >= 0 && usbdev->index <= 3) - regmap_update_bits(usbmisc->regmap, reg, val, val); + if (data->index >= 0 && data->index <= 3) + regmap_update_bits(data->regmap, reg, val, val); } return 0; } -static int usbmisc_imx6q_init(struct device *dev) +static int usbmisc_imx6q_init(struct ci_hdrc_imx_data *data) { - struct usbmisc_usb_device *usbdev; - - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); - - if (usbdev->disable_oc) - regmap_update_bits(usbmisc->regmap, usbdev->index * 4, + if (data->disable_oc) + regmap_update_bits(data->regmap, data->index * 4, MX6_BM_OVER_CUR_DIS, MX6_BM_OVER_CUR_DIS); return 0; } -static const struct usbmisc_ops imx25_usbmisc_ops = { +const struct usbmisc_data imx25_usbmisc_data = { .post = usbmisc_imx25_post, }; -static const struct usbmisc_ops imx53_usbmisc_ops = { +const struct usbmisc_data imx53_usbmisc_data = { .init = usbmisc_imx53_init, }; -static const struct usbmisc_ops imx6q_usbmisc_ops = { +const struct usbmisc_data imx6q_usbmisc_data = { .init = usbmisc_imx6q_init, }; - -static const struct of_device_id usbmisc_imx_dt_ids[] = { - { - .compatible = "fsl,imx25-usbmisc", - .data = &imx25_usbmisc_ops, - }, - { - .compatible = "fsl,imx53-usbmisc", - .data = &imx53_usbmisc_ops, - }, - { - .compatible = "fsl,imx6q-usbmisc", - .data = &imx6q_usbmisc_ops, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); - -static struct regmap_config usbmisc_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - -static int usbmisc_imx_probe(struct platform_device *pdev) -{ - struct resource *res; - struct imx_usbmisc *data; - int ret; - struct of_device_id *tmp_dev; - - if (usbmisc) - return -EBUSY; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); - - usbmisc_regmap_config.max_register = res->end - res->start - 3; - data->regmap = devm_regmap_init_mmio(&pdev->dev, data->base, - &usbmisc_regmap_config); - if (IS_ERR(data->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); - return PTR_ERR(data->regmap); - } - - 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; - } - - tmp_dev = (struct of_device_id *) - of_match_device(usbmisc_imx_dt_ids, &pdev->dev); - data->ops = (const struct usbmisc_ops *)tmp_dev->data; - usbmisc = data; - ret = usbmisc_set_ops(data->ops); - if (ret) { - usbmisc = NULL; - clk_disable_unprepare(data->clk); - return ret; - } - - return 0; -} - -static int usbmisc_imx_remove(struct platform_device *pdev) -{ - usbmisc_unset_ops(usbmisc->ops); - clk_disable_unprepare(usbmisc->clk); - usbmisc = NULL; - return 0; -} - -static struct platform_driver usbmisc_imx_driver = { - .probe = usbmisc_imx_probe, - .remove = usbmisc_imx_remove, - .driver = { - .name = "usbmisc_imx", - .owner = THIS_MODULE, - .of_match_table = usbmisc_imx_dt_ids, - }, -}; - -module_platform_driver(usbmisc_imx_driver); - -MODULE_ALIAS("platform:usbmisc-imx"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("driver for imx usb non-core registers"); -MODULE_AUTHOR("Richard Zhao <richard.zhao@xxxxxxxxxxxxx>"); diff --git a/drivers/usb/chipidea/usbmisc_imx.h b/drivers/usb/chipidea/usbmisc_imx.h new file mode 100644 index 0000000..5a5be4b --- /dev/null +++ b/drivers/usb/chipidea/usbmisc_imx.h @@ -0,0 +1,14 @@ +/* + * Copyright 2013 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 + */ + +extern const struct usbmisc_data imx25_usbmisc_data; +extern const struct usbmisc_data imx53_usbmisc_data; +extern const struct usbmisc_data imx6q_usbmisc_data; -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html