예제 #1
0
static void hi_mci_cmd_done(struct himci_host *host, unsigned int stat)
{
	unsigned int i;
	struct mmc_command *cmd = host->cmd;

	himci_trace(2, "begin");
	himci_assert(host);
	himci_assert(cmd);

	host->cmd = NULL;

	for (i = 0; i < 4; i++) {
		if (mmc_resp_type(cmd) == MMC_RSP_R2)
			cmd->resp[i] = himci_readl(host->base +
					MCI_RESP3 - i * 0x4);
		else
			cmd->resp[i] = himci_readl(host->base +
					MCI_RESP0 + i * 0x4);
	}

	if (stat & RTO_INT_STATUS) {
		cmd->error = -ETIMEDOUT;
		himci_trace(3, "irq cmd status stat = 0x%x is timeout error!",
				stat);
	} else if (stat & (RCRC_INT_STATUS | RE_INT_STATUS)) {
		cmd->error = -EILSEQ;
		himci_trace(3, "irq cmd status stat = 0x%x is response error!",
				stat);
	}
}
예제 #2
0
static void hi_mci_data_done(struct himci_host *host, unsigned int stat)
{
	struct mmc_data *data = host->data;

	himci_trace(2, "begin");
	himci_assert(host);
	himci_assert(data);

	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);

	if (stat & (HTO_INT_STATUS | DRTO_INT_STATUS)) {
		data->error = -ETIMEDOUT;
		himci_trace(3, "irq data status stat = 0x%x is timeout error!",
				stat);
	} else if (stat & (EBE_INT_STATUS | SBE_INT_STATUS | FRUN_INT_STATUS
				| DCRC_INT_STATUS)) {
		data->error = -EILSEQ;
		himci_trace(3, "irq data status stat = 0x%x is data error!",
				stat);
	}

	if (!data->error)
		data->bytes_xfered = data->blocks * data->blksz;
	else
		data->bytes_xfered = 0;

	host->data = NULL;
}
예제 #3
0
static int hi_mci_wait_card_complete(struct himci_host *host,
		struct mmc_data *data)
{
	unsigned int card_retry_count = 0;
	unsigned long card_jiffies_timeout;
	unsigned int card_status_reg = 0;

	himci_trace(2, "begin");
	himci_assert(host);
	himci_assert(data);

	card_jiffies_timeout = jiffies + request_timeout;
	while (1) {
		if (!time_before(jiffies, card_jiffies_timeout)) {
			data->error = -ETIMEDOUT;
			himci_trace(3, "wait card ready complete is timeout!");
			return -1;
		}

		do {
			card_status_reg = readl(host->base + MCI_STATUS);
			if (!(card_status_reg & DATA_BUSY)) {
				himci_trace(2, "end");
				return 0;
			}
			card_retry_count++;
		} while (card_retry_count < retry_count);
		schedule();
	}
}
예제 #4
0
static void __exit hi_mci_exit(void)
{
	himci_trace(2, "begin");

	platform_driver_unregister(&hi_mci_driver);
	platform_device_unregister(&hi_mci_device);
}
예제 #5
0
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);
}
예제 #6
0
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;
		}
	}
}
예제 #7
0
static void hi_mci_sys_undo_reset(struct himci_host *host)
{
	unsigned long flags;

	himci_trace(2, "undo reset");

	local_irq_save(flags);
	hi_mci_ctr_undo_reset();
	local_irq_restore(flags);
}
예제 #8
0
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);
}
예제 #9
0
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;
}
예제 #10
0
static int hi_mci_suspend(struct platform_device *dev, pm_message_t state)
{
	struct mmc_host *mmc = platform_get_drvdata(dev);
	struct himci_host *host;
	int ret = 0;

	himci_trace(2, "begin");
	himci_assert(dev);

	if (mmc) {
		ret = mmc_suspend_host(mmc);

		host = mmc_priv(mmc);
		hi_mci_sys_ctrl_suspend(host);
	}

	himci_trace(2, "end");

	return ret;
}
예제 #11
0
static int hi_mci_get_ro(struct mmc_host *mmc)
{
	unsigned ret;
	struct himci_host *host = mmc_priv(mmc);

	himci_trace(2, "begin");
	himci_assert(mmc);

	ret = hi_mci_ctrl_card_readonly(host);

	return ret;
}
예제 #12
0
static void hi_mci_finish_request(struct himci_host *host,
		struct mmc_request *mrq)
{
	himci_trace(2, "begin");
	himci_assert(host);
	himci_assert(mrq);

	host->mrq = NULL;
	host->cmd = NULL;
	host->data = NULL;
	mmc_request_done(host->mmc, mrq);
}
예제 #13
0
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);
	}
}
예제 #14
0
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!");
}
예제 #15
0
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!");
}
예제 #16
0
static int hi_mci_resume(struct platform_device *dev)
{
	struct mmc_host *mmc = platform_get_drvdata(dev);
	struct himci_host *host;
	int ret = 0;

	himci_trace(2, "begin");
	himci_assert(dev);

	if (mmc) {
		host = mmc_priv(mmc);
		/* enable mmc clk */
		hi_mci_sys_ctrl_init(host);
		/* enable card */
		hi_mci_init_card(host);

		ret = mmc_resume_host(mmc);
	}

	himci_trace(2, "end");

	return ret;
}
예제 #17
0
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();
	}
}
예제 #18
0
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);

}
예제 #19
0
static int __init hi_mci_init(void)
{
	int ret;

	himci_trace(2, "begin");

	ret = platform_device_register(&hi_mci_device);
	if (ret) {
		himci_error("Platform device register is failed!");
		return ret;
	}

	ret = platform_driver_register(&hi_mci_driver);
	if (ret) {
		platform_device_unregister(&hi_mci_device);
		himci_error("Platform driver register is failed!");
		return ret;
	}
	return ret;
}
예제 #20
0
/* 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);
}
예제 #21
0
static void hi_mci_detect_card(unsigned long arg)
{
	struct himci_host *host = (struct himci_host *)arg;
	unsigned int i, curr_status, status[3], detect_retry_count = 0;

	himci_assert(host);

	while (1) {
		for (i = 0; i < 3; i++) {
			status[i] = hi_mci_sys_card_detect(host);
			udelay(10);
		}
		if ((status[0] == status[1]) && (status[0] == status[2]))
			break;

		detect_retry_count++;
		if (detect_retry_count >= retry_count) {
			himci_error("this is a dithering,card detect error!");
			goto err;
		}
	}
	curr_status = status[0];
	if (curr_status != host->card_status) {
		himci_trace(2, "begin card_status = %d\n", host->card_status);
		host->card_status = curr_status;
		if (curr_status != CARD_UNPLUGED) {
			hi_mci_init_card(host);
			printk(KERN_INFO "card connected!\n");
		} else
			printk(KERN_INFO "card disconnected!\n");

		mmc_detect_change(host->mmc, 0);
	}
err:
	mod_timer(&host->timer, jiffies + detect_time);
}
예제 #22
0
static int __devexit hi_mci_remove(struct platform_device *pdev)
{
	struct mmc_host *mmc = platform_get_drvdata(pdev);

	himci_trace(2, "begin");
	himci_assert(pdev);

	platform_set_drvdata(pdev, NULL);

	if (mmc) {
		struct himci_host *host = mmc_priv(mmc);

		free_irq(host->irq, host);
		del_timer_sync(&host->timer);
		mmc_remove_host(mmc);
		hi_mci_ctrl_power(host, POWER_OFF);
		hi_mci_control_cclk(host, DISABLE);
		iounmap(host->base);
		dma_free_coherent(&pdev->dev, PAGE_SIZE, host->dma_vaddr,
				host->dma_paddr);
		mmc_free_host(mmc);
	}
	return 0;
}
예제 #23
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);
}
예제 #24
0
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);
}
예제 #25
0
static int hi_mci_setup_data(struct himci_host *host, struct mmc_data *data)
{
	unsigned int sg_phyaddr, sg_length;
	unsigned int i, ret = 0;
	unsigned int data_size;
	unsigned int max_des, des_cnt;
	struct himci_des *des;

	himci_trace(2, "begin");
	himci_assert(host);
	himci_assert(data);

	host->data = data;

	if (data->flags & MMC_DATA_READ)
		host->dma_dir = DMA_FROM_DEVICE;
	else
		host->dma_dir = DMA_TO_DEVICE;

	host->dma_sg = data->sg;
	host->dma_sg_num = dma_map_sg(mmc_dev(host->mmc),
			data->sg, data->sg_len, host->dma_dir);
	himci_assert(host->dma_sg_num);
	himci_trace(2, "host->dma_sg_num is %d\n", host->dma_sg_num);

	data_size = data->blksz * data->blocks;
	if (data_size > (DMA_BUFFER * MAX_DMA_DES)) {
		himci_error("mci request data_size is too big!\n");
		ret = -1;
		goto out;
	}

	himci_trace(2, "host->dma_paddr is 0x%08X,host->dma_vaddr is 0x%08X\n",
			(unsigned int)host->dma_paddr,
			(unsigned int)host->dma_vaddr);

	max_des = (PAGE_SIZE/sizeof(struct himci_des));
	des = (struct himci_des *)host->dma_vaddr;
	des_cnt = 0;

	for (i = 0; i < host->dma_sg_num; i++) {
		sg_length = sg_dma_len(&data->sg[i]);
		sg_phyaddr = sg_dma_address(&data->sg[i]);
		himci_trace(2, "sg[%d] sg_length is 0x%08X, "
				"sg_phyaddr is 0x%08X\n",
				i, (unsigned int)sg_length,
				(unsigned int)sg_phyaddr);
		while (sg_length) {
			des[des_cnt].idmac_des_ctrl = DMA_DES_OWN
				| DMA_DES_NEXT_DES;
			des[des_cnt].idmac_des_buf_addr = sg_phyaddr;
			/* idmac_des_next_addr is paddr for dma */
			des[des_cnt].idmac_des_next_addr = host->dma_paddr
				+ (des_cnt + 1) * sizeof(struct himci_des);

			if (sg_length >= 0x1F00) {
				des[des_cnt].idmac_des_buf_size = 0x1F00;
				sg_length -= 0x1F00;
				sg_phyaddr += 0x1F00;
			} else {
				/* FIXME:data alignment */
				des[des_cnt].idmac_des_buf_size = sg_length;
				sg_length = 0;
			}

			himci_trace(2, "des[%d] vaddr  is 0x%08X", i,
					(unsigned int)&des[i]);
			himci_trace(2, "des[%d].idmac_des_ctrl is 0x%08X",
			       i, (unsigned int)des[i].idmac_des_ctrl);
			himci_trace(2, "des[%d].idmac_des_buf_size is 0x%08X",
				i, (unsigned int)des[i].idmac_des_buf_size);
			himci_trace(2, "des[%d].idmac_des_buf_addr 0x%08X",
				i, (unsigned int)des[i].idmac_des_buf_addr);
			himci_trace(2, "des[%d].idmac_des_next_addr is 0x%08X",
				i, (unsigned int)des[i].idmac_des_next_addr);
			des_cnt++;
		}

		himci_assert(des_cnt < max_des);
	}
	des[0].idmac_des_ctrl |= DMA_DES_FIRST_DES;
	des[des_cnt - 1].idmac_des_ctrl |= DMA_DES_LAST_DES;
	des[des_cnt - 1].idmac_des_next_addr = 0;
out:
	return ret;
}
예제 #26
0
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;
}
예제 #27
0
static int __devinit hi_mci_probe(struct platform_device *pdev)
{
	struct mmc_host *mmc;
	struct himci_host *host = NULL;
	int ret = 0, irq;

	himci_trace(2, "begin");
	himci_assert(pdev);

	mmc = mmc_alloc_host(sizeof(struct himci_host), &pdev->dev);
	if (!mmc) {
		himci_error("no mem for hi mci host controller!\n");
		ret = -ENOMEM;
		goto out;
	}
	mmc->ops = &hi_mci_ops;

	mmc->f_min = CONFIG_MMC_CCLK_MIN;
	mmc->f_max = CONFIG_MMC_CCLK_MAX;
	mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED
		| MMC_CAP_MMC_HIGHSPEED;

	/* reload by this controller */
	mmc->max_blk_count = 2048;
	mmc->max_segs = 1024;
	mmc->max_req_size = 65535;/* see IP manual */
	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
	mmc->ocr = mmc->ocr_avail;


	host = mmc_priv(mmc);
	host->dma_vaddr = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
			&host->dma_paddr, GFP_KERNEL);
	if (!host->dma_vaddr) {
		himci_error("no mem for himci dma!\n");
		ret = -ENOMEM;
		goto out;
	}

	host->mmc = mmc;
	host->base = ioremap_nocache(CONFIG_HIMCI_IOBASE, HI_MCI_IO_SIZE);
	if (!host->base) {
		himci_error("no mem for himci base!\n");
		ret = -ENOMEM;
		goto out;
	}

	/* enable mmc clk */
	hi_mci_sys_ctrl_init(host);

	/* enable card */
	spin_lock_init(&host->lock);
	hi_mci_init_card(host);

	host->card_status = hi_mci_sys_card_detect(host);
	init_timer(&host->timer);

	host->timer.function = hi_mci_detect_card;
	host->timer.data = (unsigned long)host;
	host->timer.expires = jiffies + detect_time;
	platform_set_drvdata(pdev, mmc);
	mmc_add_host(mmc);

	add_timer(&host->timer);

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		printk(KERN_ERR "no IRQ defined!\n");
		goto out;
	}

	init_waitqueue_head(&host->intr_wait);

	host->irq = irq;
	ret = request_irq(irq, hisd_irq, 0, DRIVER_NAME, host);
	if (ret) {
		printk(KERN_ERR "request_irq error!\n");
		goto out;
	}

	return 0;
out:
	if (host) {
		if (host->base)
			iounmap(host->base);

		if (host->dma_vaddr)
			dma_free_coherent(&pdev->dev, PAGE_SIZE,
					host->dma_vaddr, host->dma_paddr);
	}
	if (mmc)
		mmc_free_host(mmc);

	return ret;
}
예제 #28
0
static int __devinit hi_mci_probe(struct platform_device *pdev)
{
	struct mshci_host *ms_host = NULL;
	struct himci_host *hi_host = NULL;
	struct hisik3_mmc_platform_data *plat = NULL;
	struct resource *memres = NULL;


	int ret = 0, irq;
	int err;
	bool RetVal = 0;
	unsigned long flags;
	unsigned int sdcard_frequency = 0;



    #ifdef CONFIG_MACH_HI6620OEM
    if(1 == pdev->id)
    {
        raw_mmc_turn_on();
    }
    #endif

	himci_trace(HIMCI_TRACE_GEN_API, "++");

	himci_assert(pdev);

	plat = pdev->dev.platform_data;

	himci_trace(HIMCI_TRACE_SIGNIFICANT, "id:%d", pdev->id);

    /*通过读取硬件配置项,或者sdcard时钟配置,只需要SD卡流程走,走一遍*/
    #ifdef CONFIG_MACH_HI6620OEM

    if(1 == pdev->id)
    {
        RetVal = get_hw_config_int("sd_card/sdcard_frequency", &sdcard_frequency, NULL);

    	printk("hsad: sd_card/sdcard_frequency = %d, RetVal = %d\n", sdcard_frequency, RetVal);

        /*读取失败,配置默认值*/
    	if (RetVal == false) {
            printk(KERN_ERR "get board type failed.\n");
            g_sdcard_frequency = 90;
    	}

        /*如果获取配置值异常,则配置默认值*/
    	if ((sdcard_frequency != 100)&&(sdcard_frequency != 90)) {
            printk(KERN_ERR "sdcard_frequency %x is error.\n",sdcard_frequency);
            g_sdcard_frequency = 90;
    	}

    	g_sdcard_frequency = sdcard_frequency;
	}

    #endif

	/* 获取自己定义的数据 */
	if (!plat) {
		himci_error("Platform data not available");
		return -ENOENT;
	}
	if(0 == pdev->id)
	{
	     sema_init(&sem_to_rfile_sync_req,0);
	}

    /*创建硬件信号量IPC_SEM_EMMC*/
    if (0 == pdev->id) {
        mutex_lock(&emmc_mutex);
        emmc_sem_flag = 1;
        mutex_unlock(&emmc_mutex);
    }

	irq = platform_get_irq(pdev, 0);
	memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);

	if ((!irq) || (!memres)) {
		himci_error("resource error");
		ret = -ENOENT;
        goto err_resource_get;
	}

	himci_trace(HIMCI_TRACE_SIGNIFICANT, "irq:%d,start:0x%x,size:0x%x", irq, \
										memres->start, resource_size(memres));

	ms_host = mshci_alloc_host(&pdev->dev, sizeof(struct himci_host));
	if (IS_ERR(ms_host)) {
		himci_error("mshci_alloc_host() failed\n");
		ret = PTR_ERR(ms_host);
        goto err_resource_get;
	}

	hi_host = mshci_priv(ms_host);
	hi_host->ms_host = ms_host;
	hi_host->pdev = pdev;
	hi_host->dev = &pdev->dev;
	hi_host->plat = plat;


	platform_set_drvdata(pdev, ms_host);

	/* MMC IP rstdis */
	if (plat->rstdis_mmc){
        ret = plat->rstdis_mmc();
        if ( ret < 0 ){
	        goto err_resource_get;
	    }
	}

    /* set emmc clk */

	hi_host->pclk = clk_get(&pdev->dev, plat->clk_mmc_low); /* work clk */

	if (IS_ERR(hi_host->pclk)) {
		himci_error("clk_get clk_mmc_low fail!");
		ret = PTR_ERR(hi_host->pclk);
		goto err_io_clk;
	}
	hi_host->clk_mmc_high= clk_get(&pdev->dev, plat->clk_mmc_high); /* highclk used for tuning */

    if (IS_ERR(hi_host->clk_mmc_high)) {
		himci_error("clk_get clk_mmc_high fail!");
		ret = PTR_ERR(hi_host->clk_mmc_high);
		goto err_io_clk;
	}

    ms_host->pclk = NULL;
    ms_host->clk_ref_counter = CLK_DISABLED;
    ms_host->clk_mmc_high = NULL;

	ms_host->pclk = hi_host->pclk;
	ms_host->clk_mmc_high = hi_host->clk_mmc_high;

	if (ret) {
	    himci_error("failed to clk_set_rate");
	}

    if(ms_host->clk_ref_counter == CLK_DISABLED){
        ret = clk_enable(hi_host->pclk);
        ms_host->clk_ref_counter = CLK_ENABLED;
        if (ret) {
            himci_error("clk_enable failed");
            ret = -ENOENT;
            goto err_clk_ops;
        }
    }



	ms_host->ioaddr = ioremap_nocache(memres->start, resource_size(memres));
	if (!ms_host->ioaddr) {
		himci_error("ioremap_nocache failed");
		ret = -ENXIO;
		goto err_req_regs;
	}

	ms_host->hw_name = "hisi_hi6620_mmc";
	ms_host->hw_mmc_id = hi_host->pdev->id;
	ms_host->ops = &mshci_hi_ops;
	ms_host->quirks = 0;
	ms_host->irq = irq;

	/* Setup quirks for the controller */

	if (plat->quirks) {
		ms_host->quirks |= plat->quirks;
	}

	if (plat->caps & MMC_CAP_CLOCK_GATING) {
		/* there is no reason not to use interral clock gating */
		ms_host->mmc->caps |= plat->caps;
		ms_host->mmc->caps |= MMC_CAP_CLOCK_GATING;
		ms_host->clock_gate = 1;
    } else {
		ms_host->mmc->caps |= plat->caps;
		ms_host->clock_gate = 0;
	}

	ms_host->mmc->caps2 = plat->caps2;

	/* sandisk card need clock longer than spec ask */
	/* sdcard also disable ip clock gate c00261379*/
	if (ms_host->hw_mmc_id == 0 || ms_host->hw_mmc_id == 1)
		ms_host->clock_gate = 0;

	if (plat->ocr_mask)
		ms_host->mmc->ocr_avail |= plat->ocr_mask;


    #ifdef CONFIG_MACH_HI6620OEM
    if (plat->iomux_name){
        hi_host->piomux_block = iomux_get_block(plat->iomux_name);
	    hi_host->pblock_config = iomux_get_blockconfig(plat->iomux_name);
    }

    /* todo requlator */
	if (plat->reg_name_ldo) {
		himci_trace(HIMCI_TRACE_SIGNIFICANT, "devname : %s, regname: %s",
					dev_name(hi_host->dev), plat->reg_name_ldo);
		hi_host->vcc_ldo = regulator_get(hi_host->dev, plat->reg_name_ldo);
		if (!IS_ERR(hi_host->vcc_ldo)) {
			/*
			 * Setup a notifier block to update this if another device
			 * causes the voltage to change
			 */
			hi_host->nb.notifier_call = &mshci_hi_disable_voltage;
			ret = regulator_register_notifier(hi_host->vcc_ldo, &hi_host->nb);
			if (ret) {
				dev_err(&pdev->dev,
					"regulator notifier request failed\n");
			}
		} else {
			dev_err(&pdev->dev, "regulator_get() failed\n");
			hi_host->vcc_ldo = NULL;
		}
	}

	if (plat->reg_name_lvs) {
		himci_trace(HIMCI_TRACE_SIGNIFICANT, "devname : %s, regname: %s",
					dev_name(hi_host->dev), plat->reg_name_lvs);
		hi_host->vcc_lvs = regulator_get(hi_host->dev, plat->reg_name_lvs);
		if (!IS_ERR(hi_host->vcc_lvs)) {
			/*
			 * Setup a notifier block to update this if another device
			 * causes the voltage to change
			 */
			hi_host->nb.notifier_call = &mshci_hi_disable_voltage;
			ret = regulator_register_notifier(hi_host->vcc_lvs, &hi_host->nb);
			if (ret) {
				dev_err(&pdev->dev,
					"regulator notifier request failed\n");
			}
		} else {
			dev_err(&pdev->dev, "regulator_get() failed\n");
			hi_host->vcc_lvs = NULL;
		}
	}
	hi_host->ocp_flag = 0;

	if (plat->signal_reg_name) {
		himci_trace(HIMCI_TRACE_SIGNIFICANT, "devname : %s, signal regname: %s",
						dev_name(hi_host->dev), plat->signal_reg_name);
		hi_host->signal_vcc = regulator_get(hi_host->dev, plat->signal_reg_name);
		if (IS_ERR(hi_host->signal_vcc)) {
			dev_err(&pdev->dev, "regulator_get() failed\n");
			hi_host->signal_vcc = NULL;
		}
	}
    #endif

	if( (1 == pdev->id)&&( (ms_host->quirks & MSHCI_QUIRK_CBP_DETECTION) == 0)) {
		ret = blockmux_set(hi_host->piomux_block, hi_host->pblock_config, LOWPOWER);
		if (ret) {
			himci_error("failed to blockmux_set");
		}
		mshci_sd_lowpower();
		if (hi_host->vcc_lvs){
			ret = regulator_enable(hi_host->vcc_lvs);
			if (ret) {
				himci_error("failed to regulator_enable LDO7");
			}
			ret = regulator_disable(hi_host->vcc_lvs);
			if (ret) {
			himci_error("failed to regulator_disable LDO7");
			}
		}
		udelay(30);
		if (hi_host->signal_vcc){
			ret = regulator_enable(hi_host->signal_vcc);
			if (ret) {
				himci_error("failed to regulator_enable LDO22");
			}
			ret = regulator_disable(hi_host->signal_vcc);
			if (ret) {
				himci_error("failed to regulator_disable LDO22");
			}
		}
	}else {/*for cbp*/
		ret = blockmux_set(hi_host->piomux_block, hi_host->pblock_config, NORMAL);
		if (ret) {
			himci_error("failed to blockmux_set");
		}

		if (hi_host->vcc_lvs){
			ret = regulator_enable(hi_host->vcc_lvs);
			if (ret) {
				himci_error("failed to regulator_enable LDO7");
			}
		}
		udelay(30);
		if (hi_host->signal_vcc){
			ret = regulator_enable(hi_host->signal_vcc);
			if (ret) {
				himci_error("failed to regulator_enable LDO22");
			}

			ret = regulator_disable(hi_host->signal_vcc);
			if (ret) {
				himci_error("failed to regulator_disable LDO22");
			}
		}
	}

	hi_host->old_sig_voltage = plat->default_signal_voltage;
	hi_host->old_timing = MMC_TIMING_UHS_DDR50;
	hi_host->timing_config = plat->timing_config;
	hi_host->allow_switch_signal_voltage = plat->allow_switch_signal_voltage;
	hi_host->suspend_timing_config = plat->suspend_timing_config;

	if (ms_host->quirks & MSHCI_QUIRK_WLAN_DETECTION) {
		ms_host->flags |= MSHCI_DEVICE_DEAD;
		ms_host->flags |= MMC_PM_KEEP_POWER;
		ms_host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
	}

	ret = mshci_add_host(ms_host);
	if (ret) {
		dev_err(&pdev->dev, "mshci_add_host() failed\n");
		goto err_add_host;
	}

	if (ms_host->quirks & MSHCI_QUIRK_WLAN_DETECTION) {
		ms_host->flags |= MSHCI_DEVICE_DEAD;
	        ms_host->flags |= MMC_PM_KEEP_POWER;
	        ms_host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
		if (plat->ext_cd_init)
			plat->ext_cd_init(&mshci_hi_notify_change);
		plat->set_power = mshci_hi_sdio_set_power;
	}

	if( (1 == pdev->id)&&( (ms_host->quirks & MSHCI_QUIRK_CBP_DETECTION) != 0)) {
	        ms_host->flags |= MMC_PM_KEEP_POWER;
	        ms_host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
	}


	if (ms_host->quirks & MSHCI_QUIRK_EXTERNAL_CARD_DETECTION) {

		err = gpio_request_one(plat->cd_gpio, GPIOF_IN, "ESDHC_CD");
		if (err) {
			dev_warn(mmc_dev(ms_host->mmc),
				"no card-detect pin available!\n");
			goto no_card_detect_pin;
		}
		/*SD_INT_FIX_suspend DTS:2013082704916 modifier: y00241633*/
		err = request_irq(gpio_to_irq(plat->cd_gpio), mshci_hi_card_detect_gpio,
				 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
				 mmc_hostname(ms_host->mmc), ms_host);
		if (err) {
			dev_warn(mmc_dev(ms_host->mmc), "request gpio irq error\n");
			goto no_card_detect_irq;
		}

        if ( plat->sw_gpio ){
            /* only sft has this gpio */
		    err = gpio_request_one(plat->sw_gpio, 0, "ESDHC_POWER_SWITCH");
		    if (err) {
			    dev_warn(mmc_dev(ms_host->mmc),
				    "no card-power-switch pin available!\n");
			    goto no_card_power_switch_pin;
		    }

            /*控制I/O口电平 1V8 or 3V3*/
            gpio_direction_output(plat->sw_gpio, 1);
        }

	}

	return 0;

no_card_power_switch_pin:
	plat->sw_gpio = err;
no_card_detect_irq:
	gpio_free(plat->cd_gpio);
no_card_detect_pin:
	plat->cd_gpio = err;
err_add_host:
	iounmap(ms_host->ioaddr);
	ms_host->ioaddr = NULL;
err_req_regs:
	spin_lock_irqsave(&ms_host->lock, flags);
	if(ms_host->clk_ref_counter == CLK_ENABLED){
		clk_disable(hi_host->pclk);
		ms_host->clk_ref_counter = CLK_DISABLED;
	}
	spin_unlock_irqrestore(&ms_host->lock, flags);
err_clk_ops:
    clk_put(hi_host->clk_mmc_high);
	clk_put(hi_host->pclk);
err_io_clk:
	mshci_free_host(ms_host);

err_resource_get:

	return ret;
}