From: zhangguijiang zhangguijiang@huawei.com
ascend inclusion category: feature feature: Ascend emmc adaption bugzilla: https://gitee.com/openeuler/kernel/issues/I4F4LL CVE: NA
-------------------
To support Ascend Hisilicon emmc chip host controller, we externs specific extensions. We provide an Ascend Hisilicon adapted dw mci operation set. Ascend Hisi mmc has different process such as Hisilicon adapted init slot, specials interrupt. We also add some properties to dts, so we need to parse these properties to customized our capability.
Signed-off-by: zhangguijiang zhangguijiang@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/mmc/host/dw_mmc-pltfm.c | 13 + drivers/mmc/host/dw_mmc-pltfm.h | 63 ++ drivers/mmc/host/dw_mmc.c | 1664 ++++++++++++++++++++++++++++-- drivers/mmc/host/dw_mmc.h | 155 +++ drivers/mmc/host/dw_mmc_extern.h | 76 ++ drivers/mmc/host/dw_mmc_hisi.h | 291 ++++++ drivers/mmc/host/hisi_mmc_pmic.h | 71 ++ 7 files changed, 2224 insertions(+), 109 deletions(-) create mode 100644 drivers/mmc/host/dw_mmc_extern.h create mode 100644 drivers/mmc/host/dw_mmc_hisi.h create mode 100644 drivers/mmc/host/hisi_mmc_pmic.h
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 58c13e21bd5a7..9dd05cce4decd 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -31,11 +31,17 @@ int dw_mci_pltfm_register(struct platform_device *pdev, { struct dw_mci *host; struct resource *regs; + int ret;
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); if (!host) return -ENOMEM;
+ if (mmc_is_ascend_customized(&pdev->dev)) { + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + } host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) return host->irq; @@ -52,6 +58,13 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
/* Get registers' physical base address */ host->phy_regs = regs->start; + if (mmc_is_ascend_customized(host->dev)) { + if (drv_data && drv_data->init) { + ret = drv_data->init(host); + if (ret) + return ret; + } + }
platform_set_drvdata(pdev, host); return dw_mci_probe(host); diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h index 68e7fd2f6148b..6d2d61d435960 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.h +++ b/drivers/mmc/host/dw_mmc-pltfm.h @@ -12,6 +12,69 @@ #ifndef _DW_MMC_PLTFM_H_ #define _DW_MMC_PLTFM_H_
+#ifdef CONFIG_ASCEND_HISI_MMC + +#define INTMSK_ALL 0xFFFFFFFF +#define INTMSK_CDETECT (0x1 << 0) +#define INTMSK_RE (0x1 << 1) +#define INTMSK_CDONE (0x1 << 2) +#define INTMSK_DTO (0x1 << 3) +#define INTMSK_TXDR (0x1 << 4) +#define INTMSK_RXDR (0x1 << 5) +#define INTMSK_RCRC (0x1 << 6) +#define INTMSK_DCRC (0x1 << 7) +#define INTMSK_RTO (0x1 << 8) +#define INTMSK_DRTO (0x1 << 9) +#define INTMSK_HTO (0x1 << 10) +#define INTMSK_VOLT_SWITCH (0x1 << 10) +#define INTMSK_FRUN (0x1 << 11) +#define INTMSK_HLE (0x1 << 12) +#define INTMSK_SBE (0x1 << 13) +#define INTMSK_ACD (0x1 << 14) +#define INTMSK_EBE (0x1 << 15) +#define INTMSK_DMA (INTMSK_ACD | INTMSK_RXDR | INTMSK_TXDR) + +#define INT_SRC_IDMAC (0x0) +#define INT_SRC_MINT (0x1) + + +#define CMD_RESP_EXP_BIT (0x1 << 6) +#define CMD_RESP_LENGTH_BIT (0x1 << 7) +#define CMD_CHECK_CRC_BIT (0x1 << 8) +#define CMD_DATA_EXP_BIT (0x1 << 9) +#define CMD_RW_BIT (0x1 << 10) +#define CMD_TRANSMODE_BIT (0x1 << 11) +#define CMD_WAIT_PRV_DAT_BIT (0x1 << 13) +#define CMD_STOP_ABORT_CMD (0x1 << 14) +#define CMD_SEND_INITIALIZATION (0x1 << 15) +#define CMD_SEND_CLK_ONLY (0x1 << 21) +#define CMD_VOLT_SWITCH (0x1 << 28) +#define CMD_USE_HOLD_REG (0x1 << 29) +#define CMD_STRT_BIT (0x1 << 31) +#define CMD_ONLY_CLK (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \ + CMD_WAIT_PRV_DAT_BIT) + +#define CLK_ENABLE (0x1 << 0) +#define CLK_DISABLE (0x0 << 0) + +#define MMC_CCLK_MAX_24M (24000000) +#define MMC_CCLK_MAX_25M (25000000) +#define MMC_CCLK_MAX_48M (48000000) +#define MMC_CCLK_MAX_50M (50000000) +#define MMC_CCLK_MAX_80M (80000000) +#define MMC_CCLK_MAX_96M (96000000) +#define MMC_CCLK_MAX_100M (100000000) +#define MMC_CCLK_MAX_150M (150000000) +#define MMC_CCLK_MAX_180M (180000000) +#define MMC_CCLK_MAX_200M (200000000) + +#define MMC_EMMC (0x0) +#define MMC_SD (0x1) +#define MMC_SDIO (0x2) +#define MMC_SDIO_FOR_EMMC (0x3) + +#endif + extern int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data); extern int dw_mci_pltfm_remove(struct platform_device *pdev); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 22c454c7aaca6..a8180f8a595ea 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -41,6 +41,8 @@ #include <linux/mmc/slot-gpio.h>
#include "dw_mmc.h" +#include "hisi_mmc_pmic.h" +#include "dw_mmc_extern.h"
/* Common flag combinations */ #define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \ @@ -271,10 +273,53 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); }
+static int hisi_mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) +{ + struct dw_mci *host = slot->host; + unsigned long timeout = jiffies + msecs_to_jiffies(100); + unsigned int cmd_status = 0; + + mci_writel(host, CMDARG, arg); + /* Synchronous execution */ + wmb(); + + mci_writel(host, CMD, SDMMC_CMD_START | cmd); + while (time_before(jiffies, timeout)) { + cmd_status = mci_readl(host, CMD); + if (!(cmd_status & SDMMC_CMD_START)) + return 0; + } + + if (!dw_mci_wait_reset(host->dev, host, SDMMC_CTRL_RESET)) + return 1; + + timeout = jiffies + msecs_to_jiffies(100); + mci_writel(host, CMD, SDMMC_CMD_START | cmd); + while (time_before(jiffies, timeout)) { + cmd_status = mci_readl(host, CMD); + if (!(cmd_status & SDMMC_CMD_START)) + return 0; + } + + dev_warn(&slot->mmc->class_dev, + "Timeout sending command (cmd %#x arg %#x status %#x)\n", + cmd, arg, cmd_status); + return 1; +} + +static int dw_mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) +{ + if (mmc_is_ascend_customized(slot->host->dev)) + return hisi_mci_send_cmd(slot, cmd, arg); + mci_send_cmd(slot, cmd, arg); + return 0; +} + static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; + u32 clk_en_a; u32 cmdr;
cmd->error = -EINPROGRESS; @@ -290,7 +335,12 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
if (cmd->opcode == SD_SWITCH_VOLTAGE) { - u32 clk_en_a; + if (mmc_is_ascend_customized(host->dev)) { + cmdr |= SDMMC_CMD_VOLT_SWITCH; + goto out; + } + +
/* Special bit makes CMD11 not die */ cmdr |= SDMMC_CMD_VOLT_SWITCH; @@ -313,10 +363,10 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) clk_en_a = mci_readl(host, CLKENA); clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + dw_mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } - +out: if (cmd->flags & MMC_RSP_PRESENT) { /* We expect a response, so set this bit */ cmdr |= SDMMC_CMD_RESP_EXP; @@ -416,6 +466,32 @@ static inline void dw_mci_set_cto(struct dw_mci *host) spin_unlock_irqrestore(&host->irq_lock, irqflags); }
+static void dw_mci_disable_clock_low_power(struct dw_mci *host, u32 cmd_flags) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + int loop_count; + u32 temp; + + temp = mci_readl(host, INTMASK); + temp &= ~SDMMC_INT_RESP_ERR; + mci_writel(host, INTMASK, temp); + cmd_flags |= SDMMC_CMD_VOLT_SWITCH; + + /* disable clock low power */ + mci_writel(host, CLKENA, (0x1<<0)); + mci_writel(host, CMD, SDMMC_CMD_ONLY_CLK | SDMMC_CMD_VOLT_SWITCH); + loop_count = 0x100000; + do { + if (!(mci_readl(host, CMD) & SDMMC_CMD_START)) + break; + loop_count--; + } while (loop_count); + if (!loop_count) + dev_warn(host->dev, + "disable clk low power failed in volt_switch.\n"); +#endif +} + static void dw_mci_start_command(struct dw_mci *host, struct mmc_command *cmd, u32 cmd_flags) { @@ -424,6 +500,10 @@ static void dw_mci_start_command(struct dw_mci *host, "start command: ARGR=0x%08x CMDR=0x%08x\n", cmd->arg, cmd_flags);
+ if (mmc_is_ascend_customized(host->dev) && + cmd->opcode == SD_SWITCH_VOLTAGE) { + dw_mci_disable_clock_low_power(host, cmd_flags); + } mci_writel(host, CMDARG, cmd->arg); wmb(); /* drain writebuffer */ dw_mci_wait_while_busy(host, cmd_flags); @@ -442,8 +522,15 @@ static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) dw_mci_start_command(host, stop, host->stop_cmdr); }
+#ifdef CONFIG_ASCEND_HISI_MMC +static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) +{ + dw_mci_start_command(host, data->stop, host->stop_cmdr); +} +#endif + /* DMA interface functions */ -static void dw_mci_stop_dma(struct dw_mci *host) +void dw_mci_stop_dma(struct dw_mci *host) { if (host->using_dma) { host->dma_ops->stop(host); @@ -453,6 +540,7 @@ static void dw_mci_stop_dma(struct dw_mci *host) /* Data transfer was stopped by the interrupt handler */ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } +EXPORT_SYMBOL(dw_mci_stop_dma);
static void dw_mci_dma_cleanup(struct dw_mci *host) { @@ -467,13 +555,14 @@ static void dw_mci_dma_cleanup(struct dw_mci *host) } }
-static void dw_mci_idmac_reset(struct dw_mci *host) +void dw_mci_idmac_reset(struct dw_mci *host) { u32 bmod = mci_readl(host, BMOD); /* Software reset of DMA */ bmod |= SDMMC_IDMAC_SWRESET; mci_writel(host, BMOD, bmod); } +EXPORT_SYMBOL(dw_mci_idmac_reset);
static void dw_mci_idmac_stop_dma(struct dw_mci *host) { @@ -597,6 +686,107 @@ static int dw_mci_idmac_init(struct dw_mci *host) return 0; }
+static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, + unsigned int sg_len) +{ + unsigned int desc_len; + + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) { + unsigned int i; + struct idmac_desc *desc = host->sg_cpu; + + if (!sg_len) + return; + + for (i = 0; i < sg_len; i++, desc++) { + unsigned int length = sg_dma_len(&data->sg[i]); + u32 mem_addr = sg_dma_address(&data->sg[i]); + + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + if (desc->des0 & IDMAC_DES0_CH) + desc->des1 = 0; + + if (desc->des0 & IDMAC_DES0_CH) + desc->des1 = 0; + + /* Buffer length */ + IDMAC_SET_BUFFER1_SIZE(desc, length); + + /* Physical address to DMA to/from */ + desc->des2 = mem_addr; + } + + /* Set first descriptor */ + desc = host->sg_cpu; + desc->des0 |= IDMAC_DES0_FD; + + /* Set last descriptor */ + desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); + desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc->des0 |= IDMAC_DES0_LD; + /* Synchronous execution */ + wmb(); + } else { + unsigned int i; + + struct idmac_desc_64addr *desc_first, *desc_last, *desc; + + desc_first = desc_last = desc = host->sg_cpu; + if (!sg_len) + return; + for (i = 0; i < sg_len; i++) { + unsigned int length = sg_dma_len(&data->sg[i]); + + u64 mem_addr = sg_dma_address(&data->sg[i]); + + for ( ; length ; desc++) { + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? + length : DW_MCI_DESC_DATA_LENGTH; + + length -= desc_len; + + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = IDMAC_DES0_OWN| + IDMAC_DES0_DIC | IDMAC_DES0_CH; + + if (desc->des0 & IDMAC_DES0_CH) + desc->des2 = 0; + + if (desc->des0 & IDMAC_DES0_CH) + desc->des2 = 0; + + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); + + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; + + /* Update physical address for the next desc */ + mem_addr += desc_len; + + /* Save pointer to the last descriptor */ + desc_last = desc; + } + } + + /* Set first descriptor */ + desc_first->des0 |= IDMAC_DES0_FD; + + /* Set last descriptor */ + desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc_last->des0 |= IDMAC_DES0_LD; + wmb(); /* drain writebuffer */ + } +} static inline int dw_mci_prepare_desc64(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) @@ -745,18 +935,22 @@ static inline int dw_mci_prepare_desc32(struct dw_mci *host, static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) { u32 temp; - int ret; + int ret = 0;
- if (host->dma_64bit_address == 1) - ret = dw_mci_prepare_desc64(host, host->data, sg_len); - else - ret = dw_mci_prepare_desc32(host, host->data, sg_len); + if (mmc_is_ascend_customized(host->dev)) + dw_mci_translate_sglist(host, host->data, sg_len); + else { + if (host->dma_64bit_address == 1) + ret = dw_mci_prepare_desc64(host, host->data, sg_len); + else + ret = dw_mci_prepare_desc32(host, host->data, sg_len);
- if (ret) - goto out; + if (ret) + goto out;
- /* drain writebuffer */ - wmb(); + /* drain writebuffer */ + wmb(); + }
/* Make sure to reset DMA in case we did PIO before this */ dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET); @@ -964,7 +1158,38 @@ static void dw_mci_post_req(struct mmc_host *mmc, data->host_cookie = COOKIE_UNMAPPED; }
-static int dw_mci_get_cd(struct mmc_host *mmc) +#ifdef CONFIG_ASCEND_HISI_MMC +int hisi_dw_mci_get_cd(struct mmc_host *mmc) +{ + int present; + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci_board *brd = slot->host->pdata; + struct dw_mci *host = slot->host; + u32 id = (u32)(slot->id); + + pm_runtime_get_sync(mmc_dev(mmc)); + /* Use platform get_cd function, else try onboard card detect */ + if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) + present = 1; + else if (brd->get_cd) + present = !brd->get_cd(slot->host, slot->id); + else if (host->hw_mmc_id == DWMMC_SDIO_ID) + present = mmc->sdio_present; + else + present = (mci_readl(slot->host, CDETECT) & (1 << id)) + == 0 ? 1 : 0; + if (!present) + dev_err(&mmc->class_dev, "card is not present\n"); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + + return present; +} +EXPORT_SYMBOL(hisi_dw_mci_get_cd); +#endif + +int dw_mci_get_cd(struct mmc_host *mmc) { int present; struct dw_mci_slot *slot = mmc_priv(mmc); @@ -1235,7 +1460,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
if (!clock) { mci_writel(host, CLKENA, 0); - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + dw_mci_send_cmd(slot, sdmmc_cmd_bits, 0); } else if (clock != host->current_speed || force_clkinit) { div = host->bus_hz / clock; if (host->bus_hz % clock && host->bus_hz > clock) @@ -1272,13 +1497,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKSRC, 0);
/* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + dw_mci_send_cmd(slot, sdmmc_cmd_bits, 0);
/* set clock to desired speed */ mci_writel(host, CLKDIV, div);
/* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + dw_mci_send_cmd(slot, sdmmc_cmd_bits, 0);
/* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; @@ -1287,7 +1512,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKENA, clk_en_a);
/* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + dw_mci_send_cmd(slot, sdmmc_cmd_bits, 0);
/* keep the last clock value that was requested from core */ slot->__clk_old = clock; @@ -1301,6 +1526,44 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CTYPE, (slot->ctype << slot->id)); }
+static void hisi_mod_host_timer(struct dw_mci *host, struct dw_mci_slot *slot) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + host->stop_snd = false; + if (host->pdata->select_slot) + host->pdata->select_slot(slot->id); + + /* Slot specific timing and width adjustment */ + dw_mci_setup_bus(slot, 0); + + if (host->flags & DWMMC_IN_TUNING) { + mod_timer(&host->timer, jiffies + msecs_to_jiffies(100)); + } else { + if (host->hw_mmc_id == DWMMC_EMMC_ID) { + /*add to 20s timeout for some write slow emmc*/ + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(20000)); + + } else if ((host->hw_mmc_id == DWMMC_SD_ID) && + (slot->mmc->card)) { + if (mmc_card_suspended(slot->mmc->card)) + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(2000)); + else + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(10000)); + } else if (host->hw_mmc_id == DWMMC_SDIO_ID) { + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(5000)); + } else + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(10000)); + } + + host->cur_slot = slot; +#endif +} + static void __dw_mci_start_request(struct dw_mci *host, struct dw_mci_slot *slot, struct mmc_command *cmd) @@ -1310,7 +1573,8 @@ static void __dw_mci_start_request(struct dw_mci *host, u32 cmdflags;
mrq = slot->mrq; - + if (mmc_is_ascend_customized(host->dev)) + hisi_mod_host_timer(host, slot); host->mrq = mrq;
host->pending_events = 0; @@ -1359,6 +1623,15 @@ static void __dw_mci_start_request(struct dw_mci *host, spin_unlock_irqrestore(&host->irq_lock, irqflags); }
+ if (mmc_is_ascend_customized(host->dev)) { + if (mrq->stop) + host->stop_cmdr = + dw_mci_prepare_command(slot->mmc, mrq->stop); + else { + if (data) + host->stop_cmdr = dw_mci_prep_stop(host, cmd); + } + } host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); }
@@ -1412,12 +1685,21 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) * atomic, otherwise the card could be removed in between and the * request wouldn't fail until another card was inserted. */ - - if (!dw_mci_get_cd(mmc)) { - mrq->cmd->error = -ENOMEDIUM; - mmc_request_done(mmc, mrq); - return; - } + if (mmc_is_ascend_customized(host->dev)) { + pm_runtime_get_sync(mmc_dev(mmc)); + if (!hisi_dw_mci_get_cd(mmc)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + return; + } + } else + if (!dw_mci_get_cd(mmc)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + return; + }
spin_lock_bh(&host->lock);
@@ -1425,6 +1707,54 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_unlock_bh(&host->lock); } +#ifdef CONFIG_ASCEND_HISI_MMC +static inline void dw_mci_set_freq_specific(const struct dw_mci_hs_priv_data + *priv, unsigned int origin_freq, + unsigned int *freq) +{ + if ((priv->chip_type == CHIP_HIXX51) && + (priv->chip_platform == SDMMC_FPGA_PLATFORM)) { + *freq = origin_freq / 4; // FPGA work in 25MHz/4=6.25MHz + } +} +#else +static inline void dw_mci_set_freq_specific(const void *priv, + unsigned int origin_freq, + unsigned int *freq) +{ + +} +#endif + +static int dw_mci_ios_mmc_power_up(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + u32 regs; + int ret; + + if (!mmc_is_ascend_customized(mmc->parent)) + if (!IS_ERR(mmc->supply.vmmc)) { + ret = mmc_regulator_set_ocr(mmc, + mmc->supply.vmmc, ios->vdd); + if (ret) { + dev_err(slot->host->dev, + "failed to enable vmmc regulator\n"); + /*return, if failed turn on vmmc*/ + return ret; + } + } + set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); + /* Power up slot */ +#ifdef CONFIG_ASCEND_HISI_MMC + if (mmc_is_ascend_customized(mmc->parent) && + slot->host->pdata->setpower) + slot->host->pdata->setpower(slot->id, mmc->ocr_avail); +#endif + regs = mci_readl(slot->host, PWREN); + regs |= (1 << slot->id); + mci_writel(slot->host, PWREN, regs); + return 0; +}
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { @@ -1433,6 +1763,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) u32 regs; int ret;
+ if (mmc_is_ascend_customized(mmc->parent)) + pm_runtime_get_sync(mmc_dev(mmc)); + switch (ios->bus_width) { case MMC_BUS_WIDTH_4: slot->ctype = SDMMC_CTYPE_4BIT; @@ -1466,25 +1799,23 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (drv_data && drv_data->set_ios) drv_data->set_ios(slot->host, ios); - + if (mmc_is_ascend_customized(mmc->parent)) { + mmc->f_min = DIV_ROUND_UP(slot->host->bus_hz, 510); + mmc->f_max = slot->host->bus_hz; + dw_mci_set_freq_specific(slot->host->priv, slot->host->bus_hz, + &(mmc->f_max)); + /* Slot specific timing and width adjustment */ + dw_mci_setup_bus(slot, false); + } switch (ios->power_mode) { case MMC_POWER_UP: - if (!IS_ERR(mmc->supply.vmmc)) { - ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, - ios->vdd); - if (ret) { - dev_err(slot->host->dev, - "failed to enable vmmc regulator\n"); - /*return, if failed turn on vmmc*/ - return; - } - } - set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - regs = mci_readl(slot->host, PWREN); - regs |= (1 << slot->id); - mci_writel(slot->host, PWREN, regs); + ret = dw_mci_ios_mmc_power_up(mmc, ios); + if (ret) + return; break; case MMC_POWER_ON: + if (mmc_is_ascend_customized(mmc->parent)) + break; if (!slot->host->vqmmc_enabled) { if (!IS_ERR(mmc->supply.vqmmc)) { ret = regulator_enable(mmc->supply.vqmmc); @@ -1509,16 +1840,25 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
break; case MMC_POWER_OFF: - /* Turn clock off before power goes down */ - dw_mci_setup_bus(slot, false); - - if (!IS_ERR(mmc->supply.vmmc)) - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); +#ifdef CONFIG_ASCEND_HISI_MMC + if (mmc_is_ascend_customized(mmc->parent) && + slot->host->pdata->setpower) + /* Power down slot */ + slot->host->pdata->setpower(slot->id, 0); + else +#endif + { + /* Turn clock off before power goes down */ + dw_mci_setup_bus(slot, false);
- if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) - regulator_disable(mmc->supply.vqmmc); - slot->host->vqmmc_enabled = false; + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ if (!IS_ERR(mmc->supply.vqmmc) && + slot->host->vqmmc_enabled) + regulator_disable(mmc->supply.vqmmc); + slot->host->vqmmc_enabled = false; + } regs = mci_readl(slot->host, PWREN); regs &= ~(1 << slot->id); mci_writel(slot->host, PWREN, regs); @@ -1529,6 +1869,10 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) slot->host->state = STATE_IDLE; + if (mmc_is_ascend_customized(mmc->parent)) { + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + } }
static int dw_mci_card_busy(struct mmc_host *mmc) @@ -1585,10 +1929,26 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
static int dw_mci_get_ro(struct mmc_host *mmc) { - int read_only; + int read_only = 0; struct dw_mci_slot *slot = mmc_priv(mmc); int gpio_ro = mmc_gpio_get_ro(mmc);
+ if (mmc_is_ascend_customized(mmc->parent)) { + /* + * Use platform get_ro function, + * else try on board write protect + */ +#ifdef CONFIG_ASCEND_HISI_MMC + if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT || + (slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT)) + read_only = 0; + else + read_only = mci_readl(slot->host, WRTPRT) & + (1 << slot->id) ? 1 : 0; +#endif + goto out; + } + /* Use platform get_ro function, else try on board write protect */ if (gpio_ro >= 0) read_only = gpio_ro; @@ -1596,6 +1956,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) read_only = mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+out: dev_dbg(&mmc->class_dev, "card is %s\n", read_only ? "read-only" : "read-write");
@@ -1658,7 +2019,7 @@ static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
if (clk_en_a != clk_en_a_old) { mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + dw_mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } } @@ -1710,7 +2071,16 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; int err = -EINVAL; - +#ifdef CONFIG_ASCEND_HISI_MMC + struct dw_mci_tuning_data tuning_data; + + if (mmc_is_ascend_customized(mmc->parent)) { + if (drv_data && drv_data->execute_tuning_hisi) + err = drv_data->execute_tuning_hisi(slot, + opcode, &tuning_data); + return err; + } +#endif if (drv_data && drv_data->execute_tuning) err = drv_data->execute_tuning(slot, opcode); return err; @@ -1791,7 +2161,7 @@ static bool dw_mci_reset(struct dw_mci *host)
ciu_out: /* After a CTRL reset we need to have CIU set clock registers */ - mci_send_cmd(host->slot, SDMMC_CMD_UPD_CLK, 0); + dw_mci_send_cmd(host->slot, SDMMC_CMD_UPD_CLK, 0);
return ret; } @@ -1813,17 +2183,112 @@ static const struct mmc_host_ops dw_mci_ops = { .prepare_hs400_tuning = dw_mci_prepare_hs400_tuning, };
-static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) +#ifdef CONFIG_ASCEND_HISI_MMC +static const struct mmc_host_ops dw_hisi_mci_ops = { + .request = dw_mci_request, + .pre_req = dw_mci_pre_req, + .post_req = dw_mci_post_req, + .set_ios = dw_mci_set_ios, + .get_ro = dw_mci_get_ro, + .get_cd = hisi_dw_mci_get_cd, + .hw_reset = dw_mci_hw_reset, + .enable_sdio_irq = dw_mci_enable_sdio_irq, + .ack_sdio_irq = dw_mci_ack_sdio_irq, + .execute_tuning = dw_mci_execute_tuning, + .card_busy = dw_mci_card_busy, + .slowdown_clk = dw_mci_slowdown_clk, + .start_signal_voltage_switch = dw_mci_start_signal_voltage_switch, + .init_card = dw_mci_init_card, + .prepare_hs400_tuning = dw_mci_prepare_hs400_tuning, +}; +#endif + +static int hisi_dw_mci_del_timer(struct dw_mci *host) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + if (del_timer(&host->timer) == 0) + dev_info(host->dev, "inactive timer\n"); + + if (host->cur_slot->mrq == NULL || host->mrq == NULL) + return 1; +#endif /* CONFIG_ASCEND_HISI_MMC */ + return 0; +} + +static void hisi_dw_mci_tuning_move(struct dw_mci *host, + struct mmc_request *mrq) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct mmc_host *prev_mmc = host->slot->mmc; + int timing = prev_mmc->ios.timing; + + if (!(drv_data->tuning_move)) + return; + + /*SOC1005*/ + if ((host->flags & DWMMC_TUNING_DONE) && mrq && mrq->cmd && + ((mrq->cmd->error) || + (mrq->cmd->data && ((mrq->cmd->data->error) || + (mrq->cmd->data->stop && + mrq->cmd->data->stop->error))))) { + /* if SD get hardware timeout, No tuning move! */ + if (host->sd_hw_timeout == 1) { + host->sd_hw_timeout = 0; + return; + } + + dev_dbg(host->dev, + "move tuning del_sel, start=%d, cmd=%d, arg=0x%x\n", + host->tuning_move_start, + mrq->cmd->opcode, + mrq->cmd->arg); + /* req error, need move del_sel */ + if (host->tuning_move_start != -1) { + if (drv_data->tuning_move(host, timing, + host->tuning_move_start)) { + host->tuning_move_start = 0; + mrq->cmd->retries++; + + if (mrq->cmd->data && mrq->cmd->data->error) + mrq->cmd->data->error = 0; + + if (mrq->cmd->data && + mrq->cmd->data->stop && + mrq->cmd->data->stop->error) + mrq->cmd->data->stop->error = 0; + + if (!mrq->cmd->error) + mrq->cmd->error = -EILSEQ; + } else { + host->tuning_move_start = -1; + } + } + } else { + host->tuning_move_start = 1; + } +#endif /* CONFIG_ASCEND_HISI_MMC */ +} + +void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) __releases(&host->lock) __acquires(&host->lock) { struct dw_mci_slot *slot; struct mmc_host *prev_mmc = host->slot->mmc;
+ if (mmc_is_ascend_customized(host->dev)) + if (hisi_dw_mci_del_timer(host)) + return; + WARN_ON(host->cmd || host->data);
host->slot->mrq = NULL; host->mrq = NULL; + + if (mmc_is_ascend_customized(host->dev)) + hisi_dw_mci_tuning_move(host, mrq); + if (!list_empty(&host->queue)) { slot = list_entry(host->queue.next, struct dw_mci_slot, queue_node); @@ -1844,6 +2309,24 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); spin_lock(&host->lock); + if (mmc_is_ascend_customized(host->dev)) { + pm_runtime_mark_last_busy(mmc_dev(prev_mmc)); + pm_runtime_put_autosuspend(mmc_dev(prev_mmc)); + } +} +EXPORT_SYMBOL(dw_mci_request_end); + +static inline void dw_mci_check_and_udelay_1000(struct dw_mci *host, + struct mmc_command *cmd) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + if (!mmc_is_ascend_customized(host->dev)) + return; + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (cmd->opcode == MMC_SEND_TUNING_BLOCK)) + udelay(1000); +#endif + return; }
static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) @@ -1867,13 +2350,16 @@ static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) } }
- if (status & SDMMC_INT_RTO) + if (status & SDMMC_INT_RTO) { + dw_mci_check_and_udelay_1000(host, cmd); cmd->error = -ETIMEDOUT; - else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC)) + } else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC)) { + dw_mci_check_and_udelay_1000(host, cmd); cmd->error = -EILSEQ; - else if (status & SDMMC_INT_RESP_ERR) + } else if (status & SDMMC_INT_RESP_ERR) { + dw_mci_check_and_udelay_1000(host, cmd); cmd->error = -EIO; - else + } else cmd->error = 0;
return cmd->error; @@ -1977,51 +2463,298 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host) return true; }
-static void dw_mci_tasklet_func(unsigned long priv) +#ifdef CONFIG_ASCEND_HISI_MMC +static void dw_hisi_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; struct mmc_data *data; struct mmc_command *cmd; - struct mmc_request *mrq; enum dw_mci_state state; enum dw_mci_state prev_state; - unsigned int err; + u32 status;
spin_lock(&host->lock);
state = host->state; data = host->data; - mrq = host->mrq; + + if (host->cmd_status & SDMMC_INT_HLE) { + clear_bit(EVENT_CMD_COMPLETE, &host->pending_events); + dev_err(host->dev, "hardware locked write error\n"); + dw_mci_reg_dump(host); + host->cmd_status &= ~SDMMC_INT_HLE; + goto unlock; + }
do { prev_state = state;
switch (state) { case STATE_IDLE: - case STATE_WAITING_CMD11_DONE: break;
- case STATE_SENDING_CMD11: case STATE_SENDING_CMD: - if (!dw_mci_clear_pending_cmd_complete(host)) + if (host->cmd == NULL) { + dev_err(host->dev, + "%s: The command currently being send to the card is NULL!\n", + __func__); + break; + } + if (!test_and_clear_bit(EVENT_CMD_COMPLETE, + &host->pending_events)) break; - cmd = host->cmd; host->cmd = NULL; set_bit(EVENT_CMD_COMPLETE, &host->completed_events); - err = dw_mci_command_complete(host, cmd); - if (cmd == mrq->sbc && !err) { - __dw_mci_start_request(host, host->slot, - mrq->cmd); + dw_mci_command_complete(host, cmd); + if (cmd == host->mrq->sbc && !cmd->error) { + prev_state = state = STATE_SENDING_CMD; + __dw_mci_start_request(host, host->cur_slot, + host->mrq->cmd); goto unlock; } + if (data && cmd->error && + cmd != data->stop) { + if (host->mrq->data->stop) + send_stop_cmd(host, host->mrq->data); + else { + dw_mci_start_command(host, + &host->stop, + host->stop_cmdr); + host->stop_snd = true; + }
- if (cmd->data && err) { - /* - * During UHS tuning sequence, sending the stop - * command after the response CRC error would - * throw the system into a confused state - * causing all future tuning phases to report + state = STATE_SENDING_STOP; + /* To avoid fifo full condition */ + dw_mci_fifo_reset(host->dev, host); + break; + } + + if (!host->mrq->data || cmd->error) { + dw_mci_request_end(host, host->mrq); + goto unlock; + } + + prev_state = state = STATE_SENDING_DATA; + /* fall through */ + + case STATE_SENDING_DATA: + if (test_and_clear_bit(EVENT_DATA_ERROR, + &host->pending_events)) { + set_bit(EVENT_XFER_COMPLETE, + &host->pending_events); + if (data) { + if (data->stop) + send_stop_cmd(host, data); + else { + dw_mci_start_command(host, + &host->stop, + host->stop_cmdr); + host->stop_snd = true; + } + } + /* To avoid fifo full condition */ + dw_mci_fifo_reset(host->dev, host); + state = STATE_DATA_ERROR; + break; + } + + if (!test_and_clear_bit(EVENT_XFER_COMPLETE, + &host->pending_events)) + break; + + set_bit(EVENT_XFER_COMPLETE, &host->completed_events); + prev_state = state = STATE_DATA_BUSY; + /* fall through */ + + case STATE_DATA_BUSY: + if (!test_and_clear_bit(EVENT_DATA_COMPLETE, + &host->pending_events)) + break; + + set_bit(EVENT_DATA_COMPLETE, &host->completed_events); + status = host->data_status; + if (!data) { + dev_err(host->dev, "data is null\n"); + break; + } + if (status & DW_MCI_DATA_ERROR_FLAGS) { + if (status & SDMMC_INT_DRTO) { + dev_warn(host->dev, + "data timeout error\n"); + data->error = -ETIMEDOUT; + } else if (status & SDMMC_INT_DCRC) { + if (!(host->flags & DWMMC_IN_TUNING)) { + dev_warn(host->dev, + "data CRC error\n"); + dw_mci_reg_dump(host); + } + data->error = -EILSEQ; + } else if (status & SDMMC_INT_EBE) { + if (host->dir_status == + DW_MCI_SEND_STATUS) { + /* + * No data CRC status + * was returned. + * The number of bytes + * transferred will + * be exaggerated in PIO mode. + */ + data->bytes_xfered = 0; + data->error = -ETIMEDOUT; + dev_warn(host->dev, + "Write no CRC\n"); + } else { + data->error = -EIO; + dev_warn(host->dev, + "End bit error\n"); + } + } else if (status & SDMMC_INT_SBE) { + dev_warn(host->dev, + "Start bit error (status=%08x)\n", + status); + data->error = -EIO; + } else { + dev_warn(host->dev, + "data FIFO erro (status=%08x)\n", + status); + data->error = -EIO; + } + /* + * After an error, there may be data lingering + * in the FIFO, so reset it - doing so + * generates a block interrupt, hence setting + * the scatter-gather pointer to NULL. + */ + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + dw_mci_fifo_reset(host->dev, host); + } else { + data->bytes_xfered = data->blocks * data->blksz; + data->error = 0; + } + + host->data = NULL; + + if (!data->stop && !host->stop_snd) { + dw_mci_request_end(host, host->mrq); + goto unlock; + } + + if (host->mrq->sbc && !data->error) { + if (data->stop) + data->stop->error = 0; + dw_mci_request_end(host, host->mrq); + goto unlock; + } + + prev_state = state = STATE_SENDING_STOP; + if (!data->error) + send_stop_cmd(host, data); + + if (test_and_clear_bit(EVENT_DATA_ERROR, + &host->pending_events)) { + if (data->stop) + send_stop_cmd(host, data); + else { + dw_mci_start_command(host, + &host->stop, + host->stop_cmdr); + host->stop_snd = true; + } + } + /* fall through */ + + case STATE_SENDING_STOP: + if (!test_and_clear_bit(EVENT_CMD_COMPLETE, + &host->pending_events)) + break; + + if (host->mrq->cmd->error && + host->mrq->data) { + dw_mci_stop_dma(host); + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + dw_mci_fifo_reset(host->dev, host); + dw_mci_ciu_reset(host->dev, host); + } + + host->cmd = NULL; + host->data = NULL; + + if (host->mrq->stop) + dw_mci_command_complete(host, host->mrq->stop); + else + host->cmd_status = 0; + dw_mci_request_end(host, host->mrq); + goto unlock; + + case STATE_DATA_ERROR: + if (!test_and_clear_bit(EVENT_XFER_COMPLETE, + &host->pending_events)) + break; + + dw_mci_stop_dma(host); + set_bit(EVENT_XFER_COMPLETE, &host->completed_events); + + state = STATE_DATA_BUSY; + break; + default: + break; + } + } while (state != prev_state); + + host->state = state; +unlock: + spin_unlock(&host->lock); +} +#endif /* CONFIG_ASCEND_HISI_MMC */ + +static void dw_mci_tasklet_func(unsigned long priv) +{ + struct dw_mci *host = (struct dw_mci *)priv; + struct mmc_data *data; + struct mmc_command *cmd; + struct mmc_request *mrq; + enum dw_mci_state state; + enum dw_mci_state prev_state; + unsigned int err; + + spin_lock(&host->lock); + + state = host->state; + data = host->data; + mrq = host->mrq; + + do { + prev_state = state; + + switch (state) { + case STATE_IDLE: + case STATE_WAITING_CMD11_DONE: + break; + + case STATE_SENDING_CMD11: + case STATE_SENDING_CMD: + if (!dw_mci_clear_pending_cmd_complete(host)) + break; + + cmd = host->cmd; + host->cmd = NULL; + set_bit(EVENT_CMD_COMPLETE, &host->completed_events); + err = dw_mci_command_complete(host, cmd); + if (cmd == mrq->sbc && !err) { + __dw_mci_start_request(host, host->slot, + mrq->cmd); + goto unlock; + } + + if (cmd->data && err) { + /* + * During UHS tuning sequence, sending the stop + * command after the response CRC error would + * throw the system into a confused state + * causing all future tuning phases to report * failure. * * In such case controller will move into a data @@ -2637,6 +3370,157 @@ static void dw_mci_handle_cd(struct dw_mci *host) msecs_to_jiffies(host->pdata->detect_delay_ms)); }
+#ifdef CONFIG_ASCEND_HISI_MMC +static irqreturn_t dw_hisi_mci_interrupt(int irq, void *dev_id) +{ + struct dw_mci *host = dev_id; + u32 pending; + int i; + u32 temp; + + pending = mci_readl(host, MINTSTS); /* read-only mask reg */ + + if (pending) { + + /* + * DTO fix - version 2.10a and below, and only if internal DMA + * is configured. + */ + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { + if (!pending && + ((mci_readl(host, STATUS) >> 17) & 0x1fff)) + pending |= SDMMC_INT_DATA_OVER; + } + + if (pending & SDMMC_INT_CMD_DONE) { + u32 cmd = mci_readl(host, CMD) & 0x3f; + + if (cmd == SD_SWITCH_VOLTAGE && + !(mci_readl(host, STATUS) & + SDMMC_STATUS_BUSY)) { + pending |= SDMMC_INT_RTO; + } + } + + if (pending & SDMMC_INT_HLE) { + mci_writel(host, RINTSTS, SDMMC_INT_HLE); + host->cmd_status = pending; + tasklet_schedule(&host->tasklet);/*dw_mci_tasklet_func*/ + } + + if (pending & DW_MCI_CMD_ERROR_FLAGS) { + mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); + host->cmd_status = pending; + smp_wmb(); /* drain writebuffer */ + set_bit(EVENT_CMD_COMPLETE, &host->pending_events); + } + + if (pending & SDMMC_INT_VOLT_SWITCH) { + u32 cmd = mci_readl(host, CMD) & 0x3f; + + if (cmd == SD_SWITCH_VOLTAGE) { + mci_writel(host, + RINTSTS, SDMMC_INT_VOLT_SWITCH); + + dw_mci_cmd_interrupt(host, pending); + + mci_writel(host, RINTSTS, SDMMC_INT_RESP_ERR); + temp = mci_readl(host, INTMASK); + temp |= SDMMC_INT_RESP_ERR; + mci_writel(host, INTMASK, temp); + } + } + + if (pending & DW_MCI_DATA_ERROR_FLAGS) { + /* if there is an error report DATA_ERROR */ + mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); + host->data_status = pending; + smp_wmb(); /* drain writebuffer */ + set_bit(EVENT_DATA_ERROR, &host->pending_events); + tasklet_schedule(&host->tasklet);/*dw_mci_tasklet_func*/ + } + + if (pending & SDMMC_INT_DATA_OVER) { + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); + if (!host->data_status) + host->data_status = pending; + smp_wmb(); /* drain writebuffer */ + if (host->dir_status == DW_MCI_RECV_STATUS) { + if (host->sg != NULL) + dw_mci_read_data_pio(host, true); + } + set_bit(EVENT_DATA_COMPLETE, &host->pending_events); + tasklet_schedule(&host->tasklet);/*dw_mci_tasklet_func*/ + } + + if (pending & SDMMC_INT_RXDR) { + mci_writel(host, RINTSTS, SDMMC_INT_RXDR); + if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) + dw_mci_read_data_pio(host, false); + } + + if (pending & SDMMC_INT_TXDR) { + mci_writel(host, RINTSTS, SDMMC_INT_TXDR); + if (host->dir_status == DW_MCI_SEND_STATUS && host->sg) + dw_mci_write_data_pio(host); + } + + if (pending & SDMMC_INT_CMD_DONE) { + mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE); + dw_mci_cmd_interrupt(host, pending); + } + + if (pending & SDMMC_INT_CD) { + mci_writel(host, RINTSTS, SDMMC_INT_CD); + /* dw_mci_work_routine_card */ + queue_work(host->card_workqueue, + &host->card_work); + } + + /* Handle SDIO Interrupts */ + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot; + + if (pending & SDMMC_INT_SDIO(i)) { + mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + mmc_signal_sdio_irq(slot->mmc); + } + } + + } + + if (host->use_dma != TRANS_MODE_IDMAC) + return IRQ_HANDLED; + + +#ifdef CONFIG_MMC_DW_IDMAC + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) { + /* Handle 32 bit DMA interrupts */ + pending = mci_readl(host, IDSTS); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, + IDSTS, + SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } + } else { + /* Handle 64 bit DMA interrupts */ + pending = mci_readl(host, IDSTS64); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, + IDSTS64, + SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } + } +#endif + + return IRQ_HANDLED; +} +#endif /* CONFIG_ASCEND_HISI_MMC */ + static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; @@ -2816,6 +3700,172 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) return 0; }
+#ifdef CONFIG_ASCEND_HISI_MMC +static int dw_hisi_mci_init_slot(struct dw_mci *host) +{ + struct mmc_host *mmc; + struct dw_mci_slot *slot; + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct dw_mci_hs_priv_data *priv = host->priv; + int ctrl_id, ret; + u8 bus_width; + unsigned int id = 0; + + mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); + if (!mmc) + return -ENOMEM; + + slot = mmc_priv(mmc); + slot->id = id; + slot->mmc = mmc; + slot->host = host; + host->slot = slot; + + mmc->ops = &dw_hisi_mci_ops; + mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); + mmc->f_max = host->bus_hz; + dw_mci_set_freq_specific(priv, slot->host->bus_hz, &(mmc->f_max)); + + mmc->sdio_present = 0; + + if (host->pdata->get_ocr) + mmc->ocr_avail = host->pdata->get_ocr(id); + else + mmc->ocr_avail = MMC_VDD_28_29; + + /* + * Start with slot power disabled, it will be enabled when a card + * is detected. + */ + if (host->pdata->setpower) + host->pdata->setpower(id, 0); + + + if (host->pdata->caps) + mmc->caps = host->pdata->caps; + + if (host->pdata->pm_caps) { + mmc->pm_caps = host->pdata->pm_caps; + mmc->pm_flags = mmc->pm_caps; + } + + if (host->dev->of_node) { + ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); + if (ctrl_id < 0) + ctrl_id = 0; + } else { + ctrl_id = to_platform_device(host->dev)->id; + } + + if (drv_data && drv_data->caps) + mmc->caps |= drv_data->caps[ctrl_id]; + + if (host->pdata->caps2) + mmc->caps2 = host->pdata->caps2; + + if (host->pdata->get_bus_wd) + bus_width = host->pdata->get_bus_wd(slot->id); + else + bus_width = 1; + + dev_info(host->dev, "%s %d mmc->caps: 0x%x, mmc->caps2: 0x%x\n", + __func__, __LINE__, mmc->caps, mmc->caps2); + + switch (bus_width) { + case 8: + mmc->caps |= MMC_CAP_8_BIT_DATA; + break; + case 4: + mmc->caps |= MMC_CAP_4_BIT_DATA; + break; + } + + if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + + if (host->pdata->blk_settings) { + mmc->max_segs = host->pdata->blk_settings->max_segs; + mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; + mmc->max_blk_count = host->pdata->blk_settings->max_blk_count; + mmc->max_req_size = host->pdata->blk_settings->max_req_size; + mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; + } else { + /* Useful defaults if platform data is unset. */ +#ifdef CONFIG_MMC_DW_IDMAC + mmc->max_segs = host->ring_size; + mmc->max_blk_size = 65536; + mmc->max_seg_size = 0x1000; + mmc->max_req_size = mmc->max_seg_size * host->ring_size; + mmc->max_blk_count = mmc->max_req_size / 512; +#else + mmc->max_segs = 64; + mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ + mmc->max_blk_count = 512; + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; +#endif /* CONFIG_MMC_DW_IDMAC */ + } + + if (priv->chip_platform == SDMMC_ASIC_PLATFORM) { + host->pinctrl = devm_pinctrl_get(mmc_dev(mmc)); + if (IS_ERR(host->pinctrl)) { + dev_warn(host->dev, "could not get pinctrl\n"); + host->pinctrl = NULL; + } + + host->pins_default = pinctrl_lookup_state(host->pinctrl, + PINCTRL_STATE_DEFAULT); + /* enable pins to be muxed in and configured */ + if (IS_ERR(host->pins_default)) { + dev_warn(host->dev, "could not get default pinstate\n"); + host->pins_default = NULL; + } + + host->pins_idle = pinctrl_lookup_state(host->pinctrl, + PINCTRL_STATE_IDLE); + if (IS_ERR(host->pins_idle)) { + dev_warn(host->dev, "could not get default pinstate\n"); + host->pins_idle = NULL; + } + } else { + host->pinctrl = NULL; + host->pins_default = NULL; + host->pins_idle = NULL; + } + + /*BEGIN vmmc and vqmmc force to NULL for ascend plat, 2018/04/26*/ + host->vmmc = NULL; + host->vqmmc = NULL; + /*BEGIN vmmc and vqmmc force to NULL for ascend plat, 2018/04/26*/ + + if (drv_data && drv_data->cd_detect_init) + drv_data->cd_detect_init(host); + + if (hisi_dw_mci_get_cd(mmc)) + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); + else + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); + + + ret = mmc_add_host(mmc); + if (ret) + goto err_setup_bus; + +#if defined(CONFIG_DEBUG_FS) + dw_mci_init_debugfs(slot); +#endif + + /* Card initially undetected */ + slot->last_detect_state = 0; + + return 0; + +err_setup_bus: + mmc_free_host(mmc); + return -EINVAL; +} +#endif /* CONFIG_ASCEND_HISI_MMC */ + static int dw_mci_init_slot(struct dw_mci *host) { struct mmc_host *mmc; @@ -2894,12 +3944,39 @@ static int dw_mci_init_slot(struct dw_mci *host)
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot) { +#ifdef CONFIG_ASCEND_HISI_MMC + unsigned int id = 0; + + if (mmc_is_ascend_customized(slot->host->dev)) { + /* Shutdown detect IRQ */ + if (slot->host->pdata->exit) + slot->host->pdata->exit(id); + } +#endif /* Debugfs stuff is cleaned up by mmc core */ mmc_remove_host(slot->mmc); slot->host->slot = NULL; mmc_free_host(slot->mmc); }
+static inline void hisi_set_disc_dz(struct dw_mci *host, int flag) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + if (flag == 64) + host->desc_sz = DW_MCI_DESC_SZ_64BIT; + else if (flag == 32) + host->desc_sz = DW_MCI_DESC_SZ; +#endif +} + +static inline void hisi_set_dma_mask_32bit(struct dw_mci *host) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + host->dma_mask = DMA_BIT_MASK(32); + host->dev->dma_mask = &host->dma_mask; +#endif +} + static void dw_mci_init_dma(struct dw_mci *host) { int addr_config; @@ -2936,6 +4013,8 @@ static void dw_mci_init_dma(struct dw_mci *host)
if (addr_config == 1) { /* host supports IDMAC in 64-bit address mode */ + if (mmc_is_ascend_customized(host->dev)) + hisi_set_disc_dz(host, 64); host->dma_64bit_address = 1; dev_info(host->dev, "IDMAC supports 64-bit address mode.\n"); @@ -2947,12 +4026,23 @@ static void dw_mci_init_dma(struct dw_mci *host) host->dma_64bit_address = 0; dev_info(host->dev, "IDMAC supports 32-bit address mode.\n"); + if (mmc_is_ascend_customized(host->dev)) { + hisi_set_disc_dz(host, 32); + hisi_set_dma_mask_32bit(host); + } }
/* Alloc memory for sg translation */ - host->sg_cpu = dmam_alloc_coherent(host->dev, - DESC_RING_BUF_SZ, - &host->sg_dma, GFP_KERNEL); + if (mmc_is_ascend_customized(host->dev)) + host->sg_cpu = + dmam_alloc_coherent(host->dev, + HISI_DISC_DZ(host) * DESC_RING_BUF_SZ, + &host->sg_dma, GFP_KERNEL); + else + host->sg_cpu = + dmam_alloc_coherent(host->dev, + DESC_RING_BUF_SZ, + &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { dev_err(host->dev, "%s: could not alloc DMA memory\n", @@ -3113,6 +4203,140 @@ static void dw_mci_dto_timer(struct timer_list *t) }
#ifdef CONFIG_OF + +#ifdef CONFIG_ASCEND_HISI_MMC +static struct dw_mci_of_quirks { + char *quirk; + int id; +} of_quirks[] = { + { + .quirk = "supports-highspeed", + .id = DW_MCI_QUIRK_HIGHSPEED, + }, { + .quirk = "broken-cd", + .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, + }, { + .quirk = "disable-wp", + .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, + }, +}; + +static inline void hisi_get_num_slot_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) +{ + struct device *dev = host->dev; + + if (!mmc_is_ascend_customized(dev)) + return; + /* find out number of slots supported */ + if (device_property_read_u32(dev, "num-slots", &pdata->num_slots)) { + dev_info(dev, "num-slots property not found\n"); + dev_info(dev, "assuming 1 slot is available\n"); + pdata->num_slots = 1; + } +} +static inline void hisi_get_quirks_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) +{ + struct device *dev = host->dev; + struct device_node *np = host->dev->of_node; + int idx; + + if (!mmc_is_ascend_customized(dev)) + return; + /* get quirks */ + for (idx = 0; idx < ARRAY_SIZE(of_quirks); idx++) + if (of_get_property(np, of_quirks[idx].quirk, NULL)) { + pdata->quirks |= of_quirks[idx].id; + if (of_quirks[idx].id == + DW_MCI_QUIRK_NO_WRITE_PROTECT) { + pdata->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; + } + } +} +static void hisi_parse_card_detection_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) +{ + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct device *dev = host->dev; + struct device_node *np = dev->of_node; + + if (!mmc_is_ascend_customized(dev)) + return; + + if (of_find_property(np, "non-removable", NULL)) + pdata->caps |= MMC_CAP_NONREMOVABLE; + + if (of_find_property(np, "keep-power-in-suspend", NULL)) + pdata->pm_caps |= MMC_PM_KEEP_POWER; + + if (of_find_property(np, "keep-power-ignore-pm-notify", NULL)) + pdata->pm_caps |= MMC_PM_IGNORE_PM_NOTIFY; + + if (of_find_property(np, "enable-sdio-wakeup", NULL)) + pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + + if (of_find_property(np, "caps2-mmc-packed-command", NULL)) + pdata->caps2 |= MMC_CAP2_PACKED_CMD; + + if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL)) + pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR; + + if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL)) + pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR; + + if (of_find_property(np, "caps2-mmc-cache-ctrl", NULL)) { + dev_info(dev, "caps2-mmc-cache-ctrl is set in dts.\n"); + dev_info(dev, "normal mode cache ctrl on\n"); + pdata->caps2 |= MMC_CAP2_CACHE_CTRL; + } + + if (of_find_property(np, "caps2-wifi-support-cmd11", NULL)) { + dev_info(dev, + "dw_mci:%d %s find wifi support cmd11 in dts\n", + host->hw_mmc_id, __func__); + pdata->caps2 |= MMC_CAP2_SUPPORT_WIFI_CMD11; + } + + if (of_find_property(np, "caps-wifi-no-lowpwr", NULL)) { + dev_info(dev, + "dw_mci:%d %s find wifi support no_lowpwr in dts\n", + host->hw_mmc_id, __func__); + pdata->caps2 |= MMC_CAP2_WIFI_NO_LOWPWR; + } + + if (of_find_property(np, "caps2-support-wifi", NULL)) { + pr_info("dw_mci:%d %s find caps2-support-wifi in dts\n", + host->hw_mmc_id, __func__); + pdata->caps2 |= MMC_CAP2_SUPPORT_WIFI; + } + + if (of_find_property(np, "caps2-support-via-modem", NULL)) { + pr_info("dw_mci:%d %s find caps2-support-via-modem in dts\n", + host->hw_mmc_id, __func__); + pdata->caps2 |= MMC_CAP2_SUPPORT_VIA_MODEM; + } + + if (of_find_property(np, "full-pwr-cycle", NULL)) + pdata->caps2 |= MMC_CAP2_FULL_PWR_CYCLE; + + if (of_find_property(np, "caps2-mmc-ddr50-notify", NULL)) { + if (drv_data && drv_data->caps) + drv_data->caps[0] |= + MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50; + } +} + +#else /* CONFIG_ASCEND_HISI_MMC */ +static inline void hisi_get_num_slot_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) {} +static inline void hisi_get_quirks_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) {} +static inline void hisi_parse_card_detection_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) {} +#endif /* CONFIG_ASCEND_HISI_MMC */ + + static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) { struct dw_mci_board *pdata; @@ -3132,6 +4356,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(-EPROBE_DEFER); }
+ hisi_get_num_slot_from_dt(host, pdata); + hisi_get_quirks_from_dt(host, pdata); if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth)) dev_info(dev, "fifo-depth property not found, using value of FIFOTH register as default\n"); @@ -3152,7 +4378,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) if (ret) return ERR_PTR(ret); } - + hisi_parse_card_detection_from_dt(host, pdata); return pdata; }
@@ -3174,15 +4400,144 @@ static void dw_mci_enable_cd(struct dw_mci *host) */ if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL) return; - - if (mmc_gpio_get_cd(host->slot->mmc) < 0) { - spin_lock_irqsave(&host->irq_lock, irqflags); + if (mmc_is_ascend_customized(host->dev)) { temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_CD; mci_writel(host, INTMASK, temp); - spin_unlock_irqrestore(&host->irq_lock, irqflags); + } else + if (mmc_gpio_get_cd(host->slot->mmc) < 0) { + spin_lock_irqsave(&host->irq_lock, irqflags); + temp = mci_readl(host, INTMASK); + temp |= SDMMC_INT_CD; + mci_writel(host, INTMASK, temp); + spin_unlock_irqrestore(&host->irq_lock, irqflags); + } +} + +#ifdef CONFIG_ASCEND_HISI_MMC +static inline int dw_mci_hisi_setup_clock(struct dw_mci *host) +{ + const struct dw_mci_drv_data *drv_data = host->drv_data; + int ret; + + if (!mmc_is_ascend_customized(host->dev)) + return 0; + if (drv_data && drv_data->setup_clock) + ret = drv_data->setup_clock(host); + return ret; +} +static int dw_mci_setup_slot(struct dw_mci *host) +{ + int ret; + + if (!mmc_is_ascend_customized(host->dev)) + return dw_mci_init_slot(host); + + ret = dw_hisi_mci_init_slot(host); + if (ret) { + destroy_workqueue(host->card_workqueue); + return ret; + } + + dev_info(host->dev, "slots initialized\n"); + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) + dev_info(host->dev, + "Internal DMAC interrupt fix enabled.\n"); + return 0; +} + +static void hisi_dw_mci_clear_interrupt(struct dw_mci *host) +{ + mci_writel(host, RINTSTS, 0xFFFFFFFF); +#ifdef CONFIG_MMC_DW_IDMAC + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) + /* 32 bit dma interrupt clear */ + mci_writel(host, IDSTS, IDMAC_INT_CLR); + else + /* 64 bit dma interrupt clear */ + mci_writel(host, IDSTS64, IDMAC_INT_CLR); +#endif /* CONFIG_MMC_DW_IDMAC */ +} +static int hisi_dw_mci_init_resource(struct dw_mci *host) +{ + int ret; + + tasklet_init(&host->tasklet, dw_hisi_mci_tasklet_func, + (unsigned long)host); + + host->card_workqueue = alloc_workqueue("dw-mci-card/%d", + WQ_MEM_RECLAIM, 1, host->hw_mmc_id); + if (!host->card_workqueue) + return -ENOBUFS; + + INIT_WORK(&host->card_work, dw_mci_work_routine_card); + timer_setup(&host->timer, dw_mci_timeout_timer, 0); + ret = devm_request_irq(host->dev, host->irq, + dw_hisi_mci_interrupt, + host->irq_flags, + "dw-mci", host); + if (ret) { + destroy_workqueue(host->card_workqueue); + return ret; + } + if (host->pdata->num_slots) + host->num_slots = host->pdata->num_slots; + else + host->num_slots = + ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; + return 0; +} + +static inline void dw_mci_disable_vmmc_regulator(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + + if (host->vmmc) { + if (regulator_disable(host->vmmc)) + dev_warn(host->dev, "regulator_disable vmmc failed\n"); + return; + } + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + if (pmu_ldo16_disable()) + dev_warn(host->dev, "pmu_ldo16_disable failed\n"); + return; } } +static inline void dw_mci_disable_vqmmc_regulator(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + + if (host->vqmmc) { + if (regulator_disable(host->vqmmc)) + dev_warn(host->dev, "regulator_disable vqmmc failed\n"); + return; + } + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + if (pmu_ldo9_disable()) + dev_warn(host->dev, "pmu_ldo9_disable failed\n"); + return; + } +} + +#else /* CONFIG_ASCEND_HISI_MMC */ +static inline int dw_mci_hisi_setup_clock(struct dw_mci *host) +{ + return 0; +} +static inline int dw_mci_setup_slot(struct dw_mci *host) +{ + return dw_mci_init_slot(host); +} +static inline int hisi_dw_mci_init_resource(struct dw_mci *host) +{ + return 0; +} +static inline void hisi_dw_mci_clear_interrupt(struct dw_mci *host) {} +static inline void dw_mci_disable_vmmc_regulator(struct dw_mci *host) {} +static inline void dw_mci_disable_vqmmc_regulator(struct dw_mci *host) {} +#endif /* CONFIG_ASCEND_HISI_MMC */
int dw_mci_probe(struct dw_mci *host) { @@ -3232,6 +4587,13 @@ int dw_mci_probe(struct dw_mci *host) host->bus_hz = clk_get_rate(host->ciu_clk); }
+ ret = dw_mci_hisi_setup_clock(host); + if (ret) { + dev_err(host->dev, + "implementation specific clock setup failed\n"); + goto err_clk_ciu; + } + if (!host->bus_hz) { dev_err(host->dev, "Platform data must supply bus speed\n"); @@ -3239,21 +4601,28 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_ciu; }
- if (!IS_ERR(host->pdata->rstc)) { - reset_control_assert(host->pdata->rstc); - usleep_range(10, 50); - reset_control_deassert(host->pdata->rstc); - } + if (mmc_is_ascend_customized(host->dev)) { +#ifdef CONFIG_ASCEND_HISI_MMC + host->quirks = host->pdata->quirks; + host->sd_reinit = 0; + host->sd_hw_timeout = 0; +#endif + } else { + if (!IS_ERR(host->pdata->rstc)) { + reset_control_assert(host->pdata->rstc); + usleep_range(10, 50); + reset_control_deassert(host->pdata->rstc); + }
- if (drv_data && drv_data->init) { - ret = drv_data->init(host); - if (ret) { - dev_err(host->dev, - "implementation specific init failed\n"); - goto err_clk_ciu; + if (drv_data && drv_data->init) { + ret = drv_data->init(host); + if (ret) { + dev_err(host->dev, + "implementation specific init failed\n"); + goto err_clk_ciu; + } } } - timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0); timer_setup(&host->cto_timer, dw_mci_cto_timer, 0); timer_setup(&host->dto_timer, dw_mci_dto_timer, 0); @@ -3343,16 +4712,26 @@ int dw_mci_probe(struct dw_mci *host) else host->fifo_reg = host->regs + DATA_240A_OFFSET;
- tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); - ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, - host->irq_flags, "dw-mci", host); - if (ret) - goto err_dmaunmap; + if (mmc_is_ascend_customized(host->dev)) { + ret = hisi_dw_mci_init_resource(host); + if (ret) + goto err_dmaunmap; + } else { + tasklet_init(&host->tasklet, dw_mci_tasklet_func, + (unsigned long)host); + + ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, + host->irq_flags, "dw-mci", host); + if (ret) + goto err_dmaunmap; + }
/* * Enable interrupts for command done, data over, data empty, * receive ready and error such as transmit, receive timeout, crc error */ + if (mmc_is_ascend_customized(host->dev)) + hisi_dw_mci_clear_interrupt(host); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | DW_MCI_ERROR_FLAGS); @@ -3364,12 +4743,11 @@ int dw_mci_probe(struct dw_mci *host) host->irq, width, fifo_size);
/* We need at least one slot to succeed */ - ret = dw_mci_init_slot(host); + ret = dw_mci_setup_slot(host); if (ret) { dev_dbg(host->dev, "slot %d init failed\n", i); goto err_dmaunmap; } - /* Now that slots are all setup, we can enable card detect */ dw_mci_enable_cd(host);
@@ -3379,7 +4757,9 @@ int dw_mci_probe(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host);
- if (!IS_ERR(host->pdata->rstc)) + if (mmc_is_ascend_customized(host->dev)) + dw_mci_disable_vmmc_regulator(host); + else if (!IS_ERR(host->pdata->rstc)) reset_control_assert(host->pdata->rstc);
err_clk_ciu: @@ -3401,14 +4781,26 @@ void dw_mci_remove(struct dw_mci *host) mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+ if (mmc_is_ascend_customized(host->dev)) { +#ifdef CONFIG_ASCEND_HISI_MMC + host->flags &= ~DWMMC_IN_TUNING; + host->flags &= ~DWMMC_TUNING_DONE; + host->sd_reinit = 0; +#endif + } /* disable clock to CIU */ mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); - + if (mmc_is_ascend_customized(host->dev)) { + HISI_DEL_TIMER_SYNC(host); + HISI_DESTROY_CARD_WORKQUEUE(host); + } if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - - if (!IS_ERR(host->pdata->rstc)) + if (mmc_is_ascend_customized(host->dev)) { + dw_mci_disable_vmmc_regulator(host); + dw_mci_disable_vqmmc_regulator(host); + } else if (!IS_ERR(host->pdata->rstc)) reset_control_assert(host->pdata->rstc);
clk_disable_unprepare(host->ciu_clk); @@ -3416,8 +4808,6 @@ void dw_mci_remove(struct dw_mci *host) } EXPORT_SYMBOL(dw_mci_remove);
- - #ifdef CONFIG_PM int dw_mci_runtime_suspend(struct device *dev) { @@ -3506,6 +4896,62 @@ int dw_mci_runtime_resume(struct device *dev) EXPORT_SYMBOL(dw_mci_runtime_resume); #endif /* CONFIG_PM */
+#ifdef CONFIG_ASCEND_HISI_MMC +#ifdef CONFIG_PM_SLEEP +/* + * we should probably disable the clock to the card in the suspend path. + */ +int dw_mci_suspend(struct dw_mci *host) +{ + return 0; +} +EXPORT_SYMBOL(dw_mci_suspend); + +int dw_mci_resume(struct dw_mci *host) +{ + int i, ret; + + if (!mci_wait_reset(host->dev, host)) { + ret = -ENODEV; + return ret; + } + + if (host->use_dma && host->dma_ops->init) + host->dma_ops->init(host); + + /* Restore the old value at FIFOTH register */ + mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + + mci_writel(host, RINTSTS, 0xFFFFFFFF); + mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | + SDMMC_INT_TXDR | SDMMC_INT_RXDR | + DW_MCI_ERROR_FLAGS); + mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); + + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot; + + if (!slot) + continue; + if (slot->mmc->ios.power_mode == MMC_POWER_OFF) + continue; + + if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { + dw_mci_set_ios(slot->mmc, &slot->mmc->ios); + dw_mci_setup_bus(slot, true); + } + } + + /* Now that slots are all setup, we can enable card detect */ + dw_mci_enable_cd(host); + + return 0; +} +EXPORT_SYMBOL(dw_mci_resume); +#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_ASCEND_HISI_MMC */ + static int __init dw_mci_init(void) { pr_info("Synopsys Designware Multimedia Card Interface Driver\n"); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 46e9f8ec53980..0a0d2834444f9 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -20,6 +20,13 @@ #include <linux/reset.h> #include <linux/interrupt.h>
+#ifdef CONFIG_ASCEND_HISI_MMC +#include "dw_mmc_hisi.h" +#define MAX_MCI_SLOTS 2 +#define TUNING_INIT_CONFIG_NUM 7 +#define TUNING_INIT_TIMING_MODE 10 +#endif + enum dw_mci_state { STATE_IDLE = 0, STATE_SENDING_CMD, @@ -234,6 +241,58 @@ struct dw_mci { struct timer_list cmd11_timer; struct timer_list cto_timer; struct timer_list dto_timer; +#ifdef CONFIG_ASCEND_HISI_MMC + struct work_struct card_work; + u32 num_slots; + u16 data_offset; + struct clk *parent_clk; + int wifi_sdio_sdr104_160M; + int wifi_sdio_sdr104_177M; + struct dw_mci_slot *cur_slot; + struct mmc_command stop; + bool stop_snd; + int saved_tuning_phase; + int tuning_result_flag; + unsigned int desc_sz; + u64 dma_mask; /* custom DMA mask */ + + struct workqueue_struct *card_workqueue; + /* S/W reset timer */ + struct timer_list timer; + /* pinctrl handles */ + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_idle; + + struct regulator *vmmc; /* Power regulator */ + struct regulator *vqmmc; /* Signaling regulator (vccq) */ + unsigned int flags; /* Host attributes */ +#define DWMMC_IN_TUNING (1 << 5) /* Host is doing tuning */ +#define DWMMC_TUNING_DONE (1 << 6) /* Host initialization tuning done */ + /* Workaround flags */ + u32 quirks; + int current_div; /* record current div */ + int tuning_current_sample; /* record current sample */ + int tuning_init_sample; /* record the initial sample */ + int tuning_move_sample; /* record the move sample */ + int tuning_move_count; /* record the move count */ + unsigned int tuning_sample_flag; /* record the sample OK or NOT */ + int tuning_move_start; /* tuning move start flag */ + + +#define DWMMC_EMMC_ID 0 +#define DWMMC_SD_ID 1 +#define DWMMC_SDIO_ID 2 + int hw_mmc_id; /* Hardware mmc id */ + int sd_reinit; + int sd_hw_timeout; + + u32 clock; /* Current clock (MHz) */ + /* Saved clock for dynamic clock gating (MHz) */ + u32 clock_to_restore; + bool tuning_done; + bool tuning_needed; /* tuning move start flag */ +#endif };
/* DMA ops for Internal/External DMAC interface */ @@ -247,6 +306,40 @@ struct dw_mci_dma_ops { void (*exit)(struct dw_mci *host); };
+#ifdef CONFIG_ASCEND_HISI_MMC +/* IP Quirks/flags. */ +/* DTO fix for command transmission with IDMAC configured */ +#define DW_MCI_QUIRK_IDMAC_DTO BIT(0) +/* delay needed between retries on some 2.11a implementations */ +#define DW_MCI_QUIRK_RETRY_DELAY BIT(1) +/* High Speed Capable - Supports HS cards (up to 50MHz) */ +#define DW_MCI_QUIRK_HIGHSPEED BIT(2) +/* Unreliable card detection */ +#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) +/* No write protect */ +#define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4) + +/* Slot level quirks */ +/* This slot has no write protect */ +#define DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT BIT(0) + + +struct block_settings { + unsigned short max_segs; /* see blk_queue_max_segments */ + unsigned int max_blk_size; /* maximum size of one mmc block */ + unsigned int max_blk_count; /* maximum number of blocks in one req*/ + unsigned int max_req_size; /* maximum number of bytes in one req*/ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ +}; +#define HISI_DISC_DZ(x) x->desc_sz +#define HISI_DEL_TIMER_SYNC(x) del_timer_sync(&((x)->timer)) +#define HISI_DESTROY_CARD_WORKQUEUE(x) destroy_workqueue((x)->card_workqueue) +#else +#define HISI_DISC_DZ(x) 1 +#define HISI_DEL_TIMER_SYNC(x) +#define HISI_DESTROY_CARD_WORKQUEUE(x) +#endif /* CONFIG_ASCEND_HISI_MMC */ + struct dma_pdata;
/* Board platform data */ @@ -269,6 +362,23 @@ struct dw_mci_board { struct reset_control *rstc; struct dw_mci_dma_ops *dma_ops; struct dma_pdata *data; +#ifdef CONFIG_ASCEND_HISI_MMC + u32 num_slots; + u32 quirks; /* Workaround / Quirk flags */ + + struct block_settings *blk_settings; + int (*get_cd)(struct dw_mci *host, u32 slot_id); + int (*get_ocr)(u32 slot_id); + int (*get_bus_wd)(u32 slot_id); + /* + * Enable power to selected slot and set voltage to desired level. + * Voltage levels are specified using MMC_VDD_xxx defines defined + * in linux/mmc/host.h file. + */ + void (*setpower)(u32 slot_id, u32 volt); + void (*exit)(u32 slot_id); + void (*select_slot)(u32 slot_id); +#endif };
#define DW_MMC_240A 0x240a @@ -505,6 +615,30 @@ extern int dw_mci_runtime_suspend(struct device *device); extern int dw_mci_runtime_resume(struct device *device); #endif
+#define SDMMC_64_BIT_DMA 1 +#define SDMMC_32_BIT_DMA 0 +#ifdef CONFIG_ASCEND_HISI_MMC +#define SDMMC_CMD_ONLY_CLK (SDMMC_CMD_START | SDMMC_CMD_UPD_CLK | \ + SDMMC_CMD_PRV_DAT_WAIT) +#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) + +extern void dw_mci_set_cd(struct dw_mci *host); +extern void dw_mci_stop_dma(struct dw_mci *host); +extern void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq); +void dw_mci_idmac_reset(struct dw_mci *host); + +#ifdef CONFIG_PM +extern int dw_mci_suspend(struct dw_mci *host); +extern int dw_mci_resume(struct dw_mci *host); +#endif /* CONFIG_PM */ +int hisi_dw_mci_get_cd(struct mmc_host *mmc); +#else +static inline int hisi_dw_mci_get_cd(struct mmc_host *mmc) +{ + return 0; +} +#endif /* CONFIG_ASCEND_HISI_MMC */ + /** * struct dw_mci_slot - MMC slot state * @mmc: The mmc_host representing this slot. @@ -541,6 +675,12 @@ struct dw_mci_slot { #define DW_MMC_CARD_NEEDS_POLL 4 int id; int sdio_id; +#ifdef CONFIG_ASCEND_HISI_MMC + unsigned int quirks; + int wp_gpio; + int last_detect_state; + int sdio_wakelog_switch; +#endif };
/** @@ -567,5 +707,20 @@ struct dw_mci_drv_data { struct mmc_ios *ios); int (*switch_voltage)(struct mmc_host *mmc, struct mmc_ios *ios); +#ifdef CONFIG_ASCEND_HISI_MMC + int (*setup_clock)(struct dw_mci *host); + void (*prepare_command)(struct dw_mci *host, u32 *cmdr); + int (*cd_detect_init)(struct dw_mci *host); + + int (*tuning_find_condition)(struct dw_mci *host, int timing); + void (*tuning_set_current_state)(struct dw_mci *host, int ok); + int (*tuning_move)(struct dw_mci *host, int timing, int start); + int (*slowdown_clk)(struct dw_mci *host, int timing); + int (*execute_tuning_hisi)(struct dw_mci_slot *slot, + u32 opcode, struct dw_mci_tuning_data *tuning_data); + int (*start_signal_voltage_switch)(struct mmc_host *mmc, + struct mmc_ios *ios); + void (*work_fail_reset)(struct dw_mci *host); +#endif }; #endif /* _DW_MMC_H_ */ diff --git a/drivers/mmc/host/dw_mmc_extern.h b/drivers/mmc/host/dw_mmc_extern.h new file mode 100644 index 0000000000000..04d8c23f39e9a --- /dev/null +++ b/drivers/mmc/host/dw_mmc_extern.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _DW_MMC_EXTERN_H +#define _DW_MMC_EXTERN_H + +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/timer.h> + +#include "dw_mmc.h" + +#ifdef CONFIG_MMC_DW_HI3XXX_MODULE +extern void dw_mci_reg_dump(struct dw_mci *host); +extern void dw_mci_set_timeout(struct dw_mci *host); +extern bool dw_mci_stop_abort_cmd(struct mmc_command *cmd); +extern bool dw_mci_wait_reset(struct device *dev, struct dw_mci *host, + unsigned int reset_val); +extern void dw_mci_ciu_reset(struct device *dev, struct dw_mci *host); +extern bool dw_mci_fifo_reset(struct device *dev, struct dw_mci *host); +extern u32 dw_mci_prep_stop(struct dw_mci *host, struct mmc_command *cmd); +extern bool dw_mci_wait_data_busy(struct dw_mci *host, struct mmc_request *mrq); +extern int dw_mci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios); +extern void dw_mci_slowdown_clk(struct mmc_host *mmc, int timing); +extern void dw_mci_timeout_timer(struct timer_list *t); +extern void dw_mci_work_routine_card(struct work_struct *work); +extern bool mci_wait_reset(struct device *dev, struct dw_mci *host); +/* only for SD voltage switch on hi3650 FPGA */ +extern int gpio_direction_output(unsigned int gpio, int value); + +#else + +static inline void dw_mci_reg_dump(struct dw_mci *host) {} +static inline void dw_mci_set_timeout(struct dw_mci *host) {} +static inline void dw_mci_timeout_timer(struct timer_list *t) {} +static inline void dw_mci_work_routine_card(struct work_struct *work) {} +static inline void dw_mci_slowdown_clk(struct mmc_host *mmc, int timing) {} +static inline void dw_mci_ciu_reset(struct device *dev, struct dw_mci *host) {} + +static inline bool dw_mci_stop_abort_cmd(struct mmc_command *cmd) +{ + return 0; +} +static inline bool dw_mci_wait_reset(struct device *dev, struct dw_mci *host, + unsigned int reset_val) +{ + return 0; +} +static inline bool dw_mci_fifo_reset(struct device *dev, struct dw_mci *host) +{ + return 0; +} +static inline u32 dw_mci_prep_stop(struct dw_mci *host, + struct mmc_command *cmd) +{ + return 0; +} +static inline bool dw_mci_wait_data_busy(struct dw_mci *host, + struct mmc_request *mrq) +{ + return 0; +} +static inline int dw_mci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + return 0; +} +static inline bool mci_wait_reset(struct device *dev, struct dw_mci *host) +{ + return 0; +} +int hisi_dw_mci_get_cd(struct mmc_host *mmc); +/* only for SD voltage switch on hi3650 FPGA */ +extern int gpio_direction_output(unsigned int gpio, int value); +#endif + +#endif diff --git a/drivers/mmc/host/dw_mmc_hisi.h b/drivers/mmc/host/dw_mmc_hisi.h new file mode 100644 index 0000000000000..394fd92dce7d1 --- /dev/null +++ b/drivers/mmc/host/dw_mmc_hisi.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _DW_MMC_HISI_ +#define _DW_MMC_HISI_ +#include <linux/pinctrl/consumer.h> +#include <linux/workqueue.h> +#include <linux/of_address.h> +#include <asm/cacheflush.h> +#include <linux/pm_runtime.h> + +#ifdef CONFIG_MMC_HISI_TRACE +#include <linux/hisi/mmc_trace.h> +#endif +#include <linux/version.h> + +#define SDMMC_UHS_REG_EXT 0x108 +#define SDMMC_ENABLE_SHIFT 0x110 + +#define SDMMC_64_BIT_DMA 1 +#define SDMMC_32_BIT_DMA 0 + +#define SDMMC_CMD_ONLY_CLK (SDMMC_CMD_START | SDMMC_CMD_UPD_CLK | \ + SDMMC_CMD_PRV_DAT_WAIT) + +#define CTRL_RESET (0x1 << 0) /* Reset DWC_mobile_storage controller */ +#define FIFO_RESET (0x1 << 1) /* Reset FIFO */ +#define DMA_RESET (0x1 << 2) /* Reset DMA interface */ +#define INT_ENABLE (0x1 << 4) /* Global interrupt enable/disable bit */ +#define DMA_ENABLE (0x1 << 5) /* DMA transfer mode enable/disable bit */ +#define ENABLE_IDMAC (0x1 << 25) + + +#define INTMSK_ALL 0xFFFFFFFF +#define INTMSK_CDETECT (0x1 << 0) +#define INTMSK_RE (0x1 << 1) +#define INTMSK_CDONE (0x1 << 2) +#define INTMSK_DTO (0x1 << 3) +#define INTMSK_TXDR (0x1 << 4) +#define INTMSK_RXDR (0x1 << 5) +#define INTMSK_RCRC (0x1 << 6) +#define INTMSK_DCRC (0x1 << 7) +#define INTMSK_RTO (0x1 << 8) +#define INTMSK_DRTO (0x1 << 9) +#define INTMSK_HTO (0x1 << 10) +#define INTMSK_VOLT_SWITCH (0x1 << 10) +#define INTMSK_FRUN (0x1 << 11) +#define INTMSK_HLE (0x1 << 12) +#define INTMSK_SBE (0x1 << 13) +#define INTMSK_ACD (0x1 << 14) +#define INTMSK_EBE (0x1 << 15) +#define INTMSK_DMA (INTMSK_ACD | INTMSK_RXDR | INTMSK_TXDR) + +#define INT_SRC_IDMAC (0x0) +#define INT_SRC_MINT (0x1) + + +#define CMD_RESP_EXP_BIT (0x1 << 6) +#define CMD_RESP_LENGTH_BIT (0x1 << 7) +#define CMD_CHECK_CRC_BIT (0x1 << 8) +#define CMD_DATA_EXP_BIT (0x1 << 9) +#define CMD_RW_BIT (0x1 << 10) +#define CMD_TRANSMODE_BIT (0x1 << 11) +#define CMD_WAIT_PRV_DAT_BIT (0x1 << 13) +#define CMD_STOP_ABORT_CMD (0x1 << 14) +#define CMD_SEND_INITIALIZATION (0x1 << 15) +#define CMD_SEND_CLK_ONLY (0x1 << 21) +#define CMD_VOLT_SWITCH (0x1 << 28) +#define CMD_USE_HOLD_REG (0x1 << 29) +#define CMD_STRT_BIT (0x1 << 31) +#define CMD_ONLY_CLK (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \ + CMD_WAIT_PRV_DAT_BIT) + +#define CLK_ENABLE (0x1 << 0) +#define CLK_DISABLE (0x0 << 0) + +#define BOARDTYPE_SFT (1) + +#define STATE_KEEP_PWR (1) +#define STATE_LEGACY (0) + +/* BEGIN PN: , Modified by , 2014/06/11 */ +#define SD_SLOT_VOL_OPEN 1 +#define SD_SLOT_VOL_CLOSE 0 +/* END PN: , Modified by , 2014/06/11 */ + +#define SDMMC_ASIC_PLATFORM (-1) +#define SDMMC_FPGA_PLATFORM (0x5a5a5a5a) + +#define PLL5_EMMC_DIV_MAX 16 +#define PLL5_CLK_FREQ_MAX 1600000000 +#define PLL5_CLK_DEFAULT_FREQ 3200000 + + +#define DW_MCI_DESC_SZ 1 +#define DW_MCI_DESC_SZ_64BIT 2 + +#define DRIVER_NAME "dwmmc_hs" + +#define DW_MCI_EMMC_ID (0x00) +#define DW_MCI_SD_ID (0x01) +#define DW_MCI_SDIO_ID (0x02) + +#define EMMC_BUS_WIDTH_8_BIT 0x08 +#define EMMC_BUS_WIDTH_4_BIT 0x04 +#define EMMC_BUS_WIDTH_1_BIT 0x01 + +#define HIXX51_EMMC_CLK_SEL_PLL_OFF 0xffffff0f +#define HIXX51_EMMC_CLK_SEL_PLL_VAL 0x00000010 +#define HIXX51_EMMC_CLK_SEL_PLL_L_S 0x4 +/* davinci mmc reset reg */ +#define HIXX51_PERI_SC_EMMC_RST_REQ (0xa30) //controller soft reset +#define HIXX51_PERI_SC_EMMC_RST_DREQ (0xa34) //controller soft disreset +#define HIXX51_PERI_SC_EMMC_ICG_EN (0x398) //clock enable +#define HIXX51_PERI_SC_EMMC_ICG_DIS (0x39c) //clock disable +#define HIXX51_PERI_SC_EMMC_CLK_SEL (0x104) // input clock select +#define HIXX51_SC_USER1_EMMC (0x2204) //eMMC user area configre +#define HIXX51_SC_AXI_EMMC (0x2210) //usb axi area configre +#define HIXX51_SC_BIAS_CTRL (0x3780) +#define HIXX51_SC_PLL_PROF_CFG0 (0x3688) + +#define HIXX10_PERI_SC_EMMC_RST_REQ (0xcb0) +#define HIXX10_PERI_SC_EMMC_RST_DREQ (0xcb4) +#define HIXX10_PERI_SC_EMMC_ICG_EN (0x5c0) +#define HIXX10_PERI_SC_EMMC_ICG_DIS (0x5c4) +#define HIXX10_PERI_SC_EMMC_CLK_SEL (0x3500) +#define HIXX10_SC_BIAS_CTRL (0x3780) +#define HIXX10_SC_PLL_PROF_CFG0 (0x3688) + +#define PERI_CRG_CLKDIV4 (0xb8) +#define PERI_CRG_CLKDIV6 (0xc0) + +enum { + PERI_SC_EMMC_RST_REQ = 0, + PERI_SC_EMMC_RST_DREQ, + PERI_SC_EMMC_ICG_EN, + PERI_SC_EMMC_ICG_DIS, + PERI_SC_EMMC_CLK_SEL, + SC_BIAS_CTRL, + SC_PLL_PROF_CFG0, + SC_USER1_EMMC, + SC_AXI_EMMC, + REG_MAX_NUM, +}; + +enum { + CHIP_HIXX10 = 0, + CHIP_HIXX51, + CHIP_TYPE_MAX_NUM, +}; + +/* mmc1 sys ctrl, start from kirin980 */ +#define MMC1_SYSCTRL_PEREN0 (0x300) +#define MMC1_SYSCTRL_PERDIS0 (0x304) +#define MMC1_SYSCTRL_PERCLKEN0 (0x308) +#define MMC1_SYSCTRL_PERSTAT0 (0x30C) +#define GT_HCLK_SDIO1_BIT (0x1) + +#define MMC1_SYSCTRL_PERRSTEN0 (0x310) +#define MMC1_SYSCTRL_PERRSTDIS0 (0x314) +#define MMC1_SYSCTRL_PERRSTSTAT0 (0x318) +#define BIT_HRST_SDIO_ATLANTA (0x1) +#define BIT_RST_SDIO_ATLANTA (0x1 << 1) +/* mmc1 sys ctrl end*/ + +#define HI3660_FPGA 1 +#define PERI_CRG_PERSTAT4 (0x04c) + +#define GTCLK_SD_EN (0x20000) + +#define BIT_VOLT_OFFSET (0x314) +#define BIT_VOLT_OFFSET_AUSTIN (0x214) +#define BIT_VOLT_VALUE_18 (0x4) + +#define BIT_RST_EMMC (1<<0) +#define BIT_RST_SD (1<<0) +#define BIT_RST_SDIO (1<<0) + +#define BIT_RST_SDIO_CHICAGO (1<<20) +#define BIT_RST_SDIO_BOSTON (1<<20) + +#define GPIO_CLK_DIV(x) (((x) & 0xf) << 8) +#define GPIO_USE_SAMPLE_DLY(x) (((x) & 0x1) << 13) + +#define GPIO_CLK_ENABLE (0x1 << 16) +#define UHS_REG_EXT_SAMPLE_PHASE(x) (((x) & 0x1f) << 16) +#define UHS_REG_EXT_SAMPLE_DLY(x) (((x) & 0x1f) << 26) +#define UHS_REG_EXT_SAMPLE_DRVPHASE(x) (((x) & 0x1f) << 21) +#define SDMMC_UHS_REG_EXT_VALUE(x, y, z) \ + (UHS_REG_EXT_SAMPLE_PHASE(x) | \ + UHS_REG_EXT_SAMPLE_DLY(y) | UHS_REG_EXT_SAMPLE_DRVPHASE(z)) +#define SDMMC_GPIO_VALUE(x, y) \ + (GPIO_CLK_DIV(x) | GPIO_USE_SAMPLE_DLY(y)) + +/*Reduce Max tuning loop,200 loops may case the watch dog timeout*/ +#define MAX_TUNING_LOOP 32 + +struct dw_mci_hs_priv_data { + int id; + int old_timing; + int gpio_cd; + int gpio_sw; + int sw_value; + int old_signal_voltage; + int old_power_mode; + unsigned int priv_bus_hz; + unsigned int cd_vol; +/* BEGIN PN: , Modified by , 2014/06/11 */ + unsigned int sd_slot_ldo10_status; +/* END PN: , Modified by , 2014/06/11 */ + int dw_mmc_bus_clk; + int dw_voltage_switch_gpio; + int chip_platform; + u32 chip_type; + int hi3660_sd_ioset_sd_sel; + int hi3660_sd_ioset_jtag_sd_sel; + int hi3660_fpga_sd_ioset; + int cs; + int in_resume; + void __iomem *ao_sysctrl; + void __iomem *peri_sysctrl; + void __iomem *ioc_off; +}; + +struct dw_mci_tuning_data { + const u8 *blk_pattern; + unsigned int blksz; +}; + +#ifdef CONFIG_MMC_DW_IDMAC +#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ + SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ + SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ + SDMMC_IDMAC_INT_TI) +#endif +enum himntnEnum { + HIMNTN_NVE_VALID = 0, + HIMNTN_WDT_MIN, + HIMNTN_AP_WDT = HIMNTN_WDT_MIN, + HIMNTN_GLOBAL_WDT, + HIMNTN_MODEM_WDT, + HIMNTN_LPM3_WDT, + HIMNTN_IOM3_WDT, + HIMNTN_HIFI_WDT, + HIMNTN_SECOS_WDT, + HIMNTN_ISP_WDT, + HIMNTN_IVP_WDT, + HIMNTN_OCBC_WDT = 10, + HIMNTN_UCE_WDT, + HIMNTN_RESERVED_WDT3, + HIMNTN_WDT_MAX = HIMNTN_RESERVED_WDT3, + HIMNTN_FST_DUMP_MEM, + HIMNTN_MNTN_DUMP_MEM, + HIMNTN_SD2JTAG, + HIMNTN_PRESS_KEY_TO_FASTBOOT, + HIMNTN_PANIC_INTO_LOOP, + HIMNTN_GOBAL_RESETLOG, + HIMNTN_NOC_INT_HAPPEN, + HIMNTN_NOC_ERROR_REBOOT = 20, + HIMNTN_DFXPARTITION_TO_FILE, + HIMNTN_DDR_ERROR_REBOOT, + HIMNTN_HISEE, + HIMNTN_WATCHPOINT_EN, + HIMNTN_KMEMLEAK_SWITCH, + HIMNTN_FB_PANIC_REBOOT, + HIMNTN_MEM_TRACE = 27, /*Bit:27: Memory Trace hook switch.*/ + HIMNTN_FTRACE, + HIMNTN_EAGLE_EYE, + /*Hook switch is the same one of kdump.*/ + HIMNTN_KERNEL_DUMP_ENABLE = 30, + HIMNTN_SD2DJTAG, + HIMNTN_MMC_TRACE, + HIMNTN_LPM3_PANIC_INTO_LOOP, + HIMNTN_TRACE_CLK_REGULATOR, + HIMNTN_CORESIGHT, + HIMNTN_DMSSPT, + HIMNTN_HHEE, + HIMNTN_KASLR, + HIMNTN_SD2UART6, + /*Add above, and keep the same as*/ + /*definition in reboot_reason.h in fastboot !!!!*/ + HIMNTN_BOTTOM +}; + +enum { + MEM_EMMC_IOBASE, + MEM_PERI_SUBCTRL_IOBASE, + MEM_SYSCTRL_IOBASE, + MEM_GPIO_IOBASE, + MEM_IOBASE_MAX, +}; + +#endif /* _DW_MMC_HISI_ */ diff --git a/drivers/mmc/host/hisi_mmc_pmic.h b/drivers/mmc/host/hisi_mmc_pmic.h new file mode 100644 index 0000000000000..0993042046f36 --- /dev/null +++ b/drivers/mmc/host/hisi_mmc_pmic.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _HISI_MMC_PMIC_ +#define _HISI_MMC_PMIC_ + +#include "linux/types.h" + +#if defined(CONFIG_HISI_SPMI) +u32 hisi_pmic_read_reg(const s32 reg); +s32 hisi_pmic_write_reg(const s32 reg, const u32 val); +s32 pmu_ldo2_enable(void); +s32 pmu_ldo2_disable(void); +s32 pmu_ldo2_set_voltage(const u32 volt_value); +s32 pmu_ldo16_enable(void); +s32 pmu_ldo16_disable(void); +s32 pmu_ldo16_set_voltage(const u32 volt_value); +s32 pmu_ldo9_enable(void); +s32 pmu_ldo9_disable(void); +s32 pmu_ldo9_set_voltage(const u32 volt_value); +s32 hisi_adc_get_value(const u32 channel); +s32 ntc_read_temp(const s32 channel, s32 *temp); +u32 hisi_second_pmic_read_reg(const s32 sid, const s32 reg); +s32 get_second_pmu_buck_volt(u32 device_id, u32 channel, u32 *volt_mv); +s32 get_main_pmu_buck_volt(u32 channel, u32 *volt_mv); +s32 get_main_pmu_ldo_volt(u32 channel, u32 *volt_mv); +s32 get_main_pmu_die_id(u8 *die_id, u8 len); +s32 get_second_pmu_die_id(u32 device_id, u8 *die_id, u8 len); +#else +static inline s32 hisi_pmic_read_reg(const s32 reg) { return 0; } +static inline s32 hisi_pmic_write_reg(const s32 reg, const u32 val) +{ + return 0; +} +static inline s32 pmu_ldo2_enable(void) { return 0; } +static inline s32 pmu_ldo2_disable(void) { return 0; } +static inline s32 pmu_ldo2_set_voltage(const u32 volt_value) { return 0; } +static inline s32 pmu_ldo16_enable(void) { return 0; } +static inline s32 pmu_ldo16_disable(void) { return 0; } +static inline s32 pmu_ldo16_set_voltage(const u32 volt_value) { return 0; } +static inline s32 pmu_ldo9_enable(void) { return 0; } +static inline s32 pmu_ldo9_disable(void) { return 0; } +static inline s32 pmu_ldo9_set_voltage(const u32 volt_value) { return 0; } +static inline s32 hisi_adc_get_value(const u32 channel) { return 0; } +static inline s32 ntc_read_temp(const s32 channel, s32 *temp) { return 0; } +static inline u32 hisi_second_pmic_read_reg(const s32 sid, const s32 reg) +{ + return 0; +}; +static inline s32 get_second_pmu_buck_volt(u32 device_id, + u32 channel, u32 *volt_mv) +{ + return 0; +}; +static inline s32 get_main_pmu_buck_volt(u32 channel, u32 *volt_mv) +{ + return 0; +}; +static inline s32 get_main_pmu_ldo_volt(u32 channel, u32 *volt_mv) +{ + return 0; +}; +static inline s32 get_main_pmu_die_id(u8 *die_id, u8 len) +{ + return 0; +}; +static inline s32 get_second_pmu_die_id(u32 device_id, u8 *die_id, u8 len) +{ + return 0; +}; +#endif + +#endif /* _HISI_MMC_PMIC_ */