core.c

来自「linux 内核源代码」· C语言 代码 · 共 829 行 · 第 1/2 页

C
829
字号
/* *  linux/drivers/mmc/core/core.c * *  Copyright (C) 2003-2004 Russell King, All Rights Reserved. *  SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. *  Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. *  MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/module.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/completion.h>#include <linux/device.h>#include <linux/delay.h>#include <linux/pagemap.h>#include <linux/err.h>#include <linux/leds.h>#include <linux/scatterlist.h>#include <linux/mmc/card.h>#include <linux/mmc/host.h>#include <linux/mmc/mmc.h>#include <linux/mmc/sd.h>#include "core.h"#include "bus.h"#include "host.h"#include "sdio_bus.h"#include "mmc_ops.h"#include "sd_ops.h"#include "sdio_ops.h"extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);static struct workqueue_struct *workqueue;/* * Enabling software CRCs on the data blocks can be a significant (30%) * performance cost, and for other reasons may not always be desired. * So we allow it it to be disabled. */int use_spi_crc = 1;module_param(use_spi_crc, bool, 0);/* * Internal function. Schedule delayed work in the MMC work queue. */static int mmc_schedule_delayed_work(struct delayed_work *work,				     unsigned long delay){	return queue_delayed_work(workqueue, work, delay);}/* * Internal function. Flush all scheduled work from the MMC work queue. */static void mmc_flush_scheduled_work(void){	flush_workqueue(workqueue);}/** *	mmc_request_done - finish processing an MMC request *	@host: MMC host which completed request *	@mrq: MMC request which request * *	MMC drivers should call this function when they have completed *	their processing of a request. */void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq){	struct mmc_command *cmd = mrq->cmd;	int err = cmd->error;	if (err && cmd->retries && mmc_host_is_spi(host)) {		if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)			cmd->retries = 0;	}	if (err && cmd->retries) {		pr_debug("%s: req failed (CMD%u): %d, retrying...\n",			mmc_hostname(host), cmd->opcode, err);		cmd->retries--;		cmd->error = 0;		host->ops->request(host, mrq);	} else {		led_trigger_event(host->led, LED_OFF);		pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",			mmc_hostname(host), cmd->opcode, err,			cmd->resp[0], cmd->resp[1],			cmd->resp[2], cmd->resp[3]);		if (mrq->data) {			pr_debug("%s:     %d bytes transferred: %d\n",				mmc_hostname(host),				mrq->data->bytes_xfered, mrq->data->error);		}		if (mrq->stop) {			pr_debug("%s:     (CMD%u): %d: %08x %08x %08x %08x\n",				mmc_hostname(host), mrq->stop->opcode,				mrq->stop->error,				mrq->stop->resp[0], mrq->stop->resp[1],				mrq->stop->resp[2], mrq->stop->resp[3]);		}		if (mrq->done)			mrq->done(mrq);	}}EXPORT_SYMBOL(mmc_request_done);static voidmmc_start_request(struct mmc_host *host, struct mmc_request *mrq){#ifdef CONFIG_MMC_DEBUG	unsigned int i, sz;#endif	pr_debug("%s: starting CMD%u arg %08x flags %08x\n",		 mmc_hostname(host), mrq->cmd->opcode,		 mrq->cmd->arg, mrq->cmd->flags);	if (mrq->data) {		pr_debug("%s:     blksz %d blocks %d flags %08x "			"tsac %d ms nsac %d\n",			mmc_hostname(host), mrq->data->blksz,			mrq->data->blocks, mrq->data->flags,			mrq->data->timeout_ns / 1000000,			mrq->data->timeout_clks);	}	if (mrq->stop) {		pr_debug("%s:     CMD%u arg %08x flags %08x\n",			 mmc_hostname(host), mrq->stop->opcode,			 mrq->stop->arg, mrq->stop->flags);	}	WARN_ON(!host->claimed);	led_trigger_event(host->led, LED_FULL);	mrq->cmd->error = 0;	mrq->cmd->mrq = mrq;	if (mrq->data) {		BUG_ON(mrq->data->blksz > host->max_blk_size);		BUG_ON(mrq->data->blocks > host->max_blk_count);		BUG_ON(mrq->data->blocks * mrq->data->blksz >			host->max_req_size);#ifdef CONFIG_MMC_DEBUG		sz = 0;		for (i = 0;i < mrq->data->sg_len;i++)			sz += mrq->data->sg[i].length;		BUG_ON(sz != mrq->data->blocks * mrq->data->blksz);#endif		mrq->cmd->data = mrq->data;		mrq->data->error = 0;		mrq->data->mrq = mrq;		if (mrq->stop) {			mrq->data->stop = mrq->stop;			mrq->stop->error = 0;			mrq->stop->mrq = mrq;		}	}	host->ops->request(host, mrq);}static void mmc_wait_done(struct mmc_request *mrq){	complete(mrq->done_data);}/** *	mmc_wait_for_req - start a request and wait for completion *	@host: MMC host to start command *	@mrq: MMC request to start * *	Start a new MMC custom command request for a host, and wait *	for the command to complete. Does not attempt to parse the *	response. */void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq){	DECLARE_COMPLETION_ONSTACK(complete);	mrq->done_data = &complete;	mrq->done = mmc_wait_done;	mmc_start_request(host, mrq);	wait_for_completion(&complete);}EXPORT_SYMBOL(mmc_wait_for_req);/** *	mmc_wait_for_cmd - start a command and wait for completion *	@host: MMC host to start command *	@cmd: MMC command to start *	@retries: maximum number of retries * *	Start a new MMC command for a host, and wait for the command *	to complete.  Return any error that occurred while the command *	was executing.  Do not attempt to parse the response. */int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries){	struct mmc_request mrq;	WARN_ON(!host->claimed);	memset(&mrq, 0, sizeof(struct mmc_request));	memset(cmd->resp, 0, sizeof(cmd->resp));	cmd->retries = retries;	mrq.cmd = cmd;	cmd->data = NULL;	mmc_wait_for_req(host, &mrq);	return cmd->error;}EXPORT_SYMBOL(mmc_wait_for_cmd);/** *	mmc_set_data_timeout - set the timeout for a data command *	@data: data phase for command *	@card: the MMC card associated with the data transfer * *	Computes the data timeout parameters according to the *	correct algorithm given the card type. */void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card){	unsigned int mult;	/*	 * SDIO cards only define an upper 1 s limit on access.	 */	if (mmc_card_sdio(card)) {		data->timeout_ns = 1000000000;		data->timeout_clks = 0;		return;	}	/*	 * SD cards use a 100 multiplier rather than 10	 */	mult = mmc_card_sd(card) ? 100 : 10;	/*	 * Scale up the multiplier (and therefore the timeout) by	 * the r2w factor for writes.	 */	if (data->flags & MMC_DATA_WRITE)		mult <<= card->csd.r2w_factor;	data->timeout_ns = card->csd.tacc_ns * mult;	data->timeout_clks = card->csd.tacc_clks * mult;	/*	 * SD cards also have an upper limit on the timeout.	 */	if (mmc_card_sd(card)) {		unsigned int timeout_us, limit_us;		timeout_us = data->timeout_ns / 1000;		timeout_us += data->timeout_clks * 1000 /			(card->host->ios.clock / 1000);		if (data->flags & MMC_DATA_WRITE)			limit_us = 250000;		else			limit_us = 100000;		/*		 * SDHC cards always use these fixed values.		 */		if (timeout_us > limit_us || mmc_card_blockaddr(card)) {			data->timeout_ns = limit_us * 1000;			data->timeout_clks = 0;		}	}}EXPORT_SYMBOL(mmc_set_data_timeout);/** *	__mmc_claim_host - exclusively claim a host *	@host: mmc host to claim *	@abort: whether or not the operation should be aborted * *	Claim a host for a set of operations.  If @abort is non null and *	dereference a non-zero value then this will return prematurely with *	that non-zero value without acquiring the lock.  Returns zero *	with the lock held otherwise. */int __mmc_claim_host(struct mmc_host *host, atomic_t *abort){	DECLARE_WAITQUEUE(wait, current);	unsigned long flags;	int stop;	might_sleep();	add_wait_queue(&host->wq, &wait);	spin_lock_irqsave(&host->lock, flags);	while (1) {		set_current_state(TASK_UNINTERRUPTIBLE);		stop = abort ? atomic_read(abort) : 0;		if (stop || !host->claimed)			break;		spin_unlock_irqrestore(&host->lock, flags);		schedule();		spin_lock_irqsave(&host->lock, flags);	}	set_current_state(TASK_RUNNING);	if (!stop)		host->claimed = 1;	else		wake_up(&host->wq);	spin_unlock_irqrestore(&host->lock, flags);	remove_wait_queue(&host->wq, &wait);	return stop;}EXPORT_SYMBOL(__mmc_claim_host);/** *	mmc_release_host - release a host *	@host: mmc host to release * *	Release a MMC host, allowing others to claim the host *	for their operations. */void mmc_release_host(struct mmc_host *host){	unsigned long flags;	WARN_ON(!host->claimed);	spin_lock_irqsave(&host->lock, flags);	host->claimed = 0;	spin_unlock_irqrestore(&host->lock, flags);	wake_up(&host->wq);}EXPORT_SYMBOL(mmc_release_host);/* * Internal function that does the actual ios call to the host driver, * optionally printing some debug output. */static inline void mmc_set_ios(struct mmc_host *host){	struct mmc_ios *ios = &host->ios;	pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "		"width %u timing %u\n",		 mmc_hostname(host), ios->clock, ios->bus_mode,		 ios->power_mode, ios->chip_select, ios->vdd,		 ios->bus_width, ios->timing);	host->ops->set_ios(host, ios);}/* * Control chip select pin on a host. */void mmc_set_chip_select(struct mmc_host *host, int mode){	host->ios.chip_select = mode;	mmc_set_ios(host);}/* * Sets the host clock to the highest possible frequency that * is below "hz". */void mmc_set_clock(struct mmc_host *host, unsigned int hz){	WARN_ON(hz < host->f_min);	if (hz > host->f_max)		hz = host->f_max;	host->ios.clock = hz;	mmc_set_ios(host);}/* * Change the bus mode (open drain/push-pull) of a host. */void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode){	host->ios.bus_mode = mode;	mmc_set_ios(host);}/* * Change data bus width of a host.

⌨️ 快捷键说明

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