linux/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/* Copyright (c) 2017 NXP. */
   3
   4#include <linux/bitfield.h>
   5#include <linux/clk.h>
   6#include <linux/delay.h>
   7#include <linux/io.h>
   8#include <linux/module.h>
   9#include <linux/of_platform.h>
  10#include <linux/phy/phy.h>
  11#include <linux/platform_device.h>
  12#include <linux/regulator/consumer.h>
  13
  14#define PHY_CTRL0                       0x0
  15#define PHY_CTRL0_REF_SSP_EN            BIT(2)
  16#define PHY_CTRL0_FSEL_MASK             GENMASK(10, 5)
  17#define PHY_CTRL0_FSEL_24M              0x2a
  18
  19#define PHY_CTRL1                       0x4
  20#define PHY_CTRL1_RESET                 BIT(0)
  21#define PHY_CTRL1_COMMONONN             BIT(1)
  22#define PHY_CTRL1_ATERESET              BIT(3)
  23#define PHY_CTRL1_VDATSRCENB0           BIT(19)
  24#define PHY_CTRL1_VDATDETENB0           BIT(20)
  25
  26#define PHY_CTRL2                       0x8
  27#define PHY_CTRL2_TXENABLEN0            BIT(8)
  28#define PHY_CTRL2_OTG_DISABLE           BIT(9)
  29
  30#define PHY_CTRL6                       0x18
  31#define PHY_CTRL6_ALT_CLK_EN            BIT(1)
  32#define PHY_CTRL6_ALT_CLK_SEL           BIT(0)
  33
  34struct imx8mq_usb_phy {
  35        struct phy *phy;
  36        struct clk *clk;
  37        void __iomem *base;
  38        struct regulator *vbus;
  39};
  40
  41static int imx8mq_usb_phy_init(struct phy *phy)
  42{
  43        struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
  44        u32 value;
  45
  46        value = readl(imx_phy->base + PHY_CTRL1);
  47        value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 |
  48                   PHY_CTRL1_COMMONONN);
  49        value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
  50        writel(value, imx_phy->base + PHY_CTRL1);
  51
  52        value = readl(imx_phy->base + PHY_CTRL0);
  53        value |= PHY_CTRL0_REF_SSP_EN;
  54        writel(value, imx_phy->base + PHY_CTRL0);
  55
  56        value = readl(imx_phy->base + PHY_CTRL2);
  57        value |= PHY_CTRL2_TXENABLEN0;
  58        writel(value, imx_phy->base + PHY_CTRL2);
  59
  60        value = readl(imx_phy->base + PHY_CTRL1);
  61        value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
  62        writel(value, imx_phy->base + PHY_CTRL1);
  63
  64        return 0;
  65}
  66
  67static int imx8mp_usb_phy_init(struct phy *phy)
  68{
  69        struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
  70        u32 value;
  71
  72        /* USB3.0 PHY signal fsel for 24M ref */
  73        value = readl(imx_phy->base + PHY_CTRL0);
  74        value &= ~PHY_CTRL0_FSEL_MASK;
  75        value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M);
  76        writel(value, imx_phy->base + PHY_CTRL0);
  77
  78        /* Disable alt_clk_en and use internal MPLL clocks */
  79        value = readl(imx_phy->base + PHY_CTRL6);
  80        value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN);
  81        writel(value, imx_phy->base + PHY_CTRL6);
  82
  83        value = readl(imx_phy->base + PHY_CTRL1);
  84        value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0);
  85        value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
  86        writel(value, imx_phy->base + PHY_CTRL1);
  87
  88        value = readl(imx_phy->base + PHY_CTRL0);
  89        value |= PHY_CTRL0_REF_SSP_EN;
  90        writel(value, imx_phy->base + PHY_CTRL0);
  91
  92        value = readl(imx_phy->base + PHY_CTRL2);
  93        value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE;
  94        writel(value, imx_phy->base + PHY_CTRL2);
  95
  96        udelay(10);
  97
  98        value = readl(imx_phy->base + PHY_CTRL1);
  99        value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
 100        writel(value, imx_phy->base + PHY_CTRL1);
 101
 102        return 0;
 103}
 104
 105static int imx8mq_phy_power_on(struct phy *phy)
 106{
 107        struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
 108        int ret;
 109
 110        ret = regulator_enable(imx_phy->vbus);
 111        if (ret)
 112                return ret;
 113
 114        return clk_prepare_enable(imx_phy->clk);
 115}
 116
 117static int imx8mq_phy_power_off(struct phy *phy)
 118{
 119        struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
 120
 121        clk_disable_unprepare(imx_phy->clk);
 122        regulator_disable(imx_phy->vbus);
 123
 124        return 0;
 125}
 126
 127static const struct phy_ops imx8mq_usb_phy_ops = {
 128        .init           = imx8mq_usb_phy_init,
 129        .power_on       = imx8mq_phy_power_on,
 130        .power_off      = imx8mq_phy_power_off,
 131        .owner          = THIS_MODULE,
 132};
 133
 134static const struct phy_ops imx8mp_usb_phy_ops = {
 135        .init           = imx8mp_usb_phy_init,
 136        .power_on       = imx8mq_phy_power_on,
 137        .power_off      = imx8mq_phy_power_off,
 138        .owner          = THIS_MODULE,
 139};
 140
 141static const struct of_device_id imx8mq_usb_phy_of_match[] = {
 142        {.compatible = "fsl,imx8mq-usb-phy",
 143         .data = &imx8mq_usb_phy_ops,},
 144        {.compatible = "fsl,imx8mp-usb-phy",
 145         .data = &imx8mp_usb_phy_ops,},
 146        { }
 147};
 148MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
 149
 150static int imx8mq_usb_phy_probe(struct platform_device *pdev)
 151{
 152        struct phy_provider *phy_provider;
 153        struct device *dev = &pdev->dev;
 154        struct imx8mq_usb_phy *imx_phy;
 155        const struct phy_ops *phy_ops;
 156
 157        imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL);
 158        if (!imx_phy)
 159                return -ENOMEM;
 160
 161        imx_phy->clk = devm_clk_get(dev, "phy");
 162        if (IS_ERR(imx_phy->clk)) {
 163                dev_err(dev, "failed to get imx8mq usb phy clock\n");
 164                return PTR_ERR(imx_phy->clk);
 165        }
 166
 167        imx_phy->base = devm_platform_ioremap_resource(pdev, 0);
 168        if (IS_ERR(imx_phy->base))
 169                return PTR_ERR(imx_phy->base);
 170
 171        phy_ops = of_device_get_match_data(dev);
 172        if (!phy_ops)
 173                return -EINVAL;
 174
 175        imx_phy->phy = devm_phy_create(dev, NULL, phy_ops);
 176        if (IS_ERR(imx_phy->phy))
 177                return PTR_ERR(imx_phy->phy);
 178
 179        imx_phy->vbus = devm_regulator_get(dev, "vbus");
 180        if (IS_ERR(imx_phy->vbus))
 181                return PTR_ERR(imx_phy->vbus);
 182
 183        phy_set_drvdata(imx_phy->phy, imx_phy);
 184
 185        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 186
 187        return PTR_ERR_OR_ZERO(phy_provider);
 188}
 189
 190static struct platform_driver imx8mq_usb_phy_driver = {
 191        .probe  = imx8mq_usb_phy_probe,
 192        .driver = {
 193                .name   = "imx8mq-usb-phy",
 194                .of_match_table = imx8mq_usb_phy_of_match,
 195        }
 196};
 197module_platform_driver(imx8mq_usb_phy_driver);
 198
 199MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver");
 200MODULE_LICENSE("GPL");
 201