summaryrefslogtreecommitdiffstats
path: root/drivers/mci/tegra-sdmmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mci/tegra-sdmmc.c')
-rw-r--r--drivers/mci/tegra-sdmmc.c214
1 files changed, 86 insertions, 128 deletions
diff --git a/drivers/mci/tegra-sdmmc.c b/drivers/mci/tegra-sdmmc.c
index 4c47918076..e940edf322 100644
--- a/drivers/mci/tegra-sdmmc.c
+++ b/drivers/mci/tegra-sdmmc.c
@@ -1,21 +1,6 @@
-/*
- * 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/>.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2013 Lucas Stach <l.stach@pengutronix.de>
+// SPDX-FileCopyrightText: 2010-2013 NVIDIA Corporation (http://www.nvidia.com/)
#include <common.h>
#include <clock.h>
@@ -74,9 +59,24 @@ struct tegra_sdmmc_host {
struct clk *clk;
struct reset_control *reset;
int gpio_cd, gpio_pwr;
+ struct sdhci sdhci;
};
#define to_tegra_sdmmc_host(mci) container_of(mci, struct tegra_sdmmc_host, mci)
+static u32 tegra_sdmmc_read32(struct sdhci *sdhci, int reg)
+{
+ struct tegra_sdmmc_host *host = container_of(sdhci, struct tegra_sdmmc_host, sdhci);
+
+ return readl(host->regs + reg);
+}
+
+static void tegra_sdmmc_write32(struct sdhci *sdhci, int reg, u32 val)
+{
+ struct tegra_sdmmc_host *host = container_of(sdhci, struct tegra_sdmmc_host, sdhci);
+
+ writel(val, host->regs + reg);
+}
+
static int tegra_sdmmc_wait_inhibit(struct tegra_sdmmc_host *host,
struct mci_cmd *cmd, struct mci_data *data,
unsigned int timeout)
@@ -91,7 +91,7 @@ static int tegra_sdmmc_wait_inhibit(struct tegra_sdmmc_host *host,
val |= TEGRA_SDMMC_PRESENT_STATE_CMD_INHIBIT_DAT;
wait_on_timeout(timeout * MSECOND,
- !(readl(host->regs + TEGRA_SDMMC_PRESENT_STATE) & val));
+ !(sdhci_read32(&host->sdhci, TEGRA_SDMMC_PRESENT_STATE) & val));
return 0;
}
@@ -101,7 +101,7 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
{
struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
unsigned int num_bytes = 0;
- u32 val = 0;
+ u32 val = 0, command, xfer;
int ret;
ret = tegra_sdmmc_wait_inhibit(host, cmd, data, 10);
@@ -113,104 +113,70 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
num_bytes = data->blocks * data->blocksize;
if (data->flags & MMC_DATA_WRITE) {
- dma_sync_single_for_device((unsigned long)data->src,
+ dma_sync_single_for_device(mci->hw_dev, (unsigned long)data->src,
num_bytes, DMA_TO_DEVICE);
- writel((u32)data->src, host->regs + SDHCI_DMA_ADDRESS);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->src)));
} else {
- dma_sync_single_for_device((unsigned long)data->dest,
+ dma_sync_single_for_device(mci->hw_dev, (unsigned long)data->dest,
num_bytes, DMA_FROM_DEVICE);
- writel((u32)data->dest, host->regs + SDHCI_DMA_ADDRESS);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->dest)));
}
- writel((7 << 12) | data->blocks << 16 | data->blocksize,
- host->regs + SDHCI_BLOCK_SIZE__BLOCK_COUNT);
+ sdhci_write32(&host->sdhci, SDHCI_BLOCK_SIZE__BLOCK_COUNT,
+ (7 << 12) | data->blocks << 16 | data->blocksize);
}
- writel(cmd->cmdarg, host->regs + SDHCI_ARGUMENT);
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
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;
- }
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, true, &command, &xfer);
- 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);
+ sdhci_write32(&host->sdhci, SDHCI_TRANSFER_MODE__COMMAND,
+ command << 16 | xfer);
ret = wait_on_timeout(100 * MSECOND,
- (val = readl(host->regs + SDHCI_INT_STATUS))
- & IRQSTAT_CC);
+ (val = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS))
+ & SDHCI_INT_CMD_COMPLETE);
if (ret) {
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
return ret;
}
- if ((val & IRQSTAT_CC) && !data)
- writel(val, host->regs + SDHCI_INT_STATUS);
+ if ((val & SDHCI_INT_CMD_COMPLETE) && !data)
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
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);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
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);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
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) {
+ sdhci_read_response(&host->sdhci, cmd);
+
+ if (cmd->resp_type & MMC_RSP_BUSY) {
ret = wait_on_timeout(100 * MSECOND,
- readl(host->regs + TEGRA_SDMMC_PRESENT_STATE)
+ sdhci_read32(&host->sdhci, 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);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
return ret;
}
- cmd->response[0] = readl(host->regs + SDHCI_RESPONSE_0);
- } else {
- cmd->response[0] = readl(host->regs + SDHCI_RESPONSE_0);
+ cmd->response[0] = sdhci_read32(&host->sdhci, SDHCI_RESPONSE_0);
}
}
@@ -218,15 +184,15 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
uint64_t start = get_time_ns();
while (1) {
- val = readl(host->regs + SDHCI_INT_STATUS);
+ val = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
if (val & TEGRA_SDMMC_INTERRUPT_STATUS_ERR_INTERRUPT) {
/* Error Interrupt */
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
dev_err(mci->hw_dev,
"error during transfer: 0x%08x\n", val);
return -EIO;
- } else if (val & IRQSTAT_DINT) {
+ } else if (val & SDHCI_INT_DMA) {
/*
* DMA Interrupt, restart the transfer where
* it was interrupted.
@@ -234,33 +200,32 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
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) {
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_DMA);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, address);
+ } else if (val & SDHCI_INT_XFER_COMPLETE) {
/* Transfer Complete */;
break;
} else if (is_timeout(start, 2 * SECOND)) {
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
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));
+ sdhci_read32(&host->sdhci, SDHCI_INT_ENABLE),
+ sdhci_read32(&host->sdhci, SDHCI_SIGNAL_ENABLE),
+ sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE));
return -ETIMEDOUT;
}
}
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
+ dma_sync_single_for_cpu(mci->hw_dev, (unsigned long)data->src,
num_bytes, DMA_TO_DEVICE);
else
- dma_sync_single_for_cpu((unsigned long)data->dest,
+ dma_sync_single_for_cpu(mci->hw_dev, (unsigned long)data->dest,
num_bytes, DMA_FROM_DEVICE);
}
@@ -277,25 +242,25 @@ static void tegra_sdmmc_set_clock(struct tegra_sdmmc_host *host, u32 clock)
}
/* clear clock related bits */
- val = readl(host->regs + TEGRA_SDMMC_CLK_CNTL);
+ val = sdhci_read32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL);
val &= 0xffff0000;
- writel(val, host->regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL, val);
/* set new frequency */
val |= prediv << 8;
val |= TEGRA_SDMMC_CLK_CNTL_INTERNAL_CLOCK_EN;
- writel(val, host->regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL, val);
clk_set_rate(host->clk, adjusted_clock);
/* wait for controller to settle */
wait_on_timeout(10 * MSECOND,
- !(readl(host->regs + TEGRA_SDMMC_CLK_CNTL) &
+ !(sdhci_read32(&host->sdhci, 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);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL, val);
}
static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
@@ -308,7 +273,7 @@ static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
tegra_sdmmc_set_clock(host, ios->clock);
/* set bus width */
- val = readl(host->regs + TEGRA_SDMMC_PWR_CNTL);
+ val = sdhci_read32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL);
val &= ~(0x21);
if (ios->bus_width == MMC_BUS_WIDTH_8)
@@ -316,10 +281,10 @@ static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
else if (ios->bus_width == MMC_BUS_WIDTH_4)
val |= (1 << 1);
- writel(val, host->regs + TEGRA_SDMMC_PWR_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL, val);
}
-static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
+static int tegra_sdmmc_init(struct mci_host *mci, struct device *dev)
{
struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
void __iomem *regs = host->regs;
@@ -327,8 +292,8 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
int ret;
/* reset controller */
- writel(TEGRA_SDMMC_CLK_CNTL_SW_RESET_FOR_ALL,
- regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL,
+ TEGRA_SDMMC_CLK_CNTL_SW_RESET_FOR_ALL);
ret = wait_on_timeout(100 * MSECOND,
!(readl(regs + TEGRA_SDMMC_CLK_CNTL) &
@@ -342,30 +307,29 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
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);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL, val);
/* sdmmc1 and sdmmc3 on T30 need a bit of padctrl init */
- if (of_device_is_compatible(mci->hw_dev->device_node,
- "nvidia,tegra30-sdhci") &&
- ((u32)regs == 0x78000000 || (u32)regs == 78000400)) {
+ if (of_device_is_compatible(mci->hw_dev->of_node, "nvidia,tegra30-sdhci") &&
+ (regs == IOMEM(0x78000000) || regs == IOMEM(0x78000400))) {
val = readl(regs + TEGRA_SDMMC_SDMEMCOMPPADCTRL);
val &= 0xfffffff0;
val |= 0x7 << TEGRA_SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_SHIFT;
- writel(val, regs + TEGRA_SDMMC_SDMEMCOMPPADCTRL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_SDMEMCOMPPADCTRL, val);
val = readl(regs + TEGRA_SDMMC_AUTO_CAL_CONFIG);
val &= 0xffff0000;
val |= (0x62 << TEGRA_SDMMC_AUTO_CAL_CONFIG_PU_OFFSET_SHIFT) |
(0x70 << TEGRA_SDMMC_AUTO_CAL_CONFIG_PD_OFFSET_SHIFT) |
TEGRA_SDMMC_AUTO_CAL_CONFIG_ENABLE;
- writel(val, regs + TEGRA_SDMMC_AUTO_CAL_CONFIG);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_AUTO_CAL_CONFIG, val);
}
/* setup signaling */
- writel(0xffffffff, regs + TEGRA_SDMMC_INT_STAT_EN);
- writel(0xffffffff, regs + TEGRA_SDMMC_INT_SIG_EN);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_INT_STAT_EN, 0xffffffff);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_INT_SIG_EN, 0xffffffff);
- writel(0xe << 16, regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL, 0xe << 16);
val = readl(regs + TEGRA_SDMMC_INT_STAT_EN);
val &= ~(0xffff);
@@ -374,12 +338,12 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
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);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_INT_STAT_EN, val);
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);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_INT_SIG_EN, val);
tegra_sdmmc_set_clock(host, 400000);
@@ -398,26 +362,19 @@ static int tegra_sdmmc_card_present(struct mci_host *mci)
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);
+ return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT);
}
static void tegra_sdmmc_parse_dt(struct tegra_sdmmc_host *host)
{
- struct device_node *np = host->mci.hw_dev->device_node;
+ struct device_node *np = host->mci.hw_dev->of_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)
+static int tegra_sdmmc_probe(struct device *dev)
{
struct resource *iores;
struct tegra_sdmmc_host *host;
@@ -442,6 +399,9 @@ static int tegra_sdmmc_probe(struct device_d *dev)
}
host->regs = IOMEM(iores->start);
+ host->sdhci.read32 = tegra_sdmmc_read32;
+ host->sdhci.write32 = tegra_sdmmc_write32;
+
mci->hw_dev = dev;
mci->f_max = 48000000;
mci->f_min = 375000;
@@ -478,9 +438,6 @@ static int tegra_sdmmc_probe(struct device_d *dev)
mci->host_caps |= 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);
}
@@ -495,8 +452,9 @@ static __maybe_unused struct of_device_id tegra_sdmmc_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra_sdmmc_compatible);
-static struct driver_d tegra_sdmmc_driver = {
+static struct driver tegra_sdmmc_driver = {
.name = "tegra-sdmmc",
.probe = tegra_sdmmc_probe,
.of_compatible = DRV_OF_COMPAT(tegra_sdmmc_compatible),