static void hi_mci_sys_ctrl_suspend(struct himci_host *host, resource_size_t host_crg_addr) { unsigned int tmp_reg; if ((SDIO_REG_BASE_CRG + PERI_CRG39) == (unsigned int)host_crg_addr) { /* SDIO soft reset */ tmp_reg = himci_readl(host_crg_addr); tmp_reg |= SDIO0_SRST_REQ; himci_writel(tmp_reg, host_crg_addr); udelay(1000); /* disable SDIO clock */ tmp_reg &= ~(SDIO0_CKEN | SDIO0_BUS_CKEN); himci_writel(tmp_reg, host_crg_addr); return; } if ((SDIO_REG_BASE_CRG + PERI_CRG40) == (unsigned int)host_crg_addr) { /* SDIO soft reset */ tmp_reg |= SDIO1_SRST_REQ; himci_writel(tmp_reg, host_crg_addr); udelay(1000); tmp_reg &= ~(SDIO0_CKEN | SDIO0_BUS_CKEN); himci_writel(tmp_reg, host_crg_addr); return; } }
static void hi_mci_idma_start(struct himci_host *host) { unsigned int tmp; himci_trace(2, "begin"); himci_writel(host->dma_paddr, host->base + MCI_DBADDR); tmp = himci_readl(host->base + MCI_BMOD); tmp |= BMOD_DMA_EN; himci_writel(tmp, host->base + MCI_BMOD); }
static void hi_mci_sys_init(void) { unsigned int reg_value; /* set detect polarity */ reg_value = himci_readl(SYS_PERIPHCTRL4); reg_value &= ~SDIO0_DET_MODE; himci_writel(reg_value, SYS_PERIPHCTRL4); /* set clk polarity, mmc clk */ reg_value = 0; reg_value = himci_readl(PERI_CRG49); reg_value &= ~(SDIO0CLK_PCTRL); reg_value |= SDIO0CLK_SEL_50M; reg_value |= SDIO0_CKEN; himci_writel(reg_value, PERI_CRG49); }
static void hi_mci_ctr_undo_reset(void) { unsigned int reg_value; reg_value = himci_readl(PERI_CRG49); reg_value &= ~(SDIO_RESET); himci_writel(reg_value, PERI_CRG49); }
static void hi_mci_init_card(struct himci_host *host) { unsigned int tmp_reg; unsigned long flags; himci_trace(2, "begin"); himci_assert(host); hi_mci_sys_reset(host); hi_mci_ctrl_power(host, POWER_OFF); udelay(500); /* card power on */ hi_mci_ctrl_power(host, POWER_ON); udelay(200); hi_mci_sys_undo_reset(host); /* clear MMC host intr */ himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); spin_lock_irqsave(&host->lock, flags); host->pending_events = 0; spin_unlock_irqrestore(&host->lock, flags); /* MASK MMC host intr */ tmp_reg = himci_readl(host->base + MCI_INTMASK); tmp_reg &= ~ALL_INT_MASK; tmp_reg |= DTO_INT_MASK; himci_writel(tmp_reg, host->base + MCI_INTMASK); /* enable inner DMA mode and close intr of MMC host controler */ tmp_reg = himci_readl(host->base + MCI_CTRL); tmp_reg &= ~INTR_EN; tmp_reg |= USE_INTERNAL_DMA | INTR_EN; himci_writel(tmp_reg, host->base + MCI_CTRL); /* set timeout param */ himci_writel(DATA_TIMEOUT | RESPONSE_TIMEOUT, host->base + MCI_TIMEOUT); /* set FIFO param */ tmp_reg = 0; tmp_reg |= BURST_SIZE | RX_WMARK | TX_WMARK; himci_writel(tmp_reg, host->base + MCI_FIFOTH); }
static inline void emmc_sys_init(void) { unsigned int tmp_reg; /* SDIO clock phase */ tmp_reg = himci_readl(CRG_REG_BASE + REG_CRG49); tmp_reg &= ~SDIO0_CLK_SEL_MASK; tmp_reg |= SDIO0_CLK_SEL_49_5M; /* phasic move to xls table. */ himci_writel(tmp_reg, CRG_REG_BASE + REG_CRG49); /* SDIO soft reset */ tmp_reg |= SDIO0_SRST_REQ; himci_writel(tmp_reg, CRG_REG_BASE + REG_CRG49); delay(1000 * DELAY_US); tmp_reg &= ~SDIO0_SRST_REQ; tmp_reg |= SDIO0_CKEN; himci_writel(tmp_reg, CRG_REG_BASE + REG_CRG49); }
static void hi_mci_idma_stop(struct himci_host *host) { unsigned int tmp_reg; himci_trace(2, "begin"); tmp_reg = himci_readl(host->base + MCI_BMOD); tmp_reg &= ~BMOD_DMA_EN; himci_writel(tmp_reg, host->base + MCI_BMOD); }
static int hi_mci_wait_cmd(struct himci_host *host) { int wait_retry_count = 0; unsigned int reg_data = 0; unsigned long flags; himci_trace(2, "begin"); himci_assert(host); while (1) { /* * Check if CMD::start_cmd bit is clear. * start_cmd = 0 means MMC Host controller has loaded registers * and next command can be loaded in. */ reg_data = readl(host->base + MCI_CMD); if ((reg_data & START_CMD) == 0) return 0; /* Check if Raw_Intr_Status::HLE bit is set. */ spin_lock_irqsave(&host->lock, flags); reg_data = readl(host->base + MCI_RINTSTS); if (reg_data & HLE_INT_STATUS) { reg_data |= HLE_INT_STATUS; himci_writel(reg_data, host->base + MCI_RINTSTS); spin_unlock_irqrestore(&host->lock, flags); himci_trace(3, "Other CMD is running," "please operate cmd again!"); return 1; } himci_writel(reg_data, host->base + MCI_RINTSTS); spin_unlock_irqrestore(&host->lock, flags); udelay(100); /* Check if number of retries for this are over. */ wait_retry_count++; if (wait_retry_count >= retry_count) { himci_trace(3, "wait cmd complete is timeout!"); return -1; } } }
static int hi_mci_wait_data_complete(struct himci_host *host) { unsigned int data_irq_reg = 0; struct mmc_data *data = host->data; long time = request_timeout; unsigned long flags; himci_trace(2, "begin"); himci_assert(host); himci_assert(data); time = wait_event_timeout(host->intr_wait, test_bit(HIMCI_PEND_DTO_b, &host->pending_events), time); if ((time <= 0) && (!test_bit(HIMCI_PEND_DTO_b, &host->pending_events))) { data->error = -ETIMEDOUT; spin_lock_irqsave(&host->lock, flags); data_irq_reg = himci_readl(host->base + MCI_RINTSTS); himci_writel(data_irq_reg, host->base + MCI_RINTSTS); spin_unlock_irqrestore(&host->lock, flags); himci_trace(3, "wait data request complete is timeout! 0x%08X", data_irq_reg); hi_mci_idma_stop(host); hi_mci_data_done(host, data_irq_reg); return -1; } spin_lock_irqsave(&host->lock, flags); data_irq_reg = himci_readl(host->base + MCI_RINTSTS); himci_writel((HTO_INT_STATUS | DRTO_INT_STATUS | EBE_INT_STATUS | SBE_INT_STATUS | FRUN_INT_STATUS | DCRC_INT_STATUS), host->base + MCI_RINTSTS); host->pending_events &= ~HIMCI_PEND_DTO_m; spin_unlock_irqrestore(&host->lock, flags); hi_mci_idma_stop(host); hi_mci_data_done(host, data_irq_reg); return 0; }
static void hi_mci_sys_init(void) { unsigned int tmp_reg; HIMCI_DEBUG_FUN("Function Call"); /* enable SDIO clock and clock 50MHz */ tmp_reg = himci_readl(REG_BASE_CRG + REG_PERI_CRG46); tmp_reg &= ~(SDIO_SRST_REQ | SDIO_CKEN | SDIO_HCLKEN | SDIO_CLK_50M | SDIO_CLK_PCTRL); tmp_reg |= SDIO_CKEN | SDIO_HCLKEN | SDIO_CLK_50M; himci_writel(tmp_reg, REG_BASE_CRG + REG_PERI_CRG46); /* SDIO soft reset */ tmp_reg |= SDIO_SRST_REQ; himci_writel(tmp_reg, REG_BASE_CRG + REG_PERI_CRG46); udelay(1000); tmp_reg &= ~SDIO_SRST_REQ; himci_writel(tmp_reg, REG_BASE_CRG + REG_PERI_CRG46); }
static void hi_mci_sys_ctrl_init(struct himci_host *host, resource_size_t host_crg_addr) { unsigned int tmp_reg; if ((SDIO_REG_BASE_CRG + PERI_CRG39) == (unsigned int)host_crg_addr) { /* enable SDIO clock */ tmp_reg = himci_readl(host_crg_addr); tmp_reg &= ~SDIO0_CLK_SEL_MASK; tmp_reg &= ~SDIO0_DRV_PS_SEL_MASK; tmp_reg &= ~SDIO0_SAP_PS_SEL_MASK; tmp_reg |= SDIO0_CLK_SEL_50M | SDIO0_DRV_PS_SEL_135 | SDIO0_SAP_PS_SEL_90; himci_writel(tmp_reg, host_crg_addr); /* SDIO soft reset */ tmp_reg = himci_readl(host_crg_addr); tmp_reg |= SDIO0_SRST_REQ; himci_writel(tmp_reg, host_crg_addr); udelay(1000); tmp_reg &= ~SDIO0_SRST_REQ; tmp_reg |= SDIO0_CKEN | SDIO0_BUS_CKEN; himci_writel(tmp_reg, host_crg_addr); return; } if ((SDIO_REG_BASE_CRG + PERI_CRG40) == (unsigned int)host_crg_addr) { /* SDIO clock phase */ if (_HI3716CV200ES == get_chipid()) { tmp_reg = himci_readl(host_crg_addr); tmp_reg &= ~(SDIO1_CLK_SEL_MASK | SDIO1_DRV_PS_SEL_MASK | SDIO1_SAP_PS_SEL_MASK); tmp_reg |= SDIO1_CLK_SEL_50M | SDIO1_DRV_PS_SEL_45; himci_writel(tmp_reg, host_crg_addr); } else { tmp_reg = himci_readl(host_crg_addr); tmp_reg &= ~SDIO1_CLK_SEL_MASK; tmp_reg |= SDIO1_CLK_SEL_50M; himci_writel(tmp_reg, host_crg_addr); } /* SDIO soft reset */ tmp_reg |= SDIO1_SRST_REQ; himci_writel(tmp_reg, host_crg_addr); udelay(1000); tmp_reg &= ~SDIO1_SRST_REQ; tmp_reg |= SDIO1_CKEN | SDIO1_BUS_CKEN; himci_writel(tmp_reg, host_crg_addr); return; } return; }
/* reset MMC host controler */ static void hi_mci_sys_reset(struct himci_host *host) { unsigned int reg_value; unsigned long flags; himci_trace(2, "reset"); local_irq_save(flags); hi_mci_ctr_reset(); reg_value = himci_readl(host->base + MCI_BMOD); reg_value |= BMOD_SWR | BURST_8; himci_writel(reg_value, host->base + MCI_BMOD); reg_value = himci_readl(host->base + MCI_CTRL); reg_value |= CTRL_RESET | FIFO_RESET | DMA_RESET; himci_writel(reg_value, host->base + MCI_CTRL); local_irq_restore(flags); }
static void hi_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct himci_host *host = mmc_priv(mmc); unsigned int tmp_reg; himci_trace(2, "begin"); himci_assert(mmc); himci_assert(ios); himci_assert(host); himci_trace(3, "ios->power_mode = %d ", ios->power_mode); switch (ios->power_mode) { case MMC_POWER_OFF: hi_mci_ctrl_power(host, POWER_OFF); break; case MMC_POWER_UP: case MMC_POWER_ON: hi_mci_ctrl_power(host, POWER_ON); break; } himci_trace(3, "ios->clock = %d ", ios->clock); if (ios->clock) { hi_mci_control_cclk(host, DISABLE); hi_mci_set_cclk(host, ios->clock); hi_mci_control_cclk(host, ENABLE); } else hi_mci_control_cclk(host, DISABLE); /* set bus_width */ himci_trace(3, "ios->bus_width = %d ", ios->bus_width); if (ios->bus_width == MMC_BUS_WIDTH_4) { tmp_reg = himci_readl(host->base + MCI_CTYPE); tmp_reg |= CARD_WIDTH; himci_writel(tmp_reg, host->base + MCI_CTYPE); } else { tmp_reg = himci_readl(host->base + MCI_CTYPE); tmp_reg &= ~CARD_WIDTH; himci_writel(tmp_reg, host->base + MCI_CTYPE); } }
static void hi_mci_set_cclk(struct himci_host *host, unsigned int cclk) { unsigned int reg_value; union cmd_arg_s clk_cmd; himci_trace(2, "begin"); himci_assert(host); himci_assert(cclk); /* * set card clk divider value, * clk_divider = Fmmcclk/(Fmmc_cclk * 2) */ reg_value = CONFIG_MMC_CLK / (cclk * 2); himci_writel(reg_value, host->base + MCI_CLKDIV); clk_cmd.cmd_arg = himci_readl(host->base + MCI_CMD); clk_cmd.bits.start_cmd = 1; clk_cmd.bits.update_clk_reg_only = 1; himci_writel(clk_cmd.cmd_arg, host->base + MCI_CMD); if (hi_mci_wait_cmd(host) != 0) himci_trace(3, "set card clk divider is failed!"); }
static void hi_mci_control_cclk(struct himci_host *host, unsigned int flag) { unsigned int reg; union cmd_arg_s cmd_reg; himci_trace(2, "begin"); himci_assert(host); reg = himci_readl(host->base + MCI_CLKENA); if (flag == ENABLE) reg |= CCLK_ENABLE; else reg &= 0xffff0000; himci_writel(reg, host->base + MCI_CLKENA); cmd_reg.cmd_arg = himci_readl(host->base + MCI_CMD); cmd_reg.bits.start_cmd = 1; cmd_reg.bits.update_clk_reg_only = 1; himci_writel(cmd_reg.cmd_arg, host->base + MCI_CMD); if (hi_mci_wait_cmd(host) != 0) himci_trace(3, "disable or enable clk is timeout!"); }
static irqreturn_t hisd_irq(int irq, void *dev_id) { struct himci_host *host = dev_id; u32 state = 0; int handle = 0; unsigned int tmp_reg; spin_lock(&host->lock); /* disable MMC host interrupt */ tmp_reg = himci_readl(host->base + MCI_INTMASK); tmp_reg &= ~ALL_INT_MASK; himci_writel(tmp_reg, host->base + MCI_INTMASK); state = himci_readl(host->base + MCI_RINTSTS); if (state & DTO_INT_STATUS) { handle = 1; host->pending_events |= HIMCI_PEND_DTO_m; himci_writel(DTO_INT_STATUS, host->base + MCI_RINTSTS); } /* enable MMC host interrupt */ tmp_reg = himci_readl(host->base + MCI_INTMASK); tmp_reg &= ~ALL_INT_MASK; tmp_reg |= DTO_INT_MASK; himci_writel(tmp_reg, host->base + MCI_INTMASK); spin_unlock(&host->lock); wake_up(&host->intr_wait); if (handle) return IRQ_HANDLED; return IRQ_NONE; }
static int hi_mci_wait_cmd_complete(struct himci_host *host) { unsigned int cmd_retry_count = 0; unsigned long cmd_jiffies_timeout; unsigned int cmd_irq_reg = 0; struct mmc_command *cmd = host->cmd; unsigned long flags; himci_trace(2, "begin"); himci_assert(host); himci_assert(cmd); cmd_jiffies_timeout = jiffies + request_timeout; while (1) { if (!time_before(jiffies, cmd_jiffies_timeout)) { cmd->error = -ETIMEDOUT; himci_trace(3, "wait cmd request complete is timeout!"); return -1; } do { spin_lock_irqsave(&host->lock, flags); cmd_irq_reg = readl(host->base + MCI_RINTSTS); if (cmd_irq_reg & CD_INT_STATUS) { himci_writel((CD_INT_STATUS | RTO_INT_STATUS | RCRC_INT_STATUS | RE_INT_STATUS), host->base + MCI_RINTSTS); spin_unlock_irqrestore(&host->lock, flags); hi_mci_cmd_done(host, cmd_irq_reg); return 0; } spin_unlock_irqrestore(&host->lock, flags); cmd_retry_count++; } while (cmd_retry_count < retry_count); schedule(); } }
static void hi_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct himci_host *host = mmc_priv(mmc); int blk_size, tmp_reg, fifo_count = 0; int ret = 0; unsigned long flags; himci_trace(2, "begin"); himci_assert(mmc); himci_assert(mrq); himci_assert(host); host->mrq = mrq; if (host->card_status == CARD_UNPLUGED) { mrq->cmd->error = -ENODEV; goto request_end; } /* prepare data */ if (mrq->data) { ret = hi_mci_setup_data(host, mrq->data); if (ret) { mrq->data->error = ret; himci_trace(3, "data setup is error!"); goto request_end; } blk_size = mrq->data->blksz * mrq->data->blocks; himci_writel(blk_size, host->base + MCI_BYTCNT); himci_writel(mrq->data->blksz, host->base + MCI_BLKSIZ); tmp_reg = himci_readl(host->base + MCI_CTRL); tmp_reg |= FIFO_RESET; himci_writel(tmp_reg, host->base + MCI_CTRL); do { tmp_reg = himci_readl(host->base + MCI_CTRL); fifo_count++; if (fifo_count >= retry_count) { printk(KERN_INFO "fifo reset is timeout!"); return; } } while (tmp_reg&FIFO_RESET); /* start DMA */ hi_mci_idma_start(host); } else { himci_writel(0, host->base + MCI_BYTCNT); himci_writel(0, host->base + MCI_BLKSIZ); } /* send command */ ret = hi_mci_exec_cmd(host, mrq->cmd, mrq->data); if (ret) { mrq->cmd->error = ret; himci_trace(3, "cmd execute is error!"); goto request_end; } /* wait command send complete */ ret = hi_mci_wait_cmd_complete(host); if (ret) goto request_end; if (!(mrq->data && !mrq->cmd->error)) goto request_end; /* start data transfer */ if (mrq->data) { /* wait data transfer complete */ ret = hi_mci_wait_data_complete(host); if (ret) goto request_end; if (mrq->stop) { /* send stop command */ ret = hi_mci_exec_cmd(host, host->mrq->stop, host->data); if (ret) { mrq->cmd->error = ret; goto request_end; } ret = hi_mci_wait_cmd_complete(host); if (ret) goto request_end; if (mrq->data->flags & MMC_DATA_WRITE) { /* wait card write data complete */ hi_mci_wait_card_complete(host, mrq->data); } } } request_end: /* clear MMC host intr */ spin_lock_irqsave(&host->lock, flags); himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); spin_unlock_irqrestore(&host->lock, flags); hi_mci_finish_request(host, mrq); }
static int hi_mci_exec_cmd(struct himci_host *host, struct mmc_command *cmd, struct mmc_data *data) { union cmd_arg_s cmd_regs; himci_trace(2, "begin"); himci_assert(host); himci_assert(cmd); host->cmd = cmd; himci_writel(cmd->arg, host->base + MCI_CMDARG); cmd_regs.cmd_arg = himci_readl(host->base + MCI_CMD); if (data) { cmd_regs.bits.data_transfer_expected = 1; if (data->flags & (MMC_DATA_WRITE | MMC_DATA_READ)) cmd_regs.bits.transfer_mode = 0; if (data->flags & MMC_DATA_STREAM) cmd_regs.bits.transfer_mode = 1; if (data->flags & MMC_DATA_WRITE) cmd_regs.bits.read_write = 1; else if (data->flags & MMC_DATA_READ) cmd_regs.bits.read_write = 0; } else { cmd_regs.bits.data_transfer_expected = 0; cmd_regs.bits.transfer_mode = 0; cmd_regs.bits.read_write = 0; } if (cmd == host->mrq->stop) { cmd_regs.bits.stop_abort_cmd = 1; cmd_regs.bits.wait_prvdata_complete = 0; } else { cmd_regs.bits.stop_abort_cmd = 0; cmd_regs.bits.wait_prvdata_complete = 1; } switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: cmd_regs.bits.response_expect = 0; cmd_regs.bits.response_length = 0; cmd_regs.bits.check_response_crc = 0; break; case MMC_RSP_R1: case MMC_RSP_R1B: cmd_regs.bits.response_expect = 1; cmd_regs.bits.response_length = 0; cmd_regs.bits.check_response_crc = 1; break; case MMC_RSP_R2: cmd_regs.bits.response_expect = 1; cmd_regs.bits.response_length = 1; cmd_regs.bits.check_response_crc = 1; break; case MMC_RSP_R3: cmd_regs.bits.response_expect = 1; cmd_regs.bits.response_length = 0; cmd_regs.bits.check_response_crc = 0; break; default: himci_error("hi_mci: unhandled response type %02x\n", mmc_resp_type(cmd)); return -EINVAL; } himci_trace(3, "send cmd of card is cmd->opcode = %d ", cmd->opcode); if (cmd->opcode == MMC_GO_IDLE_STATE) cmd_regs.bits.send_initialization = 1; else cmd_regs.bits.send_initialization = 0; cmd_regs.bits.card_number = 0; cmd_regs.bits.cmd_index = cmd->opcode; cmd_regs.bits.send_auto_stop = 0; cmd_regs.bits.start_cmd = 1; cmd_regs.bits.update_clk_reg_only = 0; himci_writel(cmd_regs.cmd_arg, host->base + MCI_CMD); if (hi_mci_wait_cmd(host) != 0) { himci_trace(3, "send card cmd is failed!"); return -EINVAL; } return 0; }
static void hi_mci_ctrl_power(struct himci_host *host, unsigned int flag) { himci_trace(2, "begin"); himci_writel(flag, host->base + MCI_PWREN); }