143611afbSLars Povlsen // SPDX-License-Identifier: GPL-2.0-or-later
243611afbSLars Povlsen /*
343611afbSLars Povlsen  * drivers/mmc/host/sdhci-of-sparx5.c
443611afbSLars Povlsen  *
543611afbSLars Povlsen  * MCHP Sparx5 SoC Secure Digital Host Controller Interface.
643611afbSLars Povlsen  *
743611afbSLars Povlsen  * Copyright (c) 2019 Microchip Inc.
843611afbSLars Povlsen  *
943611afbSLars Povlsen  * Author: Lars Povlsen <lars.povlsen@microchip.com>
1043611afbSLars Povlsen  */
1143611afbSLars Povlsen 
1243611afbSLars Povlsen #include <linux/sizes.h>
1343611afbSLars Povlsen #include <linux/delay.h>
1443611afbSLars Povlsen #include <linux/module.h>
1543611afbSLars Povlsen #include <linux/regmap.h>
1643611afbSLars Povlsen #include <linux/mfd/syscon.h>
1743611afbSLars Povlsen #include <linux/dma-mapping.h>
18c62da8a8SRob Herring #include <linux/of.h>
1943611afbSLars Povlsen 
2043611afbSLars Povlsen #include "sdhci-pltfm.h"
2143611afbSLars Povlsen 
2243611afbSLars Povlsen #define CPU_REGS_GENERAL_CTRL	(0x22 * 4)
2343611afbSLars Povlsen #define  MSHC_DLY_CC_MASK	GENMASK(16, 13)
2443611afbSLars Povlsen #define  MSHC_DLY_CC_SHIFT	13
2543611afbSLars Povlsen #define  MSHC_DLY_CC_MAX	15
2643611afbSLars Povlsen 
2743611afbSLars Povlsen #define CPU_REGS_PROC_CTRL	(0x2C * 4)
2843611afbSLars Povlsen #define  ACP_CACHE_FORCE_ENA	BIT(4)
2943611afbSLars Povlsen #define  ACP_AWCACHE		BIT(3)
3043611afbSLars Povlsen #define  ACP_ARCACHE		BIT(2)
3143611afbSLars Povlsen #define  ACP_CACHE_MASK		(ACP_CACHE_FORCE_ENA|ACP_AWCACHE|ACP_ARCACHE)
3243611afbSLars Povlsen 
3343611afbSLars Povlsen #define MSHC2_VERSION			0x500	/* Off 0x140, reg 0x0 */
3443611afbSLars Povlsen #define MSHC2_TYPE			0x504	/* Off 0x140, reg 0x1 */
3543611afbSLars Povlsen #define MSHC2_EMMC_CTRL			0x52c	/* Off 0x140, reg 0xB */
3643611afbSLars Povlsen #define  MSHC2_EMMC_CTRL_EMMC_RST_N	BIT(2)
3743611afbSLars Povlsen #define  MSHC2_EMMC_CTRL_IS_EMMC	BIT(0)
3843611afbSLars Povlsen 
3943611afbSLars Povlsen struct sdhci_sparx5_data {
4043611afbSLars Povlsen 	struct sdhci_host *host;
4143611afbSLars Povlsen 	struct regmap *cpu_ctrl;
4243611afbSLars Povlsen 	int delay_clock;
4343611afbSLars Povlsen };
4443611afbSLars Povlsen 
4543611afbSLars Povlsen #define BOUNDARY_OK(addr, len) \
4643611afbSLars Povlsen 	((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
4743611afbSLars Povlsen 
4843611afbSLars Povlsen /*
4943611afbSLars Povlsen  * If DMA addr spans 128MB boundary, we split the DMA transfer into two
5043611afbSLars Povlsen  * so that each DMA transfer doesn't exceed the boundary.
5143611afbSLars Povlsen  */
sdhci_sparx5_adma_write_desc(struct sdhci_host * host,void ** desc,dma_addr_t addr,int len,unsigned int cmd)5243611afbSLars Povlsen static void sdhci_sparx5_adma_write_desc(struct sdhci_host *host, void **desc,
5343611afbSLars Povlsen 					  dma_addr_t addr, int len,
5443611afbSLars Povlsen 					  unsigned int cmd)
5543611afbSLars Povlsen {
5643611afbSLars Povlsen 	int tmplen, offset;
5743611afbSLars Povlsen 
5843611afbSLars Povlsen 	if (likely(!len || BOUNDARY_OK(addr, len))) {
5943611afbSLars Povlsen 		sdhci_adma_write_desc(host, desc, addr, len, cmd);
6043611afbSLars Povlsen 		return;
6143611afbSLars Povlsen 	}
6243611afbSLars Povlsen 
63b5f9a2c6SKrzysztof Kozlowski 	pr_debug("%s: write_desc: splitting dma len %d, offset %pad\n",
64b5f9a2c6SKrzysztof Kozlowski 		 mmc_hostname(host->mmc), len, &addr);
6543611afbSLars Povlsen 
6643611afbSLars Povlsen 	offset = addr & (SZ_128M - 1);
6743611afbSLars Povlsen 	tmplen = SZ_128M - offset;
6843611afbSLars Povlsen 	sdhci_adma_write_desc(host, desc, addr, tmplen, cmd);
6943611afbSLars Povlsen 
7043611afbSLars Povlsen 	addr += tmplen;
7143611afbSLars Povlsen 	len -= tmplen;
7243611afbSLars Povlsen 	sdhci_adma_write_desc(host, desc, addr, len, cmd);
7343611afbSLars Povlsen }
7443611afbSLars Povlsen 
sparx5_set_cacheable(struct sdhci_host * host,u32 value)7543611afbSLars Povlsen static void sparx5_set_cacheable(struct sdhci_host *host, u32 value)
7643611afbSLars Povlsen {
7743611afbSLars Povlsen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7843611afbSLars Povlsen 	struct sdhci_sparx5_data *sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host);
7943611afbSLars Povlsen 
8043611afbSLars Povlsen 	pr_debug("%s: Set Cacheable = 0x%x\n", mmc_hostname(host->mmc), value);
8143611afbSLars Povlsen 
8243611afbSLars Povlsen 	/* Update ACP caching attributes in HW */
8343611afbSLars Povlsen 	regmap_update_bits(sdhci_sparx5->cpu_ctrl,
8443611afbSLars Povlsen 			   CPU_REGS_PROC_CTRL, ACP_CACHE_MASK, value);
8543611afbSLars Povlsen }
8643611afbSLars Povlsen 
sparx5_set_delay(struct sdhci_host * host,u8 value)8743611afbSLars Povlsen static void sparx5_set_delay(struct sdhci_host *host, u8 value)
8843611afbSLars Povlsen {
8943611afbSLars Povlsen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
9043611afbSLars Povlsen 	struct sdhci_sparx5_data *sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host);
9143611afbSLars Povlsen 
9243611afbSLars Povlsen 	pr_debug("%s: Set DLY_CC = %u\n", mmc_hostname(host->mmc), value);
9343611afbSLars Povlsen 
9443611afbSLars Povlsen 	/* Update DLY_CC in HW */
9543611afbSLars Povlsen 	regmap_update_bits(sdhci_sparx5->cpu_ctrl,
9643611afbSLars Povlsen 			   CPU_REGS_GENERAL_CTRL,
9743611afbSLars Povlsen 			   MSHC_DLY_CC_MASK,
9843611afbSLars Povlsen 			   (value << MSHC_DLY_CC_SHIFT));
9943611afbSLars Povlsen }
10043611afbSLars Povlsen 
sdhci_sparx5_set_emmc(struct sdhci_host * host)10143611afbSLars Povlsen static void sdhci_sparx5_set_emmc(struct sdhci_host *host)
10243611afbSLars Povlsen {
10343611afbSLars Povlsen 	if (!mmc_card_is_removable(host->mmc)) {
10443611afbSLars Povlsen 		u8 value;
10543611afbSLars Povlsen 
10643611afbSLars Povlsen 		value = sdhci_readb(host, MSHC2_EMMC_CTRL);
10743611afbSLars Povlsen 		if (!(value & MSHC2_EMMC_CTRL_IS_EMMC)) {
10843611afbSLars Povlsen 			value |= MSHC2_EMMC_CTRL_IS_EMMC;
10943611afbSLars Povlsen 			pr_debug("%s: Set EMMC_CTRL: 0x%08x\n",
11043611afbSLars Povlsen 				 mmc_hostname(host->mmc), value);
11143611afbSLars Povlsen 			sdhci_writeb(host, value, MSHC2_EMMC_CTRL);
11243611afbSLars Povlsen 		}
11343611afbSLars Povlsen 	}
11443611afbSLars Povlsen }
11543611afbSLars Povlsen 
sdhci_sparx5_reset_emmc(struct sdhci_host * host)11643611afbSLars Povlsen static void sdhci_sparx5_reset_emmc(struct sdhci_host *host)
11743611afbSLars Povlsen {
11843611afbSLars Povlsen 	u8 value;
11943611afbSLars Povlsen 
12043611afbSLars Povlsen 	pr_debug("%s: Toggle EMMC_CTRL.EMMC_RST_N\n", mmc_hostname(host->mmc));
12143611afbSLars Povlsen 	value = sdhci_readb(host, MSHC2_EMMC_CTRL) &
12243611afbSLars Povlsen 		~MSHC2_EMMC_CTRL_EMMC_RST_N;
12343611afbSLars Povlsen 	sdhci_writeb(host, value, MSHC2_EMMC_CTRL);
12443611afbSLars Povlsen 	/* For eMMC, minimum is 1us but give it 10us for good measure */
12543611afbSLars Povlsen 	usleep_range(10, 20);
12643611afbSLars Povlsen 	sdhci_writeb(host, value | MSHC2_EMMC_CTRL_EMMC_RST_N,
12743611afbSLars Povlsen 		     MSHC2_EMMC_CTRL);
12843611afbSLars Povlsen 	/* For eMMC, minimum is 200us but give it 300us for good measure */
12943611afbSLars Povlsen 	usleep_range(300, 400);
13043611afbSLars Povlsen }
13143611afbSLars Povlsen 
sdhci_sparx5_reset(struct sdhci_host * host,u8 mask)13243611afbSLars Povlsen static void sdhci_sparx5_reset(struct sdhci_host *host, u8 mask)
13343611afbSLars Povlsen {
13443611afbSLars Povlsen 	pr_debug("%s: *** RESET: mask %d\n", mmc_hostname(host->mmc), mask);
13543611afbSLars Povlsen 
13643611afbSLars Povlsen 	sdhci_reset(host, mask);
13743611afbSLars Povlsen 
13843611afbSLars Povlsen 	/* Be sure CARD_IS_EMMC stays set */
13943611afbSLars Povlsen 	sdhci_sparx5_set_emmc(host);
14043611afbSLars Povlsen }
14143611afbSLars Povlsen 
14243611afbSLars Povlsen static const struct sdhci_ops sdhci_sparx5_ops = {
14343611afbSLars Povlsen 	.set_clock		= sdhci_set_clock,
14443611afbSLars Povlsen 	.set_bus_width		= sdhci_set_bus_width,
14543611afbSLars Povlsen 	.set_uhs_signaling	= sdhci_set_uhs_signaling,
14643611afbSLars Povlsen 	.get_max_clock		= sdhci_pltfm_clk_get_max_clock,
14743611afbSLars Povlsen 	.reset			= sdhci_sparx5_reset,
14843611afbSLars Povlsen 	.adma_write_desc	= sdhci_sparx5_adma_write_desc,
14943611afbSLars Povlsen };
15043611afbSLars Povlsen 
15143611afbSLars Povlsen static const struct sdhci_pltfm_data sdhci_sparx5_pdata = {
15243611afbSLars Povlsen 	.quirks  = 0,
15343611afbSLars Povlsen 	.quirks2 = SDHCI_QUIRK2_HOST_NO_CMD23 | /* Controller issue */
15443611afbSLars Povlsen 		   SDHCI_QUIRK2_NO_1_8_V, /* No sdr104, ddr50, etc */
15543611afbSLars Povlsen 	.ops = &sdhci_sparx5_ops,
15643611afbSLars Povlsen };
15743611afbSLars Povlsen 
sdhci_sparx5_probe(struct platform_device * pdev)15843611afbSLars Povlsen static int sdhci_sparx5_probe(struct platform_device *pdev)
15943611afbSLars Povlsen {
16043611afbSLars Povlsen 	int ret;
16143611afbSLars Povlsen 	const char *syscon = "microchip,sparx5-cpu-syscon";
16243611afbSLars Povlsen 	struct sdhci_host *host;
16343611afbSLars Povlsen 	struct sdhci_pltfm_host *pltfm_host;
16443611afbSLars Povlsen 	struct sdhci_sparx5_data *sdhci_sparx5;
16543611afbSLars Povlsen 	struct device_node *np = pdev->dev.of_node;
16643611afbSLars Povlsen 	u32 value;
16743611afbSLars Povlsen 	u32 extra;
16843611afbSLars Povlsen 
16943611afbSLars Povlsen 	host = sdhci_pltfm_init(pdev, &sdhci_sparx5_pdata,
17043611afbSLars Povlsen 				sizeof(*sdhci_sparx5));
17143611afbSLars Povlsen 
17243611afbSLars Povlsen 	if (IS_ERR(host))
17343611afbSLars Povlsen 		return PTR_ERR(host);
17443611afbSLars Povlsen 
17543611afbSLars Povlsen 	/*
17643611afbSLars Povlsen 	 * extra adma table cnt for cross 128M boundary handling.
17743611afbSLars Povlsen 	 */
17843611afbSLars Povlsen 	extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M);
17943611afbSLars Povlsen 	if (extra > SDHCI_MAX_SEGS)
18043611afbSLars Povlsen 		extra = SDHCI_MAX_SEGS;
18143611afbSLars Povlsen 	host->adma_table_cnt += extra;
18243611afbSLars Povlsen 
18343611afbSLars Povlsen 	pltfm_host = sdhci_priv(host);
18443611afbSLars Povlsen 	sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host);
18543611afbSLars Povlsen 	sdhci_sparx5->host = host;
18643611afbSLars Povlsen 
187*18ba91acSAdrian Hunter 	pltfm_host->clk = devm_clk_get_enabled(&pdev->dev, "core");
18843611afbSLars Povlsen 	if (IS_ERR(pltfm_host->clk)) {
18943611afbSLars Povlsen 		ret = PTR_ERR(pltfm_host->clk);
190*18ba91acSAdrian Hunter 		dev_err(&pdev->dev, "failed to get and enable core clk: %d\n", ret);
19143611afbSLars Povlsen 		goto free_pltfm;
19243611afbSLars Povlsen 	}
19343611afbSLars Povlsen 
19443611afbSLars Povlsen 	if (!of_property_read_u32(np, "microchip,clock-delay", &value) &&
19543611afbSLars Povlsen 	    (value > 0 && value <= MSHC_DLY_CC_MAX))
19643611afbSLars Povlsen 		sdhci_sparx5->delay_clock = value;
19743611afbSLars Povlsen 
19843611afbSLars Povlsen 	sdhci_get_of_property(pdev);
19943611afbSLars Povlsen 
20043611afbSLars Povlsen 	ret = mmc_of_parse(host->mmc);
20143611afbSLars Povlsen 	if (ret)
202*18ba91acSAdrian Hunter 		goto free_pltfm;
20343611afbSLars Povlsen 
20443611afbSLars Povlsen 	sdhci_sparx5->cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon);
20543611afbSLars Povlsen 	if (IS_ERR(sdhci_sparx5->cpu_ctrl)) {
20643611afbSLars Povlsen 		dev_err(&pdev->dev, "No CPU syscon regmap !\n");
20743611afbSLars Povlsen 		ret = PTR_ERR(sdhci_sparx5->cpu_ctrl);
208*18ba91acSAdrian Hunter 		goto free_pltfm;
20943611afbSLars Povlsen 	}
21043611afbSLars Povlsen 
21143611afbSLars Povlsen 	if (sdhci_sparx5->delay_clock >= 0)
21243611afbSLars Povlsen 		sparx5_set_delay(host, sdhci_sparx5->delay_clock);
21343611afbSLars Povlsen 
21443611afbSLars Povlsen 	if (!mmc_card_is_removable(host->mmc)) {
21543611afbSLars Povlsen 		/* Do a HW reset of eMMC card */
21643611afbSLars Povlsen 		sdhci_sparx5_reset_emmc(host);
21743611afbSLars Povlsen 		/* Update EMMC_CTRL */
21843611afbSLars Povlsen 		sdhci_sparx5_set_emmc(host);
21943611afbSLars Povlsen 		/* If eMMC, disable SD and SDIO */
22043611afbSLars Povlsen 		host->mmc->caps2 |= (MMC_CAP2_NO_SDIO|MMC_CAP2_NO_SD);
22143611afbSLars Povlsen 	}
22243611afbSLars Povlsen 
22343611afbSLars Povlsen 	ret = sdhci_add_host(host);
22443611afbSLars Povlsen 	if (ret)
225*18ba91acSAdrian Hunter 		goto free_pltfm;
22643611afbSLars Povlsen 
22743611afbSLars Povlsen 	/* Set AXI bus master to use un-cached access (for DMA) */
22843611afbSLars Povlsen 	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA) &&
22943611afbSLars Povlsen 	    IS_ENABLED(CONFIG_DMA_DECLARE_COHERENT))
23043611afbSLars Povlsen 		sparx5_set_cacheable(host, ACP_CACHE_FORCE_ENA);
23143611afbSLars Povlsen 
23243611afbSLars Povlsen 	pr_debug("%s: SDHC version: 0x%08x\n",
23343611afbSLars Povlsen 		 mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_VERSION));
23443611afbSLars Povlsen 	pr_debug("%s: SDHC type:    0x%08x\n",
23543611afbSLars Povlsen 		 mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_TYPE));
23643611afbSLars Povlsen 
23743611afbSLars Povlsen 	return ret;
23843611afbSLars Povlsen 
23943611afbSLars Povlsen free_pltfm:
24043611afbSLars Povlsen 	sdhci_pltfm_free(pdev);
24143611afbSLars Povlsen 	return ret;
24243611afbSLars Povlsen }
24343611afbSLars Povlsen 
24443611afbSLars Povlsen static const struct of_device_id sdhci_sparx5_of_match[] = {
24543611afbSLars Povlsen 	{ .compatible = "microchip,dw-sparx5-sdhci" },
24643611afbSLars Povlsen 	{ }
24743611afbSLars Povlsen };
24843611afbSLars Povlsen MODULE_DEVICE_TABLE(of, sdhci_sparx5_of_match);
24943611afbSLars Povlsen 
25043611afbSLars Povlsen static struct platform_driver sdhci_sparx5_driver = {
25143611afbSLars Povlsen 	.driver = {
25243611afbSLars Povlsen 		.name = "sdhci-sparx5",
25331ae4035SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
25443611afbSLars Povlsen 		.of_match_table = sdhci_sparx5_of_match,
25543611afbSLars Povlsen 		.pm = &sdhci_pltfm_pmops,
25643611afbSLars Povlsen 	},
25743611afbSLars Povlsen 	.probe = sdhci_sparx5_probe,
258*18ba91acSAdrian Hunter 	.remove_new = sdhci_pltfm_remove,
25943611afbSLars Povlsen };
26043611afbSLars Povlsen 
26143611afbSLars Povlsen module_platform_driver(sdhci_sparx5_driver);
26243611afbSLars Povlsen 
26343611afbSLars Povlsen MODULE_DESCRIPTION("Sparx5 SDHCI OF driver");
26443611afbSLars Povlsen MODULE_AUTHOR("Lars Povlsen <lars.povlsen@microchip.com>");
26543611afbSLars Povlsen MODULE_LICENSE("GPL v2");
266