mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-06-21 04:45:29 +08:00

* target: add phytium support * kernel/video: add phytium platform ARM GPU support * config: add EFI support to phytium armv8 * target: phytium: remove rtl8821cs driver * target: phytium: refresh dts
1565 lines
44 KiB
C
1565 lines
44 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Driver for Phytium Multimedia Card Interface
|
|
*
|
|
* Copyright (c) 2021-2023 Phytium Technology Co., Ltd.
|
|
*/
|
|
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/module.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/swab.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/mmc/card.h>
|
|
#include <linux/mmc/core.h>
|
|
#include <linux/mmc/host.h>
|
|
#include <linux/mmc/mmc.h>
|
|
#include <linux/mmc/sd.h>
|
|
#include <linux/mmc/sdio.h>
|
|
#include <linux/delay.h>
|
|
#include "phytium-mci.h"
|
|
|
|
static const u32 cmd_ints_mask = MCI_INT_MASK_RE | MCI_INT_MASK_CMD | MCI_INT_MASK_RCRC |
|
|
MCI_INT_MASK_RTO | MCI_INT_MASK_HTO | MCI_RAW_INTS_HLE;
|
|
|
|
static const u32 data_ints_mask = MCI_INT_MASK_DTO | MCI_INT_MASK_DCRC | MCI_INT_MASK_DRTO |
|
|
MCI_INT_MASK_SBE_BCI;
|
|
static const u32 cmd_err_ints_mask = MCI_INT_MASK_RTO | MCI_INT_MASK_RCRC | MCI_INT_MASK_RE |
|
|
MCI_INT_MASK_DCRC | MCI_INT_MASK_DRTO |
|
|
MCI_MASKED_INTS_SBE_BCI;
|
|
|
|
static const u32 dmac_ints_mask = MCI_DMAC_INT_ENA_FBE | MCI_DMAC_INT_ENA_DU |
|
|
MCI_DMAC_INT_ENA_NIS | MCI_DMAC_INT_ENA_AIS;
|
|
static const u32 dmac_err_ints_mask = MCI_DMAC_INT_ENA_FBE | MCI_DMAC_INT_ENA_DU |
|
|
MCI_DMAC_INT_ENA_AIS;
|
|
|
|
static void phytium_mci_cmd_next(struct phytium_mci_host *host,
|
|
struct mmc_request *mrq,
|
|
struct mmc_command *cmd);
|
|
static void phytium_mci_adma_reset(struct phytium_mci_host *host);
|
|
static void phytium_mci_send_cmd(struct phytium_mci_host *host, u32 cmd, u32 arg);
|
|
static bool phytium_mci_data_xfer_done(struct phytium_mci_host *host, u32 events,
|
|
struct mmc_request *mrq, struct mmc_data *data);
|
|
static void phytium_mci_init_adma_table(struct phytium_mci_host *host,
|
|
struct phytium_mci_dma *dma);
|
|
static void phytium_mci_init_hw(struct phytium_mci_host *host);
|
|
static int phytium_mci_get_cd(struct mmc_host *mmc);
|
|
static int phytium_mci_err_irq(struct phytium_mci_host *host, u32 dmac_events, u32 events);
|
|
|
|
static void sdr_set_bits(void __iomem *reg, u32 bs)
|
|
{
|
|
u32 val = readl(reg);
|
|
|
|
val |= bs;
|
|
writel(val, reg);
|
|
}
|
|
|
|
static void sdr_clr_bits(void __iomem *reg, u32 bs)
|
|
{
|
|
u32 val = readl(reg);
|
|
|
|
val &= ~bs;
|
|
writel(val, reg);
|
|
}
|
|
|
|
static void phytium_mci_reset_hw(struct phytium_mci_host *host)
|
|
{
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET);
|
|
|
|
while (readl(host->base + MCI_CNTRL) & (MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET))
|
|
cpu_relax();
|
|
phytium_mci_send_cmd(host, MCI_CMD_UPD_CLK, 0);
|
|
}
|
|
|
|
static void phytium_mci_update_external_clk(struct phytium_mci_host *host, u32 uhs_reg_value)
|
|
{
|
|
writel(0, host->base + MCI_UHS_REG_EXT);
|
|
writel(uhs_reg_value, host->base + MCI_UHS_REG_EXT);
|
|
while (!(readl(host->base + MCI_CCLK_RDY) & 0x1))
|
|
cpu_relax();
|
|
}
|
|
|
|
static void phytium_mci_prepare_data(struct phytium_mci_host *host,
|
|
struct mmc_request *mrq)
|
|
{
|
|
struct mmc_data *data = mrq->data;
|
|
|
|
if (!(data->host_cookie & MCI_PREPARE_FLAG)) {
|
|
data->host_cookie |= MCI_PREPARE_FLAG;
|
|
data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len,
|
|
mmc_get_dma_dir(data));
|
|
}
|
|
}
|
|
|
|
static void phytium_mci_unprepare_data(struct phytium_mci_host *host,
|
|
struct mmc_request *mrq)
|
|
{
|
|
struct mmc_data *data = mrq->data;
|
|
|
|
if (data->host_cookie & MCI_ASYNC_FLAG)
|
|
return;
|
|
|
|
if (data->host_cookie & MCI_PREPARE_FLAG) {
|
|
dma_unmap_sg(host->dev, data->sg, data->sg_len, mmc_get_dma_dir(data));
|
|
data->host_cookie &= ~MCI_PREPARE_FLAG;
|
|
}
|
|
}
|
|
|
|
static void phytium_mci_send_cmd(struct phytium_mci_host *host, u32 cmd, u32 arg)
|
|
{
|
|
int rc;
|
|
u32 data;
|
|
|
|
writel(arg, host->base + MCI_CMDARG);
|
|
wmb(); /* drain writebuffer */
|
|
|
|
rc = readl_relaxed_poll_timeout(host->base + MCI_STATUS,
|
|
data,
|
|
!(data & MCI_STATUS_CARD_BUSY),
|
|
0, 100 * 1000);
|
|
if (rc == -ETIMEDOUT)
|
|
pr_debug("%s %d, timeout mci_status: 0x%08x\n", __func__, __LINE__, data);
|
|
|
|
writel(MCI_CMD_START | cmd, host->base + MCI_CMD);
|
|
|
|
rc = readl_relaxed_poll_timeout(host->base + MCI_CMD,
|
|
data,
|
|
!(data & MCI_CMD_START),
|
|
0, 100 * 1000);
|
|
if (rc == -ETIMEDOUT)
|
|
pr_debug("%s %d, timeout mci_cmd: 0x%08x\n", __func__, __LINE__, data);
|
|
}
|
|
|
|
static void phytium_mci_update_cmd11(struct phytium_mci_host *host, u32 cmd)
|
|
{
|
|
writel(MCI_CMD_START | cmd, host->base + MCI_CMD);
|
|
|
|
while (readl(host->base + MCI_CMD) & MCI_CMD_START)
|
|
cpu_relax();
|
|
}
|
|
|
|
static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *ios)
|
|
{
|
|
u32 div = 0xff, drv = 0, sample = 0;
|
|
unsigned long clk_rate;
|
|
u32 mci_cmd_bits = MCI_CMD_UPD_CLK;
|
|
u32 cmd_reg;
|
|
u32 cur_cmd_index;
|
|
u32 first_uhs_div, tmp_ext_reg;
|
|
|
|
cmd_reg = readl(host->base + MCI_CMD);
|
|
cur_cmd_index = cmd_reg & 0x3F;
|
|
|
|
if (cur_cmd_index == SD_SWITCH_VOLTAGE)
|
|
mci_cmd_bits |= MCI_CMD_VOLT_SWITCH;
|
|
if (ios->clock) {
|
|
if (host->current_ios_clk == ios->clock)
|
|
return;
|
|
|
|
dev_dbg(host->dev, "will change clock, host->clk_rate: %ld, ios->clock: %d\n",
|
|
host->clk_rate, ios->clock);
|
|
|
|
if (ios->clock >= 25000000)
|
|
tmp_ext_reg = 0x202;
|
|
else if (ios->clock == 400000)
|
|
tmp_ext_reg = 0x502;
|
|
else
|
|
tmp_ext_reg = 0x302;
|
|
|
|
phytium_mci_update_external_clk(host, tmp_ext_reg);
|
|
sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE);
|
|
|
|
if (cur_cmd_index == SD_SWITCH_VOLTAGE)
|
|
phytium_mci_update_cmd11(host, mci_cmd_bits | cmd_reg);
|
|
else
|
|
phytium_mci_send_cmd(host, mci_cmd_bits, 0);
|
|
|
|
clk_rate = host->clk_rate;
|
|
first_uhs_div = 1 + ((tmp_ext_reg >> 8)&0xFF);
|
|
div = clk_rate / (2 * first_uhs_div * ios->clock);
|
|
if (div > 2) {
|
|
sample = div / 2 + 1;
|
|
drv = sample - 1;
|
|
writel((sample << 16) | (drv << 8) | (div & 0xff),
|
|
host->base + MCI_CLKDIV);
|
|
} else if (div == 2) {
|
|
drv = 0;
|
|
sample = 1;
|
|
writel((drv << 8) | (sample << 16) | (div & 0xff),
|
|
host->base + MCI_CLKDIV);
|
|
}
|
|
|
|
dev_dbg(host->dev, "UHS_REG_EXT ext: %x, CLKDIV: %x\n",
|
|
readl(host->base + MCI_UHS_REG_EXT), readl(host->base + MCI_CLKDIV));
|
|
|
|
sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE);
|
|
|
|
if (cur_cmd_index == SD_SWITCH_VOLTAGE)
|
|
phytium_mci_update_cmd11(host, mci_cmd_bits | cmd_reg);
|
|
else
|
|
phytium_mci_send_cmd(host, mci_cmd_bits, 0);
|
|
|
|
host->current_ios_clk = ios->clock;
|
|
|
|
dev_dbg(host->dev, "host->clk_rate: %ld, ios->clock: %d\n",
|
|
host->clk_rate, ios->clock);
|
|
} else {
|
|
host->current_ios_clk = 0;
|
|
sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE);
|
|
|
|
if (cur_cmd_index == SD_SWITCH_VOLTAGE)
|
|
phytium_mci_update_cmd11(host, mci_cmd_bits | cmd_reg);
|
|
else
|
|
phytium_mci_send_cmd(host, mci_cmd_bits, 0);
|
|
|
|
sdr_clr_bits(host->base + MCI_UHS_REG_EXT, MCI_EXT_CLK_ENABLE);
|
|
dev_dbg(host->dev, "host->clk_rate: %ld, ios->clock: %d\n",
|
|
host->clk_rate, ios->clock);
|
|
}
|
|
}
|
|
|
|
static inline u32
|
|
phytium_mci_cmd_find_resp(struct phytium_mci_host *host,
|
|
struct mmc_request *mrq,
|
|
struct mmc_command *cmd)
|
|
{
|
|
u32 resp;
|
|
|
|
switch (mmc_resp_type(cmd)) {
|
|
case MMC_RSP_R1:
|
|
case MMC_RSP_R1B:
|
|
resp = 0x5;
|
|
break;
|
|
|
|
case MMC_RSP_R2:
|
|
resp = 0x7;
|
|
break;
|
|
|
|
case MMC_RSP_R3:
|
|
resp = 0x1;
|
|
break;
|
|
|
|
case MMC_RSP_NONE:
|
|
default:
|
|
resp = 0x0;
|
|
break;
|
|
}
|
|
|
|
return resp;
|
|
}
|
|
|
|
static inline
|
|
u32 phytium_mci_cmd_prepare_raw_cmd(struct phytium_mci_host *host,
|
|
struct mmc_request *mrq,
|
|
struct mmc_command *cmd)
|
|
{
|
|
u32 opcode = cmd->opcode;
|
|
u32 resp = phytium_mci_cmd_find_resp(host, mrq, cmd);
|
|
u32 rawcmd = ((opcode & 0x3f) | ((resp & 0x7) << 6));
|
|
|
|
if (opcode == MMC_GO_INACTIVE_STATE ||
|
|
(opcode == SD_IO_RW_DIRECT && ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT))
|
|
rawcmd |= (0x1 << 14);
|
|
else if (opcode == SD_SWITCH_VOLTAGE)
|
|
rawcmd |= (0x1 << 28);
|
|
|
|
if (test_and_clear_bit(MCI_CARD_NEED_INIT, &host->flags))
|
|
rawcmd |= (0x1 << 15);
|
|
|
|
if (cmd->data) {
|
|
struct mmc_data *data = cmd->data;
|
|
|
|
rawcmd |= (0x1 << 9);
|
|
|
|
if (data->flags & MMC_DATA_WRITE)
|
|
rawcmd |= (0x1 << 10);
|
|
}
|
|
|
|
return (rawcmd | (0x1 << 29) | (0x1 << 31));
|
|
}
|
|
|
|
static inline void
|
|
phytium_mci_adma_write_desc(struct phytium_mci_host *host,
|
|
struct phytium_adma2_64_desc *desc,
|
|
dma_addr_t addr, u32 len, u32 attribute)
|
|
{
|
|
desc->attribute = attribute;
|
|
desc->len = len;
|
|
desc->addr_lo = lower_32_bits(addr);
|
|
desc->addr_hi = upper_32_bits(addr);
|
|
dev_dbg(host->dev, "%s %d:addr_lo:0x%x ddr_hi:0x%x\n", __func__,
|
|
__LINE__, desc->addr_lo, desc->addr_hi);
|
|
|
|
if ((attribute == 0x80000004) || (attribute == 0x8000000c)) {
|
|
desc->desc_lo = 0;
|
|
desc->desc_hi = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
phytium_mci_data_sg_write_2_admc_table(struct phytium_mci_host *host, struct mmc_data *data)
|
|
{
|
|
struct phytium_adma2_64_desc *desc;
|
|
u32 dma_len, i;
|
|
dma_addr_t dma_address;
|
|
struct scatterlist *sg;
|
|
|
|
phytium_mci_init_adma_table(host, &host->dma);
|
|
|
|
desc = host->dma.adma_table;
|
|
for_each_sg(data->sg, sg, data->sg_count, i) {
|
|
dma_address = sg_dma_address(sg);
|
|
dma_len = sg_dma_len(sg);
|
|
|
|
if (i == 0) {
|
|
if (sg_is_last(sg) || (data->sg_count == 1 && dma_len == SD_BLOCK_SIZE))
|
|
phytium_mci_adma_write_desc(host, desc, dma_address,
|
|
dma_len, 0x8000000c);
|
|
else
|
|
phytium_mci_adma_write_desc(host, desc, dma_address,
|
|
dma_len, 0x8000001a);
|
|
} else if (sg_is_last(sg)) {
|
|
phytium_mci_adma_write_desc(host, desc, dma_address,
|
|
dma_len, 0x80000004);
|
|
} else {
|
|
phytium_mci_adma_write_desc(host, desc, dma_address,
|
|
dma_len, 0x80000012);
|
|
}
|
|
|
|
desc++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
phytium_mci_data_sg_write_2_fifo(struct phytium_mci_host *host, struct mmc_data *data)
|
|
{
|
|
struct scatterlist *sg;
|
|
u32 dma_len, i, j;
|
|
u32 *virt_addr;
|
|
|
|
if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
|
|
writel(0x1<<10, host->base + MCI_CMD);
|
|
for_each_sg(data->sg, sg, data->sg_count, i) {
|
|
dma_len = sg_dma_len(sg);
|
|
virt_addr = sg_virt(data->sg);
|
|
for (j = 0; j < (dma_len / 4); j++) {
|
|
writel(*virt_addr, host->base + MCI_DATA);
|
|
virt_addr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void phytium_mci_restart_clk(struct phytium_mci_host *host)
|
|
{
|
|
u32 clk_div, uhs;
|
|
|
|
while (readl(host->base + MCI_CMD) & MCI_CMD_START)
|
|
cpu_relax();
|
|
sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE);
|
|
clk_div = readl(host->base + MCI_CLKDIV);
|
|
uhs = readl(host->base + MCI_UHS_REG_EXT);
|
|
writel(0, host->base + MCI_UHS_REG_EXT);
|
|
writel(uhs, host->base + MCI_UHS_REG_EXT);
|
|
while (!(readl(host->base + MCI_CCLK_RDY) & 0x1))
|
|
cpu_relax();
|
|
|
|
writel(clk_div, host->base + MCI_CLKDIV);
|
|
sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE);
|
|
writel(MCI_CMD_START | MCI_CMD_UPD_CLK, host->base + MCI_CMD);
|
|
while (readl(host->base + MCI_CMD) & MCI_CMD_START)
|
|
cpu_relax();
|
|
}
|
|
|
|
static int
|
|
phytim_mci_start_multiple_write(struct phytium_mci_host *host,
|
|
struct mmc_request *mrq, u32 cnts, u32 offset)
|
|
{
|
|
u32 rawcmd, cmd_status;
|
|
struct mmc_command *cmd = mrq->cmd;
|
|
u32 *rsp = cmd->resp;
|
|
unsigned long deadline_time;
|
|
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && readl(host->base + MCI_CARD_DETECT))
|
|
return -ESHUTDOWN;
|
|
|
|
while ((readl(host->base + MCI_STATUS) & (MCI_STATUS_CARD_BUSY)))
|
|
cpu_relax();
|
|
|
|
writel(0xffffe, host->base + MCI_RAW_INTS);
|
|
rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd);
|
|
writel(mrq->data->blksz, host->base + MCI_BLKSIZ);
|
|
writel(cnts * mrq->data->blksz, host->base + MCI_BYTCNT);
|
|
writel(cmd->arg + offset, host->base + MCI_CMDARG);
|
|
writel(rawcmd, host->base + MCI_CMD);
|
|
deadline_time = jiffies + msecs_to_jiffies(200);
|
|
|
|
cmd_status = readl(host->base + MCI_RAW_INTS);
|
|
while (!(cmd_status & MCI_MASKED_INTS_CMD)) {
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
|
|
readl(host->base + MCI_CARD_DETECT))
|
|
return -ESHUTDOWN;
|
|
|
|
cmd_status = readl(host->base + MCI_RAW_INTS);
|
|
if (cmd_err_ints_mask & cmd_status)
|
|
return -ESHUTDOWN;
|
|
|
|
if (cmd_status & MCI_MASKED_INTS_CMD)
|
|
break;
|
|
|
|
if (time_after(jiffies, deadline_time))
|
|
return -ESHUTDOWN;
|
|
}
|
|
|
|
if (cmd_status & MCI_MASKED_INTS_CMD) {
|
|
if (cmd->flags & MMC_RSP_136) {
|
|
rsp[3] = readl(host->base + MCI_RESP0);
|
|
rsp[2] = readl(host->base + MCI_RESP1);
|
|
rsp[1] = readl(host->base + MCI_RESP2);
|
|
rsp[0] = readl(host->base + MCI_RESP3);
|
|
} else {
|
|
rsp[0] = readl(host->base + MCI_RESP0);
|
|
}
|
|
}
|
|
deadline_time = jiffies + msecs_to_jiffies(1000);
|
|
while (!(cmd_status & MCI_MASKED_INTS_DTO)) {
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
|
|
readl(host->base + MCI_CARD_DETECT))
|
|
return -ESHUTDOWN;
|
|
cmd_status = readl(host->base + MCI_RAW_INTS);
|
|
if (cmd_err_ints_mask & cmd_status)
|
|
return -ESHUTDOWN;
|
|
if (cmd_status & MCI_MASKED_INTS_DTO)
|
|
return 0;
|
|
if (time_after(jiffies, deadline_time))
|
|
return -ESHUTDOWN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
phytium_mci_start_sbc_stop_cmd(struct phytium_mci_host *host, struct mmc_request *mrq,
|
|
struct mmc_command *cmd, u32 arg)
|
|
{
|
|
u32 rawcmd, cmd_status;
|
|
u32 *rsp = cmd->resp;
|
|
unsigned long deadline_time;
|
|
|
|
writel(0xffffe, host->base + MCI_RAW_INTS);
|
|
|
|
while ((readl(host->base + MCI_STATUS) & (MCI_STATUS_CARD_BUSY)))
|
|
cpu_relax();
|
|
|
|
rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd);
|
|
writel(arg, host->base + MCI_CMDARG);
|
|
writel(rawcmd, host->base + MCI_CMD);
|
|
|
|
deadline_time = jiffies + msecs_to_jiffies(200);
|
|
cmd_status = readl(host->base + MCI_RAW_INTS);
|
|
while (!(cmd_status & MCI_MASKED_INTS_CMD)) {
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
|
|
readl(host->base + MCI_CARD_DETECT))
|
|
return -ENOMEDIUM;
|
|
|
|
cmd_status = readl(host->base + MCI_RAW_INTS);
|
|
if (cmd_err_ints_mask & cmd_status)
|
|
return -ETIMEDOUT;
|
|
|
|
if (cmd_status & MCI_MASKED_INTS_CMD)
|
|
break;
|
|
|
|
if (time_after(jiffies, deadline_time))
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
if (cmd_status & MCI_MASKED_INTS_CMD) {
|
|
if (cmd->flags & MMC_RSP_136) {
|
|
rsp[3] = readl(host->base + MCI_RESP0);
|
|
rsp[2] = readl(host->base + MCI_RESP1);
|
|
rsp[1] = readl(host->base + MCI_RESP2);
|
|
rsp[0] = readl(host->base + MCI_RESP3);
|
|
} else {
|
|
rsp[0] = readl(host->base + MCI_RESP0);
|
|
}
|
|
}
|
|
|
|
if (cmd_err_ints_mask & cmd_status)
|
|
return -ETIMEDOUT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
phytium_mci_start_write_multiple_non_dma(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
struct mmc_data *data = mrq->data;
|
|
u32 write_cnts, last_cnts;
|
|
u32 i, j, k, send_cnt_one_sg, block_offset;
|
|
int ret = 0, dma_len;
|
|
struct scatterlist *sg;
|
|
u32 *virt_addr = NULL;
|
|
|
|
write_cnts = data->blocks / 4;
|
|
(data->blocks % 4) ? write_cnts++ : write_cnts;
|
|
last_cnts = data->blocks % 4;
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && readl(host->base + MCI_CARD_DETECT)) {
|
|
ret = -ENOMEDIUM;
|
|
goto write_err;
|
|
}
|
|
|
|
dev_dbg(host->dev, "%s: cmd:%d, block counts:%d\n",
|
|
__func__, mrq->cmd->opcode, data->blocks);
|
|
|
|
sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_USE_INTERNAL_DMAC);
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_FIFO_RESET);
|
|
while (readl(host->base + MCI_CNTRL) & MCI_CNTRL_FIFO_RESET)
|
|
cpu_relax();
|
|
sdr_clr_bits(host->base + MCI_BUS_MODE, MCI_BUS_MODE_DE);
|
|
|
|
if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
|
|
block_offset = 0;
|
|
for_each_sg(data->sg, sg, data->sg_count, i) {
|
|
/* Each SG data transfor starts */
|
|
dma_len = sg_dma_len(sg);
|
|
send_cnt_one_sg = (dma_len / MCI_MAX_FIFO_CNT) + 1;
|
|
virt_addr = sg_virt(sg);
|
|
for (k = 0; k < send_cnt_one_sg; k++) {
|
|
if (dma_len && dma_len >= MCI_MAX_FIFO_CNT) {
|
|
/*first write sbc cmd*/
|
|
ret = phytium_mci_start_sbc_stop_cmd(host, mrq,
|
|
mrq->sbc, 4);
|
|
if (ret)
|
|
goto write_err;
|
|
writel(0x1 << 10, host->base + MCI_CMD);
|
|
for (j = 0; j < (MCI_MAX_FIFO_CNT / 4); j++) {
|
|
writel(*virt_addr, host->base + MCI_DATA);
|
|
virt_addr++;
|
|
}
|
|
|
|
/*second write cmd25 here*/
|
|
ret = phytim_mci_start_multiple_write(host, mrq, 4,
|
|
block_offset);
|
|
if (ret)
|
|
goto write_err;
|
|
block_offset += 4;
|
|
dma_len -= MCI_MAX_FIFO_CNT;
|
|
} else if (dma_len > 0) {
|
|
/*first write sbc cmd*/
|
|
last_cnts = dma_len / 512;
|
|
ret = phytium_mci_start_sbc_stop_cmd(host, mrq, mrq->sbc,
|
|
last_cnts);
|
|
if (ret)
|
|
goto write_err;
|
|
writel(0x1 << 10, host->base + MCI_CMD);
|
|
for (j = 0; j < (dma_len / 4); j++) {
|
|
writel(*virt_addr, host->base + MCI_DATA);
|
|
virt_addr++;
|
|
}
|
|
/*second write cmd25 here*/
|
|
ret = phytim_mci_start_multiple_write(host, mrq, last_cnts,
|
|
block_offset);
|
|
if (ret)
|
|
goto write_err;
|
|
block_offset += last_cnts;
|
|
dma_len = 0;
|
|
} else {
|
|
dev_dbg(host->dev, "%s: sg %d end\n", __func__, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
write_err:
|
|
host->data = NULL;
|
|
host->cmd = NULL;
|
|
host->mrq = NULL;
|
|
writel(0xffffe, host->base + MCI_RAW_INTS);
|
|
if (ret) {
|
|
data->bytes_xfered = 0;
|
|
if (ret == -ESHUTDOWN) {
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_FIFO_RESET);
|
|
while (readl(host->base + MCI_CNTRL) & MCI_CNTRL_FIFO_RESET)
|
|
cpu_relax();
|
|
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_CONTROLLER_RESET);
|
|
while (readl(host->base + MCI_STATUS) & MCI_STATUS_CARD_BUSY)
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_CONTROLLER_RESET);
|
|
phytium_mci_restart_clk(host);
|
|
phytium_mci_start_sbc_stop_cmd(host, mrq, mrq->stop, mrq->stop->arg);
|
|
}
|
|
data->error = -ETIMEDOUT;
|
|
mrq->cmd->error = -ETIMEDOUT;
|
|
mmc_request_done(host->mmc, mrq);
|
|
return;
|
|
}
|
|
data->bytes_xfered = data->blocks * data->blksz;
|
|
mmc_request_done(host->mmc, mrq);
|
|
}
|
|
|
|
static void
|
|
phytium_mci_start_data(struct phytium_mci_host *host, struct mmc_request *mrq,
|
|
struct mmc_command *cmd, struct mmc_data *data)
|
|
{
|
|
bool read;
|
|
u32 rawcmd;
|
|
unsigned long flags;
|
|
|
|
|
|
WARN_ON(host->cmd);
|
|
host->cmd = cmd;
|
|
cmd->error = 0;
|
|
WARN_ON(host->data);
|
|
host->data = data;
|
|
read = data->flags & MMC_DATA_READ;
|
|
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && readl(host->base + MCI_CARD_DETECT)) {
|
|
phytium_mci_err_irq(host, 0, MCI_INT_MASK_RTO);
|
|
return;
|
|
}
|
|
/* clear interrupts */
|
|
writel(0xffffe, host->base + MCI_RAW_INTS);
|
|
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET);
|
|
|
|
while (readl(host->base + MCI_CNTRL) & (MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET))
|
|
cpu_relax();
|
|
|
|
if (host->adtc_type == COMMOM_ADTC)
|
|
sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_USE_INTERNAL_DMAC);
|
|
else
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_USE_INTERNAL_DMAC);
|
|
wmb(); /* drain writebuffer */
|
|
sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_INT_ENABLE);
|
|
|
|
rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd);
|
|
if (host->is_use_dma && host->adtc_type == BLOCK_RW_ADTC)
|
|
phytium_mci_data_sg_write_2_admc_table(host, data);
|
|
else
|
|
phytium_mci_data_sg_write_2_fifo(host, data);
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
sdr_set_bits(host->base + MCI_INT_MASK, cmd_ints_mask | data_ints_mask);
|
|
if (host->is_use_dma && host->adtc_type == BLOCK_RW_ADTC) {
|
|
sdr_set_bits(host->base + MCI_DMAC_INT_ENA, dmac_ints_mask);
|
|
/* Enable the IDMAC */
|
|
sdr_set_bits(host->base + MCI_BUS_MODE, MCI_BUS_MODE_DE);
|
|
writel((u32)host->dma.adma_addr, host->base + MCI_DESC_LIST_ADDRL);
|
|
writel((u32)(host->dma.adma_addr >> 32), host->base + MCI_DESC_LIST_ADDRH);
|
|
}
|
|
writel(mrq->data->blksz, host->base + MCI_BLKSIZ);
|
|
writel(mrq->data->blocks * mrq->data->blksz, host->base + MCI_BYTCNT);
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_INT_ENABLE);
|
|
writel(cmd->arg, host->base + MCI_CMDARG);
|
|
wmb(); /* drain writebuffer */
|
|
writel(rawcmd, host->base + MCI_CMD);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
mod_timer(&host->timeout_timer,
|
|
jiffies + msecs_to_jiffies(MMC_REQ_TIMEOUT_MS));
|
|
}
|
|
|
|
static void phytium_mci_track_cmd_data(struct phytium_mci_host *host,
|
|
struct mmc_command *cmd,
|
|
struct mmc_data *data)
|
|
{
|
|
if (host->error)
|
|
dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n",
|
|
__func__, cmd->opcode, cmd->arg, host->error);
|
|
}
|
|
|
|
static void phytium_mci_request_done(struct phytium_mci_host *host, struct mmc_request *mrq)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
del_timer(&host->timeout_timer);
|
|
host->mrq = NULL;
|
|
if (host->cmd)
|
|
host->cmd = NULL;
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
phytium_mci_track_cmd_data(host, mrq->cmd, mrq->data);
|
|
|
|
if (mrq->data)
|
|
phytium_mci_unprepare_data(host, mrq);
|
|
|
|
mmc_request_done(host->mmc, mrq);
|
|
}
|
|
|
|
static bool phytium_mci_cmd_done(struct phytium_mci_host *host, int events,
|
|
struct mmc_request *mrq, struct mmc_command *cmd)
|
|
{
|
|
bool done = false;
|
|
unsigned long flags;
|
|
u32 *rsp = cmd->resp;
|
|
|
|
if (!(events & (MCI_RAW_INTS_RCRC | MCI_RAW_INTS_RE | MCI_RAW_INTS_CMD |
|
|
MCI_RAW_INTS_RTO | MCI_INT_MASK_HTO))) {
|
|
dev_err(host->dev, "No interrupt generation:h%x\n", events);
|
|
return done;
|
|
}
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
done = !host->cmd;
|
|
host->cmd = NULL;
|
|
if (done) {
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
return true;
|
|
}
|
|
sdr_clr_bits(host->base + MCI_INT_MASK, cmd_ints_mask);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
|
if (cmd->flags & MMC_RSP_136) {
|
|
rsp[3] = readl(host->base + MCI_RESP0);
|
|
rsp[2] = readl(host->base + MCI_RESP1);
|
|
rsp[1] = readl(host->base + MCI_RESP2);
|
|
rsp[0] = readl(host->base + MCI_RESP3);
|
|
} else {
|
|
/*
|
|
* Sometimes get ACMD41 cmd done irq but the respose index still the APP_CMD,
|
|
* so polling the mci status entill the respose index change.
|
|
*/
|
|
if (cmd->opcode == SD_APP_OP_COND) {
|
|
int polling_cnt = 20;
|
|
while (MMC_APP_CMD == MCI_STATUS_RESPOSE_INDEX(readl(host->base + MCI_STATUS))) {
|
|
udelay(100);
|
|
polling_cnt --;
|
|
if (polling_cnt == 0) {
|
|
dev_info(host->dev, "hw respose index not equal cmd opcode, respose value may error\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
rsp[0] = readl(host->base + MCI_RESP0);
|
|
}
|
|
|
|
if (cmd->opcode == SD_SEND_RELATIVE_ADDR)
|
|
host->current_rca = rsp[0] & 0xFFFF0000;
|
|
}
|
|
if (!(events & (MCI_RAW_INTS_CMD | MCI_INT_MASK_HTO))) {
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && (events & MCI_RAW_INTS_RTO)
|
|
&& readl(host->base + MCI_CARD_DETECT)) {
|
|
cmd->error = -ENOMEDIUM;
|
|
rsp[0] = 0;
|
|
} else if (events & MCI_RAW_INTS_RTO ||
|
|
(cmd->opcode != MMC_SEND_TUNING_BLOCK &&
|
|
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)) {
|
|
cmd->error = -ETIMEDOUT;
|
|
} else if (events & MCI_RAW_INTS_RCRC) {
|
|
cmd->error = -EILSEQ;
|
|
} else {
|
|
cmd->error = -ETIMEDOUT;
|
|
}
|
|
}
|
|
phytium_mci_cmd_next(host, mrq, cmd);
|
|
return true;
|
|
}
|
|
|
|
static void phytium_mci_start_command(struct phytium_mci_host *host,
|
|
struct mmc_request *mrq,
|
|
struct mmc_command *cmd)
|
|
{
|
|
u32 rawcmd;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
WARN_ON(host->cmd);
|
|
host->cmd = cmd;
|
|
cmd->error = 0;
|
|
writel(0xffffe, host->base + MCI_RAW_INTS);
|
|
|
|
rawcmd = phytium_mci_cmd_prepare_raw_cmd(host, mrq, cmd);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && readl(host->base + MCI_CARD_DETECT)) {
|
|
phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, mrq, cmd);
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
sdr_set_bits(host->base + MCI_INT_MASK, cmd_ints_mask);
|
|
writel(cmd->arg, host->base + MCI_CMDARG);
|
|
writel(rawcmd, host->base + MCI_CMD);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
mod_timer(&host->timeout_timer,
|
|
jiffies + msecs_to_jiffies(MMC_REQ_TIMEOUT_MS));
|
|
}
|
|
|
|
static void
|
|
phytium_mci_cmd_next(struct phytium_mci_host *host, struct mmc_request *mrq,
|
|
struct mmc_command *cmd)
|
|
{
|
|
if ((cmd->error && !(cmd->opcode == MMC_SEND_TUNING_BLOCK ||
|
|
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)) ||
|
|
(mrq->sbc && mrq->sbc->error)) {
|
|
phytium_mci_request_done(host, mrq);
|
|
} else if (cmd == mrq->sbc) {
|
|
if ((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
|
|
(mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) ||
|
|
(mrq->cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
|
|
(mrq->cmd->opcode == MMC_WRITE_BLOCK)) {
|
|
dev_dbg(host->dev, "%s %d:sbc done and next cmd :%d length:%d\n",
|
|
__func__, __LINE__, mrq->cmd->opcode, mrq->data->sg->length);
|
|
phytium_mci_prepare_data(host, mrq);
|
|
if (host->is_use_dma)
|
|
host->adtc_type = BLOCK_RW_ADTC;
|
|
else
|
|
host->adtc_type = COMMOM_ADTC;
|
|
phytium_mci_start_data(host, mrq, mrq->cmd, mrq->data);
|
|
} else {
|
|
dev_err(host->dev, "%s %d:ERROR: cmd %d followers the SBC\n",
|
|
__func__, __LINE__, cmd->opcode);
|
|
}
|
|
} else if (!cmd->data) {
|
|
phytium_mci_request_done(host, mrq);
|
|
}
|
|
}
|
|
|
|
static void phytium_mci_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
u32 data;
|
|
int rc;
|
|
|
|
host->error = 0;
|
|
WARN_ON(host->mrq);
|
|
host->mrq = mrq;
|
|
|
|
rc = readl_relaxed_poll_timeout(host->base + MCI_STATUS,
|
|
data,
|
|
!(data & MCI_STATUS_CARD_BUSY),
|
|
0, 500 * 1000);
|
|
if (rc == -ETIMEDOUT)
|
|
pr_debug("%s %d, timeout mci_status: 0x%08x\n", __func__, __LINE__, data);
|
|
|
|
dev_dbg(host->dev, "%s %d: cmd:%d arg:0x%x\n", __func__, __LINE__,
|
|
mrq->cmd->opcode, mrq->cmd->arg);
|
|
|
|
if (host->is_device_x100 && mrq->sbc && mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) {
|
|
phytium_mci_start_write_multiple_non_dma(mmc, mrq);
|
|
return;
|
|
}
|
|
|
|
if (mrq->sbc) {
|
|
phytium_mci_start_command(host, mrq, mrq->sbc);
|
|
return;
|
|
}
|
|
if (mrq->data) {
|
|
phytium_mci_prepare_data(host, mrq);
|
|
|
|
if ((mrq->data->sg->length >= 512) && host->is_use_dma &&
|
|
((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) ||
|
|
(mrq->cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
|
|
(mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) ||
|
|
(mrq->cmd->opcode == MMC_WRITE_BLOCK) ||
|
|
(mrq->cmd->opcode == SD_IO_RW_EXTENDED)))
|
|
|
|
host->adtc_type = BLOCK_RW_ADTC;
|
|
else
|
|
host->adtc_type = COMMOM_ADTC;
|
|
|
|
phytium_mci_start_data(host, mrq, mrq->cmd, mrq->data);
|
|
return;
|
|
}
|
|
phytium_mci_start_command(host, mrq, mrq->cmd);
|
|
}
|
|
|
|
static void phytium_mci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
struct mmc_data *data = mrq->data;
|
|
|
|
if (!data)
|
|
return;
|
|
|
|
phytium_mci_prepare_data(host, mrq);
|
|
data->host_cookie |= MCI_ASYNC_FLAG;
|
|
}
|
|
|
|
static void phytium_mci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|
int err)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
struct mmc_data *data = mrq->data;
|
|
|
|
if (!data)
|
|
return;
|
|
|
|
if (data->host_cookie & MCI_ASYNC_FLAG) {
|
|
data->host_cookie &= ~MCI_ASYNC_FLAG;
|
|
phytium_mci_unprepare_data(host, mrq);
|
|
}
|
|
}
|
|
|
|
static void phytium_mci_data_read_without_dma(struct phytium_mci_host *host,
|
|
struct mmc_data *data)
|
|
{
|
|
u32 length, i, data_val, dma_len, tmp = 0;
|
|
u32 *virt_addr;
|
|
unsigned long flags;
|
|
struct scatterlist *sg;
|
|
|
|
length = data->blocks * data->blksz;
|
|
|
|
if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE) {
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
if (data->host_cookie & MCI_ASYNC_FLAG) {
|
|
tmp = MCI_ASYNC_FLAG;
|
|
phytium_mci_post_req(host->mmc, data->mrq, 0);
|
|
} else {
|
|
phytium_mci_unprepare_data(host, data->mrq);
|
|
}
|
|
|
|
for_each_sg(data->sg, sg, data->sg_count, i) {
|
|
dma_len = sg_dma_len(sg);
|
|
virt_addr = sg_virt(data->sg);
|
|
|
|
for (i = 0; i < (dma_len / 4); i++) {
|
|
data_val = readl(host->base + MCI_DATA);
|
|
memcpy(virt_addr, &data_val, 4);
|
|
++virt_addr;
|
|
}
|
|
}
|
|
|
|
if (tmp & MCI_ASYNC_FLAG)
|
|
phytium_mci_pre_req(host->mmc, data->mrq);
|
|
else
|
|
phytium_mci_prepare_data(host, data->mrq);
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
}
|
|
data->bytes_xfered = length;
|
|
}
|
|
|
|
static void phytium_mci_data_xfer_next(struct phytium_mci_host *host,
|
|
struct mmc_request *mrq,
|
|
struct mmc_data *data)
|
|
{
|
|
if (mmc_op_multi(mrq->cmd->opcode) && mrq->stop &&
|
|
(data->error || !mrq->sbc)) {
|
|
while ((readl(host->base + MCI_STATUS) & (MCI_STATUS_CARD_BUSY)))
|
|
cpu_relax();
|
|
phytium_mci_start_command(host, mrq, mrq->stop);
|
|
} else {
|
|
phytium_mci_request_done(host, mrq);
|
|
}
|
|
}
|
|
|
|
static bool phytium_mci_data_xfer_done(struct phytium_mci_host *host, u32 events,
|
|
struct mmc_request *mrq, struct mmc_data *data)
|
|
{
|
|
unsigned long flags;
|
|
bool done;
|
|
|
|
unsigned int check_data = events & (MCI_RAW_INTS_DTO | MCI_RAW_INTS_RCRC |
|
|
MCI_RAW_INTS_DCRC | MCI_RAW_INTS_RE |
|
|
MCI_RAW_INTS_DRTO | MCI_RAW_INTS_EBE |
|
|
MCI_DMAC_STATUS_AIS | MCI_DMAC_STATUS_DU |
|
|
MCI_RAW_INTS_SBE_BCI | MCI_INT_MASK_RTO);
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
done = !host->data;
|
|
|
|
if (check_data || host->data)
|
|
host->data = NULL;
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
if (done)
|
|
return true;
|
|
if (check_data) {
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
sdr_clr_bits(host->base + MCI_DMAC_INT_ENA, dmac_ints_mask);
|
|
sdr_clr_bits(host->base + MCI_INT_MASK, data_ints_mask);
|
|
/* Stop the IDMAC running */
|
|
sdr_clr_bits(host->base + MCI_BUS_MODE, MCI_BUS_MODE_DE);
|
|
dev_dbg(host->dev, "DMA stop\n");
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
if (events & MCI_RAW_INTS_DTO) {
|
|
if (!host->is_use_dma ||
|
|
(host->is_use_dma && host->adtc_type == COMMOM_ADTC &&
|
|
(mrq->cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC))
|
|
phytium_mci_data_read_without_dma(host, data);
|
|
else
|
|
data->bytes_xfered = data->blocks * data->blksz;
|
|
} else {
|
|
data->bytes_xfered = 0;
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE)
|
|
&& readl(host->base + MCI_CARD_DETECT)
|
|
&& (events & cmd_err_ints_mask)) {
|
|
data->error = -ENOMEDIUM;
|
|
data->mrq->cmd->error = -ENOMEDIUM;
|
|
} else if (events & (MCI_RAW_INTS_DCRC | MCI_RAW_INTS_EBE |
|
|
MCI_RAW_INTS_SBE_BCI)) {
|
|
data->error = -EILSEQ;
|
|
host->cmd = NULL;
|
|
} else {
|
|
data->error = -ETIMEDOUT;
|
|
host->cmd = NULL;
|
|
}
|
|
}
|
|
|
|
phytium_mci_data_xfer_next(host, mrq, data);
|
|
done = true;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
static int phytium_mci_card_busy(struct mmc_host *mmc)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
u32 status;
|
|
|
|
status = readl(host->base + MCI_STATUS);
|
|
|
|
return !!(status & MCI_STATUS_CARD_BUSY);
|
|
}
|
|
|
|
static void __phytium_mci_enable_sdio_irq(struct phytium_mci_host *host, int enable)
|
|
{
|
|
if (enable)
|
|
sdr_set_bits(host->base + MCI_INT_MASK, MCI_INT_MASK_SDIO);
|
|
else
|
|
sdr_clr_bits(host->base + MCI_INT_MASK, MCI_INT_MASK_SDIO);
|
|
}
|
|
|
|
static void phytium_mci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
|
|
__phytium_mci_enable_sdio_irq(host, enable);
|
|
}
|
|
|
|
static void hotplug_timer_func(struct timer_list *t)
|
|
{
|
|
struct phytium_mci_host *host;
|
|
u32 status;
|
|
|
|
host = from_timer(host, t, hotplug_timer);
|
|
if (!host)
|
|
return;
|
|
|
|
status = readl(host->base + MCI_CARD_DETECT);
|
|
|
|
if (status & 0x1) {
|
|
if (host->mmc->card) {
|
|
cancel_delayed_work(&host->mmc->detect);
|
|
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
|
|
}
|
|
} else {
|
|
cancel_delayed_work(&host->mmc->detect);
|
|
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
|
|
}
|
|
}
|
|
|
|
static void phytium_mci_request_timeout(struct timer_list *t)
|
|
{
|
|
struct phytium_mci_host *host = from_timer(host, t, timeout_timer);
|
|
|
|
dev_err(host->dev, "request timeout\n");
|
|
if (host->mrq) {
|
|
dev_err(host->dev, "aborting mrq=%p cmd=%d\n",
|
|
host->mrq, host->mrq->cmd->opcode);
|
|
if (host->cmd) {
|
|
dev_err(host->dev, "aborting cmd=%d\n", host->cmd->opcode);
|
|
phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, host->mrq, host->cmd);
|
|
} else if (host->data) {
|
|
dev_err(host->dev, "abort data: cmd%d; %d blocks\n",
|
|
host->mrq->cmd->opcode, host->data->blocks);
|
|
phytium_mci_data_xfer_done(host, MCI_RAW_INTS_RTO, host->mrq,
|
|
host->data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int phytium_mci_err_irq(struct phytium_mci_host *host, u32 dmac_events, u32 events)
|
|
{
|
|
struct mmc_request *mrq;
|
|
struct mmc_command *cmd;
|
|
struct mmc_data *data;
|
|
|
|
mrq = host->mrq;
|
|
cmd = host->cmd;
|
|
data = host->data;
|
|
|
|
if (cmd && (cmd == mrq->sbc)) {
|
|
phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, mrq, mrq->sbc);
|
|
} else if (cmd && (cmd == mrq->stop)) {
|
|
phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, mrq, mrq->stop);
|
|
} else if (data) {
|
|
data->error = -ETIMEDOUT;
|
|
if ((data->flags & MMC_DATA_READ) == MMC_DATA_READ ||
|
|
(data->flags & MMC_DATA_WRITE) == MMC_DATA_WRITE)
|
|
phytium_mci_data_xfer_done(host, events | dmac_events, mrq, data);
|
|
} else if (cmd) {
|
|
phytium_mci_cmd_done(host, MCI_RAW_INTS_RTO, mrq, mrq->cmd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static irqreturn_t phytium_mci_irq(int irq, void *dev_id)
|
|
{
|
|
struct phytium_mci_host *host = (struct phytium_mci_host *) dev_id;
|
|
unsigned long flags;
|
|
struct mmc_request *mrq;
|
|
struct mmc_command *cmd;
|
|
struct mmc_data *data;
|
|
u32 events, event_mask, dmac_events, dmac_evt_mask;
|
|
|
|
if (!host)
|
|
return IRQ_NONE;
|
|
writel(0, host->base + 0xfd0);
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
events = readl(host->base + MCI_RAW_INTS);
|
|
dmac_events = readl(host->base + MCI_DMAC_STATUS);
|
|
event_mask = readl(host->base + MCI_INT_MASK);
|
|
dmac_evt_mask = readl(host->base + MCI_DMAC_INT_ENA);
|
|
if ((!events) && (!(dmac_events&0x1fff))) {
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
return IRQ_NONE;
|
|
}
|
|
dev_dbg(host->dev, "%s:events:%x,mask:0x%x,dmac_events:%x,dmac_mask:0x%x,cmd:%d\n",
|
|
__func__, events, event_mask, dmac_events, dmac_evt_mask,
|
|
host->mrq ? host->mrq->cmd->opcode : 255);
|
|
|
|
mrq = host->mrq;
|
|
cmd = host->cmd;
|
|
data = host->data;
|
|
|
|
#if 0
|
|
if (((events & event_mask) & MCI_RAW_INTS_SDIO) &&
|
|
((events == 0x10001) || (events == 0x10000) || (events == 0x10040))) {
|
|
writel(events, host->base + MCI_RAW_INTS);
|
|
__phytium_mci_enable_sdio_irq(host, 0);
|
|
sdio_signal_irq(host->mmc);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
goto irq_out;
|
|
}
|
|
#endif
|
|
if ((events & event_mask) & MCI_RAW_INTS_SDIO) {
|
|
__phytium_mci_enable_sdio_irq(host, 0);
|
|
}
|
|
|
|
|
|
writel(events, host->base + MCI_RAW_INTS);
|
|
writel(dmac_events, host->base + MCI_DMAC_STATUS);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
if ((events & event_mask) & MCI_RAW_INTS_SDIO) {
|
|
sdio_signal_irq(host->mmc);
|
|
}
|
|
|
|
if (((events & event_mask) == 0) && ((dmac_evt_mask & dmac_events) == 0))
|
|
goto irq_out;
|
|
|
|
if (((events & event_mask) & MCI_RAW_INTS_CD) &&
|
|
!(host->mmc->caps & MMC_CAP_NONREMOVABLE)) {
|
|
mod_timer(&host->hotplug_timer, jiffies + usecs_to_jiffies(20000));
|
|
dev_dbg(host->dev, "sd status changed here ! status:[%d] [%s %d]",
|
|
readl(host->base + MCI_CARD_DETECT), __func__, __LINE__);
|
|
|
|
if ((events & event_mask) == MCI_RAW_INTS_CD)
|
|
goto irq_out;
|
|
}
|
|
|
|
if (!mrq) {
|
|
if (events & MCI_RAW_INTS_HLE)
|
|
dev_dbg(host->dev,
|
|
"%s: MRQ=NULL and HW write locked, events=%08x,event_mask=%08x\n",
|
|
__func__, events, event_mask);
|
|
else
|
|
dev_dbg(host->dev, "%s: MRQ=NULL events:%08X evt_mask=%08X,sd_status:%d\n",
|
|
__func__, events, event_mask, readl(host->base + MCI_CARD_DETECT));
|
|
goto irq_out;
|
|
}
|
|
|
|
if ((dmac_events & dmac_err_ints_mask) || (events & cmd_err_ints_mask)) {
|
|
dev_dbg(host->dev, "ERR:events:%x,mask:0x%x,dmac_evts:%x,dmac_mask:0x%x,cmd:%d\n",
|
|
events, event_mask, dmac_events, dmac_evt_mask, mrq->cmd->opcode);
|
|
phytium_mci_err_irq(host, dmac_events & dmac_err_ints_mask,
|
|
events & cmd_err_ints_mask);
|
|
goto irq_out;
|
|
}
|
|
|
|
if ((events & MCI_MASKED_INTS_DTO) && (events & MCI_MASKED_INTS_CMD)) {
|
|
phytium_mci_cmd_done(host, events, mrq, cmd);
|
|
phytium_mci_data_xfer_done(host, (events & data_ints_mask) |
|
|
(dmac_events & dmac_ints_mask), mrq, data);
|
|
} else if (events & MCI_MASKED_INTS_CMD ||
|
|
((events & MCI_INT_MASK_HTO) && (cmd->opcode == SD_SWITCH_VOLTAGE))) {
|
|
phytium_mci_cmd_done(host, events, mrq, cmd);
|
|
} else if (events & MCI_MASKED_INTS_DTO) {
|
|
phytium_mci_data_xfer_done(host, (events & data_ints_mask) |
|
|
(dmac_events & dmac_ints_mask), mrq, data);
|
|
}
|
|
|
|
irq_out:
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void phytium_mci_init_hw(struct phytium_mci_host *host)
|
|
{
|
|
u32 val;
|
|
int uhs_reg_value = 0x502;
|
|
|
|
writel(MCI_SET_FIFOTH(0x2, 0x7, 0x100), host->base + MCI_FIFOTH);
|
|
writel(0x800001, host->base + MCI_CARD_THRCTL);
|
|
sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE);
|
|
phytium_mci_update_external_clk(host, uhs_reg_value);
|
|
|
|
sdr_set_bits(host->base + MCI_PWREN, MCI_PWREN_ENABLE);
|
|
sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE);
|
|
sdr_set_bits(host->base + MCI_UHS_REG_EXT, MCI_EXT_CLK_ENABLE);
|
|
sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT);
|
|
|
|
phytium_mci_reset_hw(host);
|
|
|
|
if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
|
|
sdr_set_bits(host->base + MCI_CARD_RESET, MCI_CARD_RESET_ENABLE);
|
|
else
|
|
sdr_clr_bits(host->base + MCI_CARD_RESET, MCI_CARD_RESET_ENABLE);
|
|
|
|
writel(0, host->base + MCI_INT_MASK);
|
|
val = readl(host->base + MCI_RAW_INTS);
|
|
writel(val, host->base + MCI_RAW_INTS);
|
|
writel(0, host->base + MCI_DMAC_INT_ENA);
|
|
val = readl(host->base + MCI_DMAC_STATUS);
|
|
writel(val, host->base + MCI_DMAC_STATUS);
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE))
|
|
writel(MCI_INT_MASK_CD, host->base + MCI_INT_MASK);
|
|
|
|
sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_INT_ENABLE |
|
|
MCI_CNTRL_USE_INTERNAL_DMAC);
|
|
|
|
writel(0xFFFFFFFF, host->base + MCI_TMOUT);
|
|
dev_info(host->dev, "init hardware done!");
|
|
|
|
}
|
|
|
|
void phytium_mci_deinit_hw(struct phytium_mci_host *host)
|
|
{
|
|
u32 val;
|
|
|
|
sdr_clr_bits(host->base + MCI_PWREN, MCI_PWREN_ENABLE);
|
|
sdr_clr_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE);
|
|
sdr_clr_bits(host->base + MCI_UHS_REG_EXT, MCI_EXT_CLK_ENABLE);
|
|
sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT);
|
|
writel(0, host->base + MCI_INT_MASK);
|
|
val = readl(host->base + MCI_RAW_INTS);
|
|
writel(val, host->base + MCI_RAW_INTS);
|
|
writel(0, host->base + MCI_DMAC_INT_ENA);
|
|
val = readl(host->base + MCI_DMAC_STATUS);
|
|
writel(val, host->base + MCI_DMAC_STATUS);
|
|
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE))
|
|
writel(MCI_INT_MASK_CD, host->base + MCI_INT_MASK);
|
|
}
|
|
EXPORT_SYMBOL_GPL(phytium_mci_deinit_hw);
|
|
|
|
static void phytium_mci_adma_reset(struct phytium_mci_host *host)
|
|
{
|
|
u32 bmod = readl(host->base + MCI_BUS_MODE);
|
|
|
|
bmod |= MCI_BUS_MODE_SWR;
|
|
writel(bmod, host->base + MCI_BUS_MODE);
|
|
}
|
|
|
|
static void phytium_mci_init_adma_table(struct phytium_mci_host *host,
|
|
struct phytium_mci_dma *dma)
|
|
{
|
|
struct phytium_adma2_64_desc *adma_table = dma->adma_table;
|
|
dma_addr_t dma_addr;
|
|
int i;
|
|
|
|
memset(adma_table, 0, sizeof(struct phytium_adma2_64_desc) * MAX_BD_NUM);
|
|
|
|
for (i = 0; i < (MAX_BD_NUM - 1); i++) {
|
|
dma_addr = dma->adma_addr + sizeof(*adma_table) * (i + 1);
|
|
adma_table[i].desc_lo = lower_32_bits(dma_addr);
|
|
adma_table[i].desc_hi = upper_32_bits(dma_addr);
|
|
adma_table[i].attribute = 0;
|
|
adma_table[i].NON1 = 0;
|
|
adma_table[i].len = 0;
|
|
adma_table[i].NON2 = 0;
|
|
}
|
|
|
|
phytium_mci_adma_reset(host);
|
|
}
|
|
|
|
static void phytium_mci_set_buswidth(struct phytium_mci_host *host, u32 width)
|
|
{
|
|
u32 val;
|
|
|
|
switch (width) {
|
|
case MMC_BUS_WIDTH_1:
|
|
val = MCI_BUS_1BITS;
|
|
break;
|
|
|
|
case MMC_BUS_WIDTH_4:
|
|
val = MCI_BUS_4BITS;
|
|
break;
|
|
|
|
case MMC_BUS_WIDTH_8:
|
|
val = MCI_BUS_8BITS;
|
|
break;
|
|
default:
|
|
val = MCI_BUS_4BITS;
|
|
break;
|
|
}
|
|
writel(val, host->base + MCI_CTYPE);
|
|
dev_dbg(host->dev, "Bus Width = %d, set value:0x%x\n", width, val);
|
|
}
|
|
|
|
static void phytium_mci_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
|
|
if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50)
|
|
sdr_set_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_DDR);
|
|
else
|
|
sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_DDR);
|
|
|
|
phytium_mci_set_buswidth(host, ios->bus_width);
|
|
|
|
switch (ios->power_mode) {
|
|
case MMC_POWER_UP:
|
|
set_bit(MCI_CARD_NEED_INIT, &host->flags);
|
|
writel(MCI_POWER_ON, host->base + MCI_PWREN);
|
|
break;
|
|
|
|
case MMC_POWER_ON:
|
|
break;
|
|
|
|
case MMC_POWER_OFF:
|
|
writel(MCI_POWER_OFF, host->base + MCI_PWREN);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
phytium_mci_set_clk(host, ios);
|
|
}
|
|
|
|
static void phytium_mci_ack_sdio_irq(struct mmc_host *mmc)
|
|
{
|
|
unsigned long flags;
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
__phytium_mci_enable_sdio_irq(host, 1);
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
}
|
|
|
|
static int phytium_mci_get_cd(struct mmc_host *mmc)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
u32 status;
|
|
|
|
if (mmc->caps & MMC_CAP_NONREMOVABLE)
|
|
return 1;
|
|
|
|
status = readl(host->base + MCI_CARD_DETECT);
|
|
|
|
if ((status & 0x1) == 0x1)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int phytium_mci_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
unsigned int is_voltage_180 = 0;
|
|
|
|
is_voltage_180 = readl(host->base + MCI_UHS_REG);
|
|
if ((mmc->caps & MMC_CAP_NONREMOVABLE) && (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_180))
|
|
return -EINVAL;
|
|
|
|
if ((ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) && (is_voltage_180 & 0x1))
|
|
sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT);
|
|
else if ((ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) && (!(is_voltage_180 & 0x1)))
|
|
sdr_set_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT);
|
|
else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_120)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
static void phytium_mci_hw_reset(struct mmc_host *mmc)
|
|
{
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
u32 reset_flag;
|
|
|
|
if (host->is_use_dma) {
|
|
reset_flag = MCI_CNTRL_FIFO_RESET | MCI_CNTRL_DMA_RESET;
|
|
phytium_mci_adma_reset(host);
|
|
sdr_set_bits(host->base + MCI_CNTRL, reset_flag);
|
|
} else {
|
|
reset_flag = MCI_CNTRL_FIFO_RESET;
|
|
sdr_set_bits(host->base + MCI_CNTRL, reset_flag);
|
|
}
|
|
|
|
while (readl(host->base + MCI_CNTRL) & reset_flag)
|
|
cpu_relax();
|
|
|
|
sdr_clr_bits(host->base + MCI_CARD_RESET, MCI_CARD_RESET_ENABLE);
|
|
udelay(5);
|
|
sdr_set_bits(host->base + MCI_CARD_RESET, MCI_CARD_RESET_ENABLE);
|
|
usleep_range(200, 300);
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
int phytium_mci_suspend(struct device *dev)
|
|
{
|
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
|
|
phytium_mci_deinit_hw(host);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(phytium_mci_suspend);
|
|
|
|
int phytium_mci_resume(struct device *dev)
|
|
{
|
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
|
|
phytium_mci_init_hw(host);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(phytium_mci_resume);
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PM
|
|
int phytium_mci_runtime_suspend(struct device *dev)
|
|
{
|
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
|
|
phytium_mci_deinit_hw(host);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(phytium_mci_runtime_suspend);
|
|
|
|
int phytium_mci_runtime_resume(struct device *dev)
|
|
{
|
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
|
struct phytium_mci_host *host = mmc_priv(mmc);
|
|
|
|
phytium_mci_init_hw(host);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(phytium_mci_runtime_resume);
|
|
|
|
#endif
|
|
|
|
static struct mmc_host_ops phytium_mci_ops = {
|
|
.post_req = phytium_mci_post_req,
|
|
.pre_req = phytium_mci_pre_req,
|
|
.request = phytium_mci_ops_request,
|
|
.set_ios = phytium_mci_ops_set_ios,
|
|
.get_cd = phytium_mci_get_cd,
|
|
.enable_sdio_irq = phytium_mci_enable_sdio_irq,
|
|
.ack_sdio_irq = phytium_mci_ack_sdio_irq,
|
|
.card_busy = phytium_mci_card_busy,
|
|
.start_signal_voltage_switch = phytium_mci_ops_switch_volt,
|
|
.hw_reset = phytium_mci_hw_reset,
|
|
};
|
|
|
|
int phytium_mci_common_probe(struct phytium_mci_host *host)
|
|
{
|
|
struct mmc_host *mmc = host->mmc;
|
|
struct device *dev = host->dev;
|
|
int ret;
|
|
|
|
dma_set_mask(dev, DMA_BIT_MASK(64));
|
|
dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
|
|
|
|
timer_setup(&host->hotplug_timer, hotplug_timer_func, 0);
|
|
timer_setup(&host->timeout_timer, phytium_mci_request_timeout, 0);
|
|
|
|
mmc->f_min = MCI_F_MIN;
|
|
if (!mmc->f_max)
|
|
mmc->f_max = MCI_F_MAX;
|
|
|
|
mmc->ops = &phytium_mci_ops;
|
|
mmc->ocr_avail_sdio = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
mmc->ocr_avail_sd = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
mmc->ocr_avail_mmc = MMC_VDD_165_195;
|
|
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
|
|
mmc->caps |= host->caps;
|
|
|
|
if (mmc->caps & MMC_CAP_SDIO_IRQ) {
|
|
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
|
dev_dbg(host->dev, "%s %d: MMC_CAP_SDIO_IRQ\n", __func__, __LINE__);
|
|
}
|
|
mmc->caps2 |= host->caps2;
|
|
if (host->is_use_dma) {
|
|
/* MMC core transfer sizes tunable parameters */
|
|
mmc->max_segs = MAX_BD_NUM;
|
|
mmc->max_seg_size = 4 * 1024;
|
|
mmc->max_blk_size = 512;
|
|
mmc->max_req_size = 512 * 1024;
|
|
mmc->max_blk_count = mmc->max_req_size / 512;
|
|
host->dma.adma_table = dma_alloc_coherent(host->dev,
|
|
MAX_BD_NUM *
|
|
sizeof(struct phytium_adma2_64_desc),
|
|
&host->dma.adma_addr, GFP_KERNEL);
|
|
if (!host->dma.adma_table)
|
|
return MCI_REALEASE_MEM;
|
|
|
|
host->dma.desc_sz = ADMA2_64_DESC_SZ;
|
|
phytium_mci_init_adma_table(host, &host->dma);
|
|
} else {
|
|
mmc->max_segs = MAX_BD_NUM;
|
|
mmc->max_seg_size = 4 * 1024;
|
|
mmc->max_blk_size = 512;
|
|
mmc->max_req_size = 4 * 512;
|
|
mmc->max_blk_count = mmc->max_req_size / 512;
|
|
}
|
|
|
|
spin_lock_init(&host->lock);
|
|
|
|
phytium_mci_init_hw(host);
|
|
ret = devm_request_irq(host->dev, host->irq, phytium_mci_irq,
|
|
host->irq_flags, "phytium-mci", host);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mmc_add_host(mmc);
|
|
|
|
if (ret) {
|
|
dev_err(host->dev, "%s %d: mmc add host!\n", __func__, __LINE__);
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(phytium_mci_common_probe);
|
|
|
|
MODULE_DESCRIPTION("Phytium Multimedia Card Interface driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Cheng Quan <chengquan@phytium.com.cn>");
|