From patchwork Fri Dec 13 08:36:06 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Sangorrin X-Patchwork-Id: 3347301 Return-Path: X-Original-To: patchwork-ltsi-dev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 994FFC0D4A for ; Sat, 14 Dec 2013 08:07:23 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 7398C20780 for ; Sat, 14 Dec 2013 08:07:22 +0000 (UTC) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) by mail.kernel.org (Postfix) with ESMTP id 1EE8F206AF for ; Sat, 14 Dec 2013 08:07:21 +0000 (UTC) Received: from mail.linux-foundation.org (localhost [IPv6:::1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 24468B37; Sat, 14 Dec 2013 08:06:39 +0000 (UTC) X-Original-To: ltsi-dev@lists.linuxfoundation.org Delivered-To: ltsi-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTP id 68A4B71B for ; Fri, 13 Dec 2013 08:36:21 +0000 (UTC) X-Greylist: from auto-whitelisted by SQLgrey-1.7.6 Received: from imx12.toshiba.co.jp (imx12.toshiba.co.jp [61.202.160.132]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id ED52C1F8C3 for ; Fri, 13 Dec 2013 08:36:19 +0000 (UTC) Received: from arc11.toshiba.co.jp ([133.199.90.127]) by imx12.toshiba.co.jp with ESMTP id rBD8aBlo024878; Fri, 13 Dec 2013 17:36:11 +0900 (JST) Received: (from root@localhost) by arc11.toshiba.co.jp id rBD8aB82007642; Fri, 13 Dec 2013 17:36:11 +0900 (JST) Received: from ovp11.toshiba.co.jp [133.199.90.148] by arc11.toshiba.co.jp with ESMTP id TAA07640; Fri, 13 Dec 2013 17:36:11 +0900 Received: from mx12.toshiba.co.jp (localhost [127.0.0.1]) by ovp11.toshiba.co.jp with ESMTP id rBD8aAWe018718; Fri, 13 Dec 2013 17:36:10 +0900 (JST) Received: from BK2211.rdc.toshiba.co.jp by toshiba.co.jp id rBD8a8cE002991; Fri, 13 Dec 2013 17:36:08 +0900 (JST) Received: from island.swc.toshiba.co.jp (localhost [127.0.0.1]) by BK2211.rdc.toshiba.co.jp (8.13.8+Sun/8.13.8) with ESMTP id rBD8a85e028036; Fri, 13 Dec 2013 17:36:08 +0900 (JST) Received: from ubuntu.swc.toshiba.co.jp (unknown [133.196.174.184]) by island.swc.toshiba.co.jp (Postfix) with ESMTP id 680A340006; Fri, 13 Dec 2013 17:35:21 +0900 (JST) From: Daniel Sangorrin To: ltsi-dev@lists.linuxfoundation.org Date: Fri, 13 Dec 2013 17:36:06 +0900 Message-Id: <1386923766-22543-5-git-send-email-daniel.sangorrin@toshiba.co.jp> X-Mailer: git-send-email 1.8.5 In-Reply-To: <1386923766-22543-1-git-send-email-daniel.sangorrin@toshiba.co.jp> References: <1386923766-22543-1-git-send-email-daniel.sangorrin@toshiba.co.jp> X-Spam-Status: No, score=-2.3 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Mailman-Approved-At: Sat, 14 Dec 2013 08:06:34 +0000 Cc: michal.simek@xilinx.com Subject: [LTSI-dev] [PATCH 4/4] mmc: host: xilinxps: Merge with Xilinx branch X-BeenThere: ltsi-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: "A list to discuss patches, development, and other things related to the LTSI project" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ltsi-dev-bounces@lists.linuxfoundation.org Errors-To: ltsi-dev-bounces@lists.linuxfoundation.org X-Virus-Scanned: ClamAV using ClamSMTP From: Michal Simek Merge changes from the Xilinx branch to add support for the SD Card in ZYNQ boards. (commit 085157e92870cf037d77194d4506bce664b760a3) Signed-off-by: Daniel Sangorrin Signed-off-by: Yoshitake Kobayashi --- drivers/mmc/host/Kconfig | 13 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-of-xilinxps.c | 240 +++++++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 drivers/mmc/host/sdhci-of-xilinxps.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9ab8f8d..8e57481 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -130,6 +130,19 @@ config MMC_SDHCI_OF_HLWD If unsure, say N. +config MMC_SDHCI_OF_XILINX_PS + tristate "SDHCI OF support for the Xilinx Zynq SDHCI controllers" + depends on MMC_SDHCI_PLTFM + depends on OF + depends on ARCH_ZYNQ + help + This selects the Secure Digital Host Controller Interface (SDHCI) + found in the Xilinx Zynq. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_CNS3XXX tristate "SDHCI support on the Cavium Networks CNS3xxx SoC" depends on ARCH_CNS3XXX diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index cd32280..f06990b 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.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_OF_XILINX_PS) += sdhci-of-xilinxps.o obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o ifeq ($(CONFIG_CB710_DEBUG),y) diff --git a/drivers/mmc/host/sdhci-of-xilinxps.c b/drivers/mmc/host/sdhci-of-xilinxps.c new file mode 100644 index 0000000..f3a590a --- /dev/null +++ b/drivers/mmc/host/sdhci-of-xilinxps.c @@ -0,0 +1,240 @@ +/* + * Xilinx Zynq Secure Digital Host Controller Interface. + * Copyright (C) 2011 - 2012 Michal Simek + * Copyright (c) 2012 Wind River Systems, Inc. + * + * Based on sdhci-of-esdhc.c + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Copyright (c) 2009 MontaVista Software, Inc. + * + * Authors: Xiaobo Xie + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include +#include +#include "sdhci-pltfm.h" + +/** + * struct xsdhcips + * @devclk Pointer to the peripheral clock + * @aperclk Pointer to the APER clock + * @clk_rate_change_nb Notifier block for clock frequency change callback + */ +struct xsdhcips { + struct clk *devclk; + struct clk *aperclk; + struct notifier_block clk_rate_change_nb; +}; + +static unsigned int zynq_of_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return pltfm_host->clock; +} + +static struct sdhci_ops sdhci_zynq_ops = { + .get_max_clock = zynq_of_get_max_clock, +}; + +static struct sdhci_pltfm_data sdhci_zynq_pdata = { + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK, + .ops = &sdhci_zynq_ops, +}; + +static int xsdhcips_clk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + switch (event) { + case PRE_RATE_CHANGE: + /* if a rate change is announced we need to check whether we can + * maintain the current frequency by changing the clock + * dividers. And we may have to suspend operation and return + * after the rate change or its abort + */ + /* fall through */ + case POST_RATE_CHANGE: + return NOTIFY_OK; + case ABORT_RATE_CHANGE: + default: + return NOTIFY_DONE; + } +} + +#ifdef CONFIG_PM_SLEEP +/** + * xsdhcips_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 xsdhcips_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 xsdhcips *xsdhcips = pltfm_host->priv; + int ret; + + ret = sdhci_suspend_host(host); + if (ret) + return ret; + + clk_disable(xsdhcips->devclk); + clk_disable(xsdhcips->aperclk); + + return 0; +} + +/** + * xsdhcips_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 xsdhcips_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 xsdhcips *xsdhcips = pltfm_host->priv; + int ret; + + ret = clk_enable(xsdhcips->aperclk); + if (ret) { + dev_err(dev, "Cannot enable APER clock.\n"); + return ret; + } + + ret = clk_enable(xsdhcips->devclk); + if (ret) { + dev_err(dev, "Cannot enable device clock.\n"); + clk_disable(xsdhcips->aperclk); + return ret; + } + + return sdhci_resume_host(host); +} +#endif /* ! CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(xsdhcips_dev_pm_ops, xsdhcips_suspend, + xsdhcips_resume); + +static int sdhci_zynq_probe(struct platform_device *pdev) +{ + int ret; + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct xsdhcips *xsdhcips; + + xsdhcips = devm_kzalloc(&pdev->dev, sizeof(*xsdhcips), GFP_KERNEL); + if (!xsdhcips) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + return -ENOMEM; + } + + xsdhcips->aperclk = devm_clk_get(&pdev->dev, "aper_clk"); + if (IS_ERR(xsdhcips->aperclk)) { + dev_err(&pdev->dev, "aper_clk clock not found.\n"); + return PTR_ERR(xsdhcips->aperclk); + } + + xsdhcips->devclk = devm_clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(xsdhcips->devclk)) { + dev_err(&pdev->dev, "ref_clk clock not found.\n"); + return PTR_ERR(xsdhcips->devclk); + } + + ret = clk_prepare_enable(xsdhcips->aperclk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable APER clock.\n"); + return ret; + } + + ret = clk_prepare_enable(xsdhcips->devclk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable device clock.\n"); + goto clk_dis_aper; + } + + xsdhcips->clk_rate_change_nb.notifier_call = xsdhcips_clk_notifier_cb; + xsdhcips->clk_rate_change_nb.next = NULL; + if (clk_notifier_register(xsdhcips->devclk, + &xsdhcips->clk_rate_change_nb)) + dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); + + + ret = sdhci_pltfm_register(pdev, &sdhci_zynq_pdata); + if (ret) { + dev_err(&pdev->dev, "Platform registration failed\n"); + goto clk_notif_unreg; + } + + host = platform_get_drvdata(pdev); + pltfm_host = sdhci_priv(host); + pltfm_host->priv = xsdhcips; + + return 0; + +clk_notif_unreg: + clk_notifier_unregister(xsdhcips->devclk, + &xsdhcips->clk_rate_change_nb); + clk_disable_unprepare(xsdhcips->devclk); +clk_dis_aper: + clk_disable_unprepare(xsdhcips->aperclk); + + return ret; +} + +static int sdhci_zynq_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct xsdhcips *xsdhcips = pltfm_host->priv; + + clk_notifier_unregister(xsdhcips->devclk, + &xsdhcips->clk_rate_change_nb); + clk_disable_unprepare(xsdhcips->devclk); + clk_disable_unprepare(xsdhcips->aperclk); + + return sdhci_pltfm_unregister(pdev); +} + +static const struct of_device_id sdhci_zynq_of_match[] = { + { .compatible = "xlnx,ps7-sdhci-1.00.a" }, + { .compatible = "generic-sdhci" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_zynq_of_match); + +static struct platform_driver sdhci_zynq_driver = { + .driver = { + .name = "sdhci-zynq", + .owner = THIS_MODULE, + .of_match_table = sdhci_zynq_of_match, + .pm = &xsdhcips_dev_pm_ops, + }, + .probe = sdhci_zynq_probe, + .remove = sdhci_zynq_remove, +}; + +module_platform_driver(sdhci_zynq_driver); + +MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver"); +MODULE_AUTHOR("Michal Simek , Vlad Lungu "); +MODULE_LICENSE("GPL v2");