xref: /linux/drivers/mmc/host/sdhci-pic32.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
15d9460d7SAndrei Pistirica /*
25d9460d7SAndrei Pistirica  * Support of SDHCI platform devices for Microchip PIC32.
35d9460d7SAndrei Pistirica  *
45d9460d7SAndrei Pistirica  * Copyright (C) 2015 Microchip
55d9460d7SAndrei Pistirica  * Andrei Pistirica, Paul Thacker
65d9460d7SAndrei Pistirica  *
75d9460d7SAndrei Pistirica  * Inspired by sdhci-pltfm.c
85d9460d7SAndrei Pistirica  *
95d9460d7SAndrei Pistirica  * This file is licensed under the terms of the GNU General Public
105d9460d7SAndrei Pistirica  * License version 2. This program is licensed "as is" without any
115d9460d7SAndrei Pistirica  * warranty of any kind, whether express or implied.
125d9460d7SAndrei Pistirica  */
135d9460d7SAndrei Pistirica 
145d9460d7SAndrei Pistirica #include <linux/clk.h>
155d9460d7SAndrei Pistirica #include <linux/delay.h>
165d9460d7SAndrei Pistirica #include <linux/highmem.h>
175d9460d7SAndrei Pistirica #include <linux/module.h>
185d9460d7SAndrei Pistirica #include <linux/interrupt.h>
195d9460d7SAndrei Pistirica #include <linux/irq.h>
205d9460d7SAndrei Pistirica #include <linux/of.h>
215d9460d7SAndrei Pistirica #include <linux/platform_device.h>
225d9460d7SAndrei Pistirica #include <linux/pm.h>
235d9460d7SAndrei Pistirica #include <linux/slab.h>
245d9460d7SAndrei Pistirica #include <linux/mmc/host.h>
255d9460d7SAndrei Pistirica #include <linux/io.h>
265d9460d7SAndrei Pistirica #include "sdhci.h"
275d9460d7SAndrei Pistirica #include "sdhci-pltfm.h"
285d9460d7SAndrei Pistirica #include <linux/platform_data/sdhci-pic32.h>
295d9460d7SAndrei Pistirica 
305d9460d7SAndrei Pistirica #define SDH_SHARED_BUS_CTRL		0x000000E0
315d9460d7SAndrei Pistirica #define SDH_SHARED_BUS_NR_CLK_PINS_MASK	0x7
325d9460d7SAndrei Pistirica #define SDH_SHARED_BUS_NR_IRQ_PINS_MASK	0x30
335d9460d7SAndrei Pistirica #define SDH_SHARED_BUS_CLK_PINS		0x10
345d9460d7SAndrei Pistirica #define SDH_SHARED_BUS_IRQ_PINS		0x14
355d9460d7SAndrei Pistirica #define SDH_CAPS_SDH_SLOT_TYPE_MASK	0xC0000000
365d9460d7SAndrei Pistirica #define SDH_SLOT_TYPE_REMOVABLE		0x0
375d9460d7SAndrei Pistirica #define SDH_SLOT_TYPE_EMBEDDED		0x1
385d9460d7SAndrei Pistirica #define SDH_SLOT_TYPE_SHARED_BUS	0x2
395d9460d7SAndrei Pistirica #define SDHCI_CTRL_CDSSEL		0x80
405d9460d7SAndrei Pistirica #define SDHCI_CTRL_CDTLVL		0x40
415d9460d7SAndrei Pistirica 
425d9460d7SAndrei Pistirica #define ADMA_FIFO_RD_THSHLD	512
435d9460d7SAndrei Pistirica #define ADMA_FIFO_WR_THSHLD	512
445d9460d7SAndrei Pistirica 
455d9460d7SAndrei Pistirica struct pic32_sdhci_priv {
465d9460d7SAndrei Pistirica 	struct platform_device	*pdev;
475d9460d7SAndrei Pistirica 	struct clk *sys_clk;
485d9460d7SAndrei Pistirica 	struct clk *base_clk;
495d9460d7SAndrei Pistirica };
505d9460d7SAndrei Pistirica 
pic32_sdhci_get_max_clock(struct sdhci_host * host)515d9460d7SAndrei Pistirica static unsigned int pic32_sdhci_get_max_clock(struct sdhci_host *host)
525d9460d7SAndrei Pistirica {
535d9460d7SAndrei Pistirica 	struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
545d9460d7SAndrei Pistirica 
555d9460d7SAndrei Pistirica 	return clk_get_rate(sdhci_pdata->base_clk);
565d9460d7SAndrei Pistirica }
575d9460d7SAndrei Pistirica 
pic32_sdhci_set_bus_width(struct sdhci_host * host,int width)585d9460d7SAndrei Pistirica static void pic32_sdhci_set_bus_width(struct sdhci_host *host, int width)
595d9460d7SAndrei Pistirica {
605d9460d7SAndrei Pistirica 	u8 ctrl;
615d9460d7SAndrei Pistirica 
625d9460d7SAndrei Pistirica 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
635d9460d7SAndrei Pistirica 	if (width == MMC_BUS_WIDTH_8) {
645d9460d7SAndrei Pistirica 		ctrl &= ~SDHCI_CTRL_4BITBUS;
655d9460d7SAndrei Pistirica 		if (host->version >= SDHCI_SPEC_300)
665d9460d7SAndrei Pistirica 			ctrl |= SDHCI_CTRL_8BITBUS;
675d9460d7SAndrei Pistirica 	} else {
685d9460d7SAndrei Pistirica 		if (host->version >= SDHCI_SPEC_300)
695d9460d7SAndrei Pistirica 			ctrl &= ~SDHCI_CTRL_8BITBUS;
705d9460d7SAndrei Pistirica 		if (width == MMC_BUS_WIDTH_4)
715d9460d7SAndrei Pistirica 			ctrl |= SDHCI_CTRL_4BITBUS;
725d9460d7SAndrei Pistirica 		else
735d9460d7SAndrei Pistirica 			ctrl &= ~SDHCI_CTRL_4BITBUS;
745d9460d7SAndrei Pistirica 	}
755d9460d7SAndrei Pistirica 
765d9460d7SAndrei Pistirica 	/* CD select and test bits must be set for errata workaround. */
775d9460d7SAndrei Pistirica 	ctrl &= ~SDHCI_CTRL_CDTLVL;
785d9460d7SAndrei Pistirica 	ctrl |= SDHCI_CTRL_CDSSEL;
795d9460d7SAndrei Pistirica 	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
805d9460d7SAndrei Pistirica }
815d9460d7SAndrei Pistirica 
pic32_sdhci_get_ro(struct sdhci_host * host)825d9460d7SAndrei Pistirica static unsigned int pic32_sdhci_get_ro(struct sdhci_host *host)
835d9460d7SAndrei Pistirica {
845d9460d7SAndrei Pistirica 	/*
855d9460d7SAndrei Pistirica 	 * The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
865d9460d7SAndrei Pistirica 	 * can't depend on its value in any way.
875d9460d7SAndrei Pistirica 	 */
885d9460d7SAndrei Pistirica 	return 0;
895d9460d7SAndrei Pistirica }
905d9460d7SAndrei Pistirica 
915d9460d7SAndrei Pistirica static const struct sdhci_ops pic32_sdhci_ops = {
925d9460d7SAndrei Pistirica 	.get_max_clock = pic32_sdhci_get_max_clock,
935d9460d7SAndrei Pistirica 	.set_clock = sdhci_set_clock,
945d9460d7SAndrei Pistirica 	.set_bus_width = pic32_sdhci_set_bus_width,
955d9460d7SAndrei Pistirica 	.reset = sdhci_reset,
965d9460d7SAndrei Pistirica 	.set_uhs_signaling = sdhci_set_uhs_signaling,
975d9460d7SAndrei Pistirica 	.get_ro = pic32_sdhci_get_ro,
985d9460d7SAndrei Pistirica };
995d9460d7SAndrei Pistirica 
100d35ade8fSJulia Lawall static const struct sdhci_pltfm_data sdhci_pic32_pdata = {
1015d9460d7SAndrei Pistirica 	.ops = &pic32_sdhci_ops,
1025d9460d7SAndrei Pistirica 	.quirks = SDHCI_QUIRK_NO_HISPD_BIT,
1035d9460d7SAndrei Pistirica 	.quirks2 = SDHCI_QUIRK2_NO_1_8_V,
1045d9460d7SAndrei Pistirica };
1055d9460d7SAndrei Pistirica 
pic32_sdhci_shared_bus(struct platform_device * pdev)1065d9460d7SAndrei Pistirica static void pic32_sdhci_shared_bus(struct platform_device *pdev)
1075d9460d7SAndrei Pistirica {
1085d9460d7SAndrei Pistirica 	struct sdhci_host *host = platform_get_drvdata(pdev);
1095d9460d7SAndrei Pistirica 	u32 bus = readl(host->ioaddr + SDH_SHARED_BUS_CTRL);
1105d9460d7SAndrei Pistirica 	u32 clk_pins = (bus & SDH_SHARED_BUS_NR_CLK_PINS_MASK) >> 0;
1115d9460d7SAndrei Pistirica 	u32 irq_pins = (bus & SDH_SHARED_BUS_NR_IRQ_PINS_MASK) >> 4;
1125d9460d7SAndrei Pistirica 
1135d9460d7SAndrei Pistirica 	/* select first clock */
1145d9460d7SAndrei Pistirica 	if (clk_pins & 1)
1155d9460d7SAndrei Pistirica 		bus |= (1 << SDH_SHARED_BUS_CLK_PINS);
1165d9460d7SAndrei Pistirica 
1175d9460d7SAndrei Pistirica 	/* select first interrupt */
1185d9460d7SAndrei Pistirica 	if (irq_pins & 1)
1195d9460d7SAndrei Pistirica 		bus |= (1 << SDH_SHARED_BUS_IRQ_PINS);
1205d9460d7SAndrei Pistirica 
1215d9460d7SAndrei Pistirica 	writel(bus, host->ioaddr + SDH_SHARED_BUS_CTRL);
1225d9460d7SAndrei Pistirica }
1235d9460d7SAndrei Pistirica 
pic32_sdhci_probe_platform(struct platform_device * pdev,struct pic32_sdhci_priv * pdata)124c24aa7b1SKaixu Xia static void pic32_sdhci_probe_platform(struct platform_device *pdev,
1255d9460d7SAndrei Pistirica 				      struct pic32_sdhci_priv *pdata)
1265d9460d7SAndrei Pistirica {
1275d9460d7SAndrei Pistirica 	u32 caps_slot_type;
1285d9460d7SAndrei Pistirica 	struct sdhci_host *host = platform_get_drvdata(pdev);
1295d9460d7SAndrei Pistirica 
1305d9460d7SAndrei Pistirica 	/* Check card slot connected on shared bus. */
1315d9460d7SAndrei Pistirica 	host->caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
1325d9460d7SAndrei Pistirica 	caps_slot_type = (host->caps & SDH_CAPS_SDH_SLOT_TYPE_MASK) >> 30;
1335d9460d7SAndrei Pistirica 	if (caps_slot_type == SDH_SLOT_TYPE_SHARED_BUS)
1345d9460d7SAndrei Pistirica 		pic32_sdhci_shared_bus(pdev);
1355d9460d7SAndrei Pistirica }
1365d9460d7SAndrei Pistirica 
pic32_sdhci_probe(struct platform_device * pdev)1375d9460d7SAndrei Pistirica static int pic32_sdhci_probe(struct platform_device *pdev)
1385d9460d7SAndrei Pistirica {
1395d9460d7SAndrei Pistirica 	struct sdhci_host *host;
1405d9460d7SAndrei Pistirica 	struct sdhci_pltfm_host *pltfm_host;
1415d9460d7SAndrei Pistirica 	struct pic32_sdhci_priv *sdhci_pdata;
1425d9460d7SAndrei Pistirica 	struct pic32_sdhci_platform_data *plat_data;
1435d9460d7SAndrei Pistirica 	int ret;
1445d9460d7SAndrei Pistirica 
1455d9460d7SAndrei Pistirica 	host = sdhci_pltfm_init(pdev, &sdhci_pic32_pdata,
1465d9460d7SAndrei Pistirica 				sizeof(struct pic32_sdhci_priv));
1475d9460d7SAndrei Pistirica 	if (IS_ERR(host)) {
1485d9460d7SAndrei Pistirica 		ret = PTR_ERR(host);
1495d9460d7SAndrei Pistirica 		goto err;
1505d9460d7SAndrei Pistirica 	}
1515d9460d7SAndrei Pistirica 
1525d9460d7SAndrei Pistirica 	pltfm_host = sdhci_priv(host);
1535d9460d7SAndrei Pistirica 	sdhci_pdata = sdhci_pltfm_priv(pltfm_host);
1545d9460d7SAndrei Pistirica 
1555d9460d7SAndrei Pistirica 	plat_data = pdev->dev.platform_data;
1565d9460d7SAndrei Pistirica 	if (plat_data && plat_data->setup_dma) {
1575d9460d7SAndrei Pistirica 		ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
1585d9460d7SAndrei Pistirica 					   ADMA_FIFO_WR_THSHLD);
1595d9460d7SAndrei Pistirica 		if (ret)
1605d9460d7SAndrei Pistirica 			goto err_host;
1615d9460d7SAndrei Pistirica 	}
1625d9460d7SAndrei Pistirica 
1635d9460d7SAndrei Pistirica 	sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
1645d9460d7SAndrei Pistirica 	if (IS_ERR(sdhci_pdata->sys_clk)) {
1655d9460d7SAndrei Pistirica 		ret = PTR_ERR(sdhci_pdata->sys_clk);
1665d9460d7SAndrei Pistirica 		dev_err(&pdev->dev, "Error getting clock\n");
1675d9460d7SAndrei Pistirica 		goto err_host;
1685d9460d7SAndrei Pistirica 	}
1695d9460d7SAndrei Pistirica 
1705d9460d7SAndrei Pistirica 	ret = clk_prepare_enable(sdhci_pdata->sys_clk);
1715d9460d7SAndrei Pistirica 	if (ret) {
1725d9460d7SAndrei Pistirica 		dev_err(&pdev->dev, "Error enabling clock\n");
1735d9460d7SAndrei Pistirica 		goto err_host;
1745d9460d7SAndrei Pistirica 	}
1755d9460d7SAndrei Pistirica 
1765d9460d7SAndrei Pistirica 	sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
1775d9460d7SAndrei Pistirica 	if (IS_ERR(sdhci_pdata->base_clk)) {
1785d9460d7SAndrei Pistirica 		ret = PTR_ERR(sdhci_pdata->base_clk);
1795d9460d7SAndrei Pistirica 		dev_err(&pdev->dev, "Error getting clock\n");
1805d9460d7SAndrei Pistirica 		goto err_sys_clk;
1815d9460d7SAndrei Pistirica 	}
1825d9460d7SAndrei Pistirica 
1835d9460d7SAndrei Pistirica 	ret = clk_prepare_enable(sdhci_pdata->base_clk);
1845d9460d7SAndrei Pistirica 	if (ret) {
1855d9460d7SAndrei Pistirica 		dev_err(&pdev->dev, "Error enabling clock\n");
1865d9460d7SAndrei Pistirica 		goto err_base_clk;
1875d9460d7SAndrei Pistirica 	}
1885d9460d7SAndrei Pistirica 
1895d9460d7SAndrei Pistirica 	ret = mmc_of_parse(host->mmc);
1905d9460d7SAndrei Pistirica 	if (ret)
1915d9460d7SAndrei Pistirica 		goto err_base_clk;
1925d9460d7SAndrei Pistirica 
193c24aa7b1SKaixu Xia 	pic32_sdhci_probe_platform(pdev, sdhci_pdata);
1945d9460d7SAndrei Pistirica 
1955d9460d7SAndrei Pistirica 	ret = sdhci_add_host(host);
196fb8617e1SJisheng Zhang 	if (ret)
1975d9460d7SAndrei Pistirica 		goto err_base_clk;
1985d9460d7SAndrei Pistirica 
1995d9460d7SAndrei Pistirica 	dev_info(&pdev->dev, "Successfully added sdhci host\n");
2005d9460d7SAndrei Pistirica 	return 0;
2015d9460d7SAndrei Pistirica 
2025d9460d7SAndrei Pistirica err_base_clk:
2035d9460d7SAndrei Pistirica 	clk_disable_unprepare(sdhci_pdata->base_clk);
2045d9460d7SAndrei Pistirica err_sys_clk:
2055d9460d7SAndrei Pistirica 	clk_disable_unprepare(sdhci_pdata->sys_clk);
2065d9460d7SAndrei Pistirica err_host:
2075d9460d7SAndrei Pistirica 	sdhci_pltfm_free(pdev);
2085d9460d7SAndrei Pistirica err:
2095d9460d7SAndrei Pistirica 	dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
2105d9460d7SAndrei Pistirica 	return ret;
2115d9460d7SAndrei Pistirica }
2125d9460d7SAndrei Pistirica 
pic32_sdhci_remove(struct platform_device * pdev)213*603b7275SYangtao Li static void pic32_sdhci_remove(struct platform_device *pdev)
2145d9460d7SAndrei Pistirica {
2155d9460d7SAndrei Pistirica 	struct sdhci_host *host = platform_get_drvdata(pdev);
2165d9460d7SAndrei Pistirica 	struct pic32_sdhci_priv *sdhci_pdata = sdhci_priv(host);
2175d9460d7SAndrei Pistirica 	u32 scratch;
2185d9460d7SAndrei Pistirica 
2195d9460d7SAndrei Pistirica 	scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
2205d9460d7SAndrei Pistirica 	sdhci_remove_host(host, scratch == (u32)~0);
2215d9460d7SAndrei Pistirica 	clk_disable_unprepare(sdhci_pdata->base_clk);
2225d9460d7SAndrei Pistirica 	clk_disable_unprepare(sdhci_pdata->sys_clk);
2235d9460d7SAndrei Pistirica 	sdhci_pltfm_free(pdev);
2245d9460d7SAndrei Pistirica }
2255d9460d7SAndrei Pistirica 
2265d9460d7SAndrei Pistirica static const struct of_device_id pic32_sdhci_id_table[] = {
2275d9460d7SAndrei Pistirica 	{ .compatible = "microchip,pic32mzda-sdhci" },
2285d9460d7SAndrei Pistirica 	{}
2295d9460d7SAndrei Pistirica };
2305d9460d7SAndrei Pistirica MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
2315d9460d7SAndrei Pistirica 
2325d9460d7SAndrei Pistirica static struct platform_driver pic32_sdhci_driver = {
2335d9460d7SAndrei Pistirica 	.driver = {
2345d9460d7SAndrei Pistirica 		.name	= "pic32-sdhci",
2352a99f3faSDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
2365d9460d7SAndrei Pistirica 		.of_match_table = of_match_ptr(pic32_sdhci_id_table),
2375d9460d7SAndrei Pistirica 	},
2385d9460d7SAndrei Pistirica 	.probe		= pic32_sdhci_probe,
239*603b7275SYangtao Li 	.remove_new	= pic32_sdhci_remove,
2405d9460d7SAndrei Pistirica };
2415d9460d7SAndrei Pistirica 
2425d9460d7SAndrei Pistirica module_platform_driver(pic32_sdhci_driver);
2435d9460d7SAndrei Pistirica 
2445d9460d7SAndrei Pistirica MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
2455d9460d7SAndrei Pistirica MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
2465d9460d7SAndrei Pistirica MODULE_LICENSE("GPL v2");
247