emmc/sd host层解析
在linux驱动相关知识整理一文中,我们说到总线、驱动、设备的关系。在emmc/sd驱动总线简析一文中讲了emmc的总线,在emmc/sd区块层解析 emmc/sd核心层解析 这两篇文章中将了驱动。是不是还差设备啊。那我们就开始吧。
在emmc/sd区块层解析和emmc/sd核心层解析这两篇文章中讲了驱动的部分代码,但是真正跟硬件打交道的代码还是不知道。特别是在核心层分析的时候,我们还欠一份账,host->ops下的函数指针的具体实现。
接下来我们分析host文件夹下的部分代码。在host下面有很多的host类型的对应的文件,在处理流程上都差不多,网上有很多网友都分享了对s3cmci的分析,在这里我就分析一下mmci这个host。
一、找到host的入口
先咱们得找着入口,linux的入口一般在文件的末尾,那我们来到末尾,看到有下面这一段:
static struct amba_driver mmci_driver = {
.drv = {
.name = DRIVER_NAME,
.pm = &mmci_dev_pm_ops,
},
.probe = mmci_probe,
.remove = mmci_remove,
.id_table = mmci_ids,
};
module_amba_driver(mmci_driver);
module_param(fmax, uint, 0444);
MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver");
MODULE_LICENSE("GPL");
咋一看,有点惊喜有点蒙,这里面除了module_amba_driver我不熟悉,其它的都熟悉,但是其它的都不是入口,于是再看看这个不熟悉的东西到底是什么:
#define module_amba_driver(__amba_drv) \
module_driver(__amba_drv, amba_driver_register, amba_driver_unregister)
再扒开看:
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
终于露出了真面目,原来这就是封装了模块的入口函数和退出函数。
入口函数mmci_driver_init就是注册了mmci_driver这个平台驱动,根据惯例,该结构体对应的probe函数会被调用,来到mmci_probe。
二、mmci_probe
static int mmci_probe(struct amba_device *dev,
const struct amba_id *id)
{
struct mmci_platform_data *plat = dev->dev.platform_data;
struct device_node *np = dev->dev.of_node;
struct variant_data *variant = id->data;
struct mmci_host *host;
struct mmc_host *mmc;
int ret;
/* Must have platform data or Device Tree. */
if (!plat && !np) {
dev_err(&dev->dev, "No plat data or DT found\n");
return -EINVAL;
}
if (!plat) {
plat = devm_kzalloc(&dev->dev, sizeof(*plat), GFP_KERNEL);
if (!plat)
return -ENOMEM;
}
if (np)
mmci_dt_populate_generic_pdata(np, plat);
ret = amba_request_regions(dev, DRIVER_NAME);
if (ret)
goto out;
mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
if (!mmc) {
ret = -ENOMEM;
goto rel_regions;
}
host = mmc_priv(mmc);
host->mmc = mmc;
host->gpio_wp = -ENOSYS;
host->gpio_cd = -ENOSYS;
host->gpio_cd_irq = -1;
host->hw_designer = amba_manf(dev);
host->hw_revision = amba_rev(dev);
dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer);
dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision);
host->clk = devm_clk_get(&dev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
goto host_free;
}
ret = clk_prepare_enable(host->clk);
if (ret)
goto host_free;
host->plat = plat;
host->variant = variant;
host->mclk = clk_get_rate(host->clk);
/*
* According to the spec, mclk is max 100 MHz,
* so we try to adjust the clock down to this,
* (if possible).
*/
if (host->mclk > 100000000) {
ret = clk_set_rate(host->clk, 100000000);
if (ret < 0)
goto clk_disable;
host->mclk = clk_get_rate(host->clk);
dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n",
host->mclk);
}
host->phybase = dev->res.start;
host->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!host->base) {
ret = -ENOMEM;
goto clk_disable;
}
if (variant->busy_detect) {
mmci_ops.card_busy = mmci_card_busy;
mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
}
mmc->ops = &mmci_ops;
/*
* The ARM and ST versions of the block have slightly different
* clock divider equations which means that the minimum divider
* differs too.
*/
if (variant->st_clkdiv)
mmc->f_min = DIV_ROUND_UP(host->mclk, 257);
else
mmc->f_min = DIV_ROUND_UP(host->mclk, 512);
/*
* If the platform data supplies a maximum operating
* frequency, this takes precedence. Else, we fall back
* to using the module parameter, which has a (low)
* default value in case it is not specified. Either
* value must not exceed the clock rate into the block,
* of course.
*/
if (plat->f_max)
mmc->f_max = min(host->mclk, plat->f_max);
else
mmc->f_max = min(host->mclk, fmax);
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
/* Get regulators and the supported OCR mask */
mmc_regulator_get_supply(mmc);
if (!mmc->ocr_avail)
mmc->ocr_avail = plat->ocr_mask;
else if (plat->ocr_mask)
dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
mmc->caps = plat->capabilities;
mmc->caps2 = plat->capabilities2;
/* We support these PM capabilities. */
mmc->pm_caps = MMC_PM_KEEP_POWER;
/*
* We can do SGIO
*/
mmc->max_segs = NR_SG;
/*
* Since only a certain number of bits are valid in the data length
* register, we must ensure that we don't exceed 2^num-1 bytes in a
* single request.
*/
mmc->max_req_size = (1 << variant->datalength_bits) - 1;
/*
* Set the maximum segment size. Since we aren't doing DMA
* (yet) we are only limited by the data length register.
*/
mmc->max_seg_size = mmc->max_req_size;
/*
* Block size can be up to 2048 bytes, but must be a power of two.
*/
mmc->max_blk_size = 1 << 11;
/*
* Limit the number of blocks transferred so that we don't overflow
* the maximum request size.
*/
mmc->max_blk_count = mmc->max_req_size >> 11;
spin_lock_init(&host->lock);
writel(0, host->base + MMCIMASK0);
writel(0, host->base + MMCIMASK1);
writel(0xfff, host->base + MMCICLEAR);
if (plat->gpio_cd == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto err_gpio_cd;
}
if (gpio_is_valid(plat->gpio_cd)) {
ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)");
if (ret == 0)
ret = gpio_direction_input(plat->gpio_cd);
if (ret == 0)
host->gpio_cd = plat->gpio_cd;
else if (ret != -ENOSYS)
goto err_gpio_cd;
/*
* A gpio pin that will detect cards when inserted and removed
* will most likely want to trigger on the edges if it is
* 0 when ejected and 1 when inserted (or mutatis mutandis
* for the inverted case) so we request triggers on both
* edges.
*/
ret = request_any_context_irq(gpio_to_irq(plat->gpio_cd),
mmci_cd_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
DRIVER_NAME " (cd)", host);
if (ret >= 0)
host->gpio_cd_irq = gpio_to_irq(plat->gpio_cd);
}
if (plat->gpio_wp == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto err_gpio_wp;
}
if (gpio_is_valid(plat->gpio_wp)) {
ret = gpio_request(plat->gpio_wp, DRIVER_NAME " (wp)");
if (ret == 0)
ret = gpio_direction_input(plat->gpio_wp);
if (ret == 0)
host->gpio_wp = plat->gpio_wp;
else if (ret != -ENOSYS)
goto err_gpio_wp;
}
if ((host->plat->status || host->gpio_cd != -ENOSYS)
&& host->gpio_cd_irq < 0)
mmc->caps |= MMC_CAP_NEEDS_POLL;
ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
if (ret)
goto unmap;
if (!dev->irq[1])
host->singleirq = true;
else {
ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED,
DRIVER_NAME " (pio)", host);
if (ret)
goto irq0_free;
}
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
amba_set_drvdata(dev, mmc);
dev_info(&dev->dev, "%s: PL%03x manf %x rev%u at 0x%08llx irq %d,%d (pio)\n",
mmc_hostname(mmc), amba_part(dev), amba_manf(dev),
amba_rev(dev), (unsigned long long)dev->res.start,
dev->irq[0], dev->irq[1]);
mmci_dma_setup(host);
pm_runtime_set_autosuspend_delay(&dev->dev, 50);
pm_runtime_use_autosuspend(&dev->dev);
pm_runtime_put(&dev->dev);
mmc_add_host(mmc);
return 0;
irq0_free:
free_irq(dev->irq[0], host);
unmap:
if (host->gpio_wp != -ENOSYS)
gpio_free(host->gpio_wp);
err_gpio_wp:
if (host->gpio_cd_irq >= 0)
free_irq(host->gpio_cd_irq, host);
if (host->gpio_cd != -ENOSYS)
gpio_free(host->gpio_cd);
err_gpio_cd:
iounmap(host->base);
clk_disable:
clk_disable_unprepare(host->clk);
host_free:
mmc_free_host(mmc);
rel_regions:
amba_release_regions(dev);
out:
return ret;
}
这么长,不要怕,太高深的我们就暂时不研究,带着我们小小的目的(
host->ops)去看它就没那么累了。
第23行之前,是参数检查和局部变量赋值;
第23--28行:我是完全不懂,其它地方我不懂的如果你懂,还烦请在评论下方告诉我,在此谢过了。
第30行,根据传进来的参数dev,初始化一个我们要用的mmc host。
第35--85行:有知道的同学吗?我反正不知道。
第86行:恭喜各位,捡到宝了。这不是我们寻觅已久的host->ops吗?是的,就是它。迫不及待刨开看一下:
static struct mmc_host_ops mmci_ops = {
.request = mmci_request,
.pre_req = mmci_pre_request,
.post_req = mmci_post_request,
.set_ios = mmci_set_ios,
.get_ro = mmci_get_ro,
.get_cd = mmci_get_cd,
.start_signal_voltage_switch = mmci_sig_volt_switch,
};
在前一篇文章中,我们要找的三个函数指针(request, pre_req, post_req),全在这里了.另外我还悄悄的告诉你,其它网友分析的s3cmci所对应的host,那里面只实现了request这一个函数指针哦,为了不分散注意,代码就不贴过来了,感兴趣的自己去找哈。
下面分节来介绍这几个函数,非核心的的我就只简单介绍它的作用。
三、mmci_set_ios
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmci_host *host = mmc_priv(mmc);
struct variant_data *variant = host->variant;
u32 pwr = 0;
unsigned long flags;
int ret;
pm_runtime_get_sync(mmc_dev(mmc));
if (host->plat->ios_handler &&
host->plat->ios_handler(mmc_dev(mmc), ios))
dev_err(mmc_dev(mmc), "platform ios_handler failed\n");
switch (ios->power_mode) {
case MMC_POWER_OFF:
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
regulator_disable(mmc->supply.vqmmc);
host->vqmmc_enabled = false;
}
break;
case MMC_POWER_UP:
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
/*
* The ST Micro variant doesn't have the PL180s MCI_PWR_UP
* and instead uses MCI_PWR_ON so apply whatever value is
* configured in the variant data.
*/
pwr |= variant->pwrreg_powerup;
break;
case MMC_POWER_ON:
if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
ret = regulator_enable(mmc->supply.vqmmc);
if (ret < 0)
dev_err(mmc_dev(mmc),
"failed to enable vqmmc regulator\n");
else
host->vqmmc_enabled = true;
}
pwr |= MCI_PWR_ON;
break;
}
if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) {
/*
* The ST Micro variant has some additional bits
* indicating signal direction for the signals in
* the SD/MMC bus and feedback-clock usage.
*/
pwr |= host->plat->sigdir;
if (ios->bus_width == MMC_BUS_WIDTH_4)
pwr &= ~MCI_ST_DATA74DIREN;
else if (ios->bus_width == MMC_BUS_WIDTH_1)
pwr &= (~MCI_ST_DATA74DIREN &
~MCI_ST_DATA31DIREN &
~MCI_ST_DATA2DIREN);
}
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
if (host->hw_designer != AMBA_VENDOR_ST)
pwr |= MCI_ROD;
else {
/*
* The ST Micro variant use the ROD bit for something
* else and only has OD (Open Drain).
*/
pwr |= MCI_OD;
}
}
/*
* If clock = 0 and the variant requires the MMCIPOWER to be used for
* gating the clock, the MCI_PWR_ON bit is cleared.
*/
if (!ios->clock && variant->pwrreg_clkgate)
pwr &= ~MCI_PWR_ON;
spin_lock_irqsave(&host->lock, flags);
mmci_set_clkreg(host, ios->clock);
mmci_write_pwrreg(host, pwr);
mmci_reg_delay(host);
spin_unlock_irqrestore(&host->lock, flags);
pm_runtime_mark_last_busy(mmc_dev(mmc));
pm_runtime_put_autosuspend(mmc_dev(mmc));
}
用于设置SD卡控制器,前面我们所见到的设置控制器时钟,数据线宽度等等一系列操作最终就是通过他来实现的;
还有一个重点就是对断电和上电的处理,暂时不做详细分析,有时间的时候再仔细分析。
四、mmci_get_ro
static int mmci_get_ro(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
if (host->gpio_wp == -ENOSYS)
return -ENOSYS;
return gpio_get_value_cansleep(host->gpio_wp);
}
static inline int gpio_get_value_cansleep(unsigned gpio)
{
/* GPIO can never have been requested or set as {in,out}put */
WARN_ON(1);
return 0;
}
获取卡的写保护状态。在驱块层里,SD卡初始化完成以后,我们进行的一个最后的工作便是检测卡的写保护状态,其实就是调用get_ro方法
五、mmci_get_cd
static int mmci_get_cd(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
struct mmci_platform_data *plat = host->plat;
unsigned int status;
if (host->gpio_cd == -ENOSYS) {
if (!plat->status)
return 1; /* Assume always present */
status = plat->status(mmc_dev(host->mmc));
} else
status = !!gpio_get_value_cansleep(host->gpio_cd)
^ plat->cd_invert;
/*
* Use positive logic throughout - status is zero for no card,
* non-zero for card inserted.
*/
return status;
}
通过后面的注释可知,该函数用来判断存储卡是否已经插入。
六、mmci_pre_request
static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq,
bool is_first_req)
{
struct mmci_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
struct mmci_host_next *nd = &host->next_data;
if (!data)
return;
BUG_ON(data->host_cookie);
if (mmci_validate_data(host, data))
return;
if (!mmci_dma_prep_next(host, data))
data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
}
第5行:获取到当前正在处理的请求数据;
第6行:获取指向下一个请求的数据指针;
第8--9行:判断当前处理的数据是否为空,为空直接返回,开始下个请求;
第11行:展开BUG_ON
#ifndef BUG_ON
#ifdef NDEBUG
#define BUG_ON(cond) do { if (cond) {} } while (0)
#else
#define BUG_ON(cond) assert(!(cond))
#endif
#endif
这里应该跑的是第一个,是不是用来判断当前host状态。
回到mmci_pre_request函数的第13行,展开:
/*
* Validate mmc prerequisites
*/
static int mmci_validate_data(struct mmci_host *host,
struct mmc_data *data)
{
if (!data)
return 0;
if (!is_power_of_2(data->blksz)) {
dev_err(mmc_dev(host->mmc),
"unsupported block size (%d bytes)\n", data->blksz);
return -EINVAL;
}
return 0;
}
该函数判断块大小是不是2的幂
回到mmci_pre_request函数的第15--16行:为下一个请求准备dma,里面太复杂,我还没懂。
七、mmci_request
前面的博文以及本文讲了这么多,现在终于来到了关键的请求函数,是不是有点小激动,上代码:
static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct mmci_host *host = mmc_priv(mmc);
unsigned long flags;
WARN_ON(host->mrq != NULL);
mrq->cmd->error = mmci_validate_data(host, mrq->data);
if (mrq->cmd->error) {
mmc_request_done(mmc, mrq);
return;
}
pm_runtime_get_sync(mmc_dev(mmc));
spin_lock_irqsave(&host->lock, flags);
host->mrq = mrq;
if (mrq->data)
mmci_get_next_data(host, mrq->data);
if (mrq->data && mrq->data->flags & MMC_DATA_READ)
mmci_start_data(host, mrq->data);
if (mrq->sbc)
mmci_start_command(host, mrq->sbc, 0);
else
mmci_start_command(host, mrq->cmd, 0);
spin_unlock_irqrestore(&host->lock, flags);
}
看起来还是蛮简洁的,一步步来吧。
第3行:获取处理消息的mmci的host;
第6行:处理的消息不能为空;
第8--12行:处理消息的合法性,mmci_validate_data函数在讲前面一个函数的时候讲过,用来检查块大小是否为2的幂;
第14行:内核函数,用来增加对设备使用的引用计数;
第16行和31行:使用锁实现同步;
第20--21行,代码展开:
static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
{
struct mmci_host_next *next = &host->next_data;
WARN_ON(data->host_cookie && data->host_cookie != next->cookie);
WARN_ON(!data->host_cookie && (next->dma_desc || next->dma_chan));
host->dma_desc_current = next->dma_desc;
host->dma_current = next->dma_chan;
next->dma_desc = NULL;
next->dma_chan = NULL;
}
该函数比较重要的是第8--9行,其中dma_chan是一个内核结构dma_chan的变量,表示一个dma通道,这里应该是将host指向下一个请求所在的dma,意思就是说:等下你就从这里取数据吧。
回到mmci_request的第23--24行,这里应该是跟用户操作的数据相关了:
static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
{
struct variant_data *variant = host->variant;
unsigned int datactrl, timeout, irqmask;
unsigned long long clks;
void __iomem *base;
int blksz_bits;
dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n",
data->blksz, data->blocks, data->flags);
host->data = data;
host->size = data->blksz * data->blocks;
data->bytes_xfered = 0;
clks = (unsigned long long)data->timeout_ns * host->cclk;
do_div(clks, 1000000000UL);
timeout = data->timeout_clks + (unsigned int)clks;
base = host->base;
writel(timeout, base + MMCIDATATIMER);
writel(host->size, base + MMCIDATALENGTH);
blksz_bits = ffs(data->blksz) - 1;
BUG_ON(1 << blksz_bits != data->blksz);
if (variant->blksz_datactrl16)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
else
datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
if (data->flags & MMC_DATA_READ)
datactrl |= MCI_DPSM_DIRECTION;
/* The ST Micro variants has a special bit to enable SDIO */
if (variant->sdio && host->mmc->card)
if (mmc_card_sdio(host->mmc->card)) {
/*
* The ST Micro variants has a special bit
* to enable SDIO.
*/
u32 clk;
datactrl |= MCI_ST_DPSM_SDIOEN;
/*
* The ST Micro variant for SDIO small write transfers
* needs to have clock H/W flow control disabled,
* otherwise the transfer will not start. The threshold
* depends on the rate of MCLK.
*/
if (data->flags & MMC_DATA_WRITE &&
(host->size < 8 ||
(host->size <= 8 && host->mclk > 50000000)))
clk = host->clk_reg & ~variant->clkreg_enable;
else
clk = host->clk_reg | variant->clkreg_enable;
mmci_write_clkreg(host, clk);
}
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
datactrl |= MCI_ST_DPSM_DDRMODE;
/*
* Attempt to use DMA operation mode, if this
* should fail, fall back to PIO mode
*/
if (!mmci_dma_start_data(host, datactrl))
return;
/* IRQ mode, map the SG list for CPU reading/writing */
mmci_init_sg(host, data);
if (data->flags & MMC_DATA_READ) {
irqmask = MCI_RXFIFOHALFFULLMASK;
/*
* If we have less than the fifo 'half-full' threshold to
* transfer, trigger a PIO interrupt as soon as any data
* is available.
*/
if (host->size < variant->fifohalfsize)
irqmask |= MCI_RXDATAAVLBLMASK;
} else {
/*
* We don't actually need to include "FIFO empty" here
* since its implicit in "FIFO half empty".
*/
irqmask = MCI_TXFIFOHALFEMPTYMASK;
}
mmci_write_datactrlreg(host, datactrl);
writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
mmci_set_mask1(host, irqmask);
}
该函数有一个mmc_data结构类型的参数,展开看一下:
struct mmc_data {
unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */
unsigned int timeout_clks; /* data timeout (in clocks) */
unsigned int blksz; /* data block size */
unsigned int blocks; /* number of blocks */
unsigned int error; /* data error */
unsigned int flags;
#define MMC_DATA_WRITE (1 << 8)
#define MMC_DATA_READ (1 << 9)
#define MMC_DATA_STREAM (1 << 10)
unsigned int bytes_xfered;
struct mmc_command *stop; /* stop command */
struct mmc_request *mrq; /* associated request */
unsigned int sg_len; /* size of scatter list */
struct scatterlist *sg; /* I/O scatter list */
s32 host_cookie; /* host private data */
};
里面有:超时时间,超时时钟,块大小,块数量,后面有一个散列表变量sg,我估计是保存数据的。
回到mmci_request函数:
第12--21行:根据入参配置相关参数;
第22--23:将操作数据大小和超时时间写到host的I/O控件,writel和readl是内核函数,用来往内存映射的I/O空间上写读数据。
第28--64行:配置操作数据的跟硬件(DPSM、SDIO)相关的控制命令;
第66-71行:根据注释可以知道,这里试图使用dma来传输数据;
第73-74行:建立数据块对应的聚散表的映射,以方便cpu读取,这个聚散表在card层建立好的,当时没细讲;
第76--92行:配置中断掩码参数,我还没搞明白,第96设置这个掩码;
第94行:设置硬件控制参数;
第95行:读写host的I/O特定位。
小结:这个函数主要用来建立操作数据的映射,方便cpu写读,还有就是设置硬件参数和相关中断掩码;
回到mmci_request函数:
第26行:对于sbc,注释是这样的:struct mmc_command *sbc;/* SET_BLOCK_COUNT for multiblock */
好像是专门针对多个块的。
第27--29行:发送命令,执行操作。
mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
{
void __iomem *base = host->base;
dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n",
cmd->opcode, cmd->arg, cmd->flags);
if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
writel(0, base + MMCICOMMAND);
udelay(1);
}
c |= cmd->opcode | MCI_CPSM_ENABLE;
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136)
c |= MCI_CPSM_LONGRSP;
c |= MCI_CPSM_RESPONSE;
}
if (/*interrupt*/0)
c |= MCI_CPSM_INTERRUPT;
host->cmd = cmd;
writel(cmd->arg, base + MMCIARGUMENT);
writel(c, base + MMCICOMMAND);
}
果真啊,你看从core层传过来的命令码opcode终于出现了;
第8--11行:清掉命令寄存器的值;
第13--21行:将命令组合;
第24--25行:写命令和相关的参数,此时命令及发出去了。
那么mmci_request函数将完了。是不是感觉还差点什么,对啊,命令发送出去之后,是应该有点什么。
我们回到mmci_probe函数,从第87行看起,因为第86行是实现各种函数指针的地方,只有一个没讲了。
第87--202行,各种参数配置,太复杂了;
第203行:这里注册了一个可以共享的中断,进去看一下:
/*
* Handle completion of command and data transfers.
*/
static irqreturn_t mmci_irq(int irq, void *dev_id)
{
struct mmci_host *host = dev_id;
u32 status;
int ret = 0;
spin_lock(&host->lock);
do {
struct mmc_command *cmd;
struct mmc_data *data;
status = readl(host->base + MMCISTATUS);
if (host->singleirq) {
if (status & readl(host->base + MMCIMASK1))
mmci_pio_irq(irq, dev_id);
status &= ~MCI_IRQ1MASK;
}
status &= readl(host->base + MMCIMASK0);
writel(status, host->base + MMCICLEAR);
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
data = host->data;
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND|
MCI_DATABLOCKEND) && data)
mmci_data_irq(host, data, status);
cmd = host->cmd;
if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
mmci_cmd_irq(host, cmd, status);
ret = 1;
} while (status);
spin_unlock(&host->lock);
return IRQ_RETVAL(ret);
}
第16行:读取发送命令的执行状态;
第20行:通过PIO中断读取数据:
/*
* PIO data transfer IRQ handler.
*/
static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
{
struct mmci_host *host = dev_id;
struct sg_mapping_iter *sg_miter = &host->sg_miter;
struct variant_data *variant = host->variant;
void __iomem *base = host->base;
unsigned long flags;
u32 status;
status = readl(base + MMCISTATUS);
dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);
local_irq_save(flags);
do {
unsigned int remain, len;
char *buffer;
/*
* For write, we only need to test the half-empty flag
* here - if the FIFO is completely empty, then by
* definition it is more than half empty.
*
* For read, check for data available.
*/
if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
break;
if (!sg_miter_next(sg_miter))
break;
buffer = sg_miter->addr;
remain = sg_miter->length;
len = 0;
if (status & MCI_RXACTIVE)
len = mmci_pio_read(host, buffer, remain);
if (status & MCI_TXACTIVE)
len = mmci_pio_write(host, buffer, remain, status);
sg_miter->consumed = len;
host->size -= len;
remain -= len;
if (remain)
break;
status = readl(base + MMCISTATUS);
} while (1);
sg_miter_stop(sg_miter);
local_irq_restore(flags);
/*
* If we have less than the fifo 'half-full' threshold to transfer,
* trigger a PIO interrupt as soon as any data is available.
*/
if (status & MCI_RXACTIVE && host->size < variant->fifohalfsize)
mmci_set_mask1(host, MCI_RXDATAAVLBLMASK);
/*
* If we run out of data, disable the data IRQs; this
* prevents a race where the FIFO becomes empty before
* the chip itself has disabled the data path, and
* stops us racing with our data end IRQ.
*/
if (host->size == 0) {
mmci_set_mask1(host, 0);
writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
}
return IRQ_HANDLED;
}
/*
* Handle completion of command and data transfers.
*/
看重点,第40--43行,读取或者发送数据,并放到参数host的sgmiter(散列结构),啊终于找到读写的源头了,哈哈哈...
这里贴一个读函数:
static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
{
void __iomem *base = host->base;
char *ptr = buffer;
u32 status;
int host_remain = host->size;
do {
int count = host_remain - (readl(base + MMCIFIFOCNT) << 2);
if (count > remain)
count = remain;
if (count <= 0)
break;
/*
* SDIO especially may want to send something that is
* not divisible by 4 (as opposed to card sectors
* etc). Therefore make sure to always read the last bytes
* while only doing full 32-bit reads towards the FIFO.
*/
if (unlikely(count & 0x3)) {
if (count < 4) {
unsigned char buf[4];
ioread32_rep(base + MMCIFIFO, buf, 1);
memcpy(ptr, buf, count);
} else {
ioread32_rep(base + MMCIFIFO, ptr, count >> 2);
count &= ~0x3;
}
} else {
ioread32_rep(base + MMCIFIFO, ptr, count >> 2);
}
ptr += count;
remain -= count;
host_remain -= count;
if (remain == 0)
break;
status = readl(base + MMCISTATUS);
} while (status & MCI_RXDATAAVLBL);
return ptr - buffer;
}
该函数实现读数据。
回到mmci_irq函数继续分析:
第31--34行:处理数据操作的返回,看一下mmci_cmd_irq:
static void
mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
unsigned int status)
{
/* First check for errors */
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
u32 remain, success;
/* Terminate the DMA transfer */
if (dma_inprogress(host)) {
mmci_dma_data_error(host);
mmci_dma_unmap(host, data);
}
/*
* Calculate how far we are into the transfer. Note that
* the data counter gives the number of bytes transferred
* on the MMC bus, not on the host side. On reads, this
* can be as much as a FIFO-worth of data ahead. This
* matters for FIFO overruns only.
*/
remain = readl(host->base + MMCIDATACNT);
success = data->blksz * data->blocks - remain;
dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n",
status, success);
if (status & MCI_DATACRCFAIL) {
/* Last block was not successful */
success -= 1;
data->error = -EILSEQ;
} else if (status & MCI_DATATIMEOUT) {
data->error = -ETIMEDOUT;
} else if (status & MCI_STARTBITERR) {
data->error = -ECOMM;
} else if (status & MCI_TXUNDERRUN) {
data->error = -EIO;
} else if (status & MCI_RXOVERRUN) {
if (success > host->variant->fifosize)
success -= host->variant->fifosize;
else
success = 0;
data->error = -EIO;
}
data->bytes_xfered = round_down(success, data->blksz);
}
if (status & MCI_DATABLOCKEND)
dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n");
if (status & MCI_DATAEND || data->error) {
if (dma_inprogress(host))
mmci_dma_finalize(host, data);
mmci_stop_data(host);
if (!data->error)
/* The error clause is handled above, success! */
data->bytes_xfered = data->blksz * data->blocks;
if (!data->stop || host->mrq->sbc) {
mmci_request_end(host, data->mrq);
} else {
mmci_start_command(host, data->stop, 0);
}
}
}
该函数从开始到第50行,错误处理,可以看下注释。
第51--53行,解除和dma的映射关系;
后面就是终止本次数据处理的工作。
回到mmci_irq函数继续分析:
第36到38行:处理命令发送后的状态;
总结:到此mmci_irq函数讲完了,这个函数很重要,处理数据操作和命令操作的结果。
回到mmci_probe函数:
第203行:使能dev->irq[1]中断,对应的中断函数为mmci_pio_irq,就是前面我们要用到的数据命令发送结果处理函数。
mmci_probe后面的是一些收尾工作。
暂时讲到这里,后面的有点流水账,下次我搞明白一点再来添加吧。
更多推荐
所有评论(0)