From patchwork Mon Sep 20 12:09:26 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhangfei Gao X-Patchwork-Id: 194662 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o8KC9TPv010507 for ; Mon, 20 Sep 2010 12:09:29 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756002Ab0ITMJ2 (ORCPT ); Mon, 20 Sep 2010 08:09:28 -0400 Received: from mail-yx0-f174.google.com ([209.85.213.174]:41465 "EHLO mail-yx0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755965Ab0ITMJ1 (ORCPT ); Mon, 20 Sep 2010 08:09:27 -0400 Received: by yxp4 with SMTP id 4so1277863yxp.19 for ; Mon, 20 Sep 2010 05:09:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:received:in-reply-to :references:date:message-id:subject:from:to:cc:content-type; bh=KfniSsW//eWlFiLO7WynDiju96T3Fdda8PDr/fIlwTI=; b=uPfYk4bkv1h1HPOCEr1B+ASrgcv0vjPLht5xwkKIzPJleycGkQYP5hubg+AuXNnfO5 TrV5pYpD/yT7NImRd1un49MOLDm++4Pf9SGispsRcun4IVGfgVF2X4j7OYdHSZe3zz8F L5pWDVhb24ho9wbaTXtlqRD4YRC71xUTH0jCE= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; b=cTac1Oz4gkYz7Aq9U6MTBg9VKUsIuPDJ+nbDq/g21YzLnrcyVTy6g6fbpBCOK6eRlm hDAna46zc37eeydF1MSgVBiiRCFkqd2CRpIbSK5xEJVNQpP2ipTI/kxVtZXRWfu2uZb3 KHkyoY7gr/tWo6iwGFiMNC6OtCLg41HHNXaqw= MIME-Version: 1.0 Received: by 10.101.28.16 with SMTP id f16mr9249486anj.59.1284984566885; Mon, 20 Sep 2010 05:09:26 -0700 (PDT) Received: by 10.231.206.203 with HTTP; Mon, 20 Sep 2010 05:09:26 -0700 (PDT) In-Reply-To: <20100920095042.GC4058@pengutronix.de> References: <20100920095042.GC4058@pengutronix.de> Date: Mon, 20 Sep 2010 08:09:26 -0400 Message-ID: Subject: Re: [PATCH] add support PXA168/PXA910/MMP2 SD Host Controller From: zhangfei gao To: Wolfram Sang Cc: Chris Ball , linux-mmc@vger.kernel.org, Kyungmin Park , eric.y.miao@gmail.com, Haojian Zhuang Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Mon, 20 Sep 2010 12:09:29 +0000 (UTC) From b49462b85bdc8f81cc074079fa768d53d8653cb1 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 20 Sep 2010 10:51:28 -0400 Subject: [PATCH] mmc: add support of sdhci-pxa driver Support Marvell PXA168/PXA910/MMP2 SD Host Controller Signed-off-by: Zhangfei Gao --- arch/arm/plat-pxa/include/plat/sdhci.h | 32 ++++ drivers/mmc/host/Kconfig | 12 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-pxa.c | 260 ++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-pxa/include/plat/sdhci.h create mode 100644 drivers/mmc/host/sdhci-pxa.c diff --git a/arch/arm/plat-pxa/include/plat/sdhci.h b/arch/arm/plat-pxa/include/plat/sdhci.h new file mode 100644 index 0000000..55e1c85 --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/sdhci.h @@ -0,0 +1,32 @@ +/* linux/arch/arm/plat-pxa/include/plat/sdhci.h + * + * Copyright 2010 Marvell + * Zhangfei Gao + * + * PXA Platform - SDHCI platform data definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __PLAT_PXA_SDHCI_H +#define __PLAT_PXA_SDHCI_H __FILE__ + +/* pxa specific quirks */ +/* Card alwayes wired to host, like emmc */ +#define PXA_QUIRK_BROKEN_CARD_DETECTION (1<<0) +/* Require clock free running */ +#define PXA_QUIRK_DISABLE_CLOCK_GATING (1<<1) + +/** + * struct pxa_sdhci_platdata() - Platform device data for PXA SDHCI + * @max_speed: The maximum speed supported. + * @pxa_quirks: specific quirk of pxa +*/ +struct sdhci_pxa_platdata { + unsigned int max_speed; + unsigned int pxa_quirk; +}; + +#endif /* __PLAT_PXA_SDHCI_H */ diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6f12d5d..40aa3ba 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -158,6 +158,18 @@ config MMC_SDHCI_S3C If unsure, say N. +config MMC_SDHCI_PXA + tristate "Marvell PXA168/PXA910/MMP2 SD Host Controller support" + depends on ARCH_PXA || ARCH_MMP + select MMC_SDHCI + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Marvell(R) PXA168/PXA910/MMP2 SD Host Controller. + If you have a PXA168/PXA910/MMP2 platform with SD Host Controller and a + card slot,say Y or M here. + + If unsure, say N. + config MMC_SDHCI_SPEAR tristate "SDHCI support on ST SPEAr platform" depends on MMC_SDHCI && PLAT_SPEAR diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 697bbfe..3a9f7fe 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_MV) += sdhci-mv.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o +obj-$(CONFIG_MMC_SDHCI_PXA) += sdhci-pxa.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_WBSD) += wbsd.o diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c new file mode 100644 index 0000000..c375a6d --- /dev/null +++ b/drivers/mmc/host/sdhci-pxa.c @@ -0,0 +1,260 @@ +/* linux/drivers/mmc/host/sdhci-pxa.c + * + * Copyright 2010 Marvell + * Zhangfei Gao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Supports: + * SDHCI support for MMP2/PXA910/PXA168 + * + * Based on sdhci-platfm.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include "sdhci.h" + +#define DRIVER_NAME "sdhci-pxa" + +#define SD_FIFO_PARAM 0x104 +#define DIS_PAD_SD_CLK_GATE 0x400 + +struct sdhci_pxa { + struct sdhci_host *host; + struct sdhci_pxa_platdata *pdata; + struct clk *clk; + struct resource *res; + + u8 clk_enable; +}; + +/*****************************************************************************\ + * * + * SDHCI core callbacks * + * * +\*****************************************************************************/ +static void set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pxa *pxa = sdhci_priv(host); + u32 tmp = 0; + + if (clock == 0) { + if (pxa->clk_enable) { + clk_disable(pxa->clk); + pxa->clk_enable = 0; + } + } else { + if (0 == pxa->clk_enable) { + if (pxa->pdata->pxa_quirk + & PXA_QUIRK_DISABLE_CLOCK_GATING) { + tmp = readl(host->ioaddr + SD_FIFO_PARAM); + tmp |= DIS_PAD_SD_CLK_GATE; + writel(tmp, host->ioaddr + SD_FIFO_PARAM); + } + clk_enable(pxa->clk); + pxa->clk_enable = 1; + } + } +} + +static struct sdhci_ops sdhci_pxa_ops = { + .set_clock = set_clock, +}; + +/*****************************************************************************\ + * * + * Device probing/removal * + * * +\*****************************************************************************/ + +static int __devinit sdhci_pxa_probe(struct platform_device *pdev) +{ + struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct sdhci_host *host = NULL; + struct resource *iomem = NULL; + struct sdhci_pxa *pxa = NULL; + int ret, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no irq specified\n"); + return irq; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) { + dev_err(dev, "no memory specified\n"); + return -ENOENT; + } + + host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pxa)); + if (IS_ERR(host)) { + dev_err(dev, "failed to alloc host\n"); + ret = PTR_ERR(host); + goto out; + } + + pxa = sdhci_priv(host); + pxa->host = host; + pxa->pdata = pdata; + pxa->clk_enable = 0; + + pxa->clk = clk_get(dev, "PXA-SDHCLK"); + if (IS_ERR(pxa->clk)) { + dev_err(dev, "failed to get io clock\n"); + ret = PTR_ERR(pxa->clk); + goto out; + } + + pxa->res = request_mem_region(iomem->start, resource_size(iomem), + mmc_hostname(host->mmc)); + if (!pxa->res) { + dev_err(&pdev->dev, "cannot request region\n"); + ret = -EBUSY; + goto out; + } + + host->ioaddr = ioremap(iomem->start, resource_size(iomem)); + if (!host->ioaddr) { + dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; + goto out; + } + + host->hw_name = "MMC"; + host->ops = &sdhci_pxa_ops; + host->irq = irq; + host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + + if (pdata->pxa_quirk & PXA_QUIRK_BROKEN_CARD_DETECTION) + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + + ret = sdhci_add_host(host); + if (ret) { + dev_err(&pdev->dev, "failed to add host\n"); + goto out; + } + + if (pxa->pdata->max_speed) + host->mmc->f_max = pxa->pdata->max_speed; + + platform_set_drvdata(pdev, host); + + return 0; +out: + if (host) { + if (host->ioaddr) + iounmap(host->ioaddr); + if (pxa->res) + release_mem_region(pxa->res->start, + resource_size(pxa->res)); + sdhci_free_host(host); + } + + return ret; +} + +static int __devexit sdhci_pxa_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pxa *pxa = sdhci_priv(host); + int dead = 0; + u32 scratch; + + if (host) { + scratch = readl(host->ioaddr + SDHCI_INT_STATUS); + if (scratch == (u32)-1) + dead = 1; + + sdhci_remove_host(host, dead); + + if (host->ioaddr) + iounmap(host->ioaddr); + if (pxa->res) + release_mem_region(pxa->res->start, + resource_size(pxa->res)); + clk_disable(pxa->clk); + clk_put(pxa->clk); + + sdhci_free_host(host); + platform_set_drvdata(pdev, NULL); + } + + return 0; +} + +#ifdef CONFIG_PM +static int sdhci_pxa_suspend(struct platform_device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sdhci_host *host = platform_get_drvdata(dev); + int ret = 0; + + ret = sdhci_suspend_host(host, state); + + return ret; +} + +static int sdhci_pxa_resume(struct platform_device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sdhci_host *host = platform_get_drvdata(dev); + int ret = 0; + + ret = sdhci_resume_host(host); + + return ret; +} +#else +#define sdhci_pxa_suspend NULL +#define sdhci_pxa_resume NULL +#endif + +static const struct dev_pm_ops sdhci_pxa_pm_ops = { + .suspend = sdhci_pxa_suspend, + .resume = sdhci_pxa_resume, +}; + + +static struct platform_driver sdhci_pxa_driver = { + .probe = sdhci_pxa_probe, + .remove = __devexit_p(sdhci_pxa_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &sdhci_pxa_pm_ops, + }, +}; + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +static int __init sdhci_pxa_init(void) +{ + return platform_driver_register(&sdhci_pxa_driver); +} + +static void __exit sdhci_pxa_exit(void) +{ + platform_driver_unregister(&sdhci_pxa_driver); +} + +module_init(sdhci_pxa_init); +module_exit(sdhci_pxa_exit); + +MODULE_DESCRIPTION("SDH controller driver for PXA168/PXA910/MMP2"); +MODULE_AUTHOR("Zhangfei Gao "); +MODULE_LICENSE("GPL v2"); -- 1.7.0.4