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 if (ret) { 82 dev_err(&pdev->dev, "Failed mmc_of_parse\n"); 83 goto err_of; 84 } 85 86 clk_prepare_enable(clk); 87 88 pltfm_host = sdhci_priv(host); 89 pltfm_host->clk = clk; 90 91 ret = sdhci_add_host(host); 92 if (ret) { 93 dev_err(&pdev->dev, "Failed sdhci_add_host\n"); 94 goto err_out; 95 } 96 97 platform_set_drvdata(pdev, host); 98 99 host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); 100 101 dev_info(&pdev->dev, "SDHCI ST Initialised: Host Version: 0x%x Vendor Version 0x%x\n", 102 ((host_version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT), 103 ((host_version & SDHCI_VENDOR_VER_MASK) >> 104 SDHCI_VENDOR_VER_SHIFT)); 105 106 return 0; 107 108 err_out: 109 clk_disable_unprepare(clk); 110 err_of: 111 sdhci_pltfm_free(pdev); 112 113 return ret; 114 } 115 116 #ifdef CONFIG_PM_SLEEP 117 static int sdhci_st_suspend(struct device *dev) 118 { 119 struct sdhci_host *host = dev_get_drvdata(dev); 120 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 121 int ret = sdhci_suspend_host(host); 122 123 if (ret) 124 goto out; 125 126 clk_disable_unprepare(pltfm_host->clk); 127 out: 128 return ret; 129 } 130 131 static int sdhci_st_resume(struct device *dev) 132 { 133 struct sdhci_host *host = dev_get_drvdata(dev); 134 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 135 136 clk_prepare_enable(pltfm_host->clk); 137 138 return sdhci_resume_host(host); 139 } 140 #endif 141 142 static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume); 143 144 static const struct of_device_id st_sdhci_match[] = { 145 { .compatible = "st,sdhci" }, 146 {}, 147 }; 148 149 MODULE_DEVICE_TABLE(of, st_sdhci_match); 150 151 static struct platform_driver sdhci_st_driver = { 152 .probe = sdhci_st_probe, 153 .remove = sdhci_pltfm_unregister, 154 .driver = { 155 .name = "sdhci-st", 156 .pm = &sdhci_st_pmops, 157 .of_match_table = of_match_ptr(st_sdhci_match), 158 }, 159 }; 160 161 module_platform_driver(sdhci_st_driver); 162 163 MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs"); 164 MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 165 MODULE_LICENSE("GPL v2"); 166 MODULE_ALIAS("platform:st-sdhci"); 167