1 /* 2 * Arasan Secure Digital Host Controller Interface. 3 * Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu> 4 * Copyright (c) 2012 Wind River Systems, Inc. 5 * Copyright (C) 2013 Pengutronix e.K. 6 * Copyright (C) 2013 Xilinx Inc. 7 * 8 * Based on sdhci-of-esdhc.c 9 * 10 * Copyright (c) 2007 Freescale Semiconductor, Inc. 11 * Copyright (c) 2009 MontaVista Software, Inc. 12 * 13 * Authors: Xiaobo Xie <X.Xie@freescale.com> 14 * Anton Vorontsov <avorontsov@ru.mvista.com> 15 * 16 * This program is free software; you can redistribute it and/or modify 17 * it under the terms of the GNU General Public License as published by 18 * the Free Software Foundation; either version 2 of the License, or (at 19 * your option) any later version. 20 */ 21 22 #include <linux/module.h> 23 #include <linux/of_device.h> 24 #include "sdhci-pltfm.h" 25 26 #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c 27 28 #define CLK_CTRL_TIMEOUT_SHIFT 16 29 #define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) 30 #define CLK_CTRL_TIMEOUT_MIN_EXP 13 31 32 /** 33 * struct sdhci_arasan_data 34 * @clk_ahb: Pointer to the AHB clock 35 */ 36 struct sdhci_arasan_data { 37 struct clk *clk_ahb; 38 }; 39 40 static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) 41 { 42 u32 div; 43 unsigned long freq; 44 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 45 46 div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET); 47 div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT; 48 49 freq = clk_get_rate(pltfm_host->clk); 50 freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div); 51 52 return freq; 53 } 54 55 static struct sdhci_ops sdhci_arasan_ops = { 56 .set_clock = sdhci_set_clock, 57 .get_max_clock = sdhci_pltfm_clk_get_max_clock, 58 .get_timeout_clock = sdhci_arasan_get_timeout_clock, 59 .set_bus_width = sdhci_set_bus_width, 60 .reset = sdhci_reset, 61 .set_uhs_signaling = sdhci_set_uhs_signaling, 62 }; 63 64 static struct sdhci_pltfm_data sdhci_arasan_pdata = { 65 .ops = &sdhci_arasan_ops, 66 }; 67 68 #ifdef CONFIG_PM_SLEEP 69 /** 70 * sdhci_arasan_suspend - Suspend method for the driver 71 * @dev: Address of the device structure 72 * Returns 0 on success and error value on error 73 * 74 * Put the device in a low power state. 75 */ 76 static int sdhci_arasan_suspend(struct device *dev) 77 { 78 struct platform_device *pdev = to_platform_device(dev); 79 struct sdhci_host *host = platform_get_drvdata(pdev); 80 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 81 struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; 82 int ret; 83 84 ret = sdhci_suspend_host(host); 85 if (ret) 86 return ret; 87 88 clk_disable(pltfm_host->clk); 89 clk_disable(sdhci_arasan->clk_ahb); 90 91 return 0; 92 } 93 94 /** 95 * sdhci_arasan_resume - Resume method for the driver 96 * @dev: Address of the device structure 97 * Returns 0 on success and error value on error 98 * 99 * Resume operation after suspend 100 */ 101 static int sdhci_arasan_resume(struct device *dev) 102 { 103 struct platform_device *pdev = to_platform_device(dev); 104 struct sdhci_host *host = platform_get_drvdata(pdev); 105 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 106 struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; 107 int ret; 108 109 ret = clk_enable(sdhci_arasan->clk_ahb); 110 if (ret) { 111 dev_err(dev, "Cannot enable AHB clock.\n"); 112 return ret; 113 } 114 115 ret = clk_enable(pltfm_host->clk); 116 if (ret) { 117 dev_err(dev, "Cannot enable SD clock.\n"); 118 clk_disable(sdhci_arasan->clk_ahb); 119 return ret; 120 } 121 122 return sdhci_resume_host(host); 123 } 124 #endif /* ! CONFIG_PM_SLEEP */ 125 126 static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, 127 sdhci_arasan_resume); 128 129 static int sdhci_arasan_probe(struct platform_device *pdev) 130 { 131 int ret; 132 struct clk *clk_xin; 133 struct sdhci_host *host; 134 struct sdhci_pltfm_host *pltfm_host; 135 struct sdhci_arasan_data *sdhci_arasan; 136 137 sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan), 138 GFP_KERNEL); 139 if (!sdhci_arasan) 140 return -ENOMEM; 141 142 sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); 143 if (IS_ERR(sdhci_arasan->clk_ahb)) { 144 dev_err(&pdev->dev, "clk_ahb clock not found.\n"); 145 return PTR_ERR(sdhci_arasan->clk_ahb); 146 } 147 148 clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); 149 if (IS_ERR(clk_xin)) { 150 dev_err(&pdev->dev, "clk_xin clock not found.\n"); 151 return PTR_ERR(clk_xin); 152 } 153 154 ret = clk_prepare_enable(sdhci_arasan->clk_ahb); 155 if (ret) { 156 dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); 157 return ret; 158 } 159 160 ret = clk_prepare_enable(clk_xin); 161 if (ret) { 162 dev_err(&pdev->dev, "Unable to enable SD clock.\n"); 163 goto clk_dis_ahb; 164 } 165 166 host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0); 167 if (IS_ERR(host)) { 168 ret = PTR_ERR(host); 169 goto clk_disable_all; 170 } 171 172 if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-4.9a")) { 173 host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; 174 host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; 175 } 176 177 sdhci_get_of_property(pdev); 178 pltfm_host = sdhci_priv(host); 179 pltfm_host->priv = sdhci_arasan; 180 pltfm_host->clk = clk_xin; 181 182 ret = mmc_of_parse(host->mmc); 183 if (ret) { 184 dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret); 185 goto clk_disable_all; 186 } 187 188 ret = sdhci_add_host(host); 189 if (ret) 190 goto err_pltfm_free; 191 192 return 0; 193 194 err_pltfm_free: 195 sdhci_pltfm_free(pdev); 196 clk_disable_all: 197 clk_disable_unprepare(clk_xin); 198 clk_dis_ahb: 199 clk_disable_unprepare(sdhci_arasan->clk_ahb); 200 201 return ret; 202 } 203 204 static int sdhci_arasan_remove(struct platform_device *pdev) 205 { 206 struct sdhci_host *host = platform_get_drvdata(pdev); 207 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 208 struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; 209 210 clk_disable_unprepare(sdhci_arasan->clk_ahb); 211 212 return sdhci_pltfm_unregister(pdev); 213 } 214 215 static const struct of_device_id sdhci_arasan_of_match[] = { 216 { .compatible = "arasan,sdhci-8.9a" }, 217 { .compatible = "arasan,sdhci-4.9a" }, 218 { } 219 }; 220 MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); 221 222 static struct platform_driver sdhci_arasan_driver = { 223 .driver = { 224 .name = "sdhci-arasan", 225 .of_match_table = sdhci_arasan_of_match, 226 .pm = &sdhci_arasan_dev_pm_ops, 227 }, 228 .probe = sdhci_arasan_probe, 229 .remove = sdhci_arasan_remove, 230 }; 231 232 module_platform_driver(sdhci_arasan_driver); 233 234 MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); 235 MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>"); 236 MODULE_LICENSE("GPL"); 237