Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: arasan: Add driver for Arasan SDHCI

Add a driver for Arasan's SDHCI controller core.

Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Acked-by: Rob Herring <rob.herring@calxeda.com> [binding]
Acked-by: Michal Simek <monstr@monstr.eu>
Signed-off-by: Chris Ball <chris@printf.net>

authored by

Soren Brinkmann and committed by
Chris Ball
e3ec3a3d 036f29d5

+265
+27
Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
··· 1 + Device Tree Bindings for the Arasan SDHCI Controller 2 + 3 + The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. Only 4 + deviations are documented here. 5 + 6 + [1] Documentation/devicetree/bindings/mmc/mmc.txt 7 + [2] Documentation/devicetree/bindings/clock/clock-bindings.txt 8 + [3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt 9 + 10 + Required Properties: 11 + - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' 12 + - reg: From mmc bindings: Register location and length. 13 + - clocks: From clock bindings: Handles to clock inputs. 14 + - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb" 15 + - interrupts: Interrupt specifier 16 + - interrupt-parent: Phandle for the interrupt controller that services 17 + interrupts for this device. 18 + 19 + Example: 20 + sdhci@e0100000 { 21 + compatible = "arasan,sdhci-8.9a"; 22 + reg = <0xe0100000 0x1000>; 23 + clock-names = "clk_xin", "clk_ahb"; 24 + clocks = <&clkc 21>, <&clkc 32>; 25 + interrupt-parent = <&gic>; 26 + interrupts = <0 24 4>; 27 + } ;
+1
MAINTAINERS
··· 1371 1371 S: Supported 1372 1372 F: arch/arm/mach-zynq/ 1373 1373 F: drivers/cpuidle/cpuidle-zynq.c 1374 + F: drivers/mmc/host/sdhci-of-arasan.c 1374 1375 1375 1376 ARM SMMU DRIVER 1376 1377 M: Will Deacon <will.deacon@arm.com>
+12
drivers/mmc/host/Kconfig
··· 104 104 105 105 If unsure, say N. 106 106 107 + config MMC_SDHCI_OF_ARASAN 108 + tristate "SDHCI OF support for the Arasan SDHCI controllers" 109 + depends on MMC_SDHCI_PLTFM 110 + depends on OF 111 + help 112 + This selects the Arasan Secure Digital Host Controller Interface 113 + (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC. 114 + 115 + If you have a controller with this interface, say Y or M here. 116 + 117 + If unsure, say N. 118 + 107 119 config MMC_SDHCI_OF_ESDHC 108 120 tristate "SDHCI OF support for the Freescale eSDHC controller" 109 121 depends on MMC_SDHCI_PLTFM
+1
drivers/mmc/host/Makefile
··· 59 59 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o 60 60 obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o 61 61 obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o 62 + obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o 62 63 obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o 63 64 obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o 64 65 obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
+224
drivers/mmc/host/sdhci-of-arasan.c
··· 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 "sdhci-pltfm.h" 24 + 25 + #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c 26 + 27 + #define CLK_CTRL_TIMEOUT_SHIFT 16 28 + #define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) 29 + #define CLK_CTRL_TIMEOUT_MIN_EXP 13 30 + 31 + /** 32 + * struct sdhci_arasan_data 33 + * @clk_ahb: Pointer to the AHB clock 34 + */ 35 + struct sdhci_arasan_data { 36 + struct clk *clk_ahb; 37 + }; 38 + 39 + static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) 40 + { 41 + u32 div; 42 + unsigned long freq; 43 + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 44 + 45 + div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET); 46 + div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT; 47 + 48 + freq = clk_get_rate(pltfm_host->clk); 49 + freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div); 50 + 51 + return freq; 52 + } 53 + 54 + static struct sdhci_ops sdhci_arasan_ops = { 55 + .get_max_clock = sdhci_pltfm_clk_get_max_clock, 56 + .get_timeout_clock = sdhci_arasan_get_timeout_clock, 57 + }; 58 + 59 + static struct sdhci_pltfm_data sdhci_arasan_pdata = { 60 + .ops = &sdhci_arasan_ops, 61 + }; 62 + 63 + #ifdef CONFIG_PM_SLEEP 64 + /** 65 + * sdhci_arasan_suspend - Suspend method for the driver 66 + * @dev: Address of the device structure 67 + * Returns 0 on success and error value on error 68 + * 69 + * Put the device in a low power state. 70 + */ 71 + static int sdhci_arasan_suspend(struct device *dev) 72 + { 73 + struct platform_device *pdev = to_platform_device(dev); 74 + struct sdhci_host *host = platform_get_drvdata(pdev); 75 + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 76 + struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; 77 + int ret; 78 + 79 + ret = sdhci_suspend_host(host); 80 + if (ret) 81 + return ret; 82 + 83 + clk_disable(pltfm_host->clk); 84 + clk_disable(sdhci_arasan->clk_ahb); 85 + 86 + return 0; 87 + } 88 + 89 + /** 90 + * sdhci_arasan_resume - Resume method for the driver 91 + * @dev: Address of the device structure 92 + * Returns 0 on success and error value on error 93 + * 94 + * Resume operation after suspend 95 + */ 96 + static int sdhci_arasan_resume(struct device *dev) 97 + { 98 + struct platform_device *pdev = to_platform_device(dev); 99 + struct sdhci_host *host = platform_get_drvdata(pdev); 100 + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 101 + struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; 102 + int ret; 103 + 104 + ret = clk_enable(sdhci_arasan->clk_ahb); 105 + if (ret) { 106 + dev_err(dev, "Cannot enable AHB clock.\n"); 107 + return ret; 108 + } 109 + 110 + ret = clk_enable(pltfm_host->clk); 111 + if (ret) { 112 + dev_err(dev, "Cannot enable SD clock.\n"); 113 + clk_disable(sdhci_arasan->clk_ahb); 114 + return ret; 115 + } 116 + 117 + return sdhci_resume_host(host); 118 + } 119 + #endif /* ! CONFIG_PM_SLEEP */ 120 + 121 + static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, 122 + sdhci_arasan_resume); 123 + 124 + static int sdhci_arasan_probe(struct platform_device *pdev) 125 + { 126 + int ret; 127 + struct clk *clk_xin; 128 + struct sdhci_host *host; 129 + struct sdhci_pltfm_host *pltfm_host; 130 + struct sdhci_arasan_data *sdhci_arasan; 131 + 132 + sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan), 133 + GFP_KERNEL); 134 + if (!sdhci_arasan) 135 + return -ENOMEM; 136 + 137 + sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); 138 + if (IS_ERR(sdhci_arasan->clk_ahb)) { 139 + dev_err(&pdev->dev, "clk_ahb clock not found.\n"); 140 + return PTR_ERR(sdhci_arasan->clk_ahb); 141 + } 142 + 143 + clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); 144 + if (IS_ERR(clk_xin)) { 145 + dev_err(&pdev->dev, "clk_xin clock not found.\n"); 146 + return PTR_ERR(clk_xin); 147 + } 148 + 149 + ret = clk_prepare_enable(sdhci_arasan->clk_ahb); 150 + if (ret) { 151 + dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); 152 + return ret; 153 + } 154 + 155 + ret = clk_prepare_enable(clk_xin); 156 + if (ret) { 157 + dev_err(&pdev->dev, "Unable to enable SD clock.\n"); 158 + goto clk_dis_ahb; 159 + } 160 + 161 + host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0); 162 + if (IS_ERR(host)) { 163 + ret = PTR_ERR(host); 164 + dev_err(&pdev->dev, "platform init failed (%u)\n", ret); 165 + goto clk_disable_all; 166 + } 167 + 168 + sdhci_get_of_property(pdev); 169 + pltfm_host = sdhci_priv(host); 170 + pltfm_host->priv = sdhci_arasan; 171 + pltfm_host->clk = clk_xin; 172 + 173 + ret = sdhci_add_host(host); 174 + if (ret) { 175 + dev_err(&pdev->dev, "platform register failed (%u)\n", ret); 176 + goto err_pltfm_free; 177 + } 178 + 179 + return 0; 180 + 181 + err_pltfm_free: 182 + sdhci_pltfm_free(pdev); 183 + clk_disable_all: 184 + clk_disable_unprepare(clk_xin); 185 + clk_dis_ahb: 186 + clk_disable_unprepare(sdhci_arasan->clk_ahb); 187 + 188 + return ret; 189 + } 190 + 191 + static int sdhci_arasan_remove(struct platform_device *pdev) 192 + { 193 + struct sdhci_host *host = platform_get_drvdata(pdev); 194 + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 195 + struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; 196 + 197 + clk_disable_unprepare(pltfm_host->clk); 198 + clk_disable_unprepare(sdhci_arasan->clk_ahb); 199 + 200 + return sdhci_pltfm_unregister(pdev); 201 + } 202 + 203 + static const struct of_device_id sdhci_arasan_of_match[] = { 204 + { .compatible = "arasan,sdhci-8.9a" }, 205 + { } 206 + }; 207 + MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); 208 + 209 + static struct platform_driver sdhci_arasan_driver = { 210 + .driver = { 211 + .name = "sdhci-arasan", 212 + .owner = THIS_MODULE, 213 + .of_match_table = sdhci_arasan_of_match, 214 + .pm = &sdhci_arasan_dev_pm_ops, 215 + }, 216 + .probe = sdhci_arasan_probe, 217 + .remove = sdhci_arasan_remove, 218 + }; 219 + 220 + module_platform_driver(sdhci_arasan_driver); 221 + 222 + MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); 223 + MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>"); 224 + MODULE_LICENSE("GPL");