linux/drivers/phy/phy-rockchip-emmc.c
<<
>>
Prefs
   1/*
   2 * Rockchip emmc PHY driver
   3 *
   4 * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
   5 * Copyright (C) 2016 ROCKCHIP, Inc.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/delay.h>
  18#include <linux/mfd/syscon.h>
  19#include <linux/module.h>
  20#include <linux/of.h>
  21#include <linux/of_address.h>
  22#include <linux/phy/phy.h>
  23#include <linux/platform_device.h>
  24#include <linux/regmap.h>
  25
  26/*
  27 * The higher 16-bit of this register is used for write protection
  28 * only if BIT(x + 16) set to 1 the BIT(x) can be written.
  29 */
  30#define HIWORD_UPDATE(val, mask, shift) \
  31                ((val) << (shift) | (mask) << ((shift) + 16))
  32
  33/* Register definition */
  34#define GRF_EMMCPHY_CON0        0x0
  35#define GRF_EMMCPHY_CON1        0x4
  36#define GRF_EMMCPHY_CON2        0x8
  37#define GRF_EMMCPHY_CON3        0xc
  38#define GRF_EMMCPHY_CON4        0x10
  39#define GRF_EMMCPHY_CON5        0x14
  40#define GRF_EMMCPHY_CON6        0x18
  41#define GRF_EMMCPHY_STATUS      0x20
  42
  43#define PHYCTRL_PDB_MASK        0x1
  44#define PHYCTRL_PDB_SHIFT       0x0
  45#define PHYCTRL_PDB_PWR_ON      0x1
  46#define PHYCTRL_PDB_PWR_OFF     0x0
  47#define PHYCTRL_ENDLL_MASK      0x1
  48#define PHYCTRL_ENDLL_SHIFT     0x1
  49#define PHYCTRL_ENDLL_ENABLE    0x1
  50#define PHYCTRL_ENDLL_DISABLE   0x0
  51#define PHYCTRL_CALDONE_MASK    0x1
  52#define PHYCTRL_CALDONE_SHIFT   0x6
  53#define PHYCTRL_CALDONE_DONE    0x1
  54#define PHYCTRL_CALDONE_GOING   0x0
  55#define PHYCTRL_DLLRDY_MASK     0x1
  56#define PHYCTRL_DLLRDY_SHIFT    0x5
  57#define PHYCTRL_DLLRDY_DONE     0x1
  58#define PHYCTRL_DLLRDY_GOING    0x0
  59
  60struct rockchip_emmc_phy {
  61        unsigned int    reg_offset;
  62        struct regmap   *reg_base;
  63};
  64
  65static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
  66                                   bool on_off)
  67{
  68        unsigned int caldone;
  69        unsigned int dllrdy;
  70
  71        /*
  72         * Keep phyctrl_pdb and phyctrl_endll low to allow
  73         * initialization of CALIO state M/C DFFs
  74         */
  75        regmap_write(rk_phy->reg_base,
  76                     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
  77                     HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
  78                                   PHYCTRL_PDB_MASK,
  79                                   PHYCTRL_PDB_SHIFT));
  80        regmap_write(rk_phy->reg_base,
  81                     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
  82                     HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
  83                                   PHYCTRL_ENDLL_MASK,
  84                                   PHYCTRL_ENDLL_SHIFT));
  85
  86        /* Already finish power_off above */
  87        if (on_off == PHYCTRL_PDB_PWR_OFF)
  88                return 0;
  89
  90        /*
  91         * According to the user manual, calpad calibration
  92         * cycle takes more than 2us without the minimal recommended
  93         * value, so we may need a little margin here
  94         */
  95        udelay(3);
  96        regmap_write(rk_phy->reg_base,
  97                     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
  98                     HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
  99                                   PHYCTRL_PDB_MASK,
 100                                   PHYCTRL_PDB_SHIFT));
 101
 102        /*
 103         * According to the user manual, it asks driver to
 104         * wait 5us for calpad busy trimming
 105         */
 106        udelay(5);
 107        regmap_read(rk_phy->reg_base,
 108                    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
 109                    &caldone);
 110        caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
 111        if (caldone != PHYCTRL_CALDONE_DONE) {
 112                pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
 113                return -ETIMEDOUT;
 114        }
 115
 116        regmap_write(rk_phy->reg_base,
 117                     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
 118                     HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
 119                                   PHYCTRL_ENDLL_MASK,
 120                                   PHYCTRL_ENDLL_SHIFT));
 121        /*
 122         * After enable analog DLL circuits, we need extra 10.2us
 123         * for dll to be ready for work.
 124         */
 125        udelay(11);
 126        regmap_read(rk_phy->reg_base,
 127                    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
 128                    &dllrdy);
 129        dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
 130        if (dllrdy != PHYCTRL_DLLRDY_DONE) {
 131                pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
 132                return -ETIMEDOUT;
 133        }
 134
 135        return 0;
 136}
 137
 138static int rockchip_emmc_phy_power_off(struct phy *phy)
 139{
 140        struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
 141        int ret = 0;
 142
 143        /* Power down emmc phy analog blocks */
 144        ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
 145        if (ret)
 146                return ret;
 147
 148        return 0;
 149}
 150
 151static int rockchip_emmc_phy_power_on(struct phy *phy)
 152{
 153        struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
 154        int ret = 0;
 155
 156        /* Power up emmc phy analog blocks */
 157        ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
 158        if (ret)
 159                return ret;
 160
 161        return 0;
 162}
 163
 164static const struct phy_ops ops = {
 165        .power_on       = rockchip_emmc_phy_power_on,
 166        .power_off      = rockchip_emmc_phy_power_off,
 167        .owner          = THIS_MODULE,
 168};
 169
 170static int rockchip_emmc_phy_probe(struct platform_device *pdev)
 171{
 172        struct device *dev = &pdev->dev;
 173        struct rockchip_emmc_phy *rk_phy;
 174        struct phy *generic_phy;
 175        struct phy_provider *phy_provider;
 176        struct regmap *grf;
 177        unsigned int reg_offset;
 178
 179        if (!dev->parent || !dev->parent->of_node)
 180                return -ENODEV;
 181
 182        grf = syscon_node_to_regmap(dev->parent->of_node);
 183        if (IS_ERR(grf)) {
 184                dev_err(dev, "Missing rockchip,grf property\n");
 185                return PTR_ERR(grf);
 186        }
 187
 188        rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
 189        if (!rk_phy)
 190                return -ENOMEM;
 191
 192        if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {
 193                dev_err(dev, "missing reg property in node %s\n",
 194                        dev->of_node->name);
 195                return -EINVAL;
 196        }
 197
 198        rk_phy->reg_offset = reg_offset;
 199        rk_phy->reg_base = grf;
 200
 201        generic_phy = devm_phy_create(dev, dev->of_node, &ops);
 202        if (IS_ERR(generic_phy)) {
 203                dev_err(dev, "failed to create PHY\n");
 204                return PTR_ERR(generic_phy);
 205        }
 206
 207        phy_set_drvdata(generic_phy, rk_phy);
 208        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 209
 210        return PTR_ERR_OR_ZERO(phy_provider);
 211}
 212
 213static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {
 214        { .compatible = "rockchip,rk3399-emmc-phy" },
 215        {}
 216};
 217
 218MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);
 219
 220static struct platform_driver rockchip_emmc_driver = {
 221        .probe          = rockchip_emmc_phy_probe,
 222        .driver         = {
 223                .name   = "rockchip-emmc-phy",
 224                .of_match_table = rockchip_emmc_phy_dt_ids,
 225        },
 226};
 227
 228module_platform_driver(rockchip_emmc_driver);
 229
 230MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
 231MODULE_DESCRIPTION("Rockchip EMMC PHY driver");
 232MODULE_LICENSE("GPL v2");
 233