From: "Christian Daudt" <csd@broadcom.com>
To: Grant Likely <grant.likely@secretlab.ca>,
Rob Herring <rob.herring@calxeda.com>,
Rob Landley <rob@landley.net>,
Russell King <linux@arm.linux.org.uk>,
Chris Ball <cjb@laptop.org>, Stephen Warren <swarren@nvidia.com>,
Olof Johansson <olof@lixom.net>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Wei WANG <wei_wang@realsil.com.cn>,
Ludovic Desroches <ludovic.desroches@atmel.com>,
Arnd Bergmann <arnd@arndb.de>,
"Mike A. Chan" <mikechan@google.com>,
devicetree-discuss@lists.ozlabs.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-mmc@vger.kernel.org
Cc: csd_b@daudt.org, Christian Daudt <csd@broadcom.com>
Subject: [PATCH] ARM: mmc: bcm281xx SDHCI driver
Date: Wed, 8 May 2013 22:55:42 -0700 [thread overview]
Message-ID: <1368078942-31265-1-git-send-email-csd@broadcom.com> (raw)
Add SDHCI driver for the Broadcom 281xx SoCs. Also
add bindings for it into bcm281xx dts files.
Still missing:
- power managemement
Signed-off-by: Christian Daudt <csd@broadcom.com>
diff --git a/Documentation/devicetree/bindings/mmc/bcm,kona-sdhci.txt b/Documentation/devicetree/bindings/mmc/bcm,kona-sdhci.txt
new file mode 100644
index 0000000..ad1c4bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/bcm,kona-sdhci.txt
@@ -0,0 +1,16 @@
+Broadcom BCM281xx SDHCI driver
+
+This file documents differences between the core properties in mmc.txt
+and the properties in the bcm281xx driver.
+
+Required properties:
+- compatible : Should be "bcm,kona-sdhci"
+
+Example:
+
+sdio2: sdio@0x3f1a0000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f1a0000 0x10000>;
+ interrupts = <0x0 74 0x4>;
+};
+
diff --git a/arch/arm/boot/dts/bcm11351-brt.dts b/arch/arm/boot/dts/bcm11351-brt.dts
index 248067c..9ae3404 100644
--- a/arch/arm/boot/dts/bcm11351-brt.dts
+++ b/arch/arm/boot/dts/bcm11351-brt.dts
@@ -27,4 +27,21 @@
status = "okay";
};
+ sdio0: sdio@0x3f180000 {
+ max-frequency = <48000000>;
+ status = "okay";
+ };
+
+ sdio1: sdio@0x3f190000 {
+ non-removable;
+ max-frequency = <48000000>;
+ status = "okay";
+ };
+
+ sdio3: sdio@0x3f1b0000 {
+ max-frequency = <48000000>;
+ status = "okay";
+ };
+
+
};
diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi
index ad13588..6606b41 100644
--- a/arch/arm/boot/dts/bcm11351.dtsi
+++ b/arch/arm/boot/dts/bcm11351.dtsi
@@ -47,4 +47,33 @@
cache-unified;
cache-level = <2>;
};
+
+ sdio0: sdio@0x3f180000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f180000 0x10000>;
+ interrupts = <0x0 77 0x4>;
+ status = "disabled";
+ };
+
+ sdio1: sdio@0x3f190000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f190000 0x10000>;
+ interrupts = <0x0 76 0x4>;
+ status = "disabled";
+ };
+
+ sdio2: sdio@0x3f1a0000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f1a0000 0x10000>;
+ interrupts = <0x0 74 0x4>;
+ status = "disabled";
+ };
+
+ sdio3: sdio@0x3f1b0000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f1b0000 0x10000>;
+ interrupts = <0x0 73 0x4>;
+ status = "disabled";
+ };
+
};
diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig
index e3bf2d6..65edf6d 100644
--- a/arch/arm/configs/bcm_defconfig
+++ b/arch/arm/configs/bcm_defconfig
@@ -78,6 +78,13 @@ CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_BCM_KONA=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_TRIGGERS=y
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index d88219e..e067c5a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -238,6 +238,16 @@ config MMC_SDHCI_S3C_DMA
YMMV.
+config MMC_SDHCI_BCM_KONA
+ tristate "SDHCI support on Broadcom KONA platform"
+ depends on ARCH_BCM && MMC_SDHCI_PLTFM
+ help
+ This selects the Broadcom Kona Secure Digital Host Controller
+ Interface(SDHCI) support.
+ This is used in Broadcom mobile SoCs.
+
+ If you have a controller with this interface, say Y or M here.
+
config MMC_SDHCI_BCM2835
tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
depends on ARCH_BCM2835
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index c380e3c..a9f582b 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -59,6 +59,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_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
ifeq ($(CONFIG_CB710_DEBUG),y)
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
new file mode 100644
index 0000000..27188b9
--- /dev/null
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/version.h>
+
+#include "sdhci-pltfm.h"
+#include "sdhci.h"
+
+#define SDHCI_SOFT_RESET 0x01000000
+#define KONA_SDHOST_CORECTRL 0x8000
+#define KONA_SDHOST_CD_PINCTRL 0x00000008
+#define KONA_SDHOST_STOP_HCLK 0x00000004
+#define KONA_SDHOST_RESET 0x00000002
+#define KONA_SDHOST_EN 0x00000001
+
+#define KONA_SDHOST_CORESTAT 0x8004
+#define KONA_SDHOST_WP 0x00000002
+#define KONA_SDHOST_CD_SW 0x00000001
+
+#define KONA_SDHOST_COREIMR 0x8008
+#define KONA_SDHOST_IP 0x00000001
+
+#define KONA_SDHOST_COREISR 0x800C
+#define KONA_SDHOST_COREIMSR 0x8010
+#define KONA_SDHOST_COREDBG1 0x8014
+#define KONA_SDHOST_COREGPO_MASK 0x8018
+
+#define SD_DETECT_GPIO_DEBOUNCE_128MS 128
+
+#define KONA_MMC_AUTOSUSPEND_DELAY (50)
+
+struct sdhci_bcm_kona_cfg {
+ unsigned int max_freq;
+ int is_8bit;
+ int irq;
+ int cd_gpio;
+ int wp_gpio;
+ int non_removable;
+};
+
+struct sdhci_bcm_kona_dev {
+ struct sdhci_bcm_kona_cfg *cfg;
+ struct device *dev;
+ struct sdhci_host *host;
+ struct clk *peri_clk;
+ struct clk *sleep_clk;
+};
+
+
+static int sdhci_bcm_kona_sd_reset(struct sdhci_host *host)
+{
+ unsigned int val;
+ unsigned long timeout;
+
+ /* This timeout should be sufficent for core to reset */
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ /* reset the host using the top level reset */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val |= KONA_SDHOST_RESET;
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+
+ while (!(sdhci_readl(host, KONA_SDHOST_CORECTRL) & KONA_SDHOST_RESET)) {
+ if (time_is_before_jiffies(timeout)) {
+ pr_err("Error: sd host is stuck in reset!!!\n");
+ return -EFAULT;
+ }
+ }
+
+ /* bring the host out of reset */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val &= ~KONA_SDHOST_RESET;
+
+ /*
+ * Back-to-Back register write needs a delay of 1ms at bootup (min 10uS)
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ */
+ udelay(1000);
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+
+ return 0;
+}
+
+static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
+{
+ unsigned int val;
+
+ /* enable the interrupt from the IP core */
+ val = sdhci_readl(host, KONA_SDHOST_COREIMR);
+ val |= KONA_SDHOST_IP;
+ sdhci_writel(host, val, KONA_SDHOST_COREIMR);
+
+ /* Enable the AHB clock gating module to the host */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val |= KONA_SDHOST_EN;
+
+ /*
+ * Back-to-Back register write needs a delay of 1ms at bootup (min 10uS)
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ */
+ udelay(1000);
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+}
+
+/*
+ * Software emulation of the SD card insertion/removal. Set insert=1 for insert
+ * and insert=0 for removal. The card detection is done by GPIO. For Broadcom
+ * IP to function properly the bit 0 of CORESTAT register needs to be set/reset
+ * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
+ */
+static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
+{
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = pltfm_priv->priv;
+ u32 val;
+ unsigned long flags;
+
+ /* this function can be called from various contexts including ISR */
+ spin_lock_irqsave(&host->lock, flags);
+ /* Ensure SD bus scanning to detect media change */
+ host->mmc->rescan_disable = 0;
+
+ /*
+ * Back-to-Back register write needs a delay of min 10uS.
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ * We keep 20uS
+ */
+ udelay(20);
+ val = sdhci_readl(host, KONA_SDHOST_CORESTAT);
+
+ if (insert) {
+ if (kona_dev->cfg->wp_gpio >= 0) {
+ int wp_status = gpio_get_value(kona_dev->cfg->wp_gpio);
+
+ if (wp_status)
+ val |= KONA_SDHOST_WP;
+ else
+ val &= ~KONA_SDHOST_WP;
+ }
+
+ val |= KONA_SDHOST_CD_SW;
+ sdhci_writel(host, val, KONA_SDHOST_CORESTAT);
+ } else {
+ val &= ~KONA_SDHOST_CD_SW;
+ sdhci_writel(host, val, KONA_SDHOST_CORESTAT);
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return 0;
+}
+
+/*
+ * SD card detection interrupt handler
+ */
+static irqreturn_t sdhci_bcm_kona_pltfm_cd_interrupt(int irq, void *host)
+{
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = pltfm_priv->priv;
+
+ if (gpio_get_value_cansleep(kona_dev->cfg->cd_gpio) == 0) {
+ pr_debug("card inserted\n");
+ sdhci_bcm_kona_sd_card_emulate(host, 1);
+ } else {
+ pr_debug("card removed\n");
+ sdhci_bcm_kona_sd_card_emulate(host, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Get the base clock. Use central clock source for now. Not sure if different
+ * clock speed to each dev is allowed
+ */
+static unsigned int sdhci_bcm_kona_get_max_clk(struct sdhci_host *host)
+{
+ struct sdhci_bcm_kona_dev *kona_dev;
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ kona_dev = pltfm_priv->priv;
+
+ return kona_dev->cfg->max_freq;
+}
+
+static unsigned int sdhci_bcm_kona_get_timeout_clock(struct sdhci_host *host)
+{
+ return sdhci_bcm_kona_get_max_clk(host);
+}
+
+static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
+ u8 power_mode)
+{
+ if (power_mode == MMC_POWER_OFF)
+ return;
+ else
+ mdelay(10);
+}
+
+static struct sdhci_ops sdhci_bcm_kona_ops = {
+ .get_max_clock = sdhci_bcm_kona_get_max_clk,
+ .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,
+ .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks,
+};
+
+static struct sdhci_pltfm_data sdhci_pltfm_data_kona = {
+ .ops = &sdhci_bcm_kona_ops,
+ .quirks = SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_32BIT_DMA_ADDR |
+ SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE |
+ SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+};
+
+static const struct of_device_id sdhci_bcm_kona_of_match[] __initdata = {
+ { .compatible = "bcm,kona-sdhci", .data = &sdhci_pltfm_data_kona },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sdhci_bcm_kona_of_match);
+
+static struct sdhci_bcm_kona_cfg * __init sdhci_bcm_kona_parse_dt(
+ struct platform_device *pdev)
+{
+ struct sdhci_bcm_kona_cfg *cfg;
+ struct device_node *np = pdev->dev.of_node;
+ u32 temp;
+
+ if (!np)
+ return NULL;
+
+ cfg = devm_kzalloc(&pdev->dev, sizeof(*cfg), GFP_KERNEL);
+ if (!cfg) {
+ dev_err(&pdev->dev, "Can't allocate platform cfg\n");
+ return NULL;
+ }
+
+ cfg->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
+ cfg->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
+ if (of_get_property(np, "non-removable", NULL))
+ cfg->non_removable = 1;
+
+ if (of_property_read_u32(np, "bus-width", &temp) == 0 && temp == 8)
+ cfg->is_8bit = 1;
+
+ if (of_property_read_u32(np, "max-frequency", &cfg->max_freq)) {
+ dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n");
+ return NULL;
+ }
+ return cfg;
+}
+
+static int __init sdhci_bcm_kona_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct sdhci_bcm_kona_cfg *kona_cfg = NULL;
+ const struct sdhci_pltfm_data *plat_data;
+ struct sdhci_bcm_kona_dev *kona_dev = NULL;
+ struct sdhci_pltfm_host *pltfm_priv;
+ struct device *dev = &pdev->dev;
+ struct sdhci_host *host;
+ int ret;
+ unsigned int irq;
+ ret = 0;
+
+ match = of_match_device(sdhci_bcm_kona_of_match, &pdev->dev);
+ if (!match)
+ plat_data = &sdhci_pltfm_data_kona;
+ else
+ plat_data = match->data;
+
+ host = sdhci_pltfm_init(pdev, (struct sdhci_pltfm_data *)plat_data);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ kona_cfg = dev->platform_data;
+ if (!kona_cfg)
+ kona_cfg = sdhci_bcm_kona_parse_dt(pdev);
+
+ if (!kona_cfg) {
+ ret = -ENXIO;
+ goto err_pltfm_free;
+ }
+
+ dev_dbg(dev, "%s: inited. IOADDR=%p\n", __func__, host->ioaddr);
+
+ pltfm_priv = sdhci_priv(host);
+
+ kona_dev = devm_kzalloc(dev, sizeof(*kona_dev), GFP_KERNEL);
+ if (!kona_dev) {
+ dev_err(dev, "Can't allocate kona_dev\n");
+ ret = -ENOMEM;
+ goto err_pltfm_free;
+ }
+ kona_dev->cfg = kona_cfg;
+ kona_dev->dev = dev;
+ pltfm_priv->priv = kona_dev;
+
+ dev_dbg(dev, "non-removable=%c\n",
+ (kona_cfg->non_removable) ? 'Y' : 'N');
+ dev_dbg(dev, "cd_gpio %d, wp_gpio %d\n", kona_cfg->cd_gpio,
+ kona_cfg->wp_gpio);
+
+ if (kona_cfg->non_removable) {
+ host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ }
+
+ dev_dbg(dev, "is_8bit=%c\n", (kona_cfg->is_8bit) ? 'Y' : 'N');
+ if (kona_cfg->is_8bit)
+ host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+
+ ret = sdhci_bcm_kona_sd_reset(host);
+ if (ret)
+ goto err_pltfm_free;
+
+ sdhci_bcm_kona_sd_init(host);
+
+ ret = sdhci_add_host(host);
+ if (ret) {
+ dev_err(dev, "Failed sdhci_add_host\n");
+ goto err_reset;
+ }
+
+ /* if device is eMMC, emulate card insert right here */
+ if (kona_cfg->non_removable) {
+ ret = sdhci_bcm_kona_sd_card_emulate(host, 1);
+ if (ret) {
+ dev_err(dev,
+ "unable to emulate card insertion\n");
+ goto err_remove_host;
+ }
+ } else if (kona_dev->cfg->cd_gpio >= 0) {
+ ret = devm_gpio_request(dev, kona_dev->cfg->cd_gpio, "sdio cd");
+ if (ret < 0) {
+ dev_err(kona_dev->dev,
+ "Unable to request GPIO pin %d\n",
+ kona_dev->cfg->cd_gpio);
+ goto err_remove_host;
+ }
+
+ gpio_direction_input(kona_dev->cfg->cd_gpio);
+
+ /* Set debounce for SD Card detect to maximum value (128ms)
+ *
+ * NOTE-1: If gpio_set_debounce() returns error we still
+ * continue with the default debounce value set. Another reason
+ * for doing this is that on rhea-ray boards the SD Detect GPIO
+ * is on GPIO Expander and gpio_set_debounce() will return error
+ * and if we return error from here, then probe() would fail and
+ * SD detection would always fail.
+ *
+ * NOTE-2: We also give a msleep() of the "debounce" time here
+ * so that we give enough time for the debounce to stabilize
+ * before we read the gpio value in gpio_get_value_cansleep().
+ */
+ ret = gpio_set_debounce(kona_dev->cfg->cd_gpio,
+ (SD_DETECT_GPIO_DEBOUNCE_128MS * 1000));
+ if (ret < 0) {
+ dev_err(kona_dev->dev,
+ "%s: gpio set debounce failed."
+ "default debounce value assumed\n", __func__);
+ }
+
+ /* Sleep for 128ms to allow debounce to stabilize */
+ msleep(SD_DETECT_GPIO_DEBOUNCE_128MS);
+ /* request irq for cd_gpio after the gpio debounce is
+ * stabilized, otherwise, some bogus gpio interrupts might be
+ * triggered.
+ */
+ irq = gpio_to_irq(kona_dev->cfg->cd_gpio);
+ ret = devm_request_threaded_irq(dev,
+ irq,
+ NULL,
+ sdhci_bcm_kona_pltfm_cd_interrupt,
+ IRQF_TRIGGER_FALLING|
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT |
+ IRQF_NO_SUSPEND, "sdio cd", host);
+ if (ret) {
+ dev_err(kona_dev->dev,
+ "Unable to request card detection irq=%d"
+ " for gpio=%d ret=%d\n",
+ gpio_to_irq(kona_dev->cfg->cd_gpio),
+ kona_dev->cfg->cd_gpio, ret);
+ goto err_remove_host;
+ }
+ if (kona_dev->cfg->wp_gpio >= 0) {
+ ret = devm_gpio_request(dev,
+ kona_dev->cfg->wp_gpio, "sdio wp");
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Unable to request WP pin %d\n",
+ kona_dev->cfg->wp_gpio);
+ kona_dev->cfg->wp_gpio = -1;
+ } else {
+ gpio_direction_input(kona_dev->cfg->wp_gpio);
+ }
+ }
+
+ /*
+ * Since the card detection GPIO interrupt is configured to be
+ * edge sensitive, check the initial GPIO value here, emulate
+ * only if the card is present
+ */
+ if (gpio_get_value_cansleep(kona_dev->cfg->cd_gpio) == 0)
+ sdhci_bcm_kona_sd_card_emulate(host, 1);
+ }
+
+ dev_dbg(dev, "initialized properly\n");
+ return 0;
+
+err_remove_host:
+ sdhci_remove_host(host, 0);
+
+err_reset:
+ sdhci_bcm_kona_sd_reset(host);
+
+err_pltfm_free:
+ sdhci_pltfm_free(pdev);
+
+ dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret);
+ return ret;
+}
+
+static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = pltfm_host->priv;
+ int dead;
+ u32 scratch;
+
+ dead = 0;
+ scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+ if (scratch == (u32)-1)
+ dead = 1;
+ sdhci_remove_host(host, dead);
+
+ sdhci_free_host(host);
+
+ return 0;
+}
+
+static struct platform_driver sdhci_bcm_kona_driver = {
+ .driver = {
+ .name = "sdhci-kona",
+ .owner = THIS_MODULE,
+ .pm = SDHCI_PLTFM_PMOPS,
+ .of_match_table = of_match_ptr(sdhci_bcm_kona_of_match),
+ },
+ .probe = sdhci_bcm_kona_probe,
+ .remove = __exit_p(sdhci_bcm_kona_remove),
+};
+module_platform_driver(sdhci_bcm_kona_driver);
+
+MODULE_DESCRIPTION("SDHCI driver for Broadcom Kona platform");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
+
--
1.7.10.4
WARNING: multiple messages have this Message-ID (diff)
From: csd@broadcom.com (Christian Daudt)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] ARM: mmc: bcm281xx SDHCI driver
Date: Wed, 8 May 2013 22:55:42 -0700 [thread overview]
Message-ID: <1368078942-31265-1-git-send-email-csd@broadcom.com> (raw)
Add SDHCI driver for the Broadcom 281xx SoCs. Also
add bindings for it into bcm281xx dts files.
Still missing:
- power managemement
Signed-off-by: Christian Daudt <csd@broadcom.com>
diff --git a/Documentation/devicetree/bindings/mmc/bcm,kona-sdhci.txt b/Documentation/devicetree/bindings/mmc/bcm,kona-sdhci.txt
new file mode 100644
index 0000000..ad1c4bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/bcm,kona-sdhci.txt
@@ -0,0 +1,16 @@
+Broadcom BCM281xx SDHCI driver
+
+This file documents differences between the core properties in mmc.txt
+and the properties in the bcm281xx driver.
+
+Required properties:
+- compatible : Should be "bcm,kona-sdhci"
+
+Example:
+
+sdio2: sdio at 0x3f1a0000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f1a0000 0x10000>;
+ interrupts = <0x0 74 0x4>;
+};
+
diff --git a/arch/arm/boot/dts/bcm11351-brt.dts b/arch/arm/boot/dts/bcm11351-brt.dts
index 248067c..9ae3404 100644
--- a/arch/arm/boot/dts/bcm11351-brt.dts
+++ b/arch/arm/boot/dts/bcm11351-brt.dts
@@ -27,4 +27,21 @@
status = "okay";
};
+ sdio0: sdio at 0x3f180000 {
+ max-frequency = <48000000>;
+ status = "okay";
+ };
+
+ sdio1: sdio at 0x3f190000 {
+ non-removable;
+ max-frequency = <48000000>;
+ status = "okay";
+ };
+
+ sdio3: sdio at 0x3f1b0000 {
+ max-frequency = <48000000>;
+ status = "okay";
+ };
+
+
};
diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi
index ad13588..6606b41 100644
--- a/arch/arm/boot/dts/bcm11351.dtsi
+++ b/arch/arm/boot/dts/bcm11351.dtsi
@@ -47,4 +47,33 @@
cache-unified;
cache-level = <2>;
};
+
+ sdio0: sdio at 0x3f180000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f180000 0x10000>;
+ interrupts = <0x0 77 0x4>;
+ status = "disabled";
+ };
+
+ sdio1: sdio at 0x3f190000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f190000 0x10000>;
+ interrupts = <0x0 76 0x4>;
+ status = "disabled";
+ };
+
+ sdio2: sdio at 0x3f1a0000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f1a0000 0x10000>;
+ interrupts = <0x0 74 0x4>;
+ status = "disabled";
+ };
+
+ sdio3: sdio at 0x3f1b0000 {
+ compatible = "bcm,kona-sdhci";
+ reg = <0x3f1b0000 0x10000>;
+ interrupts = <0x0 73 0x4>;
+ status = "disabled";
+ };
+
};
diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig
index e3bf2d6..65edf6d 100644
--- a/arch/arm/configs/bcm_defconfig
+++ b/arch/arm/configs/bcm_defconfig
@@ -78,6 +78,13 @@ CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_BCM_KONA=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_TRIGGERS=y
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index d88219e..e067c5a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -238,6 +238,16 @@ config MMC_SDHCI_S3C_DMA
YMMV.
+config MMC_SDHCI_BCM_KONA
+ tristate "SDHCI support on Broadcom KONA platform"
+ depends on ARCH_BCM && MMC_SDHCI_PLTFM
+ help
+ This selects the Broadcom Kona Secure Digital Host Controller
+ Interface(SDHCI) support.
+ This is used in Broadcom mobile SoCs.
+
+ If you have a controller with this interface, say Y or M here.
+
config MMC_SDHCI_BCM2835
tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
depends on ARCH_BCM2835
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index c380e3c..a9f582b 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -59,6 +59,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_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
ifeq ($(CONFIG_CB710_DEBUG),y)
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
new file mode 100644
index 0000000..27188b9
--- /dev/null
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2013 Broadcom Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/version.h>
+
+#include "sdhci-pltfm.h"
+#include "sdhci.h"
+
+#define SDHCI_SOFT_RESET 0x01000000
+#define KONA_SDHOST_CORECTRL 0x8000
+#define KONA_SDHOST_CD_PINCTRL 0x00000008
+#define KONA_SDHOST_STOP_HCLK 0x00000004
+#define KONA_SDHOST_RESET 0x00000002
+#define KONA_SDHOST_EN 0x00000001
+
+#define KONA_SDHOST_CORESTAT 0x8004
+#define KONA_SDHOST_WP 0x00000002
+#define KONA_SDHOST_CD_SW 0x00000001
+
+#define KONA_SDHOST_COREIMR 0x8008
+#define KONA_SDHOST_IP 0x00000001
+
+#define KONA_SDHOST_COREISR 0x800C
+#define KONA_SDHOST_COREIMSR 0x8010
+#define KONA_SDHOST_COREDBG1 0x8014
+#define KONA_SDHOST_COREGPO_MASK 0x8018
+
+#define SD_DETECT_GPIO_DEBOUNCE_128MS 128
+
+#define KONA_MMC_AUTOSUSPEND_DELAY (50)
+
+struct sdhci_bcm_kona_cfg {
+ unsigned int max_freq;
+ int is_8bit;
+ int irq;
+ int cd_gpio;
+ int wp_gpio;
+ int non_removable;
+};
+
+struct sdhci_bcm_kona_dev {
+ struct sdhci_bcm_kona_cfg *cfg;
+ struct device *dev;
+ struct sdhci_host *host;
+ struct clk *peri_clk;
+ struct clk *sleep_clk;
+};
+
+
+static int sdhci_bcm_kona_sd_reset(struct sdhci_host *host)
+{
+ unsigned int val;
+ unsigned long timeout;
+
+ /* This timeout should be sufficent for core to reset */
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ /* reset the host using the top level reset */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val |= KONA_SDHOST_RESET;
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+
+ while (!(sdhci_readl(host, KONA_SDHOST_CORECTRL) & KONA_SDHOST_RESET)) {
+ if (time_is_before_jiffies(timeout)) {
+ pr_err("Error: sd host is stuck in reset!!!\n");
+ return -EFAULT;
+ }
+ }
+
+ /* bring the host out of reset */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val &= ~KONA_SDHOST_RESET;
+
+ /*
+ * Back-to-Back register write needs a delay of 1ms at bootup (min 10uS)
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ */
+ udelay(1000);
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+
+ return 0;
+}
+
+static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
+{
+ unsigned int val;
+
+ /* enable the interrupt from the IP core */
+ val = sdhci_readl(host, KONA_SDHOST_COREIMR);
+ val |= KONA_SDHOST_IP;
+ sdhci_writel(host, val, KONA_SDHOST_COREIMR);
+
+ /* Enable the AHB clock gating module to the host */
+ val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
+ val |= KONA_SDHOST_EN;
+
+ /*
+ * Back-to-Back register write needs a delay of 1ms at bootup (min 10uS)
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ */
+ udelay(1000);
+ sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
+}
+
+/*
+ * Software emulation of the SD card insertion/removal. Set insert=1 for insert
+ * and insert=0 for removal. The card detection is done by GPIO. For Broadcom
+ * IP to function properly the bit 0 of CORESTAT register needs to be set/reset
+ * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
+ */
+static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
+{
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = pltfm_priv->priv;
+ u32 val;
+ unsigned long flags;
+
+ /* this function can be called from various contexts including ISR */
+ spin_lock_irqsave(&host->lock, flags);
+ /* Ensure SD bus scanning to detect media change */
+ host->mmc->rescan_disable = 0;
+
+ /*
+ * Back-to-Back register write needs a delay of min 10uS.
+ * Back-to-Back writes to same register needs delay when SD bus clock
+ * is very low w.r.t AHB clock, mainly during boot-time and during card
+ * insert-removal.
+ * We keep 20uS
+ */
+ udelay(20);
+ val = sdhci_readl(host, KONA_SDHOST_CORESTAT);
+
+ if (insert) {
+ if (kona_dev->cfg->wp_gpio >= 0) {
+ int wp_status = gpio_get_value(kona_dev->cfg->wp_gpio);
+
+ if (wp_status)
+ val |= KONA_SDHOST_WP;
+ else
+ val &= ~KONA_SDHOST_WP;
+ }
+
+ val |= KONA_SDHOST_CD_SW;
+ sdhci_writel(host, val, KONA_SDHOST_CORESTAT);
+ } else {
+ val &= ~KONA_SDHOST_CD_SW;
+ sdhci_writel(host, val, KONA_SDHOST_CORESTAT);
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return 0;
+}
+
+/*
+ * SD card detection interrupt handler
+ */
+static irqreturn_t sdhci_bcm_kona_pltfm_cd_interrupt(int irq, void *host)
+{
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = pltfm_priv->priv;
+
+ if (gpio_get_value_cansleep(kona_dev->cfg->cd_gpio) == 0) {
+ pr_debug("card inserted\n");
+ sdhci_bcm_kona_sd_card_emulate(host, 1);
+ } else {
+ pr_debug("card removed\n");
+ sdhci_bcm_kona_sd_card_emulate(host, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Get the base clock. Use central clock source for now. Not sure if different
+ * clock speed to each dev is allowed
+ */
+static unsigned int sdhci_bcm_kona_get_max_clk(struct sdhci_host *host)
+{
+ struct sdhci_bcm_kona_dev *kona_dev;
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ kona_dev = pltfm_priv->priv;
+
+ return kona_dev->cfg->max_freq;
+}
+
+static unsigned int sdhci_bcm_kona_get_timeout_clock(struct sdhci_host *host)
+{
+ return sdhci_bcm_kona_get_max_clk(host);
+}
+
+static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
+ u8 power_mode)
+{
+ if (power_mode == MMC_POWER_OFF)
+ return;
+ else
+ mdelay(10);
+}
+
+static struct sdhci_ops sdhci_bcm_kona_ops = {
+ .get_max_clock = sdhci_bcm_kona_get_max_clk,
+ .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,
+ .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks,
+};
+
+static struct sdhci_pltfm_data sdhci_pltfm_data_kona = {
+ .ops = &sdhci_bcm_kona_ops,
+ .quirks = SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_32BIT_DMA_ADDR |
+ SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE |
+ SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+};
+
+static const struct of_device_id sdhci_bcm_kona_of_match[] __initdata = {
+ { .compatible = "bcm,kona-sdhci", .data = &sdhci_pltfm_data_kona },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sdhci_bcm_kona_of_match);
+
+static struct sdhci_bcm_kona_cfg * __init sdhci_bcm_kona_parse_dt(
+ struct platform_device *pdev)
+{
+ struct sdhci_bcm_kona_cfg *cfg;
+ struct device_node *np = pdev->dev.of_node;
+ u32 temp;
+
+ if (!np)
+ return NULL;
+
+ cfg = devm_kzalloc(&pdev->dev, sizeof(*cfg), GFP_KERNEL);
+ if (!cfg) {
+ dev_err(&pdev->dev, "Can't allocate platform cfg\n");
+ return NULL;
+ }
+
+ cfg->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
+ cfg->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
+ if (of_get_property(np, "non-removable", NULL))
+ cfg->non_removable = 1;
+
+ if (of_property_read_u32(np, "bus-width", &temp) == 0 && temp == 8)
+ cfg->is_8bit = 1;
+
+ if (of_property_read_u32(np, "max-frequency", &cfg->max_freq)) {
+ dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n");
+ return NULL;
+ }
+ return cfg;
+}
+
+static int __init sdhci_bcm_kona_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct sdhci_bcm_kona_cfg *kona_cfg = NULL;
+ const struct sdhci_pltfm_data *plat_data;
+ struct sdhci_bcm_kona_dev *kona_dev = NULL;
+ struct sdhci_pltfm_host *pltfm_priv;
+ struct device *dev = &pdev->dev;
+ struct sdhci_host *host;
+ int ret;
+ unsigned int irq;
+ ret = 0;
+
+ match = of_match_device(sdhci_bcm_kona_of_match, &pdev->dev);
+ if (!match)
+ plat_data = &sdhci_pltfm_data_kona;
+ else
+ plat_data = match->data;
+
+ host = sdhci_pltfm_init(pdev, (struct sdhci_pltfm_data *)plat_data);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ kona_cfg = dev->platform_data;
+ if (!kona_cfg)
+ kona_cfg = sdhci_bcm_kona_parse_dt(pdev);
+
+ if (!kona_cfg) {
+ ret = -ENXIO;
+ goto err_pltfm_free;
+ }
+
+ dev_dbg(dev, "%s: inited. IOADDR=%p\n", __func__, host->ioaddr);
+
+ pltfm_priv = sdhci_priv(host);
+
+ kona_dev = devm_kzalloc(dev, sizeof(*kona_dev), GFP_KERNEL);
+ if (!kona_dev) {
+ dev_err(dev, "Can't allocate kona_dev\n");
+ ret = -ENOMEM;
+ goto err_pltfm_free;
+ }
+ kona_dev->cfg = kona_cfg;
+ kona_dev->dev = dev;
+ pltfm_priv->priv = kona_dev;
+
+ dev_dbg(dev, "non-removable=%c\n",
+ (kona_cfg->non_removable) ? 'Y' : 'N');
+ dev_dbg(dev, "cd_gpio %d, wp_gpio %d\n", kona_cfg->cd_gpio,
+ kona_cfg->wp_gpio);
+
+ if (kona_cfg->non_removable) {
+ host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ }
+
+ dev_dbg(dev, "is_8bit=%c\n", (kona_cfg->is_8bit) ? 'Y' : 'N');
+ if (kona_cfg->is_8bit)
+ host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+
+ ret = sdhci_bcm_kona_sd_reset(host);
+ if (ret)
+ goto err_pltfm_free;
+
+ sdhci_bcm_kona_sd_init(host);
+
+ ret = sdhci_add_host(host);
+ if (ret) {
+ dev_err(dev, "Failed sdhci_add_host\n");
+ goto err_reset;
+ }
+
+ /* if device is eMMC, emulate card insert right here */
+ if (kona_cfg->non_removable) {
+ ret = sdhci_bcm_kona_sd_card_emulate(host, 1);
+ if (ret) {
+ dev_err(dev,
+ "unable to emulate card insertion\n");
+ goto err_remove_host;
+ }
+ } else if (kona_dev->cfg->cd_gpio >= 0) {
+ ret = devm_gpio_request(dev, kona_dev->cfg->cd_gpio, "sdio cd");
+ if (ret < 0) {
+ dev_err(kona_dev->dev,
+ "Unable to request GPIO pin %d\n",
+ kona_dev->cfg->cd_gpio);
+ goto err_remove_host;
+ }
+
+ gpio_direction_input(kona_dev->cfg->cd_gpio);
+
+ /* Set debounce for SD Card detect to maximum value (128ms)
+ *
+ * NOTE-1: If gpio_set_debounce() returns error we still
+ * continue with the default debounce value set. Another reason
+ * for doing this is that on rhea-ray boards the SD Detect GPIO
+ * is on GPIO Expander and gpio_set_debounce() will return error
+ * and if we return error from here, then probe() would fail and
+ * SD detection would always fail.
+ *
+ * NOTE-2: We also give a msleep() of the "debounce" time here
+ * so that we give enough time for the debounce to stabilize
+ * before we read the gpio value in gpio_get_value_cansleep().
+ */
+ ret = gpio_set_debounce(kona_dev->cfg->cd_gpio,
+ (SD_DETECT_GPIO_DEBOUNCE_128MS * 1000));
+ if (ret < 0) {
+ dev_err(kona_dev->dev,
+ "%s: gpio set debounce failed."
+ "default debounce value assumed\n", __func__);
+ }
+
+ /* Sleep for 128ms to allow debounce to stabilize */
+ msleep(SD_DETECT_GPIO_DEBOUNCE_128MS);
+ /* request irq for cd_gpio after the gpio debounce is
+ * stabilized, otherwise, some bogus gpio interrupts might be
+ * triggered.
+ */
+ irq = gpio_to_irq(kona_dev->cfg->cd_gpio);
+ ret = devm_request_threaded_irq(dev,
+ irq,
+ NULL,
+ sdhci_bcm_kona_pltfm_cd_interrupt,
+ IRQF_TRIGGER_FALLING|
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT |
+ IRQF_NO_SUSPEND, "sdio cd", host);
+ if (ret) {
+ dev_err(kona_dev->dev,
+ "Unable to request card detection irq=%d"
+ " for gpio=%d ret=%d\n",
+ gpio_to_irq(kona_dev->cfg->cd_gpio),
+ kona_dev->cfg->cd_gpio, ret);
+ goto err_remove_host;
+ }
+ if (kona_dev->cfg->wp_gpio >= 0) {
+ ret = devm_gpio_request(dev,
+ kona_dev->cfg->wp_gpio, "sdio wp");
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Unable to request WP pin %d\n",
+ kona_dev->cfg->wp_gpio);
+ kona_dev->cfg->wp_gpio = -1;
+ } else {
+ gpio_direction_input(kona_dev->cfg->wp_gpio);
+ }
+ }
+
+ /*
+ * Since the card detection GPIO interrupt is configured to be
+ * edge sensitive, check the initial GPIO value here, emulate
+ * only if the card is present
+ */
+ if (gpio_get_value_cansleep(kona_dev->cfg->cd_gpio) == 0)
+ sdhci_bcm_kona_sd_card_emulate(host, 1);
+ }
+
+ dev_dbg(dev, "initialized properly\n");
+ return 0;
+
+err_remove_host:
+ sdhci_remove_host(host, 0);
+
+err_reset:
+ sdhci_bcm_kona_sd_reset(host);
+
+err_pltfm_free:
+ sdhci_pltfm_free(pdev);
+
+ dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret);
+ return ret;
+}
+
+static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = pltfm_host->priv;
+ int dead;
+ u32 scratch;
+
+ dead = 0;
+ scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+ if (scratch == (u32)-1)
+ dead = 1;
+ sdhci_remove_host(host, dead);
+
+ sdhci_free_host(host);
+
+ return 0;
+}
+
+static struct platform_driver sdhci_bcm_kona_driver = {
+ .driver = {
+ .name = "sdhci-kona",
+ .owner = THIS_MODULE,
+ .pm = SDHCI_PLTFM_PMOPS,
+ .of_match_table = of_match_ptr(sdhci_bcm_kona_of_match),
+ },
+ .probe = sdhci_bcm_kona_probe,
+ .remove = __exit_p(sdhci_bcm_kona_remove),
+};
+module_platform_driver(sdhci_bcm_kona_driver);
+
+MODULE_DESCRIPTION("SDHCI driver for Broadcom Kona platform");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
+
--
1.7.10.4
next reply other threads:[~2013-05-09 5:55 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-05-09 5:55 Christian Daudt [this message]
2013-05-09 5:55 ` [PATCH] ARM: mmc: bcm281xx SDHCI driver Christian Daudt
2013-05-09 6:35 ` Jean-Christophe PLAGNIOL-VILLARD
2013-05-09 6:35 ` Jean-Christophe PLAGNIOL-VILLARD
2013-05-09 6:35 ` Jean-Christophe PLAGNIOL-VILLARD
2013-05-10 7:23 ` Christian Daudt
2013-05-10 7:23 ` Christian Daudt
2013-05-10 7:29 ` Christian Daudt
2013-05-10 7:29 ` Christian Daudt
2013-05-10 10:26 ` Jean-Christophe PLAGNIOL-VILLARD
2013-05-10 10:26 ` Jean-Christophe PLAGNIOL-VILLARD
2013-05-10 14:37 ` Jean-Christophe PLAGNIOL-VILLARD
2013-05-10 14:37 ` Jean-Christophe PLAGNIOL-VILLARD
2013-05-10 14:37 ` Jean-Christophe PLAGNIOL-VILLARD
2013-05-10 7:45 ` Christian Daudt
2013-05-10 7:45 ` Christian Daudt
2013-05-11 4:28 ` Stephen Warren
2013-05-11 4:28 ` Stephen Warren
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1368078942-31265-1-git-send-email-csd@broadcom.com \
--to=csd@broadcom.com \
--cc=arnd@arndb.de \
--cc=cjb@laptop.org \
--cc=csd_b@daudt.org \
--cc=devicetree-discuss@lists.ozlabs.org \
--cc=grant.likely@secretlab.ca \
--cc=gregkh@linuxfoundation.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mmc@vger.kernel.org \
--cc=linux@arm.linux.org.uk \
--cc=ludovic.desroches@atmel.com \
--cc=mikechan@google.com \
--cc=olof@lixom.net \
--cc=rob.herring@calxeda.com \
--cc=rob@landley.net \
--cc=swarren@nvidia.com \
--cc=wei_wang@realsil.com.cn \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.