📄 omap_hsmmc.c
字号:
if (write) { __raw_writesw(host->base + OMAP_HSMMC_DATA, host->buffer, n); } else { __raw_readsw(host->base + OMAP_HSMMC_DATA, host->buffer, n); }}/* * Configure the resptype, cmdtype and send the given command to the card */static voidmmc_omap_start_command13(struct mmc_omap_host *host, struct mmc_command *cmd){ int cmdreg = 0, resptype = 0, cmdtype = 0; unsigned long flags; mmc_clk_enable_aggressive(host); /* Clear status bits and enable interrupts */ OMAP_HSMMC_WRITE(host->base, STAT, OMAP_HSMMC_STAT_CLEAR); resptype = 2; cmdreg = (MMC_SEND_STATUS << 24) | (resptype << 16) | (cmdtype << 22); OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); OMAP_HSMMC_WRITE(host->base, ARG, host->rca); OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); spin_lock_irqsave(&host->dma_lock, flags); host->cmd_13 = 1; spin_unlock_irqrestore(&host->dma_lock, flags);}/* * Notify the core about command completion */static voidmmc_omap_err_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd){ cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10); mmc_clk_disable_aggressive(host); if(host->cmd_13 == 1) { if(cmd->resp[0] != 0x900) dev_dbg(mmc_dev(host->mmc), "%s:CMD response error cmd->resp[0]%x\n", mmc_hostname(host->mmc),cmd->resp[0]); }}static intmmc_omap_err_calc(struct mmc_omap_host *host){ unsigned sg_len_remain; struct mmc_data *data = host->data; unsigned long flags; if(unlikely(host == NULL)) { return -1; } /* Enable DMA */ host->use_dma = 1; spin_lock_irqsave(&host->dma_lock, flags); if(host->extra_chain_reqd == 0) { if(host->data->sg_len <= NO_OF_DMA_CHAINS_USED) host->sg_idx = 0; else host->sg_idx = host->sg_idx-NO_OF_DMA_CHAINS_USED; } else { if(host->sg_idx == host->data->sg_len) host->sg_idx = host->data->sg_len; else host->sg_idx = host->sg_idx-NO_OF_DMA_CHAINS_USED; } if(host->sg_idx == host->data->sg_len) sg_len_remain =1; else sg_len_remain = data->sg_len - host->sg_idx; if(sg_len_remain >= NO_OF_DMA_CHAINS_USED) { host->chains_requested = NO_OF_DMA_CHAINS_USED; } else { host->chains_requested = 1; } if(host->data->sg_len <= NO_OF_DMA_CHAINS_USED) { host->extra_chain_reqd = 0; host->no_of_chain_reqd = 0; host->current_cb_cnt = 0; } else { host->no_of_chain_reqd = sg_len_remain/NO_OF_DMA_CHAINS_USED; host->extra_chain_reqd= sg_len_remain%NO_OF_DMA_CHAINS_USED; host->current_cb_cnt = 1; } spin_unlock_irqrestore(&host->dma_lock, flags); return 0;}/* * Routine to configure block leangth for MMC/SD/SDIO cards * and intiate the transfer. */static intmmc_omap_err_prepare_data(struct mmc_omap_host *host){ int i; unsigned count=0, count1=0; struct mmc_data *data = host->data; if(unlikely(host == NULL)) { return -1; } mmc_clk_enable_aggressive(host); if (host->use_dma && host->chain_id != -1) { omap_stop_dma_chain_transfers(host->chain_id);/* dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, host->dma_dir);*/ omap_free_dma_chain(host->chain_id); host->chain_id = -1; } for(i = 0 ;i < host->sg_idx; i++) { count1 += (sg_dma_len(&data->sg[i])/data->blksz); } for(i = host->sg_idx ;i < (host->chains_requested+host->sg_idx); i++) { count += (sg_dma_len(&data->sg[i])/data->blksz); } OMAP_HSMMC_WRITE(host->base, BLK, (host->data->blksz)); OMAP_HSMMC_WRITE(host->base, BLK, OMAP_HSMMC_READ(host->base, BLK) | (count << 16)); host->mod_addr = host->org_addr; if(host->is_high_capacity ==1) host->mod_addr += count1;//as card is high capacity else host->mod_addr += (count1*512);//as card is not high capacity host->datadir = (host->data->flags & MMC_DATA_WRITE) ? OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ; if (mmc_omap_get_dma_channel(host, data) == 0) { enum dma_data_direction dma_data_dir; if (data->flags & MMC_DATA_WRITE) dma_data_dir = DMA_TO_DEVICE; else dma_data_dir = DMA_FROM_DEVICE; host->total_bytes_left = 0; mmc_chain_dma(host, host->data); host->brs_received = 0; host->dma_done = 0; /* Enable DMA */ host->use_dma = 1; } mmc_clk_disable_aggressive(host); return 0;}/* * Notify the xfer done on MMC/SD cards to the core */static voidmmc_omap_read_err_done(struct mmc_omap_host *host, struct mmc_data *data){ unsigned long flags; spin_lock_irqsave(&host->dma_lock, flags); host->flag_err = 1; host->cmd_12 = 1; spin_unlock_irqrestore(&host->dma_lock, flags); mmc_clk_disable_aggressive(host); mmc_omap_start_command(host, data->stop);}/* * Notify the xfer done on MMC/SD cards to the core */static voidmmc_omap_err_done(struct mmc_omap_host *host, struct mmc_data *data){ host->data = NULL; if (host->use_dma && host->chain_id != -1) { omap_stop_dma_chain_transfers(host->chain_id); dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, host->dma_dir); omap_free_dma_chain(host->chain_id); host->chain_id = -1; } host->datadir = OMAP_MMC_DATADIR_NONE; host->sg_dma_len = 0; mmc_clk_disable_aggressive(host); if (!data->stop) { host->mrq = NULL; mmc_request_done(host->mmc, data->mrq); return; } mmc_omap_start_command(host, data->stop);}/* * The MMC controller IRQ handler */static irqreturn_t mmc_omap_irq(int irq, void *dev_id){ struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; int end_command, end_transfer, status, read_crc =0; unsigned long flags; typeof(jiffies) timeout; if (unlikely(host == NULL)) { return IRQ_HANDLED; } if(unlikely(host->cmd == NULL && host->data == NULL)) { status = OMAP_HSMMC_READ(host->base, STAT); if (status != 0) { OMAP_HSMMC_WRITE(host->base, STAT, status); } mmc_clk_disable_aggressive(host); return IRQ_HANDLED; } end_command = 0; end_transfer = 0; if (host->cmd) { if (host->cmd->opcode == MMC_SELECT_CARD || host->cmd->opcode == MMC_SWITCH) { timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); while (time_before(jiffies, timeout)) { if ((OMAP_HSMMC_READ(host->base, STAT) & CC) == CC) break; } } } status = OMAP_HSMMC_READ(host->base, STAT); dev_dbg(mmc_dev(host->mmc), "Status in IRQ %x\n", status); if(unlikely(host->use_dma == 0)) { if (host->total_bytes_left != 0) { if ((status & OMAP_MMC_STAT_BRR) || (status & TC)) mmc_omap_xfer_data(host, 0); if (status & OMAP_MMC_STAT_BWR) mmc_omap_xfer_data(host, 1); } } if (status & (OMAP_HSMMC_ERR)) { if (status & (OMAP_HSMMC_CMD_TIMEOUT) || status & (OMAP_HSMMC_CMD_CRC)) { if (host->cmd) { if (status & (OMAP_HSMMC_CMD_TIMEOUT)) { /* * Timeouts are normal in case of * MMC_SEND_STATUS */ if (host->cmd->opcode != MMC_ALL_SEND_CID) dev_dbg(mmc_dev(host->mmc), "CMD Timeout CMD%d\n", host->cmd->opcode); host->cmd->error |= MMC_ERR_TIMEOUT; } else { dev_dbg(mmc_dev(host->mmc), "%s: Command CRC error CMD%d\n", mmc_hostname(host->mmc), host->cmd->opcode); host->cmd->error |= MMC_ERR_BADCRC; } end_command = 1; } if (host->data) mmc_dma_cleanup(host); } if (status & (OMAP_HSMMC_DATA_TIMEOUT) || status & (OMAP_HSMMC_DATA_CRC)) { if (host->data) { if (status & (OMAP_HSMMC_DATA_TIMEOUT)) { dev_dbg(mmc_dev(host->mmc), "%s:Data timeout\n", mmc_hostname(host->mmc)); mmc_dma_cleanup(host); } else { if(host->data->flags & MMC_DATA_READ) { read_crc = 1; if(host->flag_err == 1) { dev_dbg(mmc_dev(host->mmc), "%s:Data CRC for the second time\n", mmc_hostname(host->mmc)); spin_lock_irqsave(&host->dma_lock, flags); host->crc_retry++; host->flag_err = 0; spin_unlock_irqrestore(&host->dma_lock, flags); if(host->crc_retry == MAX_CRC_RETRY) { spin_lock_irqsave(&host->dma_lock, flags); host->mod_addr = host->org_addr = 0; host->flag_err = host->cmd_12 = host->cmd_13 = 0; spin_unlock_irqrestore(&host->dma_lock, flags); host->data->error |= MMC_ERR_BADCRC; mmc_omap_err_done(host, host->data); } } } else host->data->error |= MMC_ERR_BADCRC; dev_dbg(mmc_dev(host->mmc), "%s: Data CRC error," " bytes left %d\n", mmc_hostname(host->mmc), host->bytesleft); } end_transfer = 1; } } if (status & OMAP_HSMMC_CARD_ERR) { dev_dbg(mmc_dev(host->mmc), "MMC%d: Card status error (CMD%d)\n", host->id, host->cmd->opcode); if (host->cmd) { host->cmd->error |= MMC_ERR_FAILED; end_command = 1; } if (host->data) { host->data->error |= MMC_ERR_FAILED; end_transfer = 1; } } } OMAP_HSMMC_WRITE(host->base, STAT, status); if(host->flag_err == 1) { if(host->cmd_12 == 1) { if (status & CC) { mmc_omap_err_cmd_done(host, host->cmd); spin_lock_irqsave(&host->dma_lock, flags); host->cmd_12 =0; spin_unlock_irqrestore(&host->dma_lock, flags); mmc_omap_start_command13(host, host->cmd); return IRQ_HANDLED; } } if(host->cmd_13 == 1) { if (status & CC) { mmc_omap_err_cmd_done(host, host->cmd); spin_lock_irqsave(&host->dma_lock, flags); host->cmd_13 =0; spin_unlock_irqrestore(&host->dma_lock, flags); mmc_omap_err_prepare_data(host); omap_start_dma_chain_transfers(host->chain_id); mmc_omap_start_command18(host, host->cmd); return IRQ_HANDLED; } } return IRQ_HANDLED; } if (end_command || (status & CC)) { mmc_omap_cmd_done(host, host->cmd); } if (host->mmc->mode == MMC_MODE_MMC || host->mmc->mode == MMC_MODE_SD){ if (end_transfer){ if(read_crc) { mmc_omap_err_calc(host); mmc_omap_read_err_done(host, host->data); } else mmc_omap_err_done(host, host->data); } else if (status & TC) mmc_omap_end_of_data(host, host->data); } return IRQ_HANDLED;}/* * Turn the socket power ON/OFF */static int mmc_omap_power(struct mmc_omap_host *host, int on){ int ret = 0; if (on){ if (machine_is_omap_2430sdp() || machine_is_omap_3430sdp() || machine_is_omap_3430labrador() || machine_is_omap3evm() || machine_is_omap3_beagle() ) ret = enable_mmc_power(host->id); } else { if (machine_is_omap_2430sdp() || machine_is_omap_3430sdp() || machine_is_omap_3430labrador() || machine_is_omap3evm() || machine_is_omap3_beagle() ) ret = disable_mmc_power(host->id); } return ret;}/* * power switching module for mmc slot 1 * power_mode=0 switches to 1.8V * power_mode=1 switches to 3V * Caller makes sure that it calls on slot 1 with correct cpu revision */static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int power_mode){ int ret = 0; mmc_clk_disable(host); if (cpu_is_omap2430()) clk_disable(host->dbclk); ret = mmc_omap_power(host,0); if (ret != 0) dev_dbg(mmc_dev(host->mmc),"Unable to disable power to MMC1\n"); if (machine_is_omap_2430sdp() || machine_is_omap_3430sdp() || machine_is_omap_3430labrador() || machine_is_omap3evm() || machine_is_omap3_beagle() ) { if (switch_power_mode(power_mode)) dev_dbg(mmc_dev(host->mmc), "Unable to switch operating" "voltage to the card\n"); } mmc_clk_enable(host); if (cpu_is_omap2430()) { if (clk_enable(host->dbclk) != 0) dev_dbg(mmc_dev(host->mmc), "Unable to enable MMC1 debounce clock" "while switching power\n"); } OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); if (power_mode == 0) { OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDVS18); } else { OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDVS30); host->initstream = 0; } OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDBP); return 0;}/* * Work Item to notify the core about card insertion/removal */static void mmc_omap_detect(struct work_struct *work){ struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, mmc_carddetect_work); mmc_clk_enable_aggressive(host); if (cpu_is_omap34xx() || (cpu_is_omap2430() && omap2_cpu_rev() == 2)) { if (host->id == OMAP_MMC1_DEVID) { if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { if (omap_mmc_switch_opcond(host, 1) != 0) dev_dbg(mmc_dev(host->mmc), "mmc_omap_detect:switch" "command operation failed\n"); host->mmc->ios.vdd = fls(host->mmc->ocr_avail) - 1; } } } mmc_detect_change(host->mmc, (HZ * 200) / 1000); mmc_clk_disable_aggressive(host);}/* * Interrupt service routine for handling card insertion and removal */static irqreturn_t mmc_omap_irq_cd(int irq, void *dev_id){ struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; unsigned long flags; if (machine_is_omap_2430sdp() || machine_is_omap_3430sdp() || machine_is_omap_3430labrador() || machine_is_omap3evm() || machine_is_omap3_beagle() ) { spin_lock_irqsave(&host->dma_lock, flags); host->card_detected = 1; host->mmc->mode = MMC_CARD_NONE; host->rca = host->mod_addr = host->org_addr = 0; host->is_high_capacity = 0; host->flag_err = host->cmd_12 = host->cmd_13 = 0; spin_unlock_irqrestore(&host->dma_lock, flags); schedule_work(&host->mmc_carddetect_work); } else dev_dbg(mmc_dev(host->mmc), "Place to implement MMC hotplug" "implementation based on what the other" "board can support\n"); return IRQ_HANDLED;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -