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