linux/drivers/usb/phy/phy-mxs-usb.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012-2014 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/kernel.h>
  16#include <linux/platform_device.h>
  17#include <linux/clk.h>
  18#include <linux/usb/otg.h>
  19#include <linux/stmp_device.h>
  20#include <linux/delay.h>
  21#include <linux/err.h>
  22#include <linux/io.h>
  23#include <linux/of_device.h>
  24#include <linux/regmap.h>
  25#include <linux/mfd/syscon.h>
  26
  27#define DRIVER_NAME "mxs_phy"
  28
  29#define HW_USBPHY_PWD                           0x00
  30#define HW_USBPHY_CTRL                          0x30
  31#define HW_USBPHY_CTRL_SET                      0x34
  32#define HW_USBPHY_CTRL_CLR                      0x38
  33
  34#define HW_USBPHY_DEBUG_SET                     0x54
  35#define HW_USBPHY_DEBUG_CLR                     0x58
  36
  37#define HW_USBPHY_IP                            0x90
  38#define HW_USBPHY_IP_SET                        0x94
  39#define HW_USBPHY_IP_CLR                        0x98
  40
  41#define BM_USBPHY_CTRL_SFTRST                   BIT(31)
  42#define BM_USBPHY_CTRL_CLKGATE                  BIT(30)
  43#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS        BIT(26)
  44#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE     BIT(25)
  45#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP           BIT(23)
  46#define BM_USBPHY_CTRL_ENIDCHG_WKUP             BIT(22)
  47#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP           BIT(21)
  48#define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD        BIT(20)
  49#define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE        BIT(19)
  50#define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL         BIT(18)
  51#define BM_USBPHY_CTRL_ENUTMILEVEL3             BIT(15)
  52#define BM_USBPHY_CTRL_ENUTMILEVEL2             BIT(14)
  53#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT       BIT(1)
  54
  55#define BM_USBPHY_IP_FIX                       (BIT(17) | BIT(18))
  56
  57#define BM_USBPHY_DEBUG_CLKGATE                 BIT(30)
  58
  59/* Anatop Registers */
  60#define ANADIG_ANA_MISC0                        0x150
  61#define ANADIG_ANA_MISC0_SET                    0x154
  62#define ANADIG_ANA_MISC0_CLR                    0x158
  63
  64#define ANADIG_USB1_VBUS_DET_STAT               0x1c0
  65#define ANADIG_USB2_VBUS_DET_STAT               0x220
  66
  67#define ANADIG_USB1_LOOPBACK_SET                0x1e4
  68#define ANADIG_USB1_LOOPBACK_CLR                0x1e8
  69#define ANADIG_USB2_LOOPBACK_SET                0x244
  70#define ANADIG_USB2_LOOPBACK_CLR                0x248
  71
  72#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG    BIT(12)
  73#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11)
  74
  75#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3)
  76#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID BIT(3)
  77
  78#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1   BIT(2)
  79#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN      BIT(5)
  80#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1   BIT(2)
  81#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN      BIT(5)
  82
  83#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
  84
  85/* Do disconnection between PHY and controller without vbus */
  86#define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS    BIT(0)
  87
  88/*
  89 * The PHY will be in messy if there is a wakeup after putting
  90 * bus to suspend (set portsc.suspendM) but before setting PHY to low
  91 * power mode (set portsc.phcd).
  92 */
  93#define MXS_PHY_ABNORMAL_IN_SUSPEND             BIT(1)
  94
  95/*
  96 * The SOF sends too fast after resuming, it will cause disconnection
  97 * between host and high speed device.
  98 */
  99#define MXS_PHY_SENDING_SOF_TOO_FAST            BIT(2)
 100
 101/*
 102 * IC has bug fixes logic, they include
 103 * MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST
 104 * which are described at above flags, the RTL will handle it
 105 * according to different versions.
 106 */
 107#define MXS_PHY_NEED_IP_FIX                     BIT(3)
 108
 109struct mxs_phy_data {
 110        unsigned int flags;
 111};
 112
 113static const struct mxs_phy_data imx23_phy_data = {
 114        .flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST,
 115};
 116
 117static const struct mxs_phy_data imx6q_phy_data = {
 118        .flags = MXS_PHY_SENDING_SOF_TOO_FAST |
 119                MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
 120                MXS_PHY_NEED_IP_FIX,
 121};
 122
 123static const struct mxs_phy_data imx6sl_phy_data = {
 124        .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
 125                MXS_PHY_NEED_IP_FIX,
 126};
 127
 128static const struct mxs_phy_data vf610_phy_data = {
 129        .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
 130                MXS_PHY_NEED_IP_FIX,
 131};
 132
 133static const struct mxs_phy_data imx6sx_phy_data = {
 134        .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
 135                MXS_PHY_NEED_IP_FIX,
 136};
 137
 138static const struct of_device_id mxs_phy_dt_ids[] = {
 139        { .compatible = "fsl,imx6sx-usbphy", .data = &imx6sx_phy_data, },
 140        { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, },
 141        { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, },
 142        { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, },
 143        { .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, },
 144        { /* sentinel */ }
 145};
 146MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
 147
 148struct mxs_phy {
 149        struct usb_phy phy;
 150        struct clk *clk;
 151        const struct mxs_phy_data *data;
 152        struct regmap *regmap_anatop;
 153        int port_id;
 154};
 155
 156static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
 157{
 158        return mxs_phy->data == &imx6q_phy_data;
 159}
 160
 161static inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy)
 162{
 163        return mxs_phy->data == &imx6sl_phy_data;
 164}
 165
 166/*
 167 * PHY needs some 32K cycles to switch from 32K clock to
 168 * bus (such as AHB/AXI, etc) clock.
 169 */
 170static void mxs_phy_clock_switch_delay(void)
 171{
 172        usleep_range(300, 400);
 173}
 174
 175static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
 176{
 177        int ret;
 178        void __iomem *base = mxs_phy->phy.io_priv;
 179
 180        ret = stmp_reset_block(base + HW_USBPHY_CTRL);
 181        if (ret)
 182                return ret;
 183
 184        /* Power up the PHY */
 185        writel(0, base + HW_USBPHY_PWD);
 186
 187        /*
 188         * USB PHY Ctrl Setting
 189         * - Auto clock/power on
 190         * - Enable full/low speed support
 191         */
 192        writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
 193                BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
 194                BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
 195                BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
 196                BM_USBPHY_CTRL_ENAUTO_PWRON_PLL |
 197                BM_USBPHY_CTRL_ENUTMILEVEL2 |
 198                BM_USBPHY_CTRL_ENUTMILEVEL3,
 199               base + HW_USBPHY_CTRL_SET);
 200
 201        if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX)
 202                writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET);
 203
 204        return 0;
 205}
 206
 207/* Return true if the vbus is there */
 208static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy)
 209{
 210        unsigned int vbus_value;
 211
 212        if (mxs_phy->port_id == 0)
 213                regmap_read(mxs_phy->regmap_anatop,
 214                        ANADIG_USB1_VBUS_DET_STAT,
 215                        &vbus_value);
 216        else if (mxs_phy->port_id == 1)
 217                regmap_read(mxs_phy->regmap_anatop,
 218                        ANADIG_USB2_VBUS_DET_STAT,
 219                        &vbus_value);
 220
 221        if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)
 222                return true;
 223        else
 224                return false;
 225}
 226
 227static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
 228{
 229        void __iomem *base = mxs_phy->phy.io_priv;
 230        u32 reg;
 231
 232        if (disconnect)
 233                writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
 234                        base + HW_USBPHY_DEBUG_CLR);
 235
 236        if (mxs_phy->port_id == 0) {
 237                reg = disconnect ? ANADIG_USB1_LOOPBACK_SET
 238                        : ANADIG_USB1_LOOPBACK_CLR;
 239                regmap_write(mxs_phy->regmap_anatop, reg,
 240                        BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 |
 241                        BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN);
 242        } else if (mxs_phy->port_id == 1) {
 243                reg = disconnect ? ANADIG_USB2_LOOPBACK_SET
 244                        : ANADIG_USB2_LOOPBACK_CLR;
 245                regmap_write(mxs_phy->regmap_anatop, reg,
 246                        BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 |
 247                        BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN);
 248        }
 249
 250        if (!disconnect)
 251                writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
 252                        base + HW_USBPHY_DEBUG_SET);
 253
 254        /* Delay some time, and let Linestate be SE0 for controller */
 255        if (disconnect)
 256                usleep_range(500, 1000);
 257}
 258
 259static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 260{
 261        bool vbus_is_on = false;
 262
 263        /* If the SoCs don't need to disconnect line without vbus, quit */
 264        if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
 265                return;
 266
 267        /* If the SoCs don't have anatop, quit */
 268        if (!mxs_phy->regmap_anatop)
 269                return;
 270
 271        vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
 272
 273        if (on && !vbus_is_on)
 274                __mxs_phy_disconnect_line(mxs_phy, true);
 275        else
 276                __mxs_phy_disconnect_line(mxs_phy, false);
 277
 278}
 279
 280static int mxs_phy_init(struct usb_phy *phy)
 281{
 282        int ret;
 283        struct mxs_phy *mxs_phy = to_mxs_phy(phy);
 284
 285        mxs_phy_clock_switch_delay();
 286        ret = clk_prepare_enable(mxs_phy->clk);
 287        if (ret)
 288                return ret;
 289
 290        return mxs_phy_hw_init(mxs_phy);
 291}
 292
 293static void mxs_phy_shutdown(struct usb_phy *phy)
 294{
 295        struct mxs_phy *mxs_phy = to_mxs_phy(phy);
 296
 297        writel(BM_USBPHY_CTRL_CLKGATE,
 298               phy->io_priv + HW_USBPHY_CTRL_SET);
 299
 300        clk_disable_unprepare(mxs_phy->clk);
 301}
 302
 303static int mxs_phy_suspend(struct usb_phy *x, int suspend)
 304{
 305        int ret;
 306        struct mxs_phy *mxs_phy = to_mxs_phy(x);
 307
 308        if (suspend) {
 309                writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
 310                writel(BM_USBPHY_CTRL_CLKGATE,
 311                       x->io_priv + HW_USBPHY_CTRL_SET);
 312                clk_disable_unprepare(mxs_phy->clk);
 313        } else {
 314                mxs_phy_clock_switch_delay();
 315                ret = clk_prepare_enable(mxs_phy->clk);
 316                if (ret)
 317                        return ret;
 318                writel(BM_USBPHY_CTRL_CLKGATE,
 319                       x->io_priv + HW_USBPHY_CTRL_CLR);
 320                writel(0, x->io_priv + HW_USBPHY_PWD);
 321        }
 322
 323        return 0;
 324}
 325
 326static int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled)
 327{
 328        struct mxs_phy *mxs_phy = to_mxs_phy(x);
 329        u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP |
 330                        BM_USBPHY_CTRL_ENDPDMCHG_WKUP |
 331                                BM_USBPHY_CTRL_ENIDCHG_WKUP;
 332        if (enabled) {
 333                mxs_phy_disconnect_line(mxs_phy, true);
 334                writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET);
 335        } else {
 336                writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR);
 337                mxs_phy_disconnect_line(mxs_phy, false);
 338        }
 339
 340        return 0;
 341}
 342
 343static int mxs_phy_on_connect(struct usb_phy *phy,
 344                enum usb_device_speed speed)
 345{
 346        dev_dbg(phy->dev, "%s device has connected\n",
 347                (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
 348
 349        if (speed == USB_SPEED_HIGH)
 350                writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
 351                       phy->io_priv + HW_USBPHY_CTRL_SET);
 352
 353        return 0;
 354}
 355
 356static int mxs_phy_on_disconnect(struct usb_phy *phy,
 357                enum usb_device_speed speed)
 358{
 359        dev_dbg(phy->dev, "%s device has disconnected\n",
 360                (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
 361
 362        if (speed == USB_SPEED_HIGH)
 363                writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
 364                       phy->io_priv + HW_USBPHY_CTRL_CLR);
 365
 366        return 0;
 367}
 368
 369static int mxs_phy_probe(struct platform_device *pdev)
 370{
 371        struct resource *res;
 372        void __iomem *base;
 373        struct clk *clk;
 374        struct mxs_phy *mxs_phy;
 375        int ret;
 376        const struct of_device_id *of_id =
 377                        of_match_device(mxs_phy_dt_ids, &pdev->dev);
 378        struct device_node *np = pdev->dev.of_node;
 379
 380        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 381        base = devm_ioremap_resource(&pdev->dev, res);
 382        if (IS_ERR(base))
 383                return PTR_ERR(base);
 384
 385        clk = devm_clk_get(&pdev->dev, NULL);
 386        if (IS_ERR(clk)) {
 387                dev_err(&pdev->dev,
 388                        "can't get the clock, err=%ld", PTR_ERR(clk));
 389                return PTR_ERR(clk);
 390        }
 391
 392        mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
 393        if (!mxs_phy)
 394                return -ENOMEM;
 395
 396        /* Some SoCs don't have anatop registers */
 397        if (of_get_property(np, "fsl,anatop", NULL)) {
 398                mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle
 399                        (np, "fsl,anatop");
 400                if (IS_ERR(mxs_phy->regmap_anatop)) {
 401                        dev_dbg(&pdev->dev,
 402                                "failed to find regmap for anatop\n");
 403                        return PTR_ERR(mxs_phy->regmap_anatop);
 404                }
 405        }
 406
 407        ret = of_alias_get_id(np, "usbphy");
 408        if (ret < 0)
 409                dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);
 410        mxs_phy->port_id = ret;
 411
 412        mxs_phy->phy.io_priv            = base;
 413        mxs_phy->phy.dev                = &pdev->dev;
 414        mxs_phy->phy.label              = DRIVER_NAME;
 415        mxs_phy->phy.init               = mxs_phy_init;
 416        mxs_phy->phy.shutdown           = mxs_phy_shutdown;
 417        mxs_phy->phy.set_suspend        = mxs_phy_suspend;
 418        mxs_phy->phy.notify_connect     = mxs_phy_on_connect;
 419        mxs_phy->phy.notify_disconnect  = mxs_phy_on_disconnect;
 420        mxs_phy->phy.type               = USB_PHY_TYPE_USB2;
 421        mxs_phy->phy.set_wakeup         = mxs_phy_set_wakeup;
 422
 423        mxs_phy->clk = clk;
 424        mxs_phy->data = of_id->data;
 425
 426        platform_set_drvdata(pdev, mxs_phy);
 427
 428        device_set_wakeup_capable(&pdev->dev, true);
 429
 430        ret = usb_add_phy_dev(&mxs_phy->phy);
 431        if (ret)
 432                return ret;
 433
 434        return 0;
 435}
 436
 437static int mxs_phy_remove(struct platform_device *pdev)
 438{
 439        struct mxs_phy *mxs_phy = platform_get_drvdata(pdev);
 440
 441        usb_remove_phy(&mxs_phy->phy);
 442
 443        return 0;
 444}
 445
 446#ifdef CONFIG_PM_SLEEP
 447static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
 448{
 449        unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
 450
 451        /* If the SoCs don't have anatop, quit */
 452        if (!mxs_phy->regmap_anatop)
 453                return;
 454
 455        if (is_imx6q_phy(mxs_phy))
 456                regmap_write(mxs_phy->regmap_anatop, reg,
 457                        BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
 458        else if (is_imx6sl_phy(mxs_phy))
 459                regmap_write(mxs_phy->regmap_anatop,
 460                        reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
 461}
 462
 463static int mxs_phy_system_suspend(struct device *dev)
 464{
 465        struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
 466
 467        if (device_may_wakeup(dev))
 468                mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
 469
 470        return 0;
 471}
 472
 473static int mxs_phy_system_resume(struct device *dev)
 474{
 475        struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
 476
 477        if (device_may_wakeup(dev))
 478                mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
 479
 480        return 0;
 481}
 482#endif /* CONFIG_PM_SLEEP */
 483
 484static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend,
 485                mxs_phy_system_resume);
 486
 487static struct platform_driver mxs_phy_driver = {
 488        .probe = mxs_phy_probe,
 489        .remove = mxs_phy_remove,
 490        .driver = {
 491                .name = DRIVER_NAME,
 492                .of_match_table = mxs_phy_dt_ids,
 493                .pm = &mxs_phy_pm,
 494         },
 495};
 496
 497static int __init mxs_phy_module_init(void)
 498{
 499        return platform_driver_register(&mxs_phy_driver);
 500}
 501postcore_initcall(mxs_phy_module_init);
 502
 503static void __exit mxs_phy_module_exit(void)
 504{
 505        platform_driver_unregister(&mxs_phy_driver);
 506}
 507module_exit(mxs_phy_module_exit);
 508
 509MODULE_ALIAS("platform:mxs-usb-phy");
 510MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 511MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
 512MODULE_DESCRIPTION("Freescale MXS USB PHY driver");
 513MODULE_LICENSE("GPL");
 514