// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2020-2022 ArtInChip Technology Co., Ltd. * Authors: matteo */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "artinchip_mmc.h" #define PAGE_SIZE 4096 #define SDMC_TIMEOUT 10000 #define MSEC_PER_SEC 1000 #define CLOCK_MIN 400000 /* 400 kHz */ #define FIFO_MIN 8 #define FIFO_MAX 4096 #define SDMC_THRCTL 0x100 #define SDMC_CARDRDTHR_EN BIT(0) #define SDMC_CARDTHR_SHIFT (16) #define SDMC_CARDTHR_MASK (0xfff << SDMC_CARDTHR_SHIFT) #define DAT0_STATUS_OFS (9) #define DAT0_STATUS_MSK (0x1 << DAT0_STATUS_OFS) struct aic_sdmc_plat { #if CONFIG_IS_ENABLED(OF_PLATDATA) struct dtd_artinchip_aic_sdmc dtplat; #endif struct mmc_config cfg; struct mmc mmc; }; struct aic_sdmc_priv { struct aic_sdmc host; struct clk clk; struct reset_ctl reset; int fifo_depth; bool fifo_mode; u32 f_max; u32 f_min; }; static inline void sdmc_writel(struct aic_sdmc *host, int reg, u32 val) { writel(val, host->ioaddr + reg); } static inline u32 sdmc_readl(struct aic_sdmc *host, int reg) { return readl(host->ioaddr + reg); } static int aic_sdmc_idma_start(struct aic_sdmc *host, struct mmc_data *data, struct bounce_buffer *bbstate) { u32 size = data->blocksize * data->blocks; sdmc_writel(host, SDMC_IDMAINTEN, SDMC_IDMAC_INT_MASK); if (data->flags == MMC_DATA_READ) return bounce_buffer_start(bbstate, (void *)data->dest, size, GEN_BB_WRITE); else return bounce_buffer_start(bbstate, (void *)data->src, size, GEN_BB_READ); } static int aic_sdmc_idma_stop(struct aic_sdmc *host, struct bounce_buffer *bbstate, u32 flag) { u32 i, mask, ctrl, retry = SDMC_TIMEOUT; if (flag == MMC_DATA_READ) mask = SDMC_IDMAC_INT_RI; else mask = SDMC_IDMAC_INT_TI; for (i = 0; i < retry; i++) { ctrl = sdmc_readl(host, SDMC_IDMAST); if (mask & ctrl) break; } ctrl = sdmc_readl(host, SDMC_IDMAST); sdmc_writel(host, SDMC_IDMAST, ctrl); ctrl = sdmc_readl(host, SDMC_HCTRL1); ctrl &= ~(SDMC_HCTRL1_DMA_EN); sdmc_writel(host, SDMC_HCTRL1, ctrl); ctrl = sdmc_readl(host, SDMC_IDMAINTEN); ctrl &= ~(SDMC_INT_ALL); sdmc_writel(host, SDMC_IDMAINTEN, ctrl); bounce_buffer_stop(bbstate); if (i == retry) { pr_warn("%s() - %s interrupt timeout.\n", __func__, mask == SDMC_IDMAC_INT_RI ? "Rx" : "Tx"); return -ETIMEDOUT; } return 0; } void aic_sdmc_idma_disable(struct aic_sdmc *host) { u32 temp; temp = sdmc_readl(host, SDMC_HCTRL1); temp &= ~SDMC_HCTRL1_USE_IDMAC; temp |= SDMC_HCTRL1_DMA_RESET; sdmc_writel(host, SDMC_HCTRL1, temp); temp = sdmc_readl(host, SDMC_PBUSCFG); temp &= ~(SDMC_PBUSCFG_IDMAC_EN | SDMC_PBUSCFG_IDMAC_FB); temp |= SDMC_PBUSCFG_IDMAC_SWR; sdmc_writel(host, SDMC_PBUSCFG, temp); } static void aic_sdmc_idma_prepare_desc(struct aic_sdmc_idma_desc *idmac, u32 flags, u32 cnt, u32 addr) { struct aic_sdmc_idma_desc *desc = idmac; desc->flags = flags; desc->cnt = cnt; desc->addr = addr; desc->next_addr = (ulong)desc + sizeof(struct aic_sdmc_idma_desc); } static void aic_sdmc_idma_prepare(struct aic_sdmc *host, struct mmc_data *data, struct aic_sdmc_idma_desc *cur_idma, void *bounce_buffer) { unsigned long ctrl; unsigned int i = 0, flags, cnt, blk_cnt; ulong data_start, data_end; blk_cnt = data->blocks; sdmc_writel(host, SDMC_IDMAST, 0xFFFFFFFF); data_start = (ulong)cur_idma; sdmc_writel(host, SDMC_IDMASADDR, (ulong)cur_idma); do { flags = SDMC_IDMAC_OWN | SDMC_IDMAC_CH; flags |= (i == 0) ? SDMC_IDMAC_FS : 0; if (blk_cnt <= 8) { flags |= SDMC_IDMAC_LD; cnt = data->blocksize * blk_cnt; } else { cnt = data->blocksize * 8; } aic_sdmc_idma_prepare_desc(cur_idma, flags, cnt, (ulong)bounce_buffer + (i * PAGE_SIZE)); cur_idma++; if (blk_cnt <= 8) break; blk_cnt -= 8; i++; } while (1); data_end = (ulong)cur_idma; flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN)); ctrl = sdmc_readl(host, SDMC_HCTRL1); ctrl |= SDMC_HCTRL1_USE_IDMAC | SDMC_HCTRL1_DMA_EN; sdmc_writel(host, SDMC_HCTRL1, ctrl); ctrl = sdmc_readl(host, SDMC_PBUSCFG); ctrl |= SDMC_PBUSCFG_IDMAC_FB | SDMC_PBUSCFG_IDMAC_EN; sdmc_writel(host, SDMC_PBUSCFG, ctrl); } static unsigned int aic_sdmc_get_timeout(struct mmc *mmc, const unsigned int size) { unsigned int timeout; timeout = size * 8; timeout /= mmc->bus_width; if (mmc->capacity < 1000000000) timeout *= 50; /* < 1GB, wait 50 times as long */ else timeout *= 10; /* wait 10 times as long */ timeout /= (mmc->clock / MSEC_PER_SEC); timeout /= mmc->ddr_mode ? 2 : 1; timeout = (timeout < MSEC_PER_SEC) ? MSEC_PER_SEC : timeout; return timeout; } static int aic_sdmc_fifo_rdy(struct aic_sdmc *host, u32 bit, u32 *len) { u32 timeout = SDMC_TIMEOUT; *len = sdmc_readl(host, SDMC_CTRST); while (--timeout && (*len & bit)) { udelay(200); *len = sdmc_readl(host, SDMC_CTRST); } if (!timeout) { pr_warn("%s() - FIFO underflow timeout\n", __func__); return -ETIMEDOUT; } return 0; } static int aic_sdmc_data_rx(struct aic_sdmc *host, u32 *buf, u32 size) { u32 i, len = 0; sdmc_writel(host, SDMC_OINTST, SDMC_INT_RXDR | SDMC_INT_DAT_DONE); while (size) { if (aic_sdmc_fifo_rdy(host, SDMC_CTRST_FIFO_EMPTY, &len) < 0) return size; len = SDMC_CTRST_FCNT(len); len = min(size, len); for (i = 0; i < len; i++) *buf++ = sdmc_readl(host, SDMC_FIFO_DATA); size = size > len ? (size - len) : 0; } return size; } static int aic_sdmc_data_tx(struct aic_sdmc *host, u32 *buf, u32 size) { u32 i, len = 0; u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1) * 2; while (size) { if (aic_sdmc_fifo_rdy(host, SDMC_CTRST_FIFO_FULL, &len) < 0) return size; len = fifo_depth - SDMC_CTRST_FCNT(len); len = min(size, len); for (i = 0; i < len; i++) sdmc_writel(host, SDMC_FIFO_DATA, *buf++); size = size > len ? (size - len) : 0; } sdmc_writel(host, SDMC_OINTST, SDMC_INT_TXDR); return size; } static int aic_sdmc_data_transfer(struct aic_sdmc *host, struct mmc_data *data) { struct mmc *mmc = host->mmc; int ret = 0; u32 timeout, mask, size, total; ulong start = get_timer(0); total = data->blocksize * data->blocks; timeout = aic_sdmc_get_timeout(mmc, total); size = total / 4; for (;;) { mask = sdmc_readl(host, SDMC_OINTST); if (mask & (SDMC_DATA_ERR | SDMC_DATA_TOUT)) { pr_err("%s() Data error! size %d/%d, mode %s-%s, status 0x%x\n", __func__, size * 4, total, host->fifo_mode ? "FIFO" : "IDMA", data->flags == MMC_DATA_READ ? "Read" : "Write", mask); ret = -EINVAL; break; } if (host->fifo_mode && size) { if (data->flags == MMC_DATA_READ && (mask & (SDMC_INT_RXDR | SDMC_INT_DAT_DONE))) size = aic_sdmc_data_rx(host, (u32 *)data->dest, size); else if (data->flags == MMC_DATA_WRITE && (mask & SDMC_INT_TXDR)) size = aic_sdmc_data_tx(host, (u32 *)data->src, size); } if (mask & SDMC_INT_DAT_DONE) { ret = 0; break; } if (get_timer(start) > timeout) { pr_warn("%s() Data timeout %d! size %d/%d, mode %s-%s, status 0x%x\n", __func__, timeout, size * 4, total, host->fifo_mode ? "FIFO" : "IDMA", data->flags == MMC_DATA_READ ? "Read" : "Write", mask); ret = -ETIMEDOUT; break; } } sdmc_writel(host, SDMC_OINTST, mask); return ret; } static int aic_sdmc_set_transfer_mode(struct aic_sdmc *host, struct mmc_data *data) { unsigned long mode = SDMC_CMD_DAT_EXP; if (data->flags & MMC_DATA_WRITE) mode |= SDMC_CMD_DAT_WR; return mode; } static int aic_sdmc_reset(struct aic_sdmc *host, u32 value) { unsigned long timeout = SDMC_TIMEOUT; u32 ctrl; ctrl = sdmc_readl(host, SDMC_HCTRL1); ctrl |= value; sdmc_writel(host, SDMC_HCTRL1, ctrl); while (timeout--) { ctrl = sdmc_readl(host, SDMC_HCTRL1); if (!(ctrl & value)) return 0; udelay(100); } return -ETIME; } #ifdef CONFIG_DM_MMC static int aic_sdmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct mmc *mmc = mmc_get_mmc_dev(dev); #else static int aic_sdmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { #endif struct aic_sdmc *host = mmc->priv; ALLOC_CACHE_ALIGN_BUFFER(struct aic_sdmc_idma_desc, cur_idma, data ? DIV_ROUND_UP(data->blocks, 8) : 0); int ret = 0, flags = 0; unsigned int timeout = 500; u32 mask; ulong start = get_timer(0); struct bounce_buffer bbstate; if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION) { while (sdmc_readl(host, SDMC_CTRST) & SDMC_CTRST_BUSY) { if (get_timer(start) > timeout) { pr_warn("%s() - Data transfer is busy\n", __func__); return -ETIMEDOUT; } } } sdmc_writel(host, SDMC_OINTST, SDMC_INT_ALL); if (data) { if ((data->blocksize * data->blocks) < 512) host->fifo_mode = true; else host->fifo_mode = false; sdmc_writel(host, SDMC_BLKSIZ, data->blocksize); sdmc_writel(host, SDMC_BLKCNT, data->blocks); aic_sdmc_reset(host, SDMC_HCTRL1_FIFO_RESET); if (!host->fifo_mode) { ret = aic_sdmc_idma_start(host, data, &bbstate); if (ret) return ret; aic_sdmc_idma_prepare(host, data, cur_idma, bbstate.bounce_buffer); } else { if (sdmc_readl(host, SDMC_PBUSCFG) & SDMC_PBUSCFG_IDMAC_EN) aic_sdmc_idma_disable(host); } } sdmc_writel(host, SDMC_CMDARG, cmd->cmdarg); if (data) flags = aic_sdmc_set_transfer_mode(host, data); if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) return -1; if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE) flags |= SDMC_CMD_INIT; if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) flags |= SDMC_CMD_STOP; else flags |= SDMC_CMD_PRV_DAT_WAIT; if (cmd->resp_type & MMC_RSP_PRESENT) { flags |= SDMC_CMD_RESP_EXP; if (cmd->resp_type & MMC_RSP_136) flags |= SDMC_CMD_RESP_LEN; } if (cmd->resp_type & MMC_RSP_CRC) flags |= SDMC_CMD_RESP_CRC; flags |= (cmd->cmdidx | SDMC_CMD_START | SDMC_CMD_USE_HOLD_REG); sdmc_writel(host, SDMC_CMD, flags); start = get_timer(0); timeout = 300; for (;;) { mask = sdmc_readl(host, SDMC_OINTST); if (mask & SDMC_INT_CMD_DONE) { if (!data) sdmc_writel(host, SDMC_OINTST, mask); break; } if (get_timer(start) > timeout) { pr_warn("%s()%d - Wait CMD%d done timeout.\n", __func__, __LINE__, cmd->cmdidx); return -ETIMEDOUT; } } if (mask & SDMC_INT_RTO) { pr_debug("%s()%d - CMD%d Response Timeout.\n", __func__, __LINE__, cmd->cmdidx); return -ETIMEDOUT; } else if (mask & SDMC_INT_RESP_ERR) { pr_err("%s()%d - CMD%d Response Error.\n", __func__, __LINE__, cmd->cmdidx); return -EIO; } else if ((cmd->resp_type & MMC_RSP_CRC) && (mask & SDMC_INT_RCRC)) { pr_err("%s()%d - CMD%d Response CRC Error.\n", __func__, __LINE__, cmd->cmdidx); return -EIO; } if (cmd->resp_type & MMC_RSP_PRESENT) { if (cmd->resp_type & MMC_RSP_136) { cmd->response[0] = sdmc_readl(host, SDMC_RESP3); cmd->response[1] = sdmc_readl(host, SDMC_RESP2); cmd->response[2] = sdmc_readl(host, SDMC_RESP1); cmd->response[3] = sdmc_readl(host, SDMC_RESP0); } else { cmd->response[0] = sdmc_readl(host, SDMC_RESP0); } } if (data) { ret = aic_sdmc_data_transfer(host, data); if (!host->fifo_mode) ret = aic_sdmc_idma_stop(host, &bbstate, data->flags); } return ret; } static void aic_sdmc_set_ext_clk_mux(struct aic_sdmc *host, u32 mux) { u32 temp = sdmc_readl(host, SDMC_DLYCTRL); temp &= ~SDMC_DLYCTRL_EXT_CLK_MUX_MASK; switch (mux) { case 1: temp |= (u32)SDMC_DLYCTRL_EXT_CLK_MUX_1 << SDMC_DLYCTRL_EXT_CLK_MUX_SHIFT; break; case 2: temp |= SDMC_DLYCTRL_EXT_CLK_MUX_2 << SDMC_DLYCTRL_EXT_CLK_MUX_SHIFT; break; case 4: default: temp |= SDMC_DLYCTRL_EXT_CLK_MUX_4 << SDMC_DLYCTRL_EXT_CLK_MUX_SHIFT; break; } sdmc_writel(host, SDMC_DLYCTRL, temp); } static u32 aic_sdmc_get_best_div(u32 sclk, u32 target_freq) { u32 down, up, f_down, f_up; down = sclk / target_freq; up = DIV_ROUND_UP(sclk, target_freq); f_down = down == 0 ? sclk : sclk / down; f_up = up == 0 ? sclk : sclk / up; /* Select the closest div parameter */ if ((f_down - target_freq) < (target_freq - f_up)) return down; return up; } static int aic_sdmc_setup_bus(struct aic_sdmc *host, u32 freq) { u32 mux, div, status; int timeout = SDMC_TIMEOUT; unsigned long sclk; u32 temp; if ((freq == host->clock) || (freq == 0)) return 0; if (host->get_mmc_clk) sclk = host->get_mmc_clk(host, freq); else if (host->bus_hz) sclk = host->bus_hz; else { pr_err("%s: Didn't get source clock value.\n", __func__); return -EINVAL; } if (sclk == freq) { /* bypass mode */ mux = 1; div = 0; } else { div = aic_sdmc_get_best_div(sclk, freq); if (div <= 4) { mux = DIV_ROUND_UP(div, 2); } else { if (div % 8) mux = 2; else mux = 4; } #ifdef CONFIG_FPGA_BOARD_ARTINCHIP mux = 1; #endif div /= mux * 2; if (div > SDMC_CLKCTRL_DIV_MAX) div = SDMC_CLKCTRL_DIV_MAX; } aic_sdmc_set_ext_clk_mux(host, mux); pr_info("SDMC%d Buswidth %d, sclk %ld KHz, req %d KHz, actual %ld KHz, div %d-%d\n", host->dev_index, host->mmc->bus_width, sclk / 1000, freq / 1000, div ? sclk / mux / div / 2 / 1000 : sclk / mux / 1000, mux, div); temp = sdmc_readl(host, SDMC_CLKCTRL); temp &= ~SDMC_CLKCTRL_EN; sdmc_writel(host, SDMC_CLKCTRL, temp); temp = sdmc_readl(host, SDMC_CLKCTRL); temp &= ~SDMC_CLKCTRL_DIV_MASK; temp |= (div << SDMC_CLKCTRL_DIV_SHIFT) & SDMC_CLKCTRL_DIV_MASK; sdmc_writel(host, SDMC_CLKCTRL, temp); sdmc_writel(host, SDMC_CMD, SDMC_CMD_PRV_DAT_WAIT | SDMC_CMD_UPD_CLK | SDMC_CMD_START); do { status = sdmc_readl(host, SDMC_CMD); if (timeout-- < 0) { pr_warn("%s: Timeout!\n", __func__); return -ETIMEDOUT; } } while (status & SDMC_CMD_START); temp = sdmc_readl(host, SDMC_CLKCTRL); temp |= SDMC_CLKCTRL_EN | SDMC_CLKCTRL_LOW_PWR; sdmc_writel(host, SDMC_CLKCTRL, temp); sdmc_writel(host, SDMC_CMD, SDMC_CMD_PRV_DAT_WAIT | SDMC_CMD_UPD_CLK | SDMC_CMD_START); timeout = SDMC_TIMEOUT; do { status = sdmc_readl(host, SDMC_CMD); if (timeout-- < 0) { pr_warn("%s: Timeout!\n", __func__); return -ETIMEDOUT; } } while (status & SDMC_CMD_START); host->clock = freq; return 0; } static int aic_sdmc_init(struct mmc *mmc) { u32 temp; struct aic_sdmc *host = mmc->priv; if (host->board_init) host->board_init(host); if (aic_sdmc_reset(host, SDMC_HCTRL1_RESET_ALL)) { pr_err("%s() - Failed to reset!\n", __func__); return -EIO; } aic_sdmc_setup_bus(host, mmc->cfg->f_min); sdmc_writel(host, SDMC_OINTST, 0xFFFFFFFF); sdmc_writel(host, SDMC_INTEN, 0); sdmc_writel(host, SDMC_TTMC, 0xFFFFFFFF); sdmc_writel(host, SDMC_IDMAINTEN, 0); sdmc_writel(host, SDMC_PBUSCFG, 1); if (!host->fifoth_val) { u32 fifo_size; fifo_size = sdmc_readl(host, SDMC_FIFOCFG); fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) | TX_WMARK(fifo_size / 2); } sdmc_writel(host, SDMC_FIFOCFG, host->fifoth_val); temp = sdmc_readl(host, SDMC_DLYCTRL); temp &= ~SDMC_DLYCTRL_CLK_DRV_PHA_MASK; temp &= ~SDMC_DLYCTRL_CLK_SMP_PHA_MASK; temp |= host->driver_phase << SDMC_DLYCTRL_CLK_DRV_PHA_SHIFT | host->driver_delay << SDMC_DLYCTRL_CLK_DRV_DLY_SHIFT | host->sample_phase << SDMC_DLYCTRL_CLK_SMP_PHA_SHIFT | host->sample_delay << SDMC_DLYCTRL_CLK_SMP_DLY_SHIFT; sdmc_writel(host, SDMC_DLYCTRL, temp); temp = sdmc_readl(host, SDMC_CLKCTRL); temp &= ~SDMC_CLKCTRL_EN; sdmc_writel(host, SDMC_CLKCTRL, temp); return 0; } #ifdef CONFIG_DM_MMC static int aic_sdmc_set_ios(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); #else static int aic_sdmc_set_ios(struct mmc *mmc) { #endif u32 temp; struct aic_sdmc *host = (struct aic_sdmc *)mmc->priv; u32 ctype, regs; aic_sdmc_setup_bus(host, mmc->clock); switch (mmc->bus_width) { case 8: ctype = SDMC_HCTRL2_BW_8BIT; break; case 4: ctype = SDMC_HCTRL2_BW_4BIT; break; default: ctype = SDMC_HCTRL2_BW_1BIT; break; } temp = sdmc_readl(host, SDMC_HCTRL2); temp &= ~(0x3 << 28); temp |= ctype; sdmc_writel(host, SDMC_HCTRL2, temp); regs = sdmc_readl(host, SDMC_HCTRL2); if (mmc->ddr_mode) regs |= SDMC_DDR_MODE; else regs &= ~SDMC_DDR_MODE; sdmc_writel(host, SDMC_HCTRL2, regs); if (host->clksel) host->clksel(host); #if CONFIG_IS_ENABLED(DM_REGULATOR) if (mmc->vqmmc_supply) { int ret; if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) regulator_set_value(mmc->vqmmc_supply, 1800000); else regulator_set_value(mmc->vqmmc_supply, 3300000); ret = regulator_set_enable_if_allowed(mmc->vqmmc_supply, true); if (ret) return ret; } #endif return 0; } static int aic_sdmc_wait_dat0(struct udevice *dev, int state, int timeout_us) { struct mmc *mmc = mmc_get_mmc_dev(dev); struct aic_sdmc *host = mmc->priv; bool wait_state = !!state; bool dat0_state; u32 val; timeout_us = DIV_ROUND_UP(timeout_us, 10); /* check every 10 us. */ while (timeout_us--) { val = sdmc_readl(host, SDMC_CTRST); /* Value in register is inverted of dat0 */ dat0_state = !((val & DAT0_STATUS_MSK) >> DAT0_STATUS_OFS); if (dat0_state == wait_state) return 0; udelay(10); } return -ETIMEDOUT; } #ifdef CONFIG_DM_MMC const struct dm_mmc_ops aic_dmmmc_ops = { .send_cmd = aic_sdmc_send_cmd, .set_ios = aic_sdmc_set_ios, .wait_dat0 = aic_sdmc_wait_dat0, }; #else static const struct mmc_ops aic_mmc_ops = { .send_cmd = aic_sdmc_send_cmd, .set_ios = aic_sdmc_set_ios, .wait_dat0 = aic_sdmc_wait_dat0, .init = aic_sdmc_init, }; #endif static uint aic_sdmc_get_mmc_clk(struct aic_sdmc *host, uint freq) { #ifdef CONFIG_FPGA_BOARD_ARTINCHIP freq = 48000000; #else struct udevice *dev = host->priv; struct aic_sdmc_priv *priv = dev_get_priv(dev); freq = clk_get_rate(&priv->clk); #endif freq = freq / 2; return freq; } static int aic_sdmc_ofdata_to_platdata(struct udevice *dev) { #if !CONFIG_IS_ENABLED(OF_PLATDATA) struct aic_sdmc_priv *priv = dev_get_priv(dev); struct aic_sdmc_plat *plat = dev_get_plat(dev); struct aic_sdmc *host = &priv->host; int ret; host->name = dev->name; host->ioaddr = dev_read_addr_ptr(dev); ret = mmc_of_parse(dev, &plat->cfg); if (ret) return ret; host->buswidth = dev_read_u32_default(dev, "bus-width", 4); host->get_mmc_clk = aic_sdmc_get_mmc_clk; host->priv = dev; host->dev_index = dev->seq_; priv->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0); if (priv->fifo_depth < 0) return -EINVAL; priv->fifo_mode = dev_read_bool(dev, "fifo-mode"); priv->f_max = plat->cfg.f_max; priv->f_min = CLOCK_MIN; if (!ret && priv->f_max < CLOCK_MIN) return -EINVAL; host->sample_phase = dev_read_u32_default(dev, "aic,sample-phase", 0); host->sample_delay = dev_read_u32_default(dev, "aic,sample-delay", 0); host->driver_phase = dev_read_u32_default(dev, "aic,driver-phase", 0); host->driver_delay = dev_read_u32_default(dev, "aic,driver-delay", 0); #endif return 0; } static void aic_sdmc_get_platdata(struct udevice *dev) { #if CONFIG_IS_ENABLED(OF_PLATDATA) struct aic_sdmc_plat *plat = dev_get_platdata(dev); struct dtd_artinchip_aic_sdmc *dtplat = &plat->dtplat; struct aic_sdmc_priv *priv = dev_get_priv(dev); struct aic_sdmc *host = &priv->host; host->name = dev->name; host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); host->buswidth = dtplat->bus_width; host->get_mmc_clk = aic_sdmc_get_mmc_clk; host->priv = dev; host->dev_index = 0; priv->fifo_depth = dtplat->fifo_depth; priv->fifo_mode = false; if (priv->fifo_depth < 0) priv->fifo_depth = 8; priv->f_max = dtplat->max_frequency; priv->f_min = CLOCK_MIN; #endif } static int aic_sdmc_clk_gating(struct udevice *dev) { int ret = 0; /* U-Boot always need to gating clock; * SPL should to check the CONFIG_SPL_CLK_ARTINCHIP */ #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_CLK_ARTINCHIP) struct aic_sdmc_priv *priv = dev_get_priv(dev); ret = clk_get_by_index(dev, 0, &priv->clk); if (ret < 0) return ret; clk_disable(&priv->clk); ret = reset_get_by_index(dev, 0, &priv->reset); if (ret && ret != -ENOENT) { dev_err(dev, "failed to get reset\n"); return ret; } reset_assert(&priv->reset); ret = clk_enable(&priv->clk); if (ret) { dev_err(dev, "failed to enable clock (ret=%d)\n", ret); return ret; } ret = reset_deassert(&priv->reset); if (ret) { dev_err(dev, "failed to deassert reset\n"); return ret; } clk_set_rate(&priv->clk, priv->f_max); #endif return ret; } void aic_sdmc_setup_cfg(struct mmc_config *cfg, struct aic_sdmc *host, u32 max_clk, u32 min_clk) { cfg->name = host->name; #ifndef CONFIG_DM_MMC cfg->ops = &aic_mmc_ops; #endif cfg->f_min = min_clk; cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; } static int aic_sdmc_probe(struct udevice *dev) { struct aic_sdmc_plat *plat = dev_get_plat(dev); struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct aic_sdmc_priv *priv = dev_get_priv(dev); struct aic_sdmc *host = &priv->host; int ret = 0; aic_sdmc_get_platdata(dev); ret = aic_sdmc_clk_gating(dev); if (ret) return ret; host->fifoth_val = MSIZE(2) | RX_WMARK(7) | TX_WMARK(8); host->fifo_mode = priv->fifo_mode; aic_sdmc_setup_cfg(&plat->cfg, host, priv->f_max, priv->f_min); host->mmc = &plat->mmc; host->mmc->priv = &priv->host; host->mmc->dev = dev; upriv->mmc = host->mmc; ret = aic_sdmc_init(mmc_get_mmc_dev(dev)); sdmc_writel(host, SDMC_THRCTL, (512 << SDMC_CARDTHR_SHIFT) | SDMC_CARDRDTHR_EN); return ret; } static int aic_sdmc_bind(struct udevice *dev) { struct aic_sdmc_plat *plat = dev_get_plat(dev); return mmc_bind(dev, &plat->mmc, &plat->cfg); } static const struct udevice_id aic_sdmc_ids[] = { { .compatible = "artinchip,aic-sdmc" }, { } }; U_BOOT_DRIVER(aic_sdmc_drv) = { .name = "aic_sdmc", .id = UCLASS_MMC, .of_match = aic_sdmc_ids, .of_to_plat = aic_sdmc_ofdata_to_platdata, .ops = &aic_dmmmc_ops, .bind = aic_sdmc_bind, .probe = aic_sdmc_probe, .priv_auto = sizeof(struct aic_sdmc_priv), .plat_auto = sizeof(struct aic_sdmc_plat), };