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

sdhci-cadence.c (12267B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2016 Socionext Inc.
      4 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
      5 */
      6
      7#include <linux/bitfield.h>
      8#include <linux/bits.h>
      9#include <linux/iopoll.h>
     10#include <linux/module.h>
     11#include <linux/mmc/host.h>
     12#include <linux/mmc/mmc.h>
     13#include <linux/of.h>
     14#include <linux/of_device.h>
     15
     16#include "sdhci-pltfm.h"
     17
     18/* HRS - Host Register Set (specific to Cadence) */
     19#define SDHCI_CDNS_HRS04		0x10		/* PHY access port */
     20#define   SDHCI_CDNS_HRS04_ACK			BIT(26)
     21#define   SDHCI_CDNS_HRS04_RD			BIT(25)
     22#define   SDHCI_CDNS_HRS04_WR			BIT(24)
     23#define   SDHCI_CDNS_HRS04_RDATA		GENMASK(23, 16)
     24#define   SDHCI_CDNS_HRS04_WDATA		GENMASK(15, 8)
     25#define   SDHCI_CDNS_HRS04_ADDR			GENMASK(5, 0)
     26
     27#define SDHCI_CDNS_HRS06		0x18		/* eMMC control */
     28#define   SDHCI_CDNS_HRS06_TUNE_UP		BIT(15)
     29#define   SDHCI_CDNS_HRS06_TUNE			GENMASK(13, 8)
     30#define   SDHCI_CDNS_HRS06_MODE			GENMASK(2, 0)
     31#define   SDHCI_CDNS_HRS06_MODE_SD		0x0
     32#define   SDHCI_CDNS_HRS06_MODE_MMC_SDR		0x2
     33#define   SDHCI_CDNS_HRS06_MODE_MMC_DDR		0x3
     34#define   SDHCI_CDNS_HRS06_MODE_MMC_HS200	0x4
     35#define   SDHCI_CDNS_HRS06_MODE_MMC_HS400	0x5
     36#define   SDHCI_CDNS_HRS06_MODE_MMC_HS400ES	0x6
     37
     38/* SRS - Slot Register Set (SDHCI-compatible) */
     39#define SDHCI_CDNS_SRS_BASE		0x200
     40
     41/* PHY */
     42#define SDHCI_CDNS_PHY_DLY_SD_HS	0x00
     43#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT	0x01
     44#define SDHCI_CDNS_PHY_DLY_UHS_SDR12	0x02
     45#define SDHCI_CDNS_PHY_DLY_UHS_SDR25	0x03
     46#define SDHCI_CDNS_PHY_DLY_UHS_SDR50	0x04
     47#define SDHCI_CDNS_PHY_DLY_UHS_DDR50	0x05
     48#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY	0x06
     49#define SDHCI_CDNS_PHY_DLY_EMMC_SDR	0x07
     50#define SDHCI_CDNS_PHY_DLY_EMMC_DDR	0x08
     51#define SDHCI_CDNS_PHY_DLY_SDCLK	0x0b
     52#define SDHCI_CDNS_PHY_DLY_HSMMC	0x0c
     53#define SDHCI_CDNS_PHY_DLY_STROBE	0x0d
     54
     55/*
     56 * The tuned val register is 6 bit-wide, but not the whole of the range is
     57 * available.  The range 0-42 seems to be available (then 43 wraps around to 0)
     58 * but I am not quite sure if it is official.  Use only 0 to 39 for safety.
     59 */
     60#define SDHCI_CDNS_MAX_TUNING_LOOP	40
     61
     62struct sdhci_cdns_phy_param {
     63	u8 addr;
     64	u8 data;
     65};
     66
     67struct sdhci_cdns_priv {
     68	void __iomem *hrs_addr;
     69	bool enhanced_strobe;
     70	unsigned int nr_phy_params;
     71	struct sdhci_cdns_phy_param phy_params[];
     72};
     73
     74struct sdhci_cdns_phy_cfg {
     75	const char *property;
     76	u8 addr;
     77};
     78
     79static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
     80	{ "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
     81	{ "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
     82	{ "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, },
     83	{ "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25, },
     84	{ "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50, },
     85	{ "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50, },
     86	{ "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR, },
     87	{ "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR, },
     88	{ "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK, },
     89	{ "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC, },
     90	{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
     91};
     92
     93static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
     94				    u8 addr, u8 data)
     95{
     96	void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04;
     97	u32 tmp;
     98	int ret;
     99
    100	ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
    101				 0, 10);
    102	if (ret)
    103		return ret;
    104
    105	tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
    106	      FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
    107	writel(tmp, reg);
    108
    109	tmp |= SDHCI_CDNS_HRS04_WR;
    110	writel(tmp, reg);
    111
    112	ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10);
    113	if (ret)
    114		return ret;
    115
    116	tmp &= ~SDHCI_CDNS_HRS04_WR;
    117	writel(tmp, reg);
    118
    119	ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
    120				 0, 10);
    121
    122	return ret;
    123}
    124
    125static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
    126{
    127	unsigned int count = 0;
    128	int i;
    129
    130	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)
    131		if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))
    132			count++;
    133
    134	return count;
    135}
    136
    137static void sdhci_cdns_phy_param_parse(struct device_node *np,
    138				       struct sdhci_cdns_priv *priv)
    139{
    140	struct sdhci_cdns_phy_param *p = priv->phy_params;
    141	u32 val;
    142	int ret, i;
    143
    144	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
    145		ret = of_property_read_u32(np, sdhci_cdns_phy_cfgs[i].property,
    146					   &val);
    147		if (ret)
    148			continue;
    149
    150		p->addr = sdhci_cdns_phy_cfgs[i].addr;
    151		p->data = val;
    152		p++;
    153	}
    154}
    155
    156static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
    157{
    158	int ret, i;
    159
    160	for (i = 0; i < priv->nr_phy_params; i++) {
    161		ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr,
    162					       priv->phy_params[i].data);
    163		if (ret)
    164			return ret;
    165	}
    166
    167	return 0;
    168}
    169
    170static void *sdhci_cdns_priv(struct sdhci_host *host)
    171{
    172	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    173
    174	return sdhci_pltfm_priv(pltfm_host);
    175}
    176
    177static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
    178{
    179	/*
    180	 * Cadence's spec says the Timeout Clock Frequency is the same as the
    181	 * Base Clock Frequency.
    182	 */
    183	return host->max_clk;
    184}
    185
    186static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
    187{
    188	u32 tmp;
    189
    190	/* The speed mode for eMMC is selected by HRS06 register */
    191	tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
    192	tmp &= ~SDHCI_CDNS_HRS06_MODE;
    193	tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
    194	writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
    195}
    196
    197static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
    198{
    199	u32 tmp;
    200
    201	tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
    202	return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
    203}
    204
    205static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
    206{
    207	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
    208	void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
    209	u32 tmp;
    210	int i, ret;
    211
    212	if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
    213		return -EINVAL;
    214
    215	tmp = readl(reg);
    216	tmp &= ~SDHCI_CDNS_HRS06_TUNE;
    217	tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
    218
    219	/*
    220	 * Workaround for IP errata:
    221	 * The IP6116 SD/eMMC PHY design has a timing issue on receive data
    222	 * path. Send tune request twice.
    223	 */
    224	for (i = 0; i < 2; i++) {
    225		tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
    226		writel(tmp, reg);
    227
    228		ret = readl_poll_timeout(reg, tmp,
    229					 !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
    230					 0, 1);
    231		if (ret)
    232			return ret;
    233	}
    234
    235	return 0;
    236}
    237
    238/*
    239 * In SD mode, software must not use the hardware tuning and instead perform
    240 * an almost identical procedure to eMMC.
    241 */
    242static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
    243{
    244	int cur_streak = 0;
    245	int max_streak = 0;
    246	int end_of_streak = 0;
    247	int i;
    248
    249	/*
    250	 * Do not execute tuning for UHS_SDR50 or UHS_DDR50.
    251	 * The delay is set by probe, based on the DT properties.
    252	 */
    253	if (host->timing != MMC_TIMING_MMC_HS200 &&
    254	    host->timing != MMC_TIMING_UHS_SDR104)
    255		return 0;
    256
    257	for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {
    258		if (sdhci_cdns_set_tune_val(host, i) ||
    259		    mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */
    260			cur_streak = 0;
    261		} else { /* good */
    262			cur_streak++;
    263			if (cur_streak > max_streak) {
    264				max_streak = cur_streak;
    265				end_of_streak = i;
    266			}
    267		}
    268	}
    269
    270	if (!max_streak) {
    271		dev_err(mmc_dev(host->mmc), "no tuning point found\n");
    272		return -EIO;
    273	}
    274
    275	return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
    276}
    277
    278static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
    279					 unsigned int timing)
    280{
    281	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
    282	u32 mode;
    283
    284	switch (timing) {
    285	case MMC_TIMING_MMC_HS:
    286		mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
    287		break;
    288	case MMC_TIMING_MMC_DDR52:
    289		mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
    290		break;
    291	case MMC_TIMING_MMC_HS200:
    292		mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
    293		break;
    294	case MMC_TIMING_MMC_HS400:
    295		if (priv->enhanced_strobe)
    296			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES;
    297		else
    298			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
    299		break;
    300	default:
    301		mode = SDHCI_CDNS_HRS06_MODE_SD;
    302		break;
    303	}
    304
    305	sdhci_cdns_set_emmc_mode(priv, mode);
    306
    307	/* For SD, fall back to the default handler */
    308	if (mode == SDHCI_CDNS_HRS06_MODE_SD)
    309		sdhci_set_uhs_signaling(host, timing);
    310}
    311
    312static const struct sdhci_ops sdhci_cdns_ops = {
    313	.set_clock = sdhci_set_clock,
    314	.get_timeout_clock = sdhci_cdns_get_timeout_clock,
    315	.set_bus_width = sdhci_set_bus_width,
    316	.reset = sdhci_reset,
    317	.platform_execute_tuning = sdhci_cdns_execute_tuning,
    318	.set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
    319};
    320
    321static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
    322	.ops = &sdhci_cdns_ops,
    323	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
    324};
    325
    326static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
    327	.ops = &sdhci_cdns_ops,
    328};
    329
    330static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
    331					     struct mmc_ios *ios)
    332{
    333	struct sdhci_host *host = mmc_priv(mmc);
    334	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
    335	u32 mode;
    336
    337	priv->enhanced_strobe = ios->enhanced_strobe;
    338
    339	mode = sdhci_cdns_get_emmc_mode(priv);
    340
    341	if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400 && ios->enhanced_strobe)
    342		sdhci_cdns_set_emmc_mode(priv,
    343					 SDHCI_CDNS_HRS06_MODE_MMC_HS400ES);
    344
    345	if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe)
    346		sdhci_cdns_set_emmc_mode(priv,
    347					 SDHCI_CDNS_HRS06_MODE_MMC_HS400);
    348}
    349
    350static int sdhci_cdns_probe(struct platform_device *pdev)
    351{
    352	struct sdhci_host *host;
    353	const struct sdhci_pltfm_data *data;
    354	struct sdhci_pltfm_host *pltfm_host;
    355	struct sdhci_cdns_priv *priv;
    356	struct clk *clk;
    357	unsigned int nr_phy_params;
    358	int ret;
    359	struct device *dev = &pdev->dev;
    360	static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
    361
    362	clk = devm_clk_get(dev, NULL);
    363	if (IS_ERR(clk))
    364		return PTR_ERR(clk);
    365
    366	ret = clk_prepare_enable(clk);
    367	if (ret)
    368		return ret;
    369
    370	data = of_device_get_match_data(dev);
    371	if (!data)
    372		data = &sdhci_cdns_pltfm_data;
    373
    374	nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);
    375	host = sdhci_pltfm_init(pdev, data,
    376				struct_size(priv, phy_params, nr_phy_params));
    377	if (IS_ERR(host)) {
    378		ret = PTR_ERR(host);
    379		goto disable_clk;
    380	}
    381
    382	pltfm_host = sdhci_priv(host);
    383	pltfm_host->clk = clk;
    384
    385	priv = sdhci_pltfm_priv(pltfm_host);
    386	priv->nr_phy_params = nr_phy_params;
    387	priv->hrs_addr = host->ioaddr;
    388	priv->enhanced_strobe = false;
    389	host->ioaddr += SDHCI_CDNS_SRS_BASE;
    390	host->mmc_host_ops.hs400_enhanced_strobe =
    391				sdhci_cdns_hs400_enhanced_strobe;
    392	sdhci_enable_v4_mode(host);
    393	__sdhci_read_caps(host, &version, NULL, NULL);
    394
    395	sdhci_get_of_property(pdev);
    396
    397	ret = mmc_of_parse(host->mmc);
    398	if (ret)
    399		goto free;
    400
    401	sdhci_cdns_phy_param_parse(dev->of_node, priv);
    402
    403	ret = sdhci_cdns_phy_init(priv);
    404	if (ret)
    405		goto free;
    406
    407	ret = sdhci_add_host(host);
    408	if (ret)
    409		goto free;
    410
    411	return 0;
    412free:
    413	sdhci_pltfm_free(pdev);
    414disable_clk:
    415	clk_disable_unprepare(clk);
    416
    417	return ret;
    418}
    419
    420#ifdef CONFIG_PM_SLEEP
    421static int sdhci_cdns_resume(struct device *dev)
    422{
    423	struct sdhci_host *host = dev_get_drvdata(dev);
    424	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    425	struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);
    426	int ret;
    427
    428	ret = clk_prepare_enable(pltfm_host->clk);
    429	if (ret)
    430		return ret;
    431
    432	ret = sdhci_cdns_phy_init(priv);
    433	if (ret)
    434		goto disable_clk;
    435
    436	ret = sdhci_resume_host(host);
    437	if (ret)
    438		goto disable_clk;
    439
    440	return 0;
    441
    442disable_clk:
    443	clk_disable_unprepare(pltfm_host->clk);
    444
    445	return ret;
    446}
    447#endif
    448
    449static const struct dev_pm_ops sdhci_cdns_pm_ops = {
    450	SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)
    451};
    452
    453static const struct of_device_id sdhci_cdns_match[] = {
    454	{
    455		.compatible = "socionext,uniphier-sd4hc",
    456		.data = &sdhci_cdns_uniphier_pltfm_data,
    457	},
    458	{ .compatible = "cdns,sd4hc" },
    459	{ /* sentinel */ }
    460};
    461MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
    462
    463static struct platform_driver sdhci_cdns_driver = {
    464	.driver = {
    465		.name = "sdhci-cdns",
    466		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
    467		.pm = &sdhci_cdns_pm_ops,
    468		.of_match_table = sdhci_cdns_match,
    469	},
    470	.probe = sdhci_cdns_probe,
    471	.remove = sdhci_pltfm_unregister,
    472};
    473module_platform_driver(sdhci_cdns_driver);
    474
    475MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
    476MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver");
    477MODULE_LICENSE("GPL");