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