⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sdhci.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 3 页
字号:
	/* Wait max 10 ms */	timeout = 10;	mask = SDHCI_CMD_INHIBIT;	if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))		mask |= SDHCI_DATA_INHIBIT;	/* We shouldn't wait for data inihibit for stop commands, even	   though they might use busy signaling */	if (host->mrq->data && (cmd == host->mrq->data->stop))		mask &= ~SDHCI_DATA_INHIBIT;	while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {		if (timeout == 0) {			printk(KERN_ERR "%s: Controller never released "				"inhibit bit(s). Please report this to "				BUGMAIL ".\n", mmc_hostname(host->mmc));			sdhci_dumpregs(host);			cmd->error = MMC_ERR_FAILED;			tasklet_schedule(&host->finish_tasklet);			return;		}		timeout--;		mdelay(1);	}	mod_timer(&host->timer, jiffies + 10 * HZ);	host->cmd = cmd;	sdhci_prepare_data(host, cmd->data);	writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);	sdhci_set_transfer_mode(host, cmd->data);	if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {		printk(KERN_ERR "%s: Unsupported response type! "			"Please report this to " BUGMAIL ".\n",			mmc_hostname(host->mmc));		cmd->error = MMC_ERR_INVALID;		tasklet_schedule(&host->finish_tasklet);		return;	}	if (!(cmd->flags & MMC_RSP_PRESENT))		flags = SDHCI_CMD_RESP_NONE;	else if (cmd->flags & MMC_RSP_136)		flags = SDHCI_CMD_RESP_LONG;	else if (cmd->flags & MMC_RSP_BUSY)		flags = SDHCI_CMD_RESP_SHORT_BUSY;	else		flags = SDHCI_CMD_RESP_SHORT;	if (cmd->flags & MMC_RSP_CRC)		flags |= SDHCI_CMD_CRC;	if (cmd->flags & MMC_RSP_OPCODE)		flags |= SDHCI_CMD_INDEX;	if (cmd->data)		flags |= SDHCI_CMD_DATA;	writew(SDHCI_MAKE_CMD(cmd->opcode, flags),		host->ioaddr + SDHCI_COMMAND);}static void sdhci_finish_command(struct sdhci_host *host){	int i;	BUG_ON(host->cmd == NULL);	if (host->cmd->flags & MMC_RSP_PRESENT) {		if (host->cmd->flags & MMC_RSP_136) {			/* CRC is stripped so we need to do some shifting. */			for (i = 0;i < 4;i++) {				host->cmd->resp[i] = readl(host->ioaddr +					SDHCI_RESPONSE + (3-i)*4) << 8;				if (i != 3)					host->cmd->resp[i] |=						readb(host->ioaddr +						SDHCI_RESPONSE + (3-i)*4-1);			}		} else {			host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE);		}	}	host->cmd->error = MMC_ERR_NONE;	DBG("Ending cmd (%x)\n", host->cmd->opcode);	if (host->cmd->data)		host->data = host->cmd->data;	else		tasklet_schedule(&host->finish_tasklet);	host->cmd = NULL;}static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock){	int div;	u8 ctrl;	u16 clk;	unsigned long timeout;	if (clock == host->clock)		return;	writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);	if (clock > 25000000)		ctrl |= SDHCI_CTRL_HISPD;	else		ctrl &= ~SDHCI_CTRL_HISPD;	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);	if (clock == 0)		goto out;	for (div = 1;div < 256;div *= 2) {		if ((host->max_clk / div) <= clock)			break;	}	div >>= 1;	clk = div << SDHCI_DIVIDER_SHIFT;	clk |= SDHCI_CLOCK_INT_EN;	writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);	/* Wait max 10 ms */	timeout = 10;	while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))		& SDHCI_CLOCK_INT_STABLE)) {		if (timeout == 0) {			printk(KERN_ERR "%s: Internal clock never stabilised. "				"Please report this to " BUGMAIL ".\n",				mmc_hostname(host->mmc));			sdhci_dumpregs(host);			return;		}		timeout--;		mdelay(1);	}	clk |= SDHCI_CLOCK_CARD_EN;	writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);out:	host->clock = clock;}static void sdhci_set_power(struct sdhci_host *host, unsigned short power){	u8 pwr;	if (host->power == power)		return;	writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);	if (power == (unsigned short)-1)		goto out;	pwr = SDHCI_POWER_ON;	switch (power) {	case MMC_VDD_170:	case MMC_VDD_180:	case MMC_VDD_190:		pwr |= SDHCI_POWER_180;		break;	case MMC_VDD_290:	case MMC_VDD_300:	case MMC_VDD_310:		pwr |= SDHCI_POWER_300;		break;	case MMC_VDD_320:	case MMC_VDD_330:	case MMC_VDD_340:		pwr |= SDHCI_POWER_330;		break;	default:		BUG();	}	writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);out:	host->power = power;}/*****************************************************************************\ *                                                                           * * MMC callbacks                                                             * *                                                                           *\*****************************************************************************/static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq){	struct sdhci_host *host;	unsigned long flags;	host = mmc_priv(mmc);	spin_lock_irqsave(&host->lock, flags);	WARN_ON(host->mrq != NULL);	sdhci_activate_led(host);	host->mrq = mrq;	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {		host->mrq->cmd->error = MMC_ERR_TIMEOUT;		tasklet_schedule(&host->finish_tasklet);	} else		sdhci_send_command(host, mrq->cmd);	mmiowb();	spin_unlock_irqrestore(&host->lock, flags);}static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){	struct sdhci_host *host;	unsigned long flags;	u8 ctrl;	host = mmc_priv(mmc);	spin_lock_irqsave(&host->lock, flags);	/*	 * Reset the chip on each power off.	 * Should clear out any weird states.	 */	if (ios->power_mode == MMC_POWER_OFF) {		writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);		sdhci_init(host);	}	sdhci_set_clock(host, ios->clock);	if (ios->power_mode == MMC_POWER_OFF)		sdhci_set_power(host, -1);	else		sdhci_set_power(host, ios->vdd);	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);	if (ios->bus_width == MMC_BUS_WIDTH_4)		ctrl |= SDHCI_CTRL_4BITBUS;	else		ctrl &= ~SDHCI_CTRL_4BITBUS;	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);	mmiowb();	spin_unlock_irqrestore(&host->lock, flags);}static int sdhci_get_ro(struct mmc_host *mmc){	struct sdhci_host *host;	unsigned long flags;	int present;	host = mmc_priv(mmc);	spin_lock_irqsave(&host->lock, flags);	present = readl(host->ioaddr + SDHCI_PRESENT_STATE);	spin_unlock_irqrestore(&host->lock, flags);	return !(present & SDHCI_WRITE_PROTECT);}static const struct mmc_host_ops sdhci_ops = {	.request	= sdhci_request,	.set_ios	= sdhci_set_ios,	.get_ro		= sdhci_get_ro,};/*****************************************************************************\ *                                                                           * * Tasklets                                                                  * *                                                                           *\*****************************************************************************/static void sdhci_tasklet_card(unsigned long param){	struct sdhci_host *host;	unsigned long flags;	host = (struct sdhci_host*)param;	spin_lock_irqsave(&host->lock, flags);	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {		if (host->mrq) {			printk(KERN_ERR "%s: Card removed during transfer!\n",				mmc_hostname(host->mmc));			printk(KERN_ERR "%s: Resetting controller.\n",				mmc_hostname(host->mmc));			sdhci_reset(host, SDHCI_RESET_CMD);			sdhci_reset(host, SDHCI_RESET_DATA);			host->mrq->cmd->error = MMC_ERR_FAILED;			tasklet_schedule(&host->finish_tasklet);		}	}	spin_unlock_irqrestore(&host->lock, flags);	mmc_detect_change(host->mmc, msecs_to_jiffies(500));}static void sdhci_tasklet_finish(unsigned long param){	struct sdhci_host *host;	unsigned long flags;	struct mmc_request *mrq;	host = (struct sdhci_host*)param;	spin_lock_irqsave(&host->lock, flags);	del_timer(&host->timer);	mrq = host->mrq;	DBG("Ending request, cmd (%x)\n", mrq->cmd->opcode);	/*	 * The controller needs a reset of internal state machines	 * upon error conditions.	 */	if ((mrq->cmd->error != MMC_ERR_NONE) ||		(mrq->data && ((mrq->data->error != MMC_ERR_NONE) ||		(mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) {		/* Some controllers need this kick or reset won't work here */		if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {			unsigned int clock;			/* This is to force an update */			clock = host->clock;			host->clock = 0;			sdhci_set_clock(host, clock);		}		/* Spec says we should do both at the same time, but Ricoh		   controllers do not like that. */		sdhci_reset(host, SDHCI_RESET_CMD);		sdhci_reset(host, SDHCI_RESET_DATA);	}	host->mrq = NULL;	host->cmd = NULL;	host->data = NULL;	sdhci_deactivate_led(host);	mmiowb();	spin_unlock_irqrestore(&host->lock, flags);	mmc_request_done(host->mmc, mrq);}static void sdhci_timeout_timer(unsigned long data){	struct sdhci_host *host;	unsigned long flags;	host = (struct sdhci_host*)data;	spin_lock_irqsave(&host->lock, flags);	if (host->mrq) {		printk(KERN_ERR "%s: Timeout waiting for hardware interrupt. "			"Please report this to " BUGMAIL ".\n",			mmc_hostname(host->mmc));		sdhci_dumpregs(host);		if (host->data) {			host->data->error = MMC_ERR_TIMEOUT;			sdhci_finish_data(host);		} else {			if (host->cmd)				host->cmd->error = MMC_ERR_TIMEOUT;			else				host->mrq->cmd->error = MMC_ERR_TIMEOUT;			tasklet_schedule(&host->finish_tasklet);		}	}	mmiowb();	spin_unlock_irqrestore(&host->lock, flags);}/*****************************************************************************\ *                                                                           * * Interrupt handling                                                        * *                                                                           *\*****************************************************************************/static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask){	BUG_ON(intmask == 0);	if (!host->cmd) {		printk(KERN_ERR "%s: Got command interrupt even though no "			"command operation was in progress.\n",			mmc_hostname(host->mmc));		printk(KERN_ERR "%s: Please report this to " BUGMAIL ".\n",			mmc_hostname(host->mmc));		sdhci_dumpregs(host);		return;	}	if (intmask & SDHCI_INT_RESPONSE)		sdhci_finish_command(host);	else {		if (intmask & SDHCI_INT_TIMEOUT)			host->cmd->error = MMC_ERR_TIMEOUT;		else if (intmask & SDHCI_INT_CRC)			host->cmd->error = MMC_ERR_BADCRC;		else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX))			host->cmd->error = MMC_ERR_FAILED;		else			host->cmd->error = MMC_ERR_INVALID;		tasklet_schedule(&host->finish_tasklet);	}}static void sdhci_data_irq(struct sdhci_host *host, u32 intmask){	BUG_ON(intmask == 0);	if (!host->data) {		/*		 * A data end interrupt is sent together with the response		 * for the stop command.		 */		if (intmask & SDHCI_INT_DATA_END)			return;		printk(KERN_ERR "%s: Got data interrupt even though no "			"data operation was in progress.\n",			mmc_hostname(host->mmc));		printk(KERN_ERR "%s: Please report this to " BUGMAIL ".\n",			mmc_hostname(host->mmc));		sdhci_dumpregs(host);		return;	}	if (intmask & SDHCI_INT_DATA_TIMEOUT)		host->data->error = MMC_ERR_TIMEOUT;	else if (intmask & SDHCI_INT_DATA_CRC)		host->data->error = MMC_ERR_BADCRC;	else if (intmask & SDHCI_INT_DATA_END_BIT)		host->data->error = MMC_ERR_FAILED;	if (host->data->error != MMC_ERR_NONE)		sdhci_finish_data(host);	else {		if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))			sdhci_transfer_pio(host);		if (intmask & SDHCI_INT_DATA_END)			sdhci_finish_data(host);	}}static irqreturn_t sdhci_irq(int irq, void *dev_id){	irqreturn_t result;	struct sdhci_host* host = dev_id;	u32 intmask;	spin_lock(&host->lock);	intmask = readl(host->ioaddr + SDHCI_INT_STATUS);	if (!intmask) {		result = IRQ_NONE;		goto out;	}	DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask);	if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {		writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),			host->ioaddr + SDHCI_INT_STATUS);		tasklet_schedule(&host->card_tasklet);	}	intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);	if (intmask & SDHCI_INT_CMD_MASK) {		writel(intmask & SDHCI_INT_CMD_MASK,			host->ioaddr + SDHCI_INT_STATUS);		sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);	}	if (intmask & SDHCI_INT_DATA_MASK) {		writel(intmask & SDHCI_INT_DATA_MASK,			host->ioaddr + SDHCI_INT_STATUS);		sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -