mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
036f29d554
commit
e3ec3a3d11
|
@ -0,0 +1,27 @@
|
|||
Device Tree Bindings for the Arasan SDHCI Controller
|
||||
|
||||
The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. Only
|
||||
deviations are documented here.
|
||||
|
||||
[1] Documentation/devicetree/bindings/mmc/mmc.txt
|
||||
[2] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Required Properties:
|
||||
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a'
|
||||
- reg: From mmc bindings: Register location and length.
|
||||
- clocks: From clock bindings: Handles to clock inputs.
|
||||
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
|
||||
- interrupts: Interrupt specifier
|
||||
- interrupt-parent: Phandle for the interrupt controller that services
|
||||
interrupts for this device.
|
||||
|
||||
Example:
|
||||
sdhci@e0100000 {
|
||||
compatible = "arasan,sdhci-8.9a";
|
||||
reg = <0xe0100000 0x1000>;
|
||||
clock-names = "clk_xin", "clk_ahb";
|
||||
clocks = <&clkc 21>, <&clkc 32>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 24 4>;
|
||||
} ;
|
|
@ -1371,6 +1371,7 @@ T: git git://git.xilinx.com/linux-xlnx.git
|
|||
S: Supported
|
||||
F: arch/arm/mach-zynq/
|
||||
F: drivers/cpuidle/cpuidle-zynq.c
|
||||
F: drivers/mmc/host/sdhci-of-arasan.c
|
||||
|
||||
ARM SMMU DRIVER
|
||||
M: Will Deacon <will.deacon@arm.com>
|
||||
|
|
|
@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_ARASAN
|
||||
tristate "SDHCI OF support for the Arasan SDHCI controllers"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
help
|
||||
This selects the Arasan Secure Digital Host Controller Interface
|
||||
(SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_ESDHC
|
||||
tristate "SDHCI OF support for the Freescale eSDHC controller"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
|
|
|
@ -59,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
|
|||
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
|
||||
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
||||
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Arasan Secure Digital Host Controller Interface.
|
||||
* Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu>
|
||||
* Copyright (c) 2012 Wind River Systems, Inc.
|
||||
* Copyright (C) 2013 Pengutronix e.K.
|
||||
* Copyright (C) 2013 Xilinx Inc.
|
||||
*
|
||||
* Based on sdhci-of-esdhc.c
|
||||
*
|
||||
* Copyright (c) 2007 Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
*
|
||||
* Authors: Xiaobo Xie <X.Xie@freescale.com>
|
||||
* Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
|
||||
|
||||
#define CLK_CTRL_TIMEOUT_SHIFT 16
|
||||
#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
|
||||
#define CLK_CTRL_TIMEOUT_MIN_EXP 13
|
||||
|
||||
/**
|
||||
* struct sdhci_arasan_data
|
||||
* @clk_ahb: Pointer to the AHB clock
|
||||
*/
|
||||
struct sdhci_arasan_data {
|
||||
struct clk *clk_ahb;
|
||||
};
|
||||
|
||||
static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
|
||||
{
|
||||
u32 div;
|
||||
unsigned long freq;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET);
|
||||
div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT;
|
||||
|
||||
freq = clk_get_rate(pltfm_host->clk);
|
||||
freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_arasan_ops = {
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_arasan_get_timeout_clock,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_arasan_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
* sdhci_arasan_suspend - Suspend method for the driver
|
||||
* @dev: Address of the device structure
|
||||
* Returns 0 on success and error value on error
|
||||
*
|
||||
* Put the device in a low power state.
|
||||
*/
|
||||
static int sdhci_arasan_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable(pltfm_host->clk);
|
||||
clk_disable(sdhci_arasan->clk_ahb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sdhci_arasan_resume - Resume method for the driver
|
||||
* @dev: Address of the device structure
|
||||
* Returns 0 on success and error value on error
|
||||
*
|
||||
* Resume operation after suspend
|
||||
*/
|
||||
static int sdhci_arasan_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(sdhci_arasan->clk_ahb);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable AHB clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(pltfm_host->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable SD clock.\n");
|
||||
clk_disable(sdhci_arasan->clk_ahb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
#endif /* ! CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
|
||||
sdhci_arasan_resume);
|
||||
|
||||
static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct clk *clk_xin;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_arasan_data *sdhci_arasan;
|
||||
|
||||
sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan),
|
||||
GFP_KERNEL);
|
||||
if (!sdhci_arasan)
|
||||
return -ENOMEM;
|
||||
|
||||
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
|
||||
if (IS_ERR(sdhci_arasan->clk_ahb)) {
|
||||
dev_err(&pdev->dev, "clk_ahb clock not found.\n");
|
||||
return PTR_ERR(sdhci_arasan->clk_ahb);
|
||||
}
|
||||
|
||||
clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
|
||||
if (IS_ERR(clk_xin)) {
|
||||
dev_err(&pdev->dev, "clk_xin clock not found.\n");
|
||||
return PTR_ERR(clk_xin);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk_xin);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable SD clock.\n");
|
||||
goto clk_dis_ahb;
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
dev_err(&pdev->dev, "platform init failed (%u)\n", ret);
|
||||
goto clk_disable_all;
|
||||
}
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = sdhci_arasan;
|
||||
pltfm_host->clk = clk_xin;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "platform register failed (%u)\n", ret);
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
clk_disable_all:
|
||||
clk_disable_unprepare(clk_xin);
|
||||
clk_dis_ahb:
|
||||
clk_disable_unprepare(sdhci_arasan->clk_ahb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_disable_unprepare(sdhci_arasan->clk_ahb);
|
||||
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
{ .compatible = "arasan,sdhci-8.9a" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
||||
|
||||
static struct platform_driver sdhci_arasan_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-arasan",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = sdhci_arasan_of_match,
|
||||
.pm = &sdhci_arasan_dev_pm_ops,
|
||||
},
|
||||
.probe = sdhci_arasan_probe,
|
||||
.remove = sdhci_arasan_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_arasan_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller");
|
||||
MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue