cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

phy-intel-keembay-emmc.c (8670B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Intel Keem Bay eMMC PHY driver
      4 * Copyright (C) 2020 Intel Corporation
      5 */
      6
      7#include <linux/bitfield.h>
      8#include <linux/clk.h>
      9#include <linux/delay.h>
     10#include <linux/mfd/syscon.h>
     11#include <linux/module.h>
     12#include <linux/of.h>
     13#include <linux/of_address.h>
     14#include <linux/phy/phy.h>
     15#include <linux/platform_device.h>
     16#include <linux/regmap.h>
     17
     18/* eMMC/SD/SDIO core/phy configuration registers */
     19#define PHY_CFG_0		0x24
     20#define  SEL_DLY_TXCLK_MASK	BIT(29)
     21#define  OTAP_DLY_ENA_MASK	BIT(27)
     22#define  OTAP_DLY_SEL_MASK	GENMASK(26, 23)
     23#define  DLL_EN_MASK		BIT(10)
     24#define  PWR_DOWN_MASK		BIT(0)
     25
     26#define PHY_CFG_2		0x2c
     27#define  SEL_FREQ_MASK		GENMASK(12, 10)
     28
     29#define PHY_STAT		0x40
     30#define  CAL_DONE_MASK		BIT(6)
     31#define  IS_CALDONE(x)		((x) & CAL_DONE_MASK)
     32#define  DLL_RDY_MASK		BIT(5)
     33#define  IS_DLLRDY(x)		((x) & DLL_RDY_MASK)
     34
     35/* From ACS_eMMC51_16nFFC_RO1100_Userguide_v1p0.pdf p17 */
     36#define FREQSEL_200M_170M	0x0
     37#define FREQSEL_170M_140M	0x1
     38#define FREQSEL_140M_110M	0x2
     39#define FREQSEL_110M_80M	0x3
     40#define FREQSEL_80M_50M		0x4
     41
     42struct keembay_emmc_phy {
     43	struct regmap *syscfg;
     44	struct clk *emmcclk;
     45};
     46
     47static const struct regmap_config keembay_regmap_config = {
     48	.reg_bits = 32,
     49	.val_bits = 32,
     50	.reg_stride = 4,
     51};
     52
     53static int keembay_emmc_phy_power(struct phy *phy, bool on_off)
     54{
     55	struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
     56	unsigned int caldone;
     57	unsigned int dllrdy;
     58	unsigned int freqsel;
     59	unsigned int mhz;
     60	int ret;
     61
     62	/*
     63	 * Keep phyctrl_pdb and phyctrl_endll low to allow
     64	 * initialization of CALIO state M/C DFFs
     65	 */
     66	ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK,
     67				 FIELD_PREP(PWR_DOWN_MASK, 0));
     68	if (ret) {
     69		dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
     70		return ret;
     71	}
     72
     73	ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK,
     74				 FIELD_PREP(DLL_EN_MASK, 0));
     75	if (ret) {
     76		dev_err(&phy->dev, "turn off the dll failed: %d\n", ret);
     77		return ret;
     78	}
     79
     80	/* Already finish power off above */
     81	if (!on_off)
     82		return 0;
     83
     84	mhz = DIV_ROUND_CLOSEST(clk_get_rate(priv->emmcclk), 1000000);
     85	if (mhz <= 200 && mhz >= 170)
     86		freqsel = FREQSEL_200M_170M;
     87	else if (mhz <= 170 && mhz >= 140)
     88		freqsel = FREQSEL_170M_140M;
     89	else if (mhz <= 140 && mhz >= 110)
     90		freqsel = FREQSEL_140M_110M;
     91	else if (mhz <= 110 && mhz >= 80)
     92		freqsel = FREQSEL_110M_80M;
     93	else if (mhz <= 80 && mhz >= 50)
     94		freqsel = FREQSEL_80M_50M;
     95	else
     96		freqsel = 0x0;
     97
     98	/* Check for EMMC clock rate*/
     99	if (mhz > 175)
    100		dev_warn(&phy->dev, "Unsupported rate: %d MHz\n", mhz);
    101
    102	/*
    103	 * According to the user manual, calpad calibration
    104	 * cycle takes more than 2us without the minimal recommended
    105	 * value, so we may need a little margin here
    106	 */
    107	udelay(5);
    108
    109	ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK,
    110				 FIELD_PREP(PWR_DOWN_MASK, 1));
    111	if (ret) {
    112		dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
    113		return ret;
    114	}
    115
    116	/*
    117	 * According to the user manual, it asks driver to wait 5us for
    118	 * calpad busy trimming. However it is documented that this value is
    119	 * PVT(A.K.A. process, voltage and temperature) relevant, so some
    120	 * failure cases are found which indicates we should be more tolerant
    121	 * to calpad busy trimming.
    122	 */
    123	ret = regmap_read_poll_timeout(priv->syscfg, PHY_STAT,
    124				       caldone, IS_CALDONE(caldone),
    125				       0, 50);
    126	if (ret) {
    127		dev_err(&phy->dev, "caldone failed, ret=%d\n", ret);
    128		return ret;
    129	}
    130
    131	/* Set the frequency of the DLL operation */
    132	ret = regmap_update_bits(priv->syscfg, PHY_CFG_2, SEL_FREQ_MASK,
    133				 FIELD_PREP(SEL_FREQ_MASK, freqsel));
    134	if (ret) {
    135		dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret);
    136		return ret;
    137	}
    138
    139	/* Turn on the DLL */
    140	ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK,
    141				 FIELD_PREP(DLL_EN_MASK, 1));
    142	if (ret) {
    143		dev_err(&phy->dev, "turn on the dll failed: %d\n", ret);
    144		return ret;
    145	}
    146
    147	/*
    148	 * We turned on the DLL even though the rate was 0 because we the
    149	 * clock might be turned on later.  ...but we can't wait for the DLL
    150	 * to lock when the rate is 0 because it will never lock with no
    151	 * input clock.
    152	 *
    153	 * Technically we should be checking the lock later when the clock
    154	 * is turned on, but for now we won't.
    155	 */
    156	if (mhz == 0)
    157		return 0;
    158
    159	/*
    160	 * After enabling analog DLL circuits docs say that we need 10.2 us if
    161	 * our source clock is at 50 MHz and that lock time scales linearly
    162	 * with clock speed. If we are powering on the PHY and the card clock
    163	 * is super slow (like 100kHz) this could take as long as 5.1 ms as
    164	 * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
    165	 * hopefully we won't be running at 100 kHz, but we should still make
    166	 * sure we wait long enough.
    167	 *
    168	 * NOTE: There appear to be corner cases where the DLL seems to take
    169	 * extra long to lock for reasons that aren't understood. In some
    170	 * extreme cases we've seen it take up to over 10ms (!). We'll be
    171	 * generous and give it 50ms.
    172	 */
    173	ret = regmap_read_poll_timeout(priv->syscfg, PHY_STAT,
    174				       dllrdy, IS_DLLRDY(dllrdy),
    175				       0, 50 * USEC_PER_MSEC);
    176	if (ret)
    177		dev_err(&phy->dev, "dllrdy failed, ret=%d\n", ret);
    178
    179	return ret;
    180}
    181
    182static int keembay_emmc_phy_init(struct phy *phy)
    183{
    184	struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
    185
    186	/*
    187	 * We purposely get the clock here and not in probe to avoid the
    188	 * circular dependency problem. We expect:
    189	 * - PHY driver to probe
    190	 * - SDHCI driver to start probe
    191	 * - SDHCI driver to register it's clock
    192	 * - SDHCI driver to get the PHY
    193	 * - SDHCI driver to init the PHY
    194	 *
    195	 * The clock is optional, so upon any error just return it like
    196	 * any other error to user.
    197	 */
    198	priv->emmcclk = clk_get_optional(&phy->dev, "emmcclk");
    199
    200	return PTR_ERR_OR_ZERO(priv->emmcclk);
    201}
    202
    203static int keembay_emmc_phy_exit(struct phy *phy)
    204{
    205	struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
    206
    207	clk_put(priv->emmcclk);
    208
    209	return 0;
    210};
    211
    212static int keembay_emmc_phy_power_on(struct phy *phy)
    213{
    214	struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
    215	int ret;
    216
    217	/* Delay chain based txclk: enable */
    218	ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, SEL_DLY_TXCLK_MASK,
    219				 FIELD_PREP(SEL_DLY_TXCLK_MASK, 1));
    220	if (ret) {
    221		dev_err(&phy->dev, "ERROR: delay chain txclk set: %d\n", ret);
    222		return ret;
    223	}
    224
    225	/* Output tap delay: enable */
    226	ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_ENA_MASK,
    227				 FIELD_PREP(OTAP_DLY_ENA_MASK, 1));
    228	if (ret) {
    229		dev_err(&phy->dev, "ERROR: output tap delay set: %d\n", ret);
    230		return ret;
    231	}
    232
    233	/* Output tap delay */
    234	ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_SEL_MASK,
    235				 FIELD_PREP(OTAP_DLY_SEL_MASK, 2));
    236	if (ret) {
    237		dev_err(&phy->dev, "ERROR: output tap delay select: %d\n", ret);
    238		return ret;
    239	}
    240
    241	/* Power up eMMC phy analog blocks */
    242	return keembay_emmc_phy_power(phy, true);
    243}
    244
    245static int keembay_emmc_phy_power_off(struct phy *phy)
    246{
    247	/* Power down eMMC phy analog blocks */
    248	return keembay_emmc_phy_power(phy, false);
    249}
    250
    251static const struct phy_ops ops = {
    252	.init		= keembay_emmc_phy_init,
    253	.exit		= keembay_emmc_phy_exit,
    254	.power_on	= keembay_emmc_phy_power_on,
    255	.power_off	= keembay_emmc_phy_power_off,
    256	.owner		= THIS_MODULE,
    257};
    258
    259static int keembay_emmc_phy_probe(struct platform_device *pdev)
    260{
    261	struct device *dev = &pdev->dev;
    262	struct device_node *np = dev->of_node;
    263	struct keembay_emmc_phy *priv;
    264	struct phy *generic_phy;
    265	struct phy_provider *phy_provider;
    266	void __iomem *base;
    267
    268	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    269	if (!priv)
    270		return -ENOMEM;
    271
    272	base = devm_platform_ioremap_resource(pdev, 0);
    273	if (IS_ERR(base))
    274		return PTR_ERR(base);
    275
    276	priv->syscfg = devm_regmap_init_mmio(dev, base, &keembay_regmap_config);
    277	if (IS_ERR(priv->syscfg))
    278		return PTR_ERR(priv->syscfg);
    279
    280	generic_phy = devm_phy_create(dev, np, &ops);
    281	if (IS_ERR(generic_phy))
    282		return dev_err_probe(dev, PTR_ERR(generic_phy),
    283				     "failed to create PHY\n");
    284
    285	phy_set_drvdata(generic_phy, priv);
    286	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
    287
    288	return PTR_ERR_OR_ZERO(phy_provider);
    289}
    290
    291static const struct of_device_id keembay_emmc_phy_dt_ids[] = {
    292	{ .compatible = "intel,keembay-emmc-phy" },
    293	{}
    294};
    295MODULE_DEVICE_TABLE(of, keembay_emmc_phy_dt_ids);
    296
    297static struct platform_driver keembay_emmc_phy_driver = {
    298	.probe		= keembay_emmc_phy_probe,
    299	.driver		= {
    300		.name	= "keembay-emmc-phy",
    301		.of_match_table = keembay_emmc_phy_dt_ids,
    302	},
    303};
    304module_platform_driver(keembay_emmc_phy_driver);
    305
    306MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>");
    307MODULE_DESCRIPTION("Intel Keem Bay eMMC PHY driver");
    308MODULE_LICENSE("GPL v2");