static int dwmci_prepare_data_pio(struct dwmci_host *host, struct mci_data *data) { unsigned long ctrl; dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_TXDR | DWMCI_INTMSK_RXDR); ctrl = dwmci_readl(host, DWMCI_INTMASK); ctrl |= DWMCI_INTMSK_TXDR | DWMCI_INTMSK_RXDR; dwmci_writel(host, DWMCI_INTMASK, ctrl); ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl &= ~(DWMCI_IDMAC_EN | DWMCI_DMA_EN); dwmci_writel(host, DWMCI_CTRL, ctrl); dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF); dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks); return 0; }
static int dwmci_read_data_pio(struct dwmci_host *host, struct mci_data *data) { u32 *pdata = (u32 *)data->dest; u32 val, status, timeout; u32 rcnt, rlen = 0; for (rcnt = (data->blocksize * data->blocks)>>2; rcnt; rcnt--) { timeout = 20000; status = dwmci_readl(host, DWMCI_STATUS); while (--timeout && (status & DWMCI_STATUS_FIFO_EMPTY)) { udelay(200); status = dwmci_readl(host, DWMCI_STATUS); } if (!timeout) { dev_err(host->dev, "%s: FIFO underflow timeout\n", __func__); break; } val = dwmci_readl(host, DWMCI_DATA); *pdata++ = val; rlen += 4; } dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_RXDR); return rlen; }
static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) { u32 div, status; int timeout = 10000; unsigned long sclk; if (freq == host->clock) return 0; /* * If host->mmc_clk didn't define, * then assume that host->bus_hz is source clock value. * host->bus_hz should be set from user. */ if (host->mmc_clk) sclk = host->mmc_clk(host->dev_index); else if (host->bus_hz) sclk = host->bus_hz; else { printf("Didn't get source clock value..\n"); return -EINVAL; } div = DIV_ROUND_UP(sclk, 2 * freq); dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0); dwmci_writel(host, DWMCI_CLKDIV, div); dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); do { status = dwmci_readl(host, DWMCI_CMD); if (timeout-- < 0) { printf("TIMEOUT error!!\n"); return -ETIMEDOUT; } } while (status & DWMCI_CMD_START); dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE | DWMCI_CLKEN_LOW_PWR); dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); timeout = 10000; do { status = dwmci_readl(host, DWMCI_CMD); if (timeout-- < 0) { printf("TIMEOUT error!!\n"); return -ETIMEDOUT; } } while (status & DWMCI_CMD_START); host->clock = freq; return 0; }
static void dwmci_prepare_data(struct dwmci_host *host, struct mmc_data *data) { unsigned long ctrl; unsigned int i = 0, flags, cnt, blk_cnt; ulong data_start, data_end, start_addr; ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, data->blocks); blk_cnt = data->blocks; dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); data_start = (ulong)cur_idmac; dwmci_writel(host, DWMCI_DBADDR, (unsigned int)cur_idmac); if (data->flags == MMC_DATA_READ) start_addr = (unsigned int)data->dest; else start_addr = (unsigned int)data->src; do { flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH ; flags |= (i == 0) ? DWMCI_IDMAC_FS : 0; if (blk_cnt <= 8) { flags |= DWMCI_IDMAC_LD; cnt = data->blocksize * blk_cnt; } else cnt = data->blocksize * 8; dwmci_set_idma_desc(cur_idmac, flags, cnt, start_addr + (i * PAGE_SIZE)); if(blk_cnt < 8) break; blk_cnt -= 8; cur_idmac++; i++; } while(1); data_end = (ulong)cur_idmac; flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; dwmci_writel(host, DWMCI_CTRL, ctrl); ctrl = dwmci_readl(host, DWMCI_BMOD); ctrl |= DWMCI_BMOD_IDMAC_FB | DWMCI_BMOD_IDMAC_EN; dwmci_writel(host, DWMCI_BMOD, ctrl); dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks); }
static int dwmci_init(struct mmc *mmc) { struct dwmci_host *host = (struct dwmci_host *)mmc->priv; u32 fifo_size, fifoth_val; dwmci_writel(host, DWMCI_PWREN, 1); if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) { debug("%s[%d] Fail-reset!!\n",__func__,__LINE__); return -1; } dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); dwmci_writel(host, DWMCI_INTMASK, 0); dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF); dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1); fifo_size = dwmci_readl(host, DWMCI_FIFOTH); if (host->fifoth_val) fifoth_val = host->fifoth_val; else fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size/2 -1) | TX_WMARK(fifo_size/2); dwmci_writel(host, DWMCI_FIFOTH, fifoth_val); dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0); return 0; }
static int dwmci_write_data_pio(struct dwmci_host *host, struct mci_data *data) { u32 *pdata = (u32 *)data->src; u32 status, timeout; u32 wcnt, wlen = 0; for (wcnt = (data->blocksize * data->blocks)>>2; wcnt; wcnt--) { timeout = 20000; status = dwmci_readl(host, DWMCI_STATUS); while (--timeout && (status & DWMCI_STATUS_FIFO_FULL)) { udelay(200); status = dwmci_readl(host, DWMCI_STATUS); } if (!timeout) { dev_err(host->dev, "%s: FIFO overflow timeout\n", __func__); break; } dwmci_writel(host, DWMCI_DATA, *pdata++); wlen += 4; } dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_TXDR); /* Wait for FIFO is flushed for slow-speed cards */ timeout = 20000; status = dwmci_readl(host, DWMCI_STATUS); while (--timeout && !(status & DWMCI_STATUS_FIFO_EMPTY)) { udelay(10); status = dwmci_readl(host, DWMCI_STATUS); } if (!timeout) { dev_err(host->dev, "%s: FIFO flush timeout\n", __func__); return -EIO; } return wlen; }
static int dwmci_init(struct mci_host *mci, struct device_d *dev) { struct dwmci_host *host = to_dwmci_host(mci); uint32_t fifo_size; dwmci_writel(host, DWMCI_PWREN, host->pwren_value); if (dwmci_wait_reset(host, DWMCI_RESET_ALL)) { dev_err(host->dev, "reset failed\n"); return -EIO; } dwmci_writel(host, DWMCI_RINTSTS, 0xffffffff); dwmci_writel(host, DWMCI_INTMASK, 0); dwmci_writel(host, DWMCI_TMOUT, 0xffffffff); dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1); fifo_size = dwmci_readl(host, DWMCI_FIFOTH); /* * Use reset default of the rx_wmark field to determine the * fifo depth. */ fifo_size = DWMCI_FIFOTH_FIFO_DEPTH(fifo_size); host->fifo_size_bytes = fifo_size * 4; /* * If fifo-depth property is set, use this value */ if (!of_property_read_u32(host->dev->device_node, "fifo-depth", &fifo_size)) { host->fifo_size_bytes = fifo_size; dev_dbg(host->dev, "Using fifo-depth=%u\n", host->fifo_size_bytes); } host->fifoth_val = DWMCI_FIFOTH_MSIZE(0x2) | DWMCI_FIFOTH_RX_WMARK(fifo_size / 2 - 1) | DWMCI_FIFOTH_TX_WMARK(fifo_size / 2); dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0); return 0; }
static int dwmci_wait_reset(struct dwmci_host *host, u32 value) { unsigned long timeout = 1000; u32 ctrl; dwmci_writel(host, DWMCI_CTRL, value); while (timeout--) { ctrl = dwmci_readl(host, DWMCI_CTRL); if (!(ctrl & DWMCI_RESET_ALL)) return 1; } return 0; }
static int dwmci_wait_reset(struct dwmci_host *host, uint32_t value) { uint64_t start; uint32_t ctrl; start = get_time_ns(); dwmci_writel(host, DWMCI_CTRL, value); while (!is_timeout(start, SECOND)) { ctrl = dwmci_readl(host, DWMCI_CTRL); if (!(ctrl & DWMCI_RESET_ALL)) return 0; } return -EIO; }
static int dwmci_send_cmd(struct dwmci_host *host, u32 cmd, u32 arg) { uint64_t start = get_time_ns(); uint32_t status; dwmci_writel(host, DWMCI_CMDARG, arg); dwmci_writel(host, DWMCI_CMD, cmd | DWMCI_CMD_START); while (1) { status = dwmci_readl(host, DWMCI_CMD); if (!(status & DWMCI_CMD_START)) return 0; if (is_timeout(start, 100 * MSECOND)) { dev_err(host->dev, "TIMEOUT error!!\n"); return -ETIMEDOUT; } } }
static int dwmci_init(struct mci_host *mci, struct device_d *dev) { struct dwmci_host *host = to_dwmci_host(mci); uint32_t fifo_size, fifoth_val; dwmci_writel(host, DWMCI_PWREN, 1); if (dwmci_wait_reset(host, DWMCI_RESET_ALL)) { dev_err(host->dev, "reset failed\n"); return -EIO; } dwmci_writel(host, DWMCI_RINTSTS, 0xffffffff); dwmci_writel(host, DWMCI_INTMASK, 0); dwmci_writel(host, DWMCI_TMOUT, 0xffffffff); dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1); fifo_size = dwmci_readl(host, DWMCI_FIFOTH); /* * Use reset default of the rx_wmark field to determine the * fifo depth. */ fifo_size = DWMCI_FIFOTH_FIFO_DEPTH(fifo_size); host->fifo_size_bytes = fifo_size * 4; fifoth_val = DWMCI_FIFOTH_MSIZE(0x2) | DWMCI_FIFOTH_RX_WMARK(fifo_size / 2 - 1) | DWMCI_FIFOTH_TX_WMARK(fifo_size / 2); dwmci_writel(host, DWMCI_FIFOTH, fifoth_val); dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0); return 0; }
static int dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) { struct dwmci_host *host = to_dwmci_host(mci); int flags = 0; uint32_t mask; uint32_t ctrl; uint64_t start; int ret; unsigned int num_bytes = 0; start = get_time_ns(); while (1) { if (!(dwmci_readl(host, DWMCI_STATUS) & DWMCI_STATUS_BUSY)) break; if (is_timeout(start, 100 * MSECOND)) { dev_dbg(host->dev, "Timeout on data busy\n"); return -ETIMEDOUT; } } dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); if (data) { num_bytes = data->blocks * data->blocksize; if (data->flags & MMC_DATA_WRITE) dma_sync_single_for_device((unsigned long)data->src, num_bytes, DMA_TO_DEVICE); else dma_sync_single_for_device((unsigned long)data->dest, num_bytes, DMA_FROM_DEVICE); ret = dwmci_prepare_data(host, data); if (ret) return ret; } dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); if (data) flags = dwmci_set_transfer_mode(host, data); if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) return -EINVAL; if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) flags |= DWMCI_CMD_ABORT_STOP; else flags |= DWMCI_CMD_PRV_DAT_WAIT; if (cmd->resp_type & MMC_RSP_PRESENT) { flags |= DWMCI_CMD_RESP_EXP; if (cmd->resp_type & MMC_RSP_136) flags |= DWMCI_CMD_RESP_LENGTH; } if (cmd->resp_type & MMC_RSP_CRC) flags |= DWMCI_CMD_CHECK_CRC; flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); dev_dbg(host->dev, "Sending CMD%d\n", cmd->cmdidx); dwmci_writel(host, DWMCI_CMD, flags); start = get_time_ns(); while (1) { mask = dwmci_readl(host, DWMCI_RINTSTS); if (mask & DWMCI_INTMSK_CDONE) { if (!data) dwmci_writel(host, DWMCI_RINTSTS, mask); break; } if (is_timeout(start, 100 * MSECOND)) { dev_dbg(host->dev, "Send command timeout..\n"); return -ETIMEDOUT; } } if (mask & DWMCI_INTMSK_RTO) { dev_dbg(host->dev, "Response Timeout..\n"); return -ETIMEDOUT; } else if (mask & DWMCI_INTMSK_RE) { dev_dbg(host->dev, "Response Error..\n"); return -EIO; } if (cmd->resp_type & MMC_RSP_PRESENT) { if (cmd->resp_type & MMC_RSP_136) { cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); } else { cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); } } if (data) { start = get_time_ns(); do { mask = dwmci_readl(host, DWMCI_RINTSTS); if (mask & (DWMCI_DATA_ERR)) { dev_dbg(host->dev, "DATA ERROR!\n"); return -EIO; } if (!dwmci_use_pio(host) && (mask & DWMCI_DATA_TOUT)) { dev_dbg(host->dev, "DATA TIMEOUT!\n"); return -EIO; } if (is_timeout(start, SECOND * 10)) { dev_dbg(host->dev, "Data timeout\n"); return -ETIMEDOUT; } if (dwmci_use_pio(host) && (mask & DWMCI_INTMSK_RXDR)) { dwmci_read_data_pio(host, data); mask = dwmci_readl(host, DWMCI_RINTSTS); } if (dwmci_use_pio(host) && (mask & DWMCI_INTMSK_TXDR)) { dwmci_write_data_pio(host, data); mask = dwmci_readl(host, DWMCI_RINTSTS); } } while (!(mask & DWMCI_INTMSK_DTO)); dwmci_writel(host, DWMCI_RINTSTS, mask); if (!dwmci_use_pio(host)) { ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl &= ~(DWMCI_DMA_EN); dwmci_writel(host, DWMCI_CTRL, ctrl); if (data->flags & MMC_DATA_WRITE) dma_sync_single_for_cpu((unsigned long)data->src, num_bytes, DMA_TO_DEVICE); else dma_sync_single_for_cpu((unsigned long)data->dest, num_bytes, DMA_FROM_DEVICE); } } udelay(100); return 0; }
static int dwmci_prepare_data_dma(struct dwmci_host *host, struct mci_data *data) { unsigned long ctrl; unsigned int i = 0, flags, cnt, blk_cnt; unsigned long data_start, start_addr; struct dwmci_idmac *desc = host->idmac; blk_cnt = data->blocks; if (blk_cnt > DW_MMC_NUM_IDMACS) return -EINVAL; dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); data_start = (uint32_t)desc; dwmci_writel(host, DWMCI_DBADDR, (uint32_t)desc); if (data->flags & MMC_DATA_READ) start_addr = (uint32_t)data->dest; else start_addr = (uint32_t)data->src; do { flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH; flags |= (i == 0) ? DWMCI_IDMAC_FS : 0; if (blk_cnt <= 8) { flags |= DWMCI_IDMAC_LD; cnt = data->blocksize * blk_cnt; } else { cnt = data->blocksize * 8; } desc->flags = flags; desc->cnt = cnt; desc->addr = start_addr + (i * PAGE_SIZE); desc->next_addr = (uint32_t)(desc + 1); dev_dbg(host->dev, "desc@ 0x%p 0x%08x 0x%08x 0x%08x 0x%08x\n", desc, flags, cnt, desc->addr, desc->next_addr); if (blk_cnt < 8) break; blk_cnt -= 8; desc++; i++; } while (1); ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; dwmci_writel(host, DWMCI_CTRL, ctrl); ctrl = dwmci_readl(host, DWMCI_BMOD); ctrl |= DWMCI_BMOD_IDMAC_FB | DWMCI_BMOD_IDMAC_EN; dwmci_writel(host, DWMCI_BMOD, ctrl); dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks); return 0; }
static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct dwmci_host *host = (struct dwmci_host *)mmc->priv; int flags = 0, i; unsigned int timeout = 100000; u32 retry = 10000; u32 mask, ctrl; while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { if (timeout == 0) { printf("Timeout on data busy\n"); return TIMEOUT; } timeout--; } dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); if (data) dwmci_prepare_data(host, data); dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); if (data) flags = dwmci_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_STOP_TRANSMISSION) flags |= DWMCI_CMD_ABORT_STOP; else flags |= DWMCI_CMD_PRV_DAT_WAIT; if (cmd->resp_type & MMC_RSP_PRESENT) { flags |= DWMCI_CMD_RESP_EXP; if (cmd->resp_type & MMC_RSP_136) flags |= DWMCI_CMD_RESP_LENGTH; } if (cmd->resp_type & MMC_RSP_CRC) flags |= DWMCI_CMD_CHECK_CRC; flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); debug("Sending CMD%d\n",cmd->cmdidx); dwmci_writel(host, DWMCI_CMD, flags); for (i = 0; i < retry; i++) { mask = dwmci_readl(host, DWMCI_RINTSTS); if (mask & DWMCI_INTMSK_CDONE) { if (!data) dwmci_writel(host, DWMCI_RINTSTS, mask); break; } } if (i == retry) return TIMEOUT; if (mask & DWMCI_INTMSK_RTO) { debug("Response Timeout..\n"); return TIMEOUT; } else if (mask & DWMCI_INTMSK_RE) { debug("Response Error..\n"); return -1; } if (cmd->resp_type & MMC_RSP_PRESENT) { if (cmd->resp_type & MMC_RSP_136) { cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); } else { cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); } } if (data) { do { mask = dwmci_readl(host, DWMCI_RINTSTS); if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { debug("DATA ERROR!\n"); return -1; } } while (!(mask & DWMCI_INTMSK_DTO)); dwmci_writel(host, DWMCI_RINTSTS, mask); ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl &= ~(DWMCI_DMA_EN); dwmci_writel(host, DWMCI_CTRL, ctrl); } udelay(100); return 0; }
int board_mmc_getcd(struct mmc *mmc) { struct dwmci_host *host = mmc->priv; return !(dwmci_readl(host, DWMCI_CDETECT) & 1); }