brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 62753d7 Raw
248 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * sdhci-pltfm.c Support for SDHCI platform devices4 * Copyright (c) 2009 Intel Corporation5 *6 * Copyright (c) 2007, 2011 Freescale Semiconductor, Inc.7 * Copyright (c) 2009 MontaVista Software, Inc.8 *9 * Authors: Xiaobo Xie <X.Xie@freescale.com>10 *	    Anton Vorontsov <avorontsov@ru.mvista.com>11 */12 13/* Supports:14 * SDHCI platform devices15 *16 * Inspired by sdhci-pci.c, by Pierre Ossman17 */18 19#include <linux/err.h>20#include <linux/module.h>21#include <linux/property.h>22#ifdef CONFIG_PPC23#include <asm/machdep.h>24#endif25#include "sdhci-pltfm.h"26 27unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host)28{29	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);30 31	return clk_get_rate(pltfm_host->clk);32}33EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);34 35static const struct sdhci_ops sdhci_pltfm_ops = {36	.set_clock = sdhci_set_clock,37	.set_bus_width = sdhci_set_bus_width,38	.reset = sdhci_reset,39	.set_uhs_signaling = sdhci_set_uhs_signaling,40};41 42static bool sdhci_wp_inverted(struct device *dev)43{44	if (device_property_present(dev, "sdhci,wp-inverted") ||45	    device_property_present(dev, "wp-inverted"))46		return true;47 48	/* Old device trees don't have the wp-inverted property. */49#ifdef CONFIG_PPC50	return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);51#else52	return false;53#endif /* CONFIG_PPC */54}55 56static void sdhci_get_compatibility(struct platform_device *pdev)57{58	struct device *dev = &pdev->dev;59	struct sdhci_host *host = platform_get_drvdata(pdev);60 61	if (device_is_compatible(dev, "fsl,p2020-rev1-esdhc"))62		host->quirks |= SDHCI_QUIRK_BROKEN_DMA;63 64	if (device_is_compatible(dev, "fsl,p2020-esdhc") ||65	    device_is_compatible(dev, "fsl,p1010-esdhc") ||66	    device_is_compatible(dev, "fsl,t4240-esdhc") ||67	    device_is_compatible(dev, "fsl,mpc8536-esdhc"))68		host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;69}70 71void sdhci_get_property(struct platform_device *pdev)72{73	struct device *dev = &pdev->dev;74	struct sdhci_host *host = platform_get_drvdata(pdev);75	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);76	u32 bus_width;77 78	if (device_property_present(dev, "sdhci,auto-cmd12"))79		host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;80 81	if (device_property_present(dev, "sdhci,1-bit-only") ||82	    (device_property_read_u32(dev, "bus-width", &bus_width) == 0 &&83	    bus_width == 1))84		host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;85 86	if (sdhci_wp_inverted(dev))87		host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;88 89	if (device_property_present(dev, "broken-cd"))90		host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;91 92	if (device_property_present(dev, "no-1-8-v"))93		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;94 95	sdhci_get_compatibility(pdev);96 97	device_property_read_u32(dev, "clock-frequency", &pltfm_host->clock);98 99	if (device_property_present(dev, "keep-power-in-suspend"))100		host->mmc->pm_caps |= MMC_PM_KEEP_POWER;101 102	if (device_property_read_bool(dev, "wakeup-source") ||103	    device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */104		host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;105}106EXPORT_SYMBOL_GPL(sdhci_get_property);107 108struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,109				    const struct sdhci_pltfm_data *pdata,110				    size_t priv_size)111{112	struct sdhci_host *host;113	void __iomem *ioaddr;114	int irq;115 116	ioaddr = devm_platform_ioremap_resource(pdev, 0);117	if (IS_ERR(ioaddr))118		return ERR_CAST(ioaddr);119 120	irq = platform_get_irq(pdev, 0);121	if (irq < 0)122		return ERR_PTR(irq);123 124	host = sdhci_alloc_host(&pdev->dev,125		sizeof(struct sdhci_pltfm_host) + priv_size);126	if (IS_ERR(host)) {127		dev_err(&pdev->dev, "%s failed %pe\n", __func__, host);128		return ERR_CAST(host);129	}130 131	host->ioaddr = ioaddr;132	host->irq = irq;133	host->hw_name = dev_name(&pdev->dev);134	if (pdata && pdata->ops)135		host->ops = pdata->ops;136	else137		host->ops = &sdhci_pltfm_ops;138	if (pdata) {139		host->quirks = pdata->quirks;140		host->quirks2 = pdata->quirks2;141	}142 143	platform_set_drvdata(pdev, host);144 145	return host;146}147EXPORT_SYMBOL_GPL(sdhci_pltfm_init);148 149void sdhci_pltfm_free(struct platform_device *pdev)150{151	struct sdhci_host *host = platform_get_drvdata(pdev);152 153	sdhci_free_host(host);154}155EXPORT_SYMBOL_GPL(sdhci_pltfm_free);156 157int sdhci_pltfm_init_and_add_host(struct platform_device *pdev,158				  const struct sdhci_pltfm_data *pdata,159				  size_t priv_size)160{161	struct sdhci_host *host;162	int ret = 0;163 164	host = sdhci_pltfm_init(pdev, pdata, priv_size);165	if (IS_ERR(host))166		return PTR_ERR(host);167 168	sdhci_get_property(pdev);169 170	ret = sdhci_add_host(host);171	if (ret)172		sdhci_pltfm_free(pdev);173 174	return ret;175}176EXPORT_SYMBOL_GPL(sdhci_pltfm_init_and_add_host);177 178void sdhci_pltfm_remove(struct platform_device *pdev)179{180	struct sdhci_host *host = platform_get_drvdata(pdev);181	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);182 183	sdhci_remove_host(host, dead);184	sdhci_pltfm_free(pdev);185}186EXPORT_SYMBOL_GPL(sdhci_pltfm_remove);187 188#ifdef CONFIG_PM_SLEEP189int sdhci_pltfm_suspend(struct device *dev)190{191	struct sdhci_host *host = dev_get_drvdata(dev);192	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);193	int ret;194 195	if (host->tuning_mode != SDHCI_TUNING_MODE_3)196		mmc_retune_needed(host->mmc);197 198	ret = sdhci_suspend_host(host);199	if (ret)200		return ret;201 202	clk_disable_unprepare(pltfm_host->clk);203 204	return 0;205}206EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend);207 208int sdhci_pltfm_resume(struct device *dev)209{210	struct sdhci_host *host = dev_get_drvdata(dev);211	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);212	int ret;213 214	ret = clk_prepare_enable(pltfm_host->clk);215	if (ret)216		return ret;217 218	ret = sdhci_resume_host(host);219	if (ret)220		clk_disable_unprepare(pltfm_host->clk);221 222	return ret;223}224EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);225#endif226 227const struct dev_pm_ops sdhci_pltfm_pmops = {228	SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)229};230EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);231 232static int __init sdhci_pltfm_drv_init(void)233{234	pr_info("sdhci-pltfm: SDHCI platform and OF driver helper\n");235 236	return 0;237}238module_init(sdhci_pltfm_drv_init);239 240static void __exit sdhci_pltfm_drv_exit(void)241{242}243module_exit(sdhci_pltfm_drv_exit);244 245MODULE_DESCRIPTION("SDHCI platform and OF driver helper");246MODULE_AUTHOR("Intel Corporation");247MODULE_LICENSE("GPL v2");248