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 + -
显示快捷键?