static int uniphier_emmc_load_image(uintptr_t host_base, uint32_t dev_addr, unsigned long load_addr, uint32_t block_cnt) { struct uniphier_mmc_cmd cmd = {0}; uint8_t tmp; assert((load_addr >> 32) == 0); mmio_write_32(host_base + SDHCI_DMA_ADDRESS, load_addr); mmio_write_16(host_base + SDHCI_BLOCK_SIZE, SDHCI_MAKE_BLKSZ(7, 512)); mmio_write_16(host_base + SDHCI_BLOCK_COUNT, block_cnt); tmp = mmio_read_8(host_base + SDHCI_HOST_CONTROL); tmp &= ~SDHCI_CTRL_DMA_MASK; tmp |= SDHCI_CTRL_SDMA; mmio_write_8(host_base + SDHCI_HOST_CONTROL, tmp); tmp = mmio_read_8(host_base + SDHCI_BLOCK_GAP_CONTROL); tmp &= ~1; /* clear Stop At Block Gap Request */ mmio_write_8(host_base + SDHCI_BLOCK_GAP_CONTROL, tmp); cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = dev_addr; cmd.is_data = 1; return uniphier_emmc_send_cmd(host_base, &cmd); }
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) { u8 ctrl; writeb(0xe, host->ioaddr + SDHCI_TIMEOUT_CONTROL); /* * Always uses SDMA */ dbg("data->dest: %08x\n", data->dest); writel(virt_to_phys((u32)data->dest), host->ioaddr + SDHCI_DMA_ADDRESS); ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); /* We do not handle DMA boundaries, so set it to max (512 KiB) */ writew(SDHCI_MAKE_BLKSZ(7, data->blocksize), host->ioaddr + SDHCI_BLOCK_SIZE); writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT); }
static int arasan_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) { struct mmc_cmd cmd; struct mmc_data data; u32 ctrl; struct sdhci_host *host; struct arasan_sdhci_priv *priv = dev_get_priv(mmc->dev); u8 tuning_loop_counter = 40; u8 deviceid; debug("%s\n", __func__); host = priv->host; deviceid = priv->deviceid; ctrl = sdhci_readw(host, SDHCI_HOST_CTRL2); ctrl |= SDHCI_CTRL_EXEC_TUNING; sdhci_writew(host, ctrl, SDHCI_HOST_CTRL2); mdelay(1); arasan_zynqmp_dll_reset(host, deviceid); sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); do { cmd.cmdidx = opcode; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; data.blocksize = 64; data.blocks = 1; data.flags = MMC_DATA_READ; if (tuning_loop_counter-- == 0) break; if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200 && mmc->bus_width == 8) data.blocksize = 128; sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data.blocksize), SDHCI_BLOCK_SIZE); sdhci_writew(host, data.blocks, SDHCI_BLOCK_COUNT); sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); mmc_send_cmd(mmc, &cmd, NULL); ctrl = sdhci_readw(host, SDHCI_HOST_CTRL2); if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK) udelay(1); } while (ctrl & SDHCI_CTRL_EXEC_TUNING); if (tuning_loop_counter < 0) { ctrl &= ~SDHCI_CTRL_TUNED_CLK; sdhci_writel(host, ctrl, SDHCI_HOST_CTRL2); } if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { debug("%s:Tuning failed\n", __func__); return -1; } else { udelay(1); arasan_zynqmp_dll_reset(host, deviceid); } /* Enable only interrupts served by the SD controller */ sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, SDHCI_INT_ENABLE); /* Mask all sdhci interrupt sources */ sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); return 0; }
int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct sdhci_host *host = (struct sdhci_host *)mmc->priv; unsigned int stat = 0; int ret = 0; int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; unsigned int timeout, start_addr = 0; unsigned int retry = 10000; /* Wait max 10 ms */ timeout = 10; sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) mask &= ~SDHCI_DATA_INHIBIT; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { if (timeout == 0) { printf("Controller never released inhibit bit(s).\n"); return COMM_ERR; } timeout--; udelay(1000); } mask = SDHCI_INT_RESPONSE; if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; else if (cmd->resp_type & MMC_RSP_136) flags = SDHCI_CMD_RESP_LONG; else if (cmd->resp_type & MMC_RSP_BUSY) { flags = SDHCI_CMD_RESP_SHORT_BUSY; mask |= SDHCI_INT_DATA_END; } else flags = SDHCI_CMD_RESP_SHORT; if (cmd->resp_type & MMC_RSP_CRC) flags |= SDHCI_CMD_CRC; if (cmd->resp_type & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; if (data) flags |= SDHCI_CMD_DATA; /*Set Transfer mode regarding to data flag*/ if (data != 0) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); mode = SDHCI_TRNS_BLK_CNT_EN; trans_bytes = data->blocks * data->blocksize; if (data->blocks > 1) mode |= SDHCI_TRNS_MULTI; if (data->flags == MMC_DATA_READ) mode |= SDHCI_TRNS_READ; #ifdef CONFIG_MMC_SDMA if (data->flags == MMC_DATA_READ) start_addr = (unsigned int)data->dest; else start_addr = (unsigned int)data->src; if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && (start_addr & 0x7) != 0x0) { is_aligned = 0; start_addr = (unsigned int)aligned_buffer; if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); } sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); mode |= SDHCI_TRNS_DMA; #endif sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); #ifdef CONFIG_MMC_SDMA flush_cache(start_addr, trans_bytes); #endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); do { stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) break; if (--retry == 0) break; } while ((stat & mask) != mask); if (retry == 0) { if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) return 0; else { printf("Timeout for status update!\n"); return TIMEOUT; } } if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); } else ret = -1; if (!ret && data) ret = sdhci_transfer_data(host, data, start_addr); if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) udelay(1000); stat = sdhci_readl(host, SDHCI_INT_STATUS); sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); if (!ret) { if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !is_aligned && (data->flags == MMC_DATA_READ)) memcpy(data->dest, aligned_buffer, trans_bytes); return 0; } sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); if (stat & SDHCI_INT_TIMEOUT) return TIMEOUT; else return COMM_ERR; }
static int sdhci_send_command(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct mmc *mmc = mmc_get_mmc_dev(dev); #else static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { #endif struct sdhci_host *host = mmc->priv; unsigned int stat = 0; int ret = 0; int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; unsigned int time = 0, start_addr = 0; int mmc_dev = mmc_get_blk_desc(mmc)->devnum; ulong start = get_timer(0); /* Timeout unit - ms */ static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION || ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) && !data)) mask &= ~SDHCI_DATA_INHIBIT; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { if (time >= cmd_timeout) { printf("%s: MMC: %d busy ", __func__, mmc_dev); if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) { cmd_timeout += cmd_timeout; printf("timeout increasing to: %u ms.\n", cmd_timeout); } else { puts("timeout.\n"); return -ECOMM; } } time++; udelay(1000); } sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_INT_RESPONSE; if ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) && !data) mask = SDHCI_INT_DATA_AVAIL; if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; else if (cmd->resp_type & MMC_RSP_136) flags = SDHCI_CMD_RESP_LONG; else if (cmd->resp_type & MMC_RSP_BUSY) { flags = SDHCI_CMD_RESP_SHORT_BUSY; if (data) mask |= SDHCI_INT_DATA_END; } else flags = SDHCI_CMD_RESP_SHORT; if (cmd->resp_type & MMC_RSP_CRC) flags |= SDHCI_CMD_CRC; if (cmd->resp_type & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; if (data || cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) flags |= SDHCI_CMD_DATA; /* Set Transfer mode regarding to data flag */ if (data) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); mode = SDHCI_TRNS_BLK_CNT_EN; trans_bytes = data->blocks * data->blocksize; if (data->blocks > 1) mode |= SDHCI_TRNS_MULTI; if (data->flags == MMC_DATA_READ) mode |= SDHCI_TRNS_READ; #ifdef CONFIG_MMC_SDHCI_SDMA if (data->flags == MMC_DATA_READ) start_addr = (unsigned long)data->dest; else start_addr = (unsigned long)data->src; if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && (start_addr & 0x7) != 0x0) { is_aligned = 0; start_addr = (unsigned long)aligned_buffer; if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); } #if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) /* * Always use this bounce-buffer when * CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined */ is_aligned = 0; start_addr = (unsigned long)aligned_buffer; if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); #endif sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); mode |= SDHCI_TRNS_DMA; #endif sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } else if (cmd->resp_type & MMC_RSP_BUSY) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); } sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); #ifdef CONFIG_MMC_SDHCI_SDMA if (data) { trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE); flush_cache(start_addr, trans_bytes); } #endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); start = get_timer(0); do { stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) break; if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) { if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) { return 0; } else { printf("%s: Timeout for status update!\n", __func__); return -ETIMEDOUT; } } } while ((stat & mask) != mask); if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); } else ret = -1; if (!ret && data) ret = sdhci_transfer_data(host, data, start_addr); if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) udelay(1000); stat = sdhci_readl(host, SDHCI_INT_STATUS); sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); if (!ret) { if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !is_aligned && (data->flags == MMC_DATA_READ)) memcpy(data->dest, aligned_buffer, trans_bytes); return 0; } sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); if (stat & SDHCI_INT_TIMEOUT) return -ETIMEDOUT; else return -ECOMM; } #if defined(CONFIG_DM_MMC) && defined(MMC_SUPPORTS_TUNING) static int sdhci_execute_tuning(struct udevice *dev, uint opcode) { int err; struct mmc *mmc = mmc_get_mmc_dev(dev); struct sdhci_host *host = mmc->priv; debug("%s\n", __func__); if (host->ops && host->ops->platform_execute_tuning) { err = host->ops->platform_execute_tuning(mmc, opcode); if (err) return err; return 0; } return 0; }
static int sdhci_send_command(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct mmc *mmc = mmc_get_mmc_dev(dev); #else static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { #endif struct sdhci_host *host = mmc->priv; unsigned int stat = 0; int ret = 0; int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; unsigned int time = 0, start_addr = 0; int mmc_dev = mmc_get_blk_desc(mmc)->devnum; unsigned start = get_timer(0); /* Timeout unit - ms */ static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) mask &= ~SDHCI_DATA_INHIBIT; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { if (time >= cmd_timeout) { printf("%s: MMC: %d busy ", __func__, mmc_dev); if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) { cmd_timeout += cmd_timeout; printf("timeout increasing to: %u ms.\n", cmd_timeout); } else { puts("timeout.\n"); return -ECOMM; } } time++; udelay(1000); } mask = SDHCI_INT_RESPONSE; if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; else if (cmd->resp_type & MMC_RSP_136) flags = SDHCI_CMD_RESP_LONG; else if (cmd->resp_type & MMC_RSP_BUSY) { flags = SDHCI_CMD_RESP_SHORT_BUSY; if (data) mask |= SDHCI_INT_DATA_END; } else flags = SDHCI_CMD_RESP_SHORT; if (cmd->resp_type & MMC_RSP_CRC) flags |= SDHCI_CMD_CRC; if (cmd->resp_type & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; if (data) flags |= SDHCI_CMD_DATA; /* Set Transfer mode regarding to data flag */ if (data != 0) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); mode = SDHCI_TRNS_BLK_CNT_EN; trans_bytes = data->blocks * data->blocksize; if (data->blocks > 1) mode |= SDHCI_TRNS_MULTI; if (data->flags == MMC_DATA_READ) mode |= SDHCI_TRNS_READ; #ifdef CONFIG_MMC_SDHCI_SDMA if (data->flags == MMC_DATA_READ) start_addr = (unsigned long)data->dest; else start_addr = (unsigned long)data->src; if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && (start_addr & 0x7) != 0x0) { is_aligned = 0; start_addr = (unsigned long)aligned_buffer; if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); } #if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) /* * Always use this bounce-buffer when * CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined */ is_aligned = 0; start_addr = (unsigned long)aligned_buffer; if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); #endif sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); mode |= SDHCI_TRNS_DMA; #endif sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } else if (cmd->resp_type & MMC_RSP_BUSY) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); } sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); #ifdef CONFIG_MMC_SDHCI_SDMA trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE); flush_cache(start_addr, trans_bytes); #endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); start = get_timer(0); do { stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) break; if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) { if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) { return 0; } else { printf("%s: Timeout for status update!\n", __func__); return -ETIMEDOUT; } } } while ((stat & mask) != mask); if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); } else ret = -1; if (!ret && data) ret = sdhci_transfer_data(host, data, start_addr); if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) udelay(1000); stat = sdhci_readl(host, SDHCI_INT_STATUS); sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); if (!ret) { if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !is_aligned && (data->flags == MMC_DATA_READ)) memcpy(data->dest, aligned_buffer, trans_bytes); return 0; } sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); if (stat & SDHCI_INT_TIMEOUT) return -ETIMEDOUT; else return -ECOMM; } static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) { struct sdhci_host *host = mmc->priv; unsigned int div, clk = 0, timeout, reg; /* Wait max 20 ms */ timeout = 200; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) { if (timeout == 0) { printf("%s: Timeout to wait cmd & data inhibit\n", __func__); return -EBUSY; } timeout--; udelay(100); } reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN); sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); if (clock == 0) return 0; if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { /* * Check if the Host Controller supports Programmable Clock * Mode. */ if (host->clk_mul) { for (div = 1; div <= 1024; div++) { if ((mmc->cfg->f_max * host->clk_mul / div) <= clock) break; } /* * Set Programmable Clock Mode in the Clock * Control register. */ clk = SDHCI_PROG_CLOCK_MODE; div--; } else { /* Version 3.00 divisors must be a multiple of 2. */ if (mmc->cfg->f_max <= clock) { div = 1; } else { for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { if ((mmc->cfg->f_max / div) <= clock) break; } } div >>= 1; } } else { /* Version 2.00 divisors must be a power of 2. */ for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) { if ((mmc->cfg->f_max / div) <= clock) break; } div >>= 1; } if (host->ops && host->ops->set_clock) host->ops->set_clock(host, div); clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; clk |= SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); /* Wait max 20 ms */ timeout = 20; while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) & SDHCI_CLOCK_INT_STABLE)) { if (timeout == 0) { printf("%s: Internal clock never stabilised.\n", __func__); return -EBUSY; } timeout--; udelay(1000); } clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); return 0; }
int sdhci_send_command(struct mmc_host *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { bool present; u32 mask, flags, mode; int ret = 0, trans_bytes = 0, is_aligned = 1; u32 timeout, retry = 10000, stat = 0, start_addr = 0; struct sdhci_host *host = mmc_priv(mmc); /* If polling, assume that the card is always present. */ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) { present = TRUE; } else { present = sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT; } /* If card not present then return error */ if (!present) { return VMM_EIO; } /* Wait max 10 ms */ timeout = 10; sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) { mask &= ~SDHCI_DATA_INHIBIT; } while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { if (timeout == 0) { vmm_printf("%s: Controller never released " "inhibit bit(s).\n", __func__); return VMM_EIO; } timeout--; vmm_udelay(1000); } mask = SDHCI_INT_RESPONSE; if (!(cmd->resp_type & MMC_RSP_PRESENT)) { flags = SDHCI_CMD_RESP_NONE; } else if (cmd->resp_type & MMC_RSP_136) { flags = SDHCI_CMD_RESP_LONG; } else if (cmd->resp_type & MMC_RSP_BUSY) { flags = SDHCI_CMD_RESP_SHORT_BUSY; mask |= SDHCI_INT_DATA_END; } else { flags = SDHCI_CMD_RESP_SHORT; } if (cmd->resp_type & MMC_RSP_CRC) { flags |= SDHCI_CMD_CRC; } if (cmd->resp_type & MMC_RSP_OPCODE) { flags |= SDHCI_CMD_INDEX; } if (data) { flags |= SDHCI_CMD_DATA; } /* Set Transfer mode regarding to data flag */ if (data != 0) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); mode = SDHCI_TRNS_BLK_CNT_EN; trans_bytes = data->blocks * data->blocksize; if (data->blocks > 1) { mode |= SDHCI_TRNS_MULTI; } if (data->flags == MMC_DATA_READ) { mode |= SDHCI_TRNS_READ; } if (host->sdhci_caps & SDHCI_CAN_DO_SDMA) { if (data->flags == MMC_DATA_READ) { start_addr = (u32)data->dest; } else { start_addr = (u32)data->src; } if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && (start_addr & 0x7) != 0x0) { is_aligned = 0; start_addr = (u32)host->aligned_buffer; if (data->flags != MMC_DATA_READ) { memcpy(host->aligned_buffer, data->src, trans_bytes); } } sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); mode |= SDHCI_TRNS_DMA; vmm_flush_cache_range(start_addr, start_addr + trans_bytes); } sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); do { stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) { break; } if (--retry == 0) { break; } } while ((stat & mask) != mask); if (retry == 0) { if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) return VMM_OK; else { vmm_printf("%s: Status update timeout!\n", __func__); return VMM_ETIMEDOUT; } } if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); } else { ret = VMM_EFAIL; } if (!ret && data) { ret = sdhci_transfer_data(host, data, start_addr); } if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) { vmm_udelay(1000); } stat = sdhci_readl(host, SDHCI_INT_STATUS); sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); if (!ret) { if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !is_aligned && (data->flags == MMC_DATA_READ)) { memcpy(data->dest, host->aligned_buffer, trans_bytes); } return VMM_OK; } sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); if (stat & SDHCI_INT_TIMEOUT) { return VMM_ETIMEDOUT; } else { return VMM_EIO; } }
int sdhci_send_command_backstage(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct sdhci_host *host = (struct sdhci_host *)mmc->priv; unsigned int stat = 0; int ret = 0; int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; unsigned int time = 0, start_addr = 0; unsigned int retry = 8000000; int mmc_dev = mmc->block_dev.dev; /* Timeout unit - ms */ static unsigned int cmd_timeout = CONFIG_SDHCI_CMD_DEFAULT_TIMEOUT; sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) mask &= ~SDHCI_DATA_INHIBIT; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { if (time >= cmd_timeout) { debugf("%s: MMC: %d busy ", __func__, mmc_dev); if (2 * cmd_timeout <= CONFIG_SDHCI_CMD_MAX_TIMEOUT) { cmd_timeout += cmd_timeout; debugf("timeout increasing to: %u ms.\n", cmd_timeout); sdio_dump(host->ioaddr); } else { errorf("timeout.\n"); sdio_dump(host->ioaddr); return COMM_ERR; } } time++; udelay(10000); } mask = SDHCI_INT_RESPONSE; if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; else if (cmd->resp_type & MMC_RSP_136) flags = SDHCI_CMD_RESP_LONG; else if (cmd->resp_type & MMC_RSP_BUSY) { flags = SDHCI_CMD_RESP_SHORT_BUSY; mask |= SDHCI_INT_DATA_END; } else flags = SDHCI_CMD_RESP_SHORT; if (cmd->resp_type & MMC_RSP_CRC) flags |= SDHCI_CMD_CRC; if (cmd->resp_type & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; if (data) flags |= SDHCI_CMD_DATA; /* Set Transfer mode regarding to data flag */ if (data != 0) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); mode = SDHCI_TRNS_BLK_CNT_EN; trans_bytes = data->blocks * data->blocksize; if (data->blocks > 1) mode |= SDHCI_TRNS_MULTI; if (data->flags == MMC_DATA_READ) mode |= SDHCI_TRNS_READ; #ifdef CONFIG_MMC_SDMA if (data->flags == MMC_DATA_READ) start_addr = (unsigned int)data->dest; else start_addr = (unsigned int)data->src; if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && (start_addr & 0x7) != 0x0) { is_aligned = 0; start_addr = (unsigned int)aligned_buffer; if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); } sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); mode |= SDHCI_TRNS_DMA; #endif sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); #ifdef CONFIG_MMC_SDMA flush_cache(start_addr, trans_bytes); #endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); do { stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) break; if (--retry == 0) break; } while ((stat & mask) != mask); if (retry == 0) { if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) return 0; else { errorf("%s: Timeout for status update!\n", __func__); sdio_dump(host->ioaddr); return TIMEOUT; } } if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); /*do not wait for transfer complete*/ return 0; } if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) udelay(800000); stat = sdhci_readl(host, SDHCI_INT_STATUS); sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); if (stat & SDHCI_INT_TIMEOUT){ //sdio_dump(host->ioaddr); sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); return TIMEOUT; } else{ //sdio_dump(host->ioaddr); sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); return COMM_ERR; } }