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/usb/of.h>
  22#include <linux/clk.h>
  23
  24#include "ci.h"
  25#include "ci_hdrc_imx.h"
  26
  27struct ci_hdrc_imx_platform_flag {
  28        unsigned int flags;
  29        bool runtime_pm;
  30};
  31
  32static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
  33        .flags = CI_HDRC_TURN_VBUS_EARLY_ON |
  34                CI_HDRC_DISABLE_STREAMING,
  35};
  36
  37static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
  38                CI_HDRC_DISABLE_STREAMING,
  39};
  40
  41static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
  42        .flags = CI_HDRC_IMX28_WRITE_FIX |
  43                CI_HDRC_TURN_VBUS_EARLY_ON |
  44                CI_HDRC_DISABLE_STREAMING,
  45};
  46
  47static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
  48        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
  49                CI_HDRC_TURN_VBUS_EARLY_ON |
  50                CI_HDRC_DISABLE_STREAMING,
  51};
  52
  53static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
  54        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
  55                CI_HDRC_TURN_VBUS_EARLY_ON |
  56                CI_HDRC_DISABLE_HOST_STREAMING,
  57};
  58
  59static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
  60        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
  61                CI_HDRC_TURN_VBUS_EARLY_ON |
  62                CI_HDRC_DISABLE_HOST_STREAMING,
  63};
  64
  65static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
  66        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
  67                CI_HDRC_TURN_VBUS_EARLY_ON,
  68};
  69
  70static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
  71        .flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
  72};
  73
  74static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
  75        { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
  76        { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
  77        { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
  78        { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
  79        { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
  80        { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
  81        { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
  82        { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
  83        { /* sentinel */ }
  84};
  85MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
  86
  87struct ci_hdrc_imx_data {
  88        struct usb_phy *phy;
  89        struct platform_device *ci_pdev;
  90        struct clk *clk;
  91        struct imx_usbmisc_data *usbmisc_data;
  92        bool supports_runtime_pm;
  93        bool in_lpm;
  94        /* SoC before i.mx6 (except imx23/imx28) needs three clks */
  95        bool need_three_clks;
  96        struct clk *clk_ipg;
  97        struct clk *clk_ahb;
  98        struct clk *clk_per;
  99        /* --------------------------------- */
 100};
 101
 102/* Common functions shared by usbmisc drivers */
 103
 104static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
 105{
 106        struct platform_device *misc_pdev;
 107        struct device_node *np = dev->of_node;
 108        struct of_phandle_args args;
 109        struct imx_usbmisc_data *data;
 110        int ret;
 111
 112        /*
 113         * In case the fsl,usbmisc property is not present this device doesn't
 114         * need usbmisc. Return NULL (which is no error here)
 115         */
 116        if (!of_get_property(np, "fsl,usbmisc", NULL))
 117                return NULL;
 118
 119        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 120        if (!data)
 121                return ERR_PTR(-ENOMEM);
 122
 123        ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
 124                                        0, &args);
 125        if (ret) {
 126                dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
 127                        ret);
 128                return ERR_PTR(ret);
 129        }
 130
 131        data->index = args.args[0];
 132
 133        misc_pdev = of_find_device_by_node(args.np);
 134        of_node_put(args.np);
 135
 136        if (!misc_pdev || !platform_get_drvdata(misc_pdev))
 137                return ERR_PTR(-EPROBE_DEFER);
 138
 139        data->dev = &misc_pdev->dev;
 140
 141        if (of_find_property(np, "disable-over-current", NULL))
 142                data->disable_oc = 1;
 143
 144        if (of_find_property(np, "over-current-active-high", NULL))
 145                data->oc_polarity = 1;
 146
 147        if (of_find_property(np, "external-vbus-divider", NULL))
 148                data->evdo = 1;
 149
 150        if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
 151                data->ulpi = 1;
 152
 153        return data;
 154}
 155
 156/* End of common functions shared by usbmisc drivers*/
 157static int imx_get_clks(struct device *dev)
 158{
 159        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 160        int ret = 0;
 161
 162        data->clk_ipg = devm_clk_get(dev, "ipg");
 163        if (IS_ERR(data->clk_ipg)) {
 164                /* If the platform only needs one clocks */
 165                data->clk = devm_clk_get(dev, NULL);
 166                if (IS_ERR(data->clk)) {
 167                        ret = PTR_ERR(data->clk);
 168                        dev_err(dev,
 169                                "Failed to get clks, err=%ld,%ld\n",
 170                                PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
 171                        return ret;
 172                }
 173                return ret;
 174        }
 175
 176        data->clk_ahb = devm_clk_get(dev, "ahb");
 177        if (IS_ERR(data->clk_ahb)) {
 178                ret = PTR_ERR(data->clk_ahb);
 179                dev_err(dev,
 180                        "Failed to get ahb clock, err=%d\n", ret);
 181                return ret;
 182        }
 183
 184        data->clk_per = devm_clk_get(dev, "per");
 185        if (IS_ERR(data->clk_per)) {
 186                ret = PTR_ERR(data->clk_per);
 187                dev_err(dev,
 188                        "Failed to get per clock, err=%d\n", ret);
 189                return ret;
 190        }
 191
 192        data->need_three_clks = true;
 193        return ret;
 194}
 195
 196static int imx_prepare_enable_clks(struct device *dev)
 197{
 198        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 199        int ret = 0;
 200
 201        if (data->need_three_clks) {
 202                ret = clk_prepare_enable(data->clk_ipg);
 203                if (ret) {
 204                        dev_err(dev,
 205                                "Failed to prepare/enable ipg clk, err=%d\n",
 206                                ret);
 207                        return ret;
 208                }
 209
 210                ret = clk_prepare_enable(data->clk_ahb);
 211                if (ret) {
 212                        dev_err(dev,
 213                                "Failed to prepare/enable ahb clk, err=%d\n",
 214                                ret);
 215                        clk_disable_unprepare(data->clk_ipg);
 216                        return ret;
 217                }
 218
 219                ret = clk_prepare_enable(data->clk_per);
 220                if (ret) {
 221                        dev_err(dev,
 222                                "Failed to prepare/enable per clk, err=%d\n",
 223                                ret);
 224                        clk_disable_unprepare(data->clk_ahb);
 225                        clk_disable_unprepare(data->clk_ipg);
 226                        return ret;
 227                }
 228        } else {
 229                ret = clk_prepare_enable(data->clk);
 230                if (ret) {
 231                        dev_err(dev,
 232                                "Failed to prepare/enable clk, err=%d\n",
 233                                ret);
 234                        return ret;
 235                }
 236        }
 237
 238        return ret;
 239}
 240
 241static void imx_disable_unprepare_clks(struct device *dev)
 242{
 243        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 244
 245        if (data->need_three_clks) {
 246                clk_disable_unprepare(data->clk_per);
 247                clk_disable_unprepare(data->clk_ahb);
 248                clk_disable_unprepare(data->clk_ipg);
 249        } else {
 250                clk_disable_unprepare(data->clk);
 251        }
 252}
 253
 254static int ci_hdrc_imx_probe(struct platform_device *pdev)
 255{
 256        struct ci_hdrc_imx_data *data;
 257        struct ci_hdrc_platform_data pdata = {
 258                .name           = dev_name(&pdev->dev),
 259                .capoffset      = DEF_CAPOFFSET,
 260        };
 261        int ret;
 262        const struct of_device_id *of_id;
 263        const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
 264
 265        of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
 266        if (!of_id)
 267                return -ENODEV;
 268
 269        imx_platform_flag = of_id->data;
 270
 271        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 272        if (!data)
 273                return -ENOMEM;
 274
 275        platform_set_drvdata(pdev, data);
 276        data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
 277        if (IS_ERR(data->usbmisc_data))
 278                return PTR_ERR(data->usbmisc_data);
 279
 280        ret = imx_get_clks(&pdev->dev);
 281        if (ret)
 282                return ret;
 283
 284        ret = imx_prepare_enable_clks(&pdev->dev);
 285        if (ret)
 286                return ret;
 287
 288        data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
 289        if (IS_ERR(data->phy)) {
 290                ret = PTR_ERR(data->phy);
 291                /* Return -EINVAL if no usbphy is available */
 292                if (ret == -ENODEV)
 293                        ret = -EINVAL;
 294                goto err_clk;
 295        }
 296
 297        pdata.usb_phy = data->phy;
 298        pdata.flags |= imx_platform_flag->flags;
 299        if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
 300                data->supports_runtime_pm = true;
 301
 302        ret = imx_usbmisc_init(data->usbmisc_data);
 303        if (ret) {
 304                dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
 305                goto err_clk;
 306        }
 307
 308        data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
 309                                pdev->resource, pdev->num_resources,
 310                                &pdata);
 311        if (IS_ERR(data->ci_pdev)) {
 312                ret = PTR_ERR(data->ci_pdev);
 313                if (ret != -EPROBE_DEFER)
 314                        dev_err(&pdev->dev,
 315                                "ci_hdrc_add_device failed, err=%d\n", ret);
 316                goto err_clk;
 317        }
 318
 319        ret = imx_usbmisc_init_post(data->usbmisc_data);
 320        if (ret) {
 321                dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
 322                goto disable_device;
 323        }
 324
 325        if (data->supports_runtime_pm) {
 326                pm_runtime_set_active(&pdev->dev);
 327                pm_runtime_enable(&pdev->dev);
 328        }
 329
 330        device_set_wakeup_capable(&pdev->dev, true);
 331
 332        return 0;
 333
 334disable_device:
 335        ci_hdrc_remove_device(data->ci_pdev);
 336err_clk:
 337        imx_disable_unprepare_clks(&pdev->dev);
 338        return ret;
 339}
 340
 341static int ci_hdrc_imx_remove(struct platform_device *pdev)
 342{
 343        struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
 344
 345        if (data->supports_runtime_pm) {
 346                pm_runtime_get_sync(&pdev->dev);
 347                pm_runtime_disable(&pdev->dev);
 348                pm_runtime_put_noidle(&pdev->dev);
 349        }
 350        ci_hdrc_remove_device(data->ci_pdev);
 351        imx_disable_unprepare_clks(&pdev->dev);
 352
 353        return 0;
 354}
 355
 356static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
 357{
 358        ci_hdrc_imx_remove(pdev);
 359}
 360
 361#ifdef CONFIG_PM
 362static int imx_controller_suspend(struct device *dev)
 363{
 364        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 365
 366        dev_dbg(dev, "at %s\n", __func__);
 367
 368        imx_disable_unprepare_clks(dev);
 369        data->in_lpm = true;
 370
 371        return 0;
 372}
 373
 374static int imx_controller_resume(struct device *dev)
 375{
 376        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 377        int ret = 0;
 378
 379        dev_dbg(dev, "at %s\n", __func__);
 380
 381        if (!data->in_lpm) {
 382                WARN_ON(1);
 383                return 0;
 384        }
 385
 386        ret = imx_prepare_enable_clks(dev);
 387        if (ret)
 388                return ret;
 389
 390        data->in_lpm = false;
 391
 392        ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
 393        if (ret) {
 394                dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
 395                goto clk_disable;
 396        }
 397
 398        return 0;
 399
 400clk_disable:
 401        imx_disable_unprepare_clks(dev);
 402        return ret;
 403}
 404
 405#ifdef CONFIG_PM_SLEEP
 406static int ci_hdrc_imx_suspend(struct device *dev)
 407{
 408        int ret;
 409
 410        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 411
 412        if (data->in_lpm)
 413                /* The core's suspend doesn't run */
 414                return 0;
 415
 416        if (device_may_wakeup(dev)) {
 417                ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
 418                if (ret) {
 419                        dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
 420                                        ret);
 421                        return ret;
 422                }
 423        }
 424
 425        return imx_controller_suspend(dev);
 426}
 427
 428static int ci_hdrc_imx_resume(struct device *dev)
 429{
 430        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 431        int ret;
 432
 433        ret = imx_controller_resume(dev);
 434        if (!ret && data->supports_runtime_pm) {
 435                pm_runtime_disable(dev);
 436                pm_runtime_set_active(dev);
 437                pm_runtime_enable(dev);
 438        }
 439
 440        return ret;
 441}
 442#endif /* CONFIG_PM_SLEEP */
 443
 444static int ci_hdrc_imx_runtime_suspend(struct device *dev)
 445{
 446        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
 447        int ret;
 448
 449        if (data->in_lpm) {
 450                WARN_ON(1);
 451                return 0;
 452        }
 453
 454        ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
 455        if (ret) {
 456                dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
 457                return ret;
 458        }
 459
 460        return imx_controller_suspend(dev);
 461}
 462
 463static int ci_hdrc_imx_runtime_resume(struct device *dev)
 464{
 465        return imx_controller_resume(dev);
 466}
 467
 468#endif /* CONFIG_PM */
 469
 470static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
 471        SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
 472        SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
 473                        ci_hdrc_imx_runtime_resume, NULL)
 474};
 475static struct platform_driver ci_hdrc_imx_driver = {
 476        .probe = ci_hdrc_imx_probe,
 477        .remove = ci_hdrc_imx_remove,
 478        .shutdown = ci_hdrc_imx_shutdown,
 479        .driver = {
 480                .name = "imx_usb",
 481                .of_match_table = ci_hdrc_imx_dt_ids,
 482                .pm = &ci_hdrc_imx_pm_ops,
 483         },
 484};
 485
 486module_platform_driver(ci_hdrc_imx_driver);
 487
 488MODULE_ALIAS("platform:imx-usb");
 489MODULE_LICENSE("GPL v2");
 490MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
 491MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 492MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
 493