1f52d9c4fSPeter Griffin /* 2f52d9c4fSPeter Griffin * Support for SDHCI on STMicroelectronics SoCs 3f52d9c4fSPeter Griffin * 4f52d9c4fSPeter Griffin * Copyright (C) 2014 STMicroelectronics Ltd 5f52d9c4fSPeter Griffin * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> 6f52d9c4fSPeter Griffin * Contributors: Peter Griffin <peter.griffin@linaro.org> 7f52d9c4fSPeter Griffin * 8f52d9c4fSPeter Griffin * Based on sdhci-cns3xxx.c 9f52d9c4fSPeter Griffin * 10f52d9c4fSPeter Griffin * This program is free software; you can redistribute it and/or modify 11f52d9c4fSPeter Griffin * it under the terms of the GNU General Public License version 2 as 12f52d9c4fSPeter Griffin * published by the Free Software Foundation. 13f52d9c4fSPeter Griffin * 14f52d9c4fSPeter Griffin * This program is distributed in the hope that it will be useful, 15f52d9c4fSPeter Griffin * but WITHOUT ANY WARRANTY; without even the implied warranty of 16f52d9c4fSPeter Griffin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17f52d9c4fSPeter Griffin * GNU General Public License for more details. 18f52d9c4fSPeter Griffin * 19f52d9c4fSPeter Griffin */ 20f52d9c4fSPeter Griffin 21f52d9c4fSPeter Griffin #include <linux/io.h> 22f52d9c4fSPeter Griffin #include <linux/of.h> 23f52d9c4fSPeter Griffin #include <linux/module.h> 24f52d9c4fSPeter Griffin #include <linux/err.h> 25f52d9c4fSPeter Griffin #include <linux/mmc/host.h> 26f52d9c4fSPeter Griffin 27f52d9c4fSPeter Griffin #include "sdhci-pltfm.h" 28f52d9c4fSPeter Griffin 29f52d9c4fSPeter Griffin static u32 sdhci_st_readl(struct sdhci_host *host, int reg) 30f52d9c4fSPeter Griffin { 31f52d9c4fSPeter Griffin u32 ret; 32f52d9c4fSPeter Griffin 33f52d9c4fSPeter Griffin switch (reg) { 34f52d9c4fSPeter Griffin case SDHCI_CAPABILITIES: 35f52d9c4fSPeter Griffin ret = readl_relaxed(host->ioaddr + reg); 36f52d9c4fSPeter Griffin /* Support 3.3V and 1.8V */ 37f52d9c4fSPeter Griffin ret &= ~SDHCI_CAN_VDD_300; 38f52d9c4fSPeter Griffin break; 39f52d9c4fSPeter Griffin default: 40f52d9c4fSPeter Griffin ret = readl_relaxed(host->ioaddr + reg); 41f52d9c4fSPeter Griffin } 42f52d9c4fSPeter Griffin return ret; 43f52d9c4fSPeter Griffin } 44f52d9c4fSPeter Griffin 45f52d9c4fSPeter Griffin static const struct sdhci_ops sdhci_st_ops = { 46f52d9c4fSPeter Griffin .get_max_clock = sdhci_pltfm_clk_get_max_clock, 47f52d9c4fSPeter Griffin .set_clock = sdhci_set_clock, 48f52d9c4fSPeter Griffin .set_bus_width = sdhci_set_bus_width, 49f52d9c4fSPeter Griffin .read_l = sdhci_st_readl, 50f52d9c4fSPeter Griffin .reset = sdhci_reset, 51f52d9c4fSPeter Griffin }; 52f52d9c4fSPeter Griffin 53f52d9c4fSPeter Griffin static const struct sdhci_pltfm_data sdhci_st_pdata = { 54f52d9c4fSPeter Griffin .ops = &sdhci_st_ops, 55f52d9c4fSPeter Griffin .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | 56f52d9c4fSPeter Griffin SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 57f52d9c4fSPeter Griffin }; 58f52d9c4fSPeter Griffin 59f52d9c4fSPeter Griffin 60f52d9c4fSPeter Griffin static int sdhci_st_probe(struct platform_device *pdev) 61f52d9c4fSPeter Griffin { 62f52d9c4fSPeter Griffin struct sdhci_host *host; 63f52d9c4fSPeter Griffin struct sdhci_pltfm_host *pltfm_host; 64f52d9c4fSPeter Griffin struct clk *clk; 65f52d9c4fSPeter Griffin int ret = 0; 66f52d9c4fSPeter Griffin u16 host_version; 67f52d9c4fSPeter Griffin 68f52d9c4fSPeter Griffin clk = devm_clk_get(&pdev->dev, "mmc"); 69f52d9c4fSPeter Griffin if (IS_ERR(clk)) { 70f52d9c4fSPeter Griffin dev_err(&pdev->dev, "Peripheral clk not found\n"); 71f52d9c4fSPeter Griffin return PTR_ERR(clk); 72f52d9c4fSPeter Griffin } 73f52d9c4fSPeter Griffin 74f52d9c4fSPeter Griffin host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0); 75f52d9c4fSPeter Griffin if (IS_ERR(host)) { 76f52d9c4fSPeter Griffin dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n"); 77f52d9c4fSPeter Griffin return PTR_ERR(host); 78f52d9c4fSPeter Griffin } 79f52d9c4fSPeter Griffin 80f52d9c4fSPeter Griffin ret = mmc_of_parse(host->mmc); 81f52d9c4fSPeter Griffin 82f52d9c4fSPeter Griffin if (ret) { 83f52d9c4fSPeter Griffin dev_err(&pdev->dev, "Failed mmc_of_parse\n"); 84f52d9c4fSPeter Griffin return ret; 85f52d9c4fSPeter Griffin } 86f52d9c4fSPeter Griffin 87f52d9c4fSPeter Griffin clk_prepare_enable(clk); 88f52d9c4fSPeter Griffin 89f52d9c4fSPeter Griffin pltfm_host = sdhci_priv(host); 90f52d9c4fSPeter Griffin pltfm_host->clk = clk; 91f52d9c4fSPeter Griffin 92f52d9c4fSPeter Griffin ret = sdhci_add_host(host); 93f52d9c4fSPeter Griffin if (ret) { 94f52d9c4fSPeter Griffin dev_err(&pdev->dev, "Failed sdhci_add_host\n"); 95f52d9c4fSPeter Griffin goto err_out; 96f52d9c4fSPeter Griffin } 97f52d9c4fSPeter Griffin 98f52d9c4fSPeter Griffin platform_set_drvdata(pdev, host); 99f52d9c4fSPeter Griffin 100f52d9c4fSPeter Griffin host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); 101f52d9c4fSPeter Griffin 102f52d9c4fSPeter Griffin dev_info(&pdev->dev, "SDHCI ST Initialised: Host Version: 0x%x Vendor Version 0x%x\n", 103f52d9c4fSPeter Griffin ((host_version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT), 104f52d9c4fSPeter Griffin ((host_version & SDHCI_VENDOR_VER_MASK) >> 105f52d9c4fSPeter Griffin SDHCI_VENDOR_VER_SHIFT)); 106f52d9c4fSPeter Griffin 107f52d9c4fSPeter Griffin return 0; 108f52d9c4fSPeter Griffin 109f52d9c4fSPeter Griffin err_out: 110f52d9c4fSPeter Griffin clk_disable_unprepare(clk); 111f52d9c4fSPeter Griffin sdhci_pltfm_free(pdev); 112f52d9c4fSPeter Griffin 113f52d9c4fSPeter Griffin return ret; 114f52d9c4fSPeter Griffin } 115f52d9c4fSPeter Griffin 116f52d9c4fSPeter Griffin static int sdhci_st_remove(struct platform_device *pdev) 117f52d9c4fSPeter Griffin { 118f52d9c4fSPeter Griffin struct sdhci_host *host = platform_get_drvdata(pdev); 119f52d9c4fSPeter Griffin struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 120f52d9c4fSPeter Griffin 121f52d9c4fSPeter Griffin clk_disable_unprepare(pltfm_host->clk); 122f52d9c4fSPeter Griffin 123f52d9c4fSPeter Griffin return sdhci_pltfm_unregister(pdev); 124f52d9c4fSPeter Griffin } 125f52d9c4fSPeter Griffin 126f52d9c4fSPeter Griffin #ifdef CONFIG_PM_SLEEP 127f52d9c4fSPeter Griffin static int sdhci_st_suspend(struct device *dev) 128f52d9c4fSPeter Griffin { 129f52d9c4fSPeter Griffin struct sdhci_host *host = dev_get_drvdata(dev); 130f52d9c4fSPeter Griffin struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 131f52d9c4fSPeter Griffin int ret = sdhci_suspend_host(host); 132f52d9c4fSPeter Griffin 133f52d9c4fSPeter Griffin if (ret) 134f52d9c4fSPeter Griffin goto out; 135f52d9c4fSPeter Griffin 136f52d9c4fSPeter Griffin clk_disable_unprepare(pltfm_host->clk); 137f52d9c4fSPeter Griffin out: 138f52d9c4fSPeter Griffin return ret; 139f52d9c4fSPeter Griffin } 140f52d9c4fSPeter Griffin 141f52d9c4fSPeter Griffin static int sdhci_st_resume(struct device *dev) 142f52d9c4fSPeter Griffin { 143f52d9c4fSPeter Griffin struct sdhci_host *host = dev_get_drvdata(dev); 144f52d9c4fSPeter Griffin struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 145f52d9c4fSPeter Griffin 146f52d9c4fSPeter Griffin clk_prepare_enable(pltfm_host->clk); 147f52d9c4fSPeter Griffin 148f52d9c4fSPeter Griffin return sdhci_resume_host(host); 149f52d9c4fSPeter Griffin } 150f52d9c4fSPeter Griffin #endif 151f52d9c4fSPeter Griffin 152f52d9c4fSPeter Griffin static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume); 153f52d9c4fSPeter Griffin 154f52d9c4fSPeter Griffin static const struct of_device_id st_sdhci_match[] = { 155f52d9c4fSPeter Griffin { .compatible = "st,sdhci" }, 156f52d9c4fSPeter Griffin {}, 157f52d9c4fSPeter Griffin }; 158f52d9c4fSPeter Griffin 159f52d9c4fSPeter Griffin MODULE_DEVICE_TABLE(of, st_sdhci_match); 160f52d9c4fSPeter Griffin 161f52d9c4fSPeter Griffin static struct platform_driver sdhci_st_driver = { 162f52d9c4fSPeter Griffin .probe = sdhci_st_probe, 163f52d9c4fSPeter Griffin .remove = sdhci_st_remove, 164f52d9c4fSPeter Griffin .driver = { 165f52d9c4fSPeter Griffin .name = "sdhci-st", 166f52d9c4fSPeter Griffin .pm = &sdhci_st_pmops, 167f52d9c4fSPeter Griffin .of_match_table = of_match_ptr(st_sdhci_match), 168f52d9c4fSPeter Griffin }, 169f52d9c4fSPeter Griffin }; 170f52d9c4fSPeter Griffin 171f52d9c4fSPeter Griffin module_platform_driver(sdhci_st_driver); 172f52d9c4fSPeter Griffin 173f52d9c4fSPeter Griffin MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs"); 174f52d9c4fSPeter Griffin MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 175f52d9c4fSPeter Griffin MODULE_LICENSE("GPL v2"); 176f52d9c4fSPeter Griffin MODULE_ALIAS("platform:st-sdhci"); 177