linux/drivers/phy/mediatek/phy-mtk-ufs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2019 MediaTek Inc.
   4 * Author: Stanley Chu <stanley.chu@mediatek.com>
   5 */
   6
   7#include <linux/clk.h>
   8#include <linux/delay.h>
   9#include <linux/io.h>
  10#include <linux/module.h>
  11#include <linux/phy/phy.h>
  12#include <linux/platform_device.h>
  13
  14/* mphy register and offsets */
  15#define MP_GLB_DIG_8C               0x008C
  16#define FRC_PLL_ISO_EN              BIT(8)
  17#define PLL_ISO_EN                  BIT(9)
  18#define FRC_FRC_PWR_ON              BIT(10)
  19#define PLL_PWR_ON                  BIT(11)
  20
  21#define MP_LN_DIG_RX_9C             0xA09C
  22#define FSM_DIFZ_FRC                BIT(18)
  23
  24#define MP_LN_DIG_RX_AC             0xA0AC
  25#define FRC_RX_SQ_EN                BIT(0)
  26#define RX_SQ_EN                    BIT(1)
  27
  28#define MP_LN_RX_44                 0xB044
  29#define FRC_CDR_PWR_ON              BIT(17)
  30#define CDR_PWR_ON                  BIT(18)
  31#define FRC_CDR_ISO_EN              BIT(19)
  32#define CDR_ISO_EN                  BIT(20)
  33
  34struct ufs_mtk_phy {
  35        struct device *dev;
  36        void __iomem *mmio;
  37        struct clk *mp_clk;
  38        struct clk *unipro_clk;
  39};
  40
  41static inline u32 mphy_readl(struct ufs_mtk_phy *phy, u32 reg)
  42{
  43        return readl(phy->mmio + reg);
  44}
  45
  46static inline void mphy_writel(struct ufs_mtk_phy *phy, u32 val, u32 reg)
  47{
  48        writel(val, phy->mmio + reg);
  49}
  50
  51static void mphy_set_bit(struct ufs_mtk_phy *phy, u32 reg, u32 bit)
  52{
  53        u32 val;
  54
  55        val = mphy_readl(phy, reg);
  56        val |= bit;
  57        mphy_writel(phy, val, reg);
  58}
  59
  60static void mphy_clr_bit(struct ufs_mtk_phy *phy, u32 reg, u32 bit)
  61{
  62        u32 val;
  63
  64        val = mphy_readl(phy, reg);
  65        val &= ~bit;
  66        mphy_writel(phy, val, reg);
  67}
  68
  69static struct ufs_mtk_phy *get_ufs_mtk_phy(struct phy *generic_phy)
  70{
  71        return (struct ufs_mtk_phy *)phy_get_drvdata(generic_phy);
  72}
  73
  74static int ufs_mtk_phy_clk_init(struct ufs_mtk_phy *phy)
  75{
  76        struct device *dev = phy->dev;
  77
  78        phy->unipro_clk = devm_clk_get(dev, "unipro");
  79        if (IS_ERR(phy->unipro_clk)) {
  80                dev_err(dev, "failed to get clock: unipro");
  81                return PTR_ERR(phy->unipro_clk);
  82        }
  83
  84        phy->mp_clk = devm_clk_get(dev, "mp");
  85        if (IS_ERR(phy->mp_clk)) {
  86                dev_err(dev, "failed to get clock: mp");
  87                return PTR_ERR(phy->mp_clk);
  88        }
  89
  90        return 0;
  91}
  92
  93static void ufs_mtk_phy_set_active(struct ufs_mtk_phy *phy)
  94{
  95        /* release DA_MP_PLL_PWR_ON */
  96        mphy_set_bit(phy, MP_GLB_DIG_8C, PLL_PWR_ON);
  97        mphy_clr_bit(phy, MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
  98
  99        /* release DA_MP_PLL_ISO_EN */
 100        mphy_clr_bit(phy, MP_GLB_DIG_8C, PLL_ISO_EN);
 101        mphy_clr_bit(phy, MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
 102
 103        /* release DA_MP_CDR_PWR_ON */
 104        mphy_set_bit(phy, MP_LN_RX_44, CDR_PWR_ON);
 105        mphy_clr_bit(phy, MP_LN_RX_44, FRC_CDR_PWR_ON);
 106
 107        /* release DA_MP_CDR_ISO_EN */
 108        mphy_clr_bit(phy, MP_LN_RX_44, CDR_ISO_EN);
 109        mphy_clr_bit(phy, MP_LN_RX_44, FRC_CDR_ISO_EN);
 110
 111        /* release DA_MP_RX0_SQ_EN */
 112        mphy_set_bit(phy, MP_LN_DIG_RX_AC, RX_SQ_EN);
 113        mphy_clr_bit(phy, MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
 114
 115        /* delay 1us to wait DIFZ stable */
 116        udelay(1);
 117
 118        /* release DIFZ */
 119        mphy_clr_bit(phy, MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
 120}
 121
 122static void ufs_mtk_phy_set_deep_hibern(struct ufs_mtk_phy *phy)
 123{
 124        /* force DIFZ */
 125        mphy_set_bit(phy, MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
 126
 127        /* force DA_MP_RX0_SQ_EN */
 128        mphy_set_bit(phy, MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
 129        mphy_clr_bit(phy, MP_LN_DIG_RX_AC, RX_SQ_EN);
 130
 131        /* force DA_MP_CDR_ISO_EN */
 132        mphy_set_bit(phy, MP_LN_RX_44, FRC_CDR_ISO_EN);
 133        mphy_set_bit(phy, MP_LN_RX_44, CDR_ISO_EN);
 134
 135        /* force DA_MP_CDR_PWR_ON */
 136        mphy_set_bit(phy, MP_LN_RX_44, FRC_CDR_PWR_ON);
 137        mphy_clr_bit(phy, MP_LN_RX_44, CDR_PWR_ON);
 138
 139        /* force DA_MP_PLL_ISO_EN */
 140        mphy_set_bit(phy, MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
 141        mphy_set_bit(phy, MP_GLB_DIG_8C, PLL_ISO_EN);
 142
 143        /* force DA_MP_PLL_PWR_ON */
 144        mphy_set_bit(phy, MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
 145        mphy_clr_bit(phy, MP_GLB_DIG_8C, PLL_PWR_ON);
 146}
 147
 148static int ufs_mtk_phy_power_on(struct phy *generic_phy)
 149{
 150        struct ufs_mtk_phy *phy = get_ufs_mtk_phy(generic_phy);
 151        int ret;
 152
 153        ret = clk_prepare_enable(phy->unipro_clk);
 154        if (ret) {
 155                dev_err(phy->dev, "unipro_clk enable failed %d\n", ret);
 156                goto out;
 157        }
 158
 159        ret = clk_prepare_enable(phy->mp_clk);
 160        if (ret) {
 161                dev_err(phy->dev, "mp_clk enable failed %d\n", ret);
 162                goto out_unprepare_unipro_clk;
 163        }
 164
 165        ufs_mtk_phy_set_active(phy);
 166
 167        return 0;
 168
 169out_unprepare_unipro_clk:
 170        clk_disable_unprepare(phy->unipro_clk);
 171out:
 172        return ret;
 173}
 174
 175static int ufs_mtk_phy_power_off(struct phy *generic_phy)
 176{
 177        struct ufs_mtk_phy *phy = get_ufs_mtk_phy(generic_phy);
 178
 179        ufs_mtk_phy_set_deep_hibern(phy);
 180
 181        clk_disable_unprepare(phy->unipro_clk);
 182        clk_disable_unprepare(phy->mp_clk);
 183
 184        return 0;
 185}
 186
 187static const struct phy_ops ufs_mtk_phy_ops = {
 188        .power_on       = ufs_mtk_phy_power_on,
 189        .power_off      = ufs_mtk_phy_power_off,
 190        .owner          = THIS_MODULE,
 191};
 192
 193static int ufs_mtk_phy_probe(struct platform_device *pdev)
 194{
 195        struct device *dev = &pdev->dev;
 196        struct phy *generic_phy;
 197        struct phy_provider *phy_provider;
 198        struct resource *res;
 199        struct ufs_mtk_phy *phy;
 200        int ret;
 201
 202        phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
 203        if (!phy)
 204                return -ENOMEM;
 205
 206        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 207        phy->mmio = devm_ioremap_resource(dev, res);
 208        if (IS_ERR(phy->mmio))
 209                return PTR_ERR(phy->mmio);
 210
 211        phy->dev = dev;
 212
 213        ret = ufs_mtk_phy_clk_init(phy);
 214        if (ret)
 215                return ret;
 216
 217        generic_phy = devm_phy_create(dev, NULL, &ufs_mtk_phy_ops);
 218        if (IS_ERR(generic_phy))
 219                return PTR_ERR(generic_phy);
 220
 221        phy_set_drvdata(generic_phy, phy);
 222
 223        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 224
 225        return PTR_ERR_OR_ZERO(phy_provider);
 226}
 227
 228static const struct of_device_id ufs_mtk_phy_of_match[] = {
 229        {.compatible = "mediatek,mt8183-ufsphy"},
 230        {},
 231};
 232MODULE_DEVICE_TABLE(of, ufs_mtk_phy_of_match);
 233
 234static struct platform_driver ufs_mtk_phy_driver = {
 235        .probe = ufs_mtk_phy_probe,
 236        .driver = {
 237                .of_match_table = ufs_mtk_phy_of_match,
 238                .name = "ufs_mtk_phy",
 239        },
 240};
 241module_platform_driver(ufs_mtk_phy_driver);
 242
 243MODULE_DESCRIPTION("Universal Flash Storage (UFS) MediaTek MPHY");
 244MODULE_AUTHOR("Stanley Chu <stanley.chu@mediatek.com>");
 245MODULE_LICENSE("GPL v2");
 246