brintos

brintos / linux-shallow public Read only

0
0
Text · 16.0 KiB · be1505e Raw
618 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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/platform_device.h>15#include <linux/reset.h>16 17#include "sdhci-pltfm.h"18 19/* HRS - Host Register Set (specific to Cadence) */20#define SDHCI_CDNS_HRS04		0x10		/* PHY access port */21#define   SDHCI_CDNS_HRS04_ACK			BIT(26)22#define   SDHCI_CDNS_HRS04_RD			BIT(25)23#define   SDHCI_CDNS_HRS04_WR			BIT(24)24#define   SDHCI_CDNS_HRS04_RDATA		GENMASK(23, 16)25#define   SDHCI_CDNS_HRS04_WDATA		GENMASK(15, 8)26#define   SDHCI_CDNS_HRS04_ADDR			GENMASK(5, 0)27 28#define SDHCI_CDNS_HRS06		0x18		/* eMMC control */29#define   SDHCI_CDNS_HRS06_TUNE_UP		BIT(15)30#define   SDHCI_CDNS_HRS06_TUNE			GENMASK(13, 8)31#define   SDHCI_CDNS_HRS06_MODE			GENMASK(2, 0)32#define   SDHCI_CDNS_HRS06_MODE_SD		0x033#define   SDHCI_CDNS_HRS06_MODE_MMC_SDR		0x234#define   SDHCI_CDNS_HRS06_MODE_MMC_DDR		0x335#define   SDHCI_CDNS_HRS06_MODE_MMC_HS200	0x436#define   SDHCI_CDNS_HRS06_MODE_MMC_HS400	0x537#define   SDHCI_CDNS_HRS06_MODE_MMC_HS400ES	0x638 39/* SRS - Slot Register Set (SDHCI-compatible) */40#define SDHCI_CDNS_SRS_BASE		0x20041 42/* PHY */43#define SDHCI_CDNS_PHY_DLY_SD_HS	0x0044#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT	0x0145#define SDHCI_CDNS_PHY_DLY_UHS_SDR12	0x0246#define SDHCI_CDNS_PHY_DLY_UHS_SDR25	0x0347#define SDHCI_CDNS_PHY_DLY_UHS_SDR50	0x0448#define SDHCI_CDNS_PHY_DLY_UHS_DDR50	0x0549#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY	0x0650#define SDHCI_CDNS_PHY_DLY_EMMC_SDR	0x0751#define SDHCI_CDNS_PHY_DLY_EMMC_DDR	0x0852#define SDHCI_CDNS_PHY_DLY_SDCLK	0x0b53#define SDHCI_CDNS_PHY_DLY_HSMMC	0x0c54#define SDHCI_CDNS_PHY_DLY_STROBE	0x0d55 56/*57 * The tuned val register is 6 bit-wide, but not the whole of the range is58 * available.  The range 0-42 seems to be available (then 43 wraps around to 0)59 * but I am not quite sure if it is official.  Use only 0 to 39 for safety.60 */61#define SDHCI_CDNS_MAX_TUNING_LOOP	4062 63struct sdhci_cdns_phy_param {64	u8 addr;65	u8 data;66};67 68struct sdhci_cdns_priv {69	void __iomem *hrs_addr;70	void __iomem *ctl_addr;	/* write control */71	spinlock_t wrlock;	/* write lock */72	bool enhanced_strobe;73	void (*priv_writel)(struct sdhci_cdns_priv *priv, u32 val, void __iomem *reg);74	struct reset_control *rst_hw;75	unsigned int nr_phy_params;76	struct sdhci_cdns_phy_param phy_params[];77};78 79struct sdhci_cdns_phy_cfg {80	const char *property;81	u8 addr;82};83 84struct sdhci_cdns_drv_data {85	int (*init)(struct platform_device *pdev);86	const struct sdhci_pltfm_data pltfm_data;87};88 89static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {90	{ "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },91	{ "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },92	{ "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, },93	{ "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25, },94	{ "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50, },95	{ "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50, },96	{ "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR, },97	{ "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR, },98	{ "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK, },99	{ "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC, },100	{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },101};102 103static inline void cdns_writel(struct sdhci_cdns_priv *priv, u32 val,104			       void __iomem *reg)105{106	writel(val, reg);107}108 109static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,110				    u8 addr, u8 data)111{112	void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04;113	u32 tmp;114	int ret;115 116	ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),117				 0, 10);118	if (ret)119		return ret;120 121	tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |122	      FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);123	priv->priv_writel(priv, tmp, reg);124 125	tmp |= SDHCI_CDNS_HRS04_WR;126	priv->priv_writel(priv, tmp, reg);127 128	ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10);129	if (ret)130		return ret;131 132	tmp &= ~SDHCI_CDNS_HRS04_WR;133	priv->priv_writel(priv, tmp, reg);134 135	ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),136				 0, 10);137 138	return ret;139}140 141static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)142{143	unsigned int count = 0;144	int i;145 146	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)147		if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))148			count++;149 150	return count;151}152 153static void sdhci_cdns_phy_param_parse(struct device_node *np,154				       struct sdhci_cdns_priv *priv)155{156	struct sdhci_cdns_phy_param *p = priv->phy_params;157	u32 val;158	int ret, i;159 160	for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {161		ret = of_property_read_u32(np, sdhci_cdns_phy_cfgs[i].property,162					   &val);163		if (ret)164			continue;165 166		p->addr = sdhci_cdns_phy_cfgs[i].addr;167		p->data = val;168		p++;169	}170}171 172static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)173{174	int ret, i;175 176	for (i = 0; i < priv->nr_phy_params; i++) {177		ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr,178					       priv->phy_params[i].data);179		if (ret)180			return ret;181	}182 183	return 0;184}185 186static void *sdhci_cdns_priv(struct sdhci_host *host)187{188	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);189 190	return sdhci_pltfm_priv(pltfm_host);191}192 193static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)194{195	/*196	 * Cadence's spec says the Timeout Clock Frequency is the same as the197	 * Base Clock Frequency.198	 */199	return host->max_clk;200}201 202static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)203{204	u32 tmp;205 206	/* The speed mode for eMMC is selected by HRS06 register */207	tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);208	tmp &= ~SDHCI_CDNS_HRS06_MODE;209	tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);210	priv->priv_writel(priv, tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);211}212 213static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)214{215	u32 tmp;216 217	tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);218	return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);219}220 221static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)222{223	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);224	void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;225	u32 tmp;226	int i, ret;227 228	if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))229		return -EINVAL;230 231	tmp = readl(reg);232	tmp &= ~SDHCI_CDNS_HRS06_TUNE;233	tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);234 235	/*236	 * Workaround for IP errata:237	 * The IP6116 SD/eMMC PHY design has a timing issue on receive data238	 * path. Send tune request twice.239	 */240	for (i = 0; i < 2; i++) {241		tmp |= SDHCI_CDNS_HRS06_TUNE_UP;242		priv->priv_writel(priv, tmp, reg);243 244		ret = readl_poll_timeout(reg, tmp,245					 !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),246					 0, 1);247		if (ret)248			return ret;249	}250 251	return 0;252}253 254/*255 * In SD mode, software must not use the hardware tuning and instead perform256 * an almost identical procedure to eMMC.257 */258static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)259{260	int cur_streak = 0;261	int max_streak = 0;262	int end_of_streak = 0;263	int i;264 265	/*266	 * Do not execute tuning for UHS_SDR50 or UHS_DDR50.267	 * The delay is set by probe, based on the DT properties.268	 */269	if (host->timing != MMC_TIMING_MMC_HS200 &&270	    host->timing != MMC_TIMING_UHS_SDR104)271		return 0;272 273	for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {274		if (sdhci_cdns_set_tune_val(host, i) ||275		    mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */276			cur_streak = 0;277		} else { /* good */278			cur_streak++;279			if (cur_streak > max_streak) {280				max_streak = cur_streak;281				end_of_streak = i;282			}283		}284	}285 286	if (!max_streak) {287		dev_err(mmc_dev(host->mmc), "no tuning point found\n");288		return -EIO;289	}290 291	return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);292}293 294static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,295					 unsigned int timing)296{297	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);298	u32 mode;299 300	switch (timing) {301	case MMC_TIMING_MMC_HS:302		mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;303		break;304	case MMC_TIMING_MMC_DDR52:305		mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;306		break;307	case MMC_TIMING_MMC_HS200:308		mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;309		break;310	case MMC_TIMING_MMC_HS400:311		if (priv->enhanced_strobe)312			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES;313		else314			mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;315		break;316	default:317		mode = SDHCI_CDNS_HRS06_MODE_SD;318		break;319	}320 321	sdhci_cdns_set_emmc_mode(priv, mode);322 323	/* For SD, fall back to the default handler */324	if (mode == SDHCI_CDNS_HRS06_MODE_SD)325		sdhci_set_uhs_signaling(host, timing);326}327 328/* Elba control register bits [6:3] are byte-lane enables */329#define ELBA_BYTE_ENABLE_MASK(x)	((x) << 3)330 331/*332 * The Pensando Elba SoC explicitly controls byte-lane enabling on writes333 * which includes writes to the HRS registers.  The write lock (wrlock)334 * is used to ensure byte-lane enable, using write control (ctl_addr),335 * occurs before the data write.336 */337static void elba_priv_writel(struct sdhci_cdns_priv *priv, u32 val,338			     void __iomem *reg)339{340	unsigned long flags;341 342	spin_lock_irqsave(&priv->wrlock, flags);343	writel(GENMASK(7, 3), priv->ctl_addr);344	writel(val, reg);345	spin_unlock_irqrestore(&priv->wrlock, flags);346}347 348static void elba_write_l(struct sdhci_host *host, u32 val, int reg)349{350	elba_priv_writel(sdhci_cdns_priv(host), val, host->ioaddr + reg);351}352 353static void elba_write_w(struct sdhci_host *host, u16 val, int reg)354{355	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);356	u32 shift = reg & GENMASK(1, 0);357	unsigned long flags;358	u32 byte_enables;359 360	byte_enables = GENMASK(1, 0) << shift;361	spin_lock_irqsave(&priv->wrlock, flags);362	writel(ELBA_BYTE_ENABLE_MASK(byte_enables), priv->ctl_addr);363	writew(val, host->ioaddr + reg);364	spin_unlock_irqrestore(&priv->wrlock, flags);365}366 367static void elba_write_b(struct sdhci_host *host, u8 val, int reg)368{369	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);370	u32 shift = reg & GENMASK(1, 0);371	unsigned long flags;372	u32 byte_enables;373 374	byte_enables = BIT(0) << shift;375	spin_lock_irqsave(&priv->wrlock, flags);376	writel(ELBA_BYTE_ENABLE_MASK(byte_enables), priv->ctl_addr);377	writeb(val, host->ioaddr + reg);378	spin_unlock_irqrestore(&priv->wrlock, flags);379}380 381static const struct sdhci_ops sdhci_elba_ops = {382	.write_l = elba_write_l,383	.write_w = elba_write_w,384	.write_b = elba_write_b,385	.set_clock = sdhci_set_clock,386	.get_timeout_clock = sdhci_cdns_get_timeout_clock,387	.set_bus_width = sdhci_set_bus_width,388	.reset = sdhci_reset,389	.set_uhs_signaling = sdhci_cdns_set_uhs_signaling,390};391 392static int elba_drv_init(struct platform_device *pdev)393{394	struct sdhci_host *host = platform_get_drvdata(pdev);395	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);396	void __iomem *ioaddr;397 398	host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA;399	spin_lock_init(&priv->wrlock);400 401	/* Byte-lane control register */402	ioaddr = devm_platform_ioremap_resource(pdev, 1);403	if (IS_ERR(ioaddr))404		return PTR_ERR(ioaddr);405 406	priv->ctl_addr = ioaddr;407	priv->priv_writel = elba_priv_writel;408	writel(ELBA_BYTE_ENABLE_MASK(0xf), priv->ctl_addr);409 410	return 0;411}412 413static const struct sdhci_ops sdhci_cdns_ops = {414	.set_clock = sdhci_set_clock,415	.get_timeout_clock = sdhci_cdns_get_timeout_clock,416	.set_bus_width = sdhci_set_bus_width,417	.reset = sdhci_reset,418	.platform_execute_tuning = sdhci_cdns_execute_tuning,419	.set_uhs_signaling = sdhci_cdns_set_uhs_signaling,420};421 422static const struct sdhci_cdns_drv_data sdhci_cdns_uniphier_drv_data = {423	.pltfm_data = {424		.ops = &sdhci_cdns_ops,425		.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,426	},427};428 429static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = {430	.init = elba_drv_init,431	.pltfm_data = {432		.ops = &sdhci_elba_ops,433	},434};435 436static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = {437	.pltfm_data = {438		.ops = &sdhci_cdns_ops,439	},440};441 442static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,443					     struct mmc_ios *ios)444{445	struct sdhci_host *host = mmc_priv(mmc);446	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);447	u32 mode;448 449	priv->enhanced_strobe = ios->enhanced_strobe;450 451	mode = sdhci_cdns_get_emmc_mode(priv);452 453	if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400 && ios->enhanced_strobe)454		sdhci_cdns_set_emmc_mode(priv,455					 SDHCI_CDNS_HRS06_MODE_MMC_HS400ES);456 457	if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe)458		sdhci_cdns_set_emmc_mode(priv,459					 SDHCI_CDNS_HRS06_MODE_MMC_HS400);460}461 462static void sdhci_cdns_mmc_hw_reset(struct mmc_host *mmc)463{464	struct sdhci_host *host = mmc_priv(mmc);465	struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);466 467	dev_dbg(mmc_dev(host->mmc), "emmc hardware reset\n");468 469	reset_control_assert(priv->rst_hw);470	/* For eMMC, minimum is 1us but give it 3us for good measure */471	udelay(3);472 473	reset_control_deassert(priv->rst_hw);474	/* For eMMC, minimum is 200us but give it 300us for good measure */475	usleep_range(300, 1000);476}477 478static int sdhci_cdns_probe(struct platform_device *pdev)479{480	struct sdhci_host *host;481	const struct sdhci_cdns_drv_data *data;482	struct sdhci_pltfm_host *pltfm_host;483	struct sdhci_cdns_priv *priv;484	struct clk *clk;485	unsigned int nr_phy_params;486	int ret;487	struct device *dev = &pdev->dev;488	static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;489 490	clk = devm_clk_get_enabled(dev, NULL);491	if (IS_ERR(clk))492		return PTR_ERR(clk);493 494	data = of_device_get_match_data(dev);495	if (!data)496		data = &sdhci_cdns_drv_data;497 498	nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);499	host = sdhci_pltfm_init(pdev, &data->pltfm_data,500				struct_size(priv, phy_params, nr_phy_params));501	if (IS_ERR(host))502		return PTR_ERR(host);503 504	pltfm_host = sdhci_priv(host);505	pltfm_host->clk = clk;506 507	priv = sdhci_pltfm_priv(pltfm_host);508	priv->nr_phy_params = nr_phy_params;509	priv->hrs_addr = host->ioaddr;510	priv->enhanced_strobe = false;511	priv->priv_writel = cdns_writel;512	host->ioaddr += SDHCI_CDNS_SRS_BASE;513	host->mmc_host_ops.hs400_enhanced_strobe =514				sdhci_cdns_hs400_enhanced_strobe;515	if (data->init) {516		ret = data->init(pdev);517		if (ret)518			goto free;519	}520	sdhci_enable_v4_mode(host);521	__sdhci_read_caps(host, &version, NULL, NULL);522 523	sdhci_get_of_property(pdev);524 525	ret = mmc_of_parse(host->mmc);526	if (ret)527		goto free;528 529	sdhci_cdns_phy_param_parse(dev->of_node, priv);530 531	ret = sdhci_cdns_phy_init(priv);532	if (ret)533		goto free;534 535	if (host->mmc->caps & MMC_CAP_HW_RESET) {536		priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL);537		if (IS_ERR(priv->rst_hw)) {538			ret = dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw),539					    "reset controller error\n");540			goto free;541		}542		if (priv->rst_hw)543			host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset;544	}545 546	ret = sdhci_add_host(host);547	if (ret)548		goto free;549 550	return 0;551free:552	sdhci_pltfm_free(pdev);553	return ret;554}555 556#ifdef CONFIG_PM_SLEEP557static int sdhci_cdns_resume(struct device *dev)558{559	struct sdhci_host *host = dev_get_drvdata(dev);560	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);561	struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);562	int ret;563 564	ret = clk_prepare_enable(pltfm_host->clk);565	if (ret)566		return ret;567 568	ret = sdhci_cdns_phy_init(priv);569	if (ret)570		goto disable_clk;571 572	ret = sdhci_resume_host(host);573	if (ret)574		goto disable_clk;575 576	return 0;577 578disable_clk:579	clk_disable_unprepare(pltfm_host->clk);580 581	return ret;582}583#endif584 585static const struct dev_pm_ops sdhci_cdns_pm_ops = {586	SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)587};588 589static const struct of_device_id sdhci_cdns_match[] = {590	{591		.compatible = "socionext,uniphier-sd4hc",592		.data = &sdhci_cdns_uniphier_drv_data,593	},594	{595		.compatible = "amd,pensando-elba-sd4hc",596		.data = &sdhci_elba_drv_data,597	},598	{ .compatible = "cdns,sd4hc" },599	{ /* sentinel */ }600};601MODULE_DEVICE_TABLE(of, sdhci_cdns_match);602 603static struct platform_driver sdhci_cdns_driver = {604	.driver = {605		.name = "sdhci-cdns",606		.probe_type = PROBE_PREFER_ASYNCHRONOUS,607		.pm = &sdhci_cdns_pm_ops,608		.of_match_table = sdhci_cdns_match,609	},610	.probe = sdhci_cdns_probe,611	.remove_new = sdhci_pltfm_remove,612};613module_platform_driver(sdhci_cdns_driver);614 615MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");616MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver");617MODULE_LICENSE("GPL");618