diff options
Diffstat (limited to 'drivers/mci/tegra-sdmmc.c')
-rw-r--r-- | drivers/mci/tegra-sdmmc.c | 214 |
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), |