summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2013-12-06 08:23:27 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2013-12-06 08:23:27 +0100
commit6d8a85d6c35a6e7fb08163e7235211c8e7370d3a (patch)
treec3ba788038fa7aad35976c8d3ce75a9f033547a0 /drivers
parentb1e0f08cb0cd3b568b4e6b55a599fbcfd21a517c (diff)
parent1a476f8fb3be55e3a419e8c4d3206d5398e4ddd5 (diff)
downloadbarebox-6d8a85d6c35a6e7fb08163e7235211c8e7370d3a.tar.gz
barebox-6d8a85d6c35a6e7fb08163e7235211c8e7370d3a.tar.xz
Merge branch 'for-next/tegra'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/tegra/clk-tegra20.c25
-rw-r--r--drivers/gpio/gpio-tegra.c2
-rw-r--r--drivers/mci/Kconfig7
-rw-r--r--drivers/mci/Makefile1
-rw-r--r--drivers/mci/tegra-sdmmc.c464
-rw-r--r--drivers/of/of_gpio.c2
6 files changed, 498 insertions, 3 deletions
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index e70f99a1fc..f68c811a8d 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -295,6 +295,20 @@ static void tegra20_periph_init(void)
clks[uarte] = tegra_clk_register_periph_nodiv("uarte", mux_pllpcm_clkm,
ARRAY_SIZE(mux_pllpcm_clkm), car_base,
CRC_CLK_SOURCE_UARTE, uarte, TEGRA_PERIPH_ON_APB);
+
+ /* peripheral clocks with a divider */
+ clks[sdmmc1] = tegra_clk_register_periph("sdmmc1", mux_pllpcm_clkm,
+ ARRAY_SIZE(mux_pllpcm_clkm), car_base,
+ CRC_CLK_SOURCE_SDMMC1, sdmmc1, 1);
+ clks[sdmmc2] = tegra_clk_register_periph("sdmmc2", mux_pllpcm_clkm,
+ ARRAY_SIZE(mux_pllpcm_clkm), car_base,
+ CRC_CLK_SOURCE_SDMMC2, sdmmc2, 1);
+ clks[sdmmc3] = tegra_clk_register_periph("sdmmc3", mux_pllpcm_clkm,
+ ARRAY_SIZE(mux_pllpcm_clkm), car_base,
+ CRC_CLK_SOURCE_SDMMC3, sdmmc3, 1);
+ clks[sdmmc4] = tegra_clk_register_periph("sdmmc4", mux_pllpcm_clkm,
+ ARRAY_SIZE(mux_pllpcm_clkm), car_base,
+ CRC_CLK_SOURCE_SDMMC4, sdmmc4, 1);
}
static struct tegra_clk_init_table init_table[] = {
@@ -310,6 +324,10 @@ static struct tegra_clk_init_table init_table[] = {
{uartc, pll_p, 0, 1},
{uartd, pll_p, 0, 1},
{uarte, pll_p, 0, 1},
+ {sdmmc1, pll_p, 48000000, 0},
+ {sdmmc2, pll_p, 48000000, 0},
+ {sdmmc3, pll_p, 48000000, 0},
+ {sdmmc4, pll_p, 48000000, 0},
{clk_max, clk_max, 0, 0}, /* sentinel */
};
@@ -325,6 +343,13 @@ static int tegra20_car_probe(struct device_d *dev)
tegra_init_from_table(init_table, clks, clk_max);
+ /* speed up system bus */
+ writel(CRC_SCLK_BURST_POLICY_SYS_STATE_RUN <<
+ CRC_SCLK_BURST_POLICY_SYS_STATE_SHIFT |
+ CRC_SCLK_BURST_POLICY_SRC_PLLC_OUT1 <<
+ CRC_SCLK_BURST_POLICY_RUN_SRC_SHIFT,
+ car_base + CRC_SCLK_BURST_POLICY);
+
clk_data.clks = clks;
clk_data.clk_num = ARRAY_SIZE(clks);
of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 1e00f5ecc6..e84c71a234 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -106,12 +106,10 @@ static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset)
{
/* If gpio is in output mode then read from the out value */
if ((tegra_gpio_readl(GPIO_OE(offset)) >> GPIO_BIT(offset)) & 1) {
- printf("GPIO output mode\n");
return (tegra_gpio_readl(GPIO_OUT(offset)) >>
GPIO_BIT(offset)) & 0x1;
}
- printf("GPIO input mode\n");
return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1;
}
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 1efb40ac01..dbcc38d097 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -108,6 +108,13 @@ config MCI_MMCI
Enable this entry to add support to read and write SD cards on a
ARM AMBA PL180.
+config MCI_TEGRA
+ bool "Tegra SD/MMC"
+ depends on ARCH_TEGRA
+ help
+ Enable this to support SD and MMC card read/write on a Tegra based
+ systems.
+
config MCI_SPI
bool "MMC/SD over SPI"
select CRC7
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 421ca9f26a..1e8443c1af 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_MCI_MXS) += mxs.o
obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
obj-$(CONFIG_MCI_PXA) += pxamci.o
obj-$(CONFIG_MCI_S3C) += s3c.o
+obj-$(CONFIG_MCI_TEGRA) += tegra-sdmmc.o
obj-$(CONFIG_MCI_SPI) += mci_spi.o
obj-$(CONFIG_MCI_DW) += dw_mmc.o
obj-$(CONFIG_MCI_MMCI) += mmci.o
diff --git a/drivers/mci/tegra-sdmmc.c b/drivers/mci/tegra-sdmmc.c
new file mode 100644
index 0000000000..64c4550754
--- /dev/null
+++ b/drivers/mci/tegra-sdmmc.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2013 Lucas Stach <l.stach@pengutronix.de>
+ *
+ * Partly based on code (C) Copyright 2010-2013
+ * NVIDIA Corporation <www.nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <asm/mmu.h>
+#include <common.h>
+#include <clock.h>
+#include <driver.h>
+#include <gpio.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <mci.h>
+#include <of_gpio.h>
+#include <linux/clk.h>
+
+#include "sdhci.h"
+
+#define TEGRA_SDMMC_PRESENT_STATE 0x024
+#define TEGRA_SDMMC_PRESENT_STATE_CMD_INHIBIT_CMD (1 << 0)
+#define TEGRA_SDMMC_PRESENT_STATE_CMD_INHIBIT_DAT (1 << 1)
+
+#define TEGRA_SDMMC_PWR_CNTL 0x028
+#define TEGRA_SDMMC_PWR_CNTL_SD_BUS (1 << 8)
+#define TEGRA_SDMMC_PWR_CNTL_33_V (7 << 9)
+
+#define TEGRA_SDMMC_CLK_CNTL 0x02c
+#define TEGRA_SDMMC_CLK_CNTL_SW_RESET_FOR_ALL (1 << 24)
+#define TEGRA_SDMMC_CLK_CNTL_SD_CLOCK_EN (1 << 2)
+#define TEGRA_SDMMC_CLK_INTERNAL_CLOCK_STABLE (1 << 1)
+#define TEGRA_SDMMC_CLK_CNTL_INTERNAL_CLOCK_EN (1 << 0)
+
+#define TEGRA_SDMMC_INTERRUPT_STATUS_ERR_INTERRUPT (1 << 15)
+#define TEGRA_SDMMC_INTERRUPT_STATUS_CMD_TIMEOUT (1 << 16)
+
+#define TEGRA_SDMMC_INT_STAT_EN 0x034
+#define TEGRA_SDMMC_INT_STAT_EN_CMD_COMPLETE (1 << 0)
+#define TEGRA_SDMMC_INT_STAT_EN_XFER_COMPLETE (1 << 1)
+#define TEGRA_SDMMC_INT_STAT_EN_DMA_INTERRUPT (1 << 3)
+#define TEGRA_SDMMC_INT_STAT_EN_BUFFER_WRITE_READY (1 << 4)
+#define TEGRA_SDMMC_INT_STAT_EN_BUFFER_READ_READY (1 << 5)
+
+#define TEGRA_SDMMC_INT_SIG_EN 0x038
+#define TEGRA_SDMMC_INT_SIG_EN_XFER_COMPLETE (1 << 1)
+
+struct tegra_sdmmc_host {
+ struct mci_host mci;
+ void __iomem *regs;
+ struct clk *clk;
+ int gpio_cd, gpio_pwr;
+};
+#define to_tegra_sdmmc_host(mci) container_of(mci, struct tegra_sdmmc_host, mci)
+
+static int tegra_sdmmc_wait_inhibit(struct tegra_sdmmc_host *host,
+ struct mci_cmd *cmd, struct mci_data *data,
+ unsigned int timeout)
+{
+ u32 val = TEGRA_SDMMC_PRESENT_STATE_CMD_INHIBIT_CMD;
+
+ /*
+ * We shouldn't wait for data inhibit for stop commands, even
+ * though they might use busy signaling
+ */
+ if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY))
+ val |= TEGRA_SDMMC_PRESENT_STATE_CMD_INHIBIT_DAT;
+
+ wait_on_timeout(timeout * MSECOND,
+ !(readl(host->regs + TEGRA_SDMMC_PRESENT_STATE) & val));
+
+ return 0;
+}
+
+static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
+ u32 val = 0;
+ int ret;
+
+ ret = tegra_sdmmc_wait_inhibit(host, cmd, data, 10);
+ if (ret < 0)
+ return ret;
+
+ /* Set up for a data transfer if we have one */
+ if (data) {
+ if (data->flags & MMC_DATA_WRITE) {
+ dma_flush_range((unsigned long)data->src,
+ (unsigned long)(data->src +
+ data->blocks * 512));
+ writel((u32)data->src, host->regs + SDHCI_DMA_ADDRESS);
+ } else {
+ dma_clean_range((unsigned long)data->src,
+ (unsigned long)(data->src +
+ data->blocks * 512));
+ writel((u32)data->dest, host->regs + SDHCI_DMA_ADDRESS);
+ }
+
+ writel((7 << 12) | data->blocks << 16 | data->blocksize,
+ host->regs + SDHCI_BLOCK_SIZE__BLOCK_COUNT);
+ }
+
+ writel(cmd->cmdarg, host->regs + SDHCI_ARGUMENT);
+
+ if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
+ return -1;
+
+ if (data) {
+ if (data->blocks > 1)
+ val |= TRANSFER_MODE_MSBSEL;
+
+ if (data->flags & MMC_DATA_READ)
+ val |= TRANSFER_MODE_DTDSEL;
+
+ val |= TRANSFER_MODE_DMAEN | TRANSFER_MODE_BCEN;
+ }
+
+ if (!(cmd->resp_type & MMC_RSP_PRESENT))
+ val |= COMMAND_RSPTYP_NONE;
+ else if (cmd->resp_type & MMC_RSP_136)
+ val |= COMMAND_RSPTYP_136;
+ else if (cmd->resp_type & MMC_RSP_BUSY)
+ val |= COMMAND_RSPTYP_48_BUSY;
+ else
+ val |= COMMAND_RSPTYP_48;
+
+ if (cmd->resp_type & MMC_RSP_CRC)
+ val |= COMMAND_CCCEN;
+ if (cmd->resp_type & MMC_RSP_OPCODE)
+ val |= COMMAND_CICEN;
+
+ if (data)
+ val |= COMMAND_DPSEL;
+
+ writel(COMMAND_CMD(cmd->cmdidx) | val,
+ host->regs + SDHCI_TRANSFER_MODE__COMMAND);
+
+ ret = wait_on_timeout(100 * MSECOND,
+ (val = readl(host->regs + SDHCI_INT_STATUS))
+ & IRQSTAT_CC);
+
+ if (ret) {
+ writel(val, host->regs + SDHCI_INT_STATUS);
+ return ret;
+ }
+
+ if ((val & IRQSTAT_CC) && !data)
+ writel(val, host->regs + SDHCI_INT_STATUS);
+
+ if (val & TEGRA_SDMMC_INTERRUPT_STATUS_CMD_TIMEOUT) {
+ /* Timeout Error */
+ dev_dbg(mci->hw_dev, "timeout: %08x cmd %d\n", val, cmd->cmdidx);
+ writel(val, host->regs + SDHCI_INT_STATUS);
+ return -ETIMEDOUT;
+ } else if (val & TEGRA_SDMMC_INTERRUPT_STATUS_ERR_INTERRUPT) {
+ /* Error Interrupt */
+ dev_dbg(mci->hw_dev, "error: %08x cmd %d\n", val, cmd->cmdidx);
+ writel(val, host->regs + SDHCI_INT_STATUS);
+ return -EIO;
+ }
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (cmd->resp_type & MMC_RSP_136) {
+ u32 cmdrsp[4];
+
+ cmdrsp[3] = readl(host->regs + SDHCI_RESPONSE_3);
+ cmdrsp[2] = readl(host->regs + SDHCI_RESPONSE_2);
+ cmdrsp[1] = readl(host->regs + SDHCI_RESPONSE_1);
+ cmdrsp[0] = readl(host->regs + SDHCI_RESPONSE_0);
+ cmd->response[0] = (cmdrsp[3] << 8) | (cmdrsp[2] >> 24);
+ cmd->response[1] = (cmdrsp[2] << 8) | (cmdrsp[1] >> 24);
+ cmd->response[2] = (cmdrsp[1] << 8) | (cmdrsp[0] >> 24);
+ cmd->response[3] = (cmdrsp[0] << 8);
+ } else if (cmd->resp_type & MMC_RSP_BUSY) {
+ ret = wait_on_timeout(100 * MSECOND,
+ readl(host->regs + TEGRA_SDMMC_PRESENT_STATE)
+ & (1 << 20));
+
+ if (ret) {
+ dev_err(mci->hw_dev, "card is still busy\n");
+ writel(val, host->regs + SDHCI_INT_STATUS);
+ return ret;
+ }
+
+ cmd->response[0] = readl(host->regs + SDHCI_RESPONSE_0);
+ } else {
+ cmd->response[0] = readl(host->regs + SDHCI_RESPONSE_0);
+ }
+ }
+
+ if (data) {
+ uint64_t start = get_time_ns();
+
+ while (1) {
+ val = readl(host->regs + SDHCI_INT_STATUS);
+
+ if (val & TEGRA_SDMMC_INTERRUPT_STATUS_ERR_INTERRUPT) {
+ /* Error Interrupt */
+ writel(val, host->regs + SDHCI_INT_STATUS);
+ dev_err(mci->hw_dev,
+ "error during transfer: 0x%08x\n", val);
+ return -EIO;
+ } else if (val & IRQSTAT_DINT) {
+ /*
+ * DMA Interrupt, restart the transfer where
+ * it was interrupted.
+ */
+ u32 address = readl(host->regs +
+ SDHCI_DMA_ADDRESS);
+
+ writel(IRQSTAT_DINT,
+ host->regs + SDHCI_INT_STATUS);
+ writel(address, host->regs + SDHCI_DMA_ADDRESS);
+ } else if (val & IRQSTAT_TC) {
+ /* Transfer Complete */;
+ break;
+ } else if (is_timeout(start, 2 * SECOND)) {
+ writel(val, host->regs + SDHCI_INT_STATUS);
+ dev_err(mci->hw_dev, "MMC Timeout\n"
+ " Interrupt status 0x%08x\n"
+ " Interrupt status enable 0x%08x\n"
+ " Interrupt signal enable 0x%08x\n"
+ " Present status 0x%08x\n",
+ val,
+ readl(host->regs + SDHCI_INT_ENABLE),
+ readl(host->regs + SDHCI_SIGNAL_ENABLE),
+ readl(host->regs + SDHCI_PRESENT_STATE));
+ return -ETIMEDOUT;
+ }
+ }
+ writel(val, host->regs + SDHCI_INT_STATUS);
+
+ if (data->flags & MMC_DATA_READ) {
+ dma_inv_range((unsigned long)data->dest,
+ (unsigned long)(data->dest +
+ data->blocks * 512));
+ }
+ }
+
+ return 0;
+}
+
+static void tegra_sdmmc_set_clock(struct tegra_sdmmc_host *host, u32 clock)
+{
+ u32 prediv = 1, adjusted_clock = clock, val;
+
+ while (adjusted_clock < 3200000) {
+ prediv *= 2;
+ adjusted_clock = clock * prediv * 2;
+ }
+
+ /* clear clock related bits */
+ val = readl(host->regs + TEGRA_SDMMC_CLK_CNTL);
+ val &= 0xffff0000;
+ writel(val, host->regs + TEGRA_SDMMC_CLK_CNTL);
+
+ /* set new frequency */
+ val |= prediv << 8;
+ val |= TEGRA_SDMMC_CLK_CNTL_INTERNAL_CLOCK_EN;
+ writel(val, host->regs + TEGRA_SDMMC_CLK_CNTL);
+
+ clk_set_rate(host->clk, adjusted_clock);
+
+ /* wait for controller to settle */
+ wait_on_timeout(10 * MSECOND,
+ !(readl(host->regs + TEGRA_SDMMC_CLK_CNTL) &
+ TEGRA_SDMMC_CLK_INTERNAL_CLOCK_STABLE));
+
+ /* enable card clock */
+ val |= TEGRA_SDMMC_CLK_CNTL_SD_CLOCK_EN;
+ writel(val, host->regs + TEGRA_SDMMC_CLK_CNTL);
+}
+
+static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
+ u32 val;
+
+ /* set clock */
+ if (ios->clock)
+ tegra_sdmmc_set_clock(host, ios->clock);
+
+ /* set bus width */
+ val = readl(host->regs + TEGRA_SDMMC_PWR_CNTL);
+ val &= ~(0x21);
+
+ if (ios->bus_width == MMC_BUS_WIDTH_8)
+ val |= (1 << 5);
+ else if (ios->bus_width == MMC_BUS_WIDTH_4)
+ val |= (1 << 1);
+
+ writel(val, host->regs + TEGRA_SDMMC_PWR_CNTL);
+}
+
+static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
+{
+ struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
+ void __iomem *regs = host->regs;
+ u32 val;
+ int ret;
+
+ /* reset controller */
+ writel(TEGRA_SDMMC_CLK_CNTL_SW_RESET_FOR_ALL,
+ regs + TEGRA_SDMMC_CLK_CNTL);
+
+ ret = wait_on_timeout(100 * MSECOND,
+ !(readl(regs + TEGRA_SDMMC_CLK_CNTL) &
+ TEGRA_SDMMC_CLK_CNTL_SW_RESET_FOR_ALL));
+ if (ret) {
+ dev_err(dev, "timeout while reset\n");
+ return ret;
+ }
+
+ /* set power */
+ val = readl(regs + TEGRA_SDMMC_PWR_CNTL);
+ val &= ~(0xff << 8);
+ val |= TEGRA_SDMMC_PWR_CNTL_33_V | TEGRA_SDMMC_PWR_CNTL_SD_BUS;
+ writel(val, regs + TEGRA_SDMMC_PWR_CNTL);
+
+ /* setup signaling */
+ writel(0xffffffff, regs + TEGRA_SDMMC_INT_STAT_EN);
+ writel(0xffffffff, regs + TEGRA_SDMMC_INT_SIG_EN);
+
+ writel(0xe << 16, regs + TEGRA_SDMMC_CLK_CNTL);
+
+ val = readl(regs + TEGRA_SDMMC_INT_STAT_EN);
+ val &= ~(0xffff);
+ val = (TEGRA_SDMMC_INT_STAT_EN_CMD_COMPLETE |
+ TEGRA_SDMMC_INT_STAT_EN_XFER_COMPLETE |
+ TEGRA_SDMMC_INT_STAT_EN_DMA_INTERRUPT |
+ TEGRA_SDMMC_INT_STAT_EN_BUFFER_WRITE_READY |
+ TEGRA_SDMMC_INT_STAT_EN_BUFFER_READ_READY);
+ writel(val, regs + TEGRA_SDMMC_INT_STAT_EN);
+
+ val = readl(regs + TEGRA_SDMMC_INT_SIG_EN);
+ val &= ~(0xffff);
+ val = TEGRA_SDMMC_INT_SIG_EN_XFER_COMPLETE;
+ writel(val, regs + TEGRA_SDMMC_INT_SIG_EN);
+
+ tegra_sdmmc_set_clock(host, 400000);
+
+ return 0;
+}
+
+static int tegra_sdmmc_card_present(struct mci_host *mci)
+{
+ struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
+ int ret;
+
+ if (gpio_is_valid(host->gpio_cd)) {
+ ret = gpio_direction_input(host->gpio_cd);
+ if (ret)
+ return 0;
+ return gpio_get_value(host->gpio_cd) ? 0 : 1;
+ }
+
+ return !(readl(host->regs + SDHCI_PRESENT_STATE) & PRSSTAT_WPSPL);
+}
+
+static int tegra_sdmmc_detect(struct device_d *dev)
+{
+ struct tegra_sdmmc_host *host = dev->priv;
+
+ return mci_detect_card(&host->mci);
+}
+
+static void tegra_sdmmc_parse_dt(struct tegra_sdmmc_host *host)
+{
+ struct device_node *np = host->mci.hw_dev->device_node;
+
+ host->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
+ host->gpio_pwr = of_get_named_gpio(np, "power-gpios", 0);
+ mci_of_parse(&host->mci);
+}
+
+static int tegra_sdmmc_probe(struct device_d *dev)
+{
+ struct tegra_sdmmc_host *host;
+ struct mci_host *mci;
+ int ret;
+
+ host = xzalloc(sizeof(*host));
+ mci = &host->mci;
+
+ host->clk = clk_get(dev, NULL);
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
+
+ host->regs = dev_request_mem_region(dev, 0);
+ if (!host->regs) {
+ dev_err(dev, "could not get iomem region\n");
+ return -ENODEV;
+ }
+
+ mci->hw_dev = dev;
+ mci->f_max = 48000000;
+ mci->f_min = 375000;
+ tegra_sdmmc_parse_dt(host);
+
+ if (gpio_is_valid(host->gpio_pwr)) {
+ ret = gpio_request(host->gpio_pwr, "tegra_sdmmc_power");
+ if (ret) {
+ dev_err(dev, "failed to allocate power gpio\n");
+ return -ENODEV;
+ }
+ gpio_direction_output(host->gpio_pwr, 1);
+ }
+
+ if (gpio_is_valid(host->gpio_cd)) {
+ ret = gpio_request(host->gpio_cd, "tegra_sdmmc_cd");
+ if (ret) {
+ dev_err(dev, "failed to allocate cd gpio\n");
+ return -ENODEV;
+ }
+ gpio_direction_input(host->gpio_cd);
+ }
+
+ clk_enable(host->clk);
+
+ mci->init = tegra_sdmmc_init;
+ mci->card_present = tegra_sdmmc_card_present;
+ mci->set_ios = tegra_sdmmc_set_ios;
+ mci->send_cmd = tegra_sdmmc_send_cmd;
+ mci->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+ mci->host_caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA |
+ MMC_CAP_MMC_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED_52MHZ |
+ MMC_CAP_SD_HIGHSPEED;
+
+ dev->priv = host;
+ dev->detect = tegra_sdmmc_detect;
+
+ return mci_register(&host->mci);
+}
+
+static __maybe_unused struct of_device_id tegra_sdmmc_compatible[] = {
+ {
+ .compatible = "nvidia,tegra30-sdhci",
+ }, {
+ .compatible = "nvidia,tegra20-sdhci",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d tegra_sdmmc_driver = {
+ .name = "tegra-sdmmc",
+ .probe = tegra_sdmmc_probe,
+ .of_compatible = DRV_OF_COMPAT(tegra_sdmmc_compatible),
+};
+device_platform_driver(tegra_sdmmc_driver);
diff --git a/drivers/of/of_gpio.c b/drivers/of/of_gpio.c
index 3afdf0d092..1370d8fe1f 100644
--- a/drivers/of/of_gpio.c
+++ b/drivers/of/of_gpio.c
@@ -25,7 +25,7 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname,
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells",
index, &out_args);
if (ret) {
- pr_err("%s: cannot parse %s property: %d\n",
+ pr_debug("%s: cannot parse %s property: %d\n",
__func__, propname, ret);
return ret;
}