linux/drivers/usb/chipidea/ci_hdrc_imx.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Freescale Semiconductor, Inc.
   3 * Copyright (C) 2012 Marek Vasut <marex@denx.de>
   4 * on behalf of DENX Software Engineering GmbH
   5 *
   6 * The code contained herein is licensed under the GNU General Public
   7 * License. You may obtain a copy of the GNU General Public License
   8 * Version 2 or later at the following locations:
   9 *
  10 * http://www.opensource.org/licenses/gpl-license.html
  11 * http://www.gnu.org/copyleft/gpl.html
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/of_platform.h>
  16#include <linux/of_gpio.h>
  17#include <linux/platform_device.h>
  18#include <linux/pm_runtime.h>
  19#include <linux/dma-mapping.h>
  20#include <linux/usb/chipidea.h>
  21#include <linux/clk.h>
  22
  23#include "ci.h"
  24#include "ci_hdrc_imx.h"
  25
  26struct ci_hdrc_imx_platform_flag {
  27        unsigned int flags;
  28        bool runtime_pm;
  29};
  30
  31static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
  32};
  33
  34static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
  35        .flags = CI_HDRC_IMX28_WRITE_FIX |
  36                CI_HDRC_TURN_VBUS_EARLY_ON,
  37};
  38
  39static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
  40        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
  41                CI_HDRC_TURN_VBUS_EARLY_ON,
  42};
  43
  44static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
  45        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
  46                CI_HDRC_TURN_VBUS_EARLY_ON,
  47};
  48
  49static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
  50        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
  51                CI_HDRC_TURN_VBUS_EARLY_ON,
  52};
  53
  54static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
  55        { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
  56        { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
  57        { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
  58        { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
  59        { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data},
  60        { /* sentinel */ }
  61};
  62MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
  63
  64struct ci_hdrc_imx_data {
  65        struct usb_phy *phy;
  66        struct platform_device *ci_pdev;
  67        struct clk *clk;
  68        struct imx_usbmisc_data *usbmisc_data;
  69        bool supports_runtime_pm;
  70        bool in_lpm;
  71};
  72
  73/* Common functions shared by usbmisc drivers */
  74
  75static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
  76{
  77        struct platform_device *misc_pdev;
  78        struct device_node *np = dev->of_node;
  79        struct of_phandle_args args;
  80        struct imx_usbmisc_data *data;
  81        int ret;
  82
  83        /*
  84         * In case the fsl,usbmisc property is not present this device doesn't
  85         * need usbmisc. Return NULL (which is no error here)
  86         */
  87        if (!of_get_property(np, "fsl,usbmisc", NULL))
  88                return NULL;
  89
  90        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
  91        if (!data)
  92                return ERR_PTR(-ENOMEM);
  93
  94        ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
  95                                        0, &args);
  96        if (ret) {
  97                dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
  98                        ret);
  99                return ERR_PTR(ret);
 100        }
 101
 102        data->index = args.args[0];
 103
 104        misc_pdev = of_find_device_by_node(args.np);
 105        of_node_put(args.np);
 106
 107        if (!misc_pdev)
 108                return ERR_PTR(-EPROBE_DEFER);
 109
 110        data->dev = &misc_pdev->dev;
 111
 112        if (of_find_property(np, "disable-over-current", NULL))
 113                data->disable_oc = 1;
 114
 115        if (of_find_property(np, "external-vbus-divider", NULL))
 116                data->evdo = 1;
 117
 118        return data;
 119}
 120
 121/* End of common functions shared by usbmisc drivers*/
 122
 123static int ci_hdrc_imx_probe(struct platform_device *pdev)
 124{
 125        struct ci_hdrc_imx_data *data;
 126        struct ci_hdrc_platform_data pdata = {
 127                .name           = dev_name(&pdev->dev),
 128                .capoffset      = DEF_CAPOFFSET,
 129                .flags          = CI_HDRC_DISABLE_STREAMING,
 130        };
 131        int ret;
 132        const struct of_device_id *of_id =
 133                        of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
 134        const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
 135
 136        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 137        if (!data)
 138                return -ENOMEM;
 139
 140        data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
 141        if (IS_ERR(data->usbmisc_data))
 142                return PTR_ERR(data->usbmisc_data);
 143
 144        data->clk = devm_clk_get(&pdev->dev, NULL);
 145        if (IS_ERR(data->clk)) {
 146                dev_err(&pdev->dev,
 147                        "Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
 148                return PTR_ERR(data->clk);
 149        }
 150
 151        ret = clk_prepare_enable(data->clk);
 152        if (ret) {
 153                dev_err(&pdev->dev,
 154                        "Failed to prepare or enable clock, err=%d\n", ret);
 155                return ret;
 156        }
 157
 158        data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
 159        if (IS_ERR(data->phy)) {
 160                ret = PTR_ERR(data->phy);
 161                /* Return -EINVAL if no usbphy is available */
 162                if (ret == -ENODEV)
 163                        ret = -EINVAL;
 164                goto err_clk;
 165        }
 166
 167        pdata.usb_phy = data->phy;
 168        pdata.flags |= imx_platform_flag->flags;
 169        if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
 170                data->supports_runtime_pm = true;
 171
 172        ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
 173        if (ret)
 174                goto err_clk;
 175
 176        ret = imx_usbmisc_init(data->usbmisc_data);
 177        if (ret) {
 178                dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
 179                goto err_clk;
 180        }
 181
 182        data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
 183                                pdev->resource, pdev->num_resources,
 184                                &pdata);
 185        if (IS_ERR(data->ci_pdev)) {
 186                ret = PTR_ERR(data->ci_pdev);
 187                dev_err(&pdev->dev,
 188                        "Can't register ci_hdrc platform device, err=%d\n",
 189                        ret);
 190                goto err_clk;
 191        }
 192
 193        ret = imx_usbmisc_init_post(data->usbmisc_data);
 194        if (ret) {
 195                dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
 196                goto disable_device;
 197        }
 198
 199        platform_set_drvdata(pdev, data);
 200
 201        if (data->supports_runtime_pm) {
 202                pm_runtime_set_active(&pdev->dev);
 203                pm_runtime_enable(&pdev->dev);
 204        }
 205
 206        device_set_wakeup_capable(&pdev->dev, true);
 207
 208        return 0;
 209
 210disable_device:
 211        ci_hdrc_remove_device(data->ci_pdev);
 212err_clk:
 213        clk_disable_unprepare(data->clk);
 214        return ret;
 215}
 216
 217static int ci_hdrc_imx_remove(struct platform_device *pdev)
 218{
 219        struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
 220
 221        if (data->supports_runtime_pm) {
 222                pm_runtime_get_sync(&pdev->dev);
 223                pm_runtime_disable(&pdev->dev);
 224                pm_runtime_put_noidle(&pdev->dev);
 225        }
 226        ci_hdrc_remove_device(data->ci_pdev);
 227        clk_disable_unprepare(data->clk);
 228
 229        return 0;
 230}
 231
 232#ifdef CONFIG_PM
 233static int imx_controller_suspend(struct device *dev)
 234{
 235        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 236
 237        dev_dbg(dev, "at %s\n", __func__);
 238
 239        clk_disable_unprepare(data->clk);
 240        data->in_lpm = true;
 241
 242        return 0;
 243}
 244
 245static int imx_controller_resume(struct device *dev)
 246{
 247        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 248        int ret = 0;
 249
 250        dev_dbg(dev, "at %s\n", __func__);
 251
 252        if (!data->in_lpm) {
 253                WARN_ON(1);
 254                return 0;
 255        }
 256
 257        ret = clk_prepare_enable(data->clk);
 258        if (ret)
 259                return ret;
 260
 261        data->in_lpm = false;
 262
 263        ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
 264        if (ret) {
 265                dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
 266                goto clk_disable;
 267        }
 268
 269        return 0;
 270
 271clk_disable:
 272        clk_disable_unprepare(data->clk);
 273        return ret;
 274}
 275
 276#ifdef CONFIG_PM_SLEEP
 277static int ci_hdrc_imx_suspend(struct device *dev)
 278{
 279        int ret;
 280
 281        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 282
 283        if (data->in_lpm)
 284                /* The core's suspend doesn't run */
 285                return 0;
 286
 287        if (device_may_wakeup(dev)) {
 288                ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
 289                if (ret) {
 290                        dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
 291                                        ret);
 292                        return ret;
 293                }
 294        }
 295
 296        return imx_controller_suspend(dev);
 297}
 298
 299static int ci_hdrc_imx_resume(struct device *dev)
 300{
 301        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 302        int ret;
 303
 304        ret = imx_controller_resume(dev);
 305        if (!ret && data->supports_runtime_pm) {
 306                pm_runtime_disable(dev);
 307                pm_runtime_set_active(dev);
 308                pm_runtime_enable(dev);
 309        }
 310
 311        return ret;
 312}
 313#endif /* CONFIG_PM_SLEEP */
 314
 315static int ci_hdrc_imx_runtime_suspend(struct device *dev)
 316{
 317        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 318        int ret;
 319
 320        if (data->in_lpm) {
 321                WARN_ON(1);
 322                return 0;
 323        }
 324
 325        ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
 326        if (ret) {
 327                dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
 328                return ret;
 329        }
 330
 331        return imx_controller_suspend(dev);
 332}
 333
 334static int ci_hdrc_imx_runtime_resume(struct device *dev)
 335{
 336        return imx_controller_resume(dev);
 337}
 338
 339#endif /* CONFIG_PM */
 340
 341static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
 342        SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
 343        SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
 344                        ci_hdrc_imx_runtime_resume, NULL)
 345};
 346static struct platform_driver ci_hdrc_imx_driver = {
 347        .probe = ci_hdrc_imx_probe,
 348        .remove = ci_hdrc_imx_remove,
 349        .driver = {
 350                .name = "imx_usb",
 351                .of_match_table = ci_hdrc_imx_dt_ids,
 352                .pm = &ci_hdrc_imx_pm_ops,
 353         },
 354};
 355
 356module_platform_driver(ci_hdrc_imx_driver);
 357
 358MODULE_ALIAS("platform:imx-usb");
 359MODULE_LICENSE("GPL v2");
 360MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
 361MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 362MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
 363