赞
踩
插曲:
因为使用的平台是telechips的tcc803x,其芯片用户手册描述寄存器都是四字节寻址的,但是在代码驱动中用的很可能是单字节寻址,咋一看,有可能有的地址在芯片手册上没有或者感觉写错了,其实不是,这个需要注意一下。
简单流程:
mmc host主控器注册完成之后,会分别生成一个底层硬件相关的主控制设备(struct)sdhci_host和通用抽象的主控制器设备(struct)mmc_host,当检测到有sd卡等mmc从设备插入时,mmc host主控制器会向sd卡等mmc从设备发起会话,sd卡等从设备作出相应的应答。mmc host主控制建立会话的机制是先通过通用抽象的(struct)mmc_host主设备的通用操作集(struct)mmc_host_ops,再由通用操作集(struct)mmc_host_ops进一步调用底层硬件相关的操作集(struct)sdhci_ops实现主从设备的通信的。
转载:linux MMC framework(2) - sdhci host driver_Hacker_Albert的博客-CSDN博客
注册mmc主控制器:
- static int sdhci_tcc_probe(struct platform_device *pdev)
- {
- const struct of_device_id *match;
- const struct sdhci_tcc_soc_data *soc_data;
- struct sdhci_host *host;
- struct sdhci_pltfm_host *pltfm_host;
- struct sdhci_tcc *tcc = NULL;
-
- match = of_match_device(sdhci_tcc_of_match_table, &pdev->dev);
-
- soc_data = match->data;
-
- host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tcc));
-
- pltfm_host = sdhci_priv(host);
- tcc = sdhci_pltfm_priv(pltfm_host);
-
- sdhci_get_of_property(pdev);
- mmc_of_parse(host->mmc);
- sdhci_tcc_parse_configs(pdev, host);
-
- //设置SDMMC1的基础时钟,即PCLKCTL_IO2,输出到外设SDMMC1的时钟
- tcc->soc_data->set_core_clock(host);
-
- sdhci_add_host(host);
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
sdhci_tcc_of_match_table表:
- static const struct of_device_id sdhci_tcc_of_match_table[] = {
- { .compatible = "telechips,tcc803x-sdhci,module-only", .data = &soc_data_tcc803x},
- {}
- };
表里的soc_data_tcc803x结构:
- static const struct sdhci_tcc_soc_data soc_data_tcc803x = {
- .pdata = &sdhci_tcc803x_pdata,
- .parse_channel_configs = sdhci_tcc803x_parse_channel_configs,
- .set_channel_configs = sdhci_tcc803x_set_channel_configs,
- .set_core_clock = sdhci_tcc803x_set_core_clock,
- .sdhci_tcc_quirks = 0,
- };
sdhci_tcc803x_pdata平台数据结构:
- static const struct sdhci_pltfm_data sdhci_tcc803x_pdata = {
- .ops = &sdhci_tcc803x_ops,
- //重点关注quirks,表示异于通常特性
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
- SDHCI_QUIRK2_STOP_WITH_TC,
- };
sdhci_tcc803x_ops操作集:
- static const struct sdhci_ops sdhci_tcc803x_ops = {
- .get_max_clock = sdhci_tcc803x_clk_get_max_clock,
- .set_clock = sdhci_tcc_set_clock,
- .set_bus_width = sdhci_set_bus_width,
- .reset = sdhci_tcc_reset,
- .hw_reset = sdhci_tcc_hw_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
- .get_ro = sdhci_tcc_get_ro,
-
- };
sdhci_ops与主控制硬件打交道,所以是再host主控制器驱动中实现的。
重新回到probe函数,看sdhci_pltfm_init函数:
- struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
- const struct sdhci_pltfm_data *pdata,
- size_t priv_size)
- {
- struct sdhci_host *host;
-
- //分配一个struct sdhci_host结构空间,附带分配一个struct sdhci_pltfm_host结构空间
- //和一个priv_size大小的空间
- host = sdhci_alloc_host(&pdev->dev,sizeof(struct sdhci_pltfm_host) + priv_size);
-
- if (pdata && pdata->ops)
- host->ops = pdata->ops; //对应上面定义的struct sdhci_ops sdhci_tcc803x_ops变量
-
- return host;
- }
sdhci_alloc_host函数:
- struct sdhci_host *sdhci_alloc_host(struct device *dev,
- size_t priv_size)
- {
- struct mmc_host *mmc;
- struct sdhci_host *host;
-
- //在分配一个mmc_host内存空间的同时也分配了一个sdhci_host内存空间以及sdhci_host附属空间
- mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
-
- host = mmc_priv(mmc);
- host->mmc = mmc;
- host->mmc_host_ops = sdhci_ops;
- mmc->ops = &host->mmc_host_ops;
-
- return host;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
mmc_alloc_host函数:分配一个mmc_host结构体内存空间,也分配了其它以外的内存空间。
- struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
- {
- int err;
- struct mmc_host *host;
-
- //除了分配mmc_host内存空间,也分配了其它以外的内存空间
- host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
-
- /* scanning will be enabled when we're ready */
- host->rescan_disable = 1;
-
- dev_set_name(&host->class_dev, "mmc%d", host->index);
-
- //mmc_host的父设备是probe函数层层传下来的pdev,就是sdhc主控制器设备,即对应sdhc设备节点
- host->parent = dev;
- host->class_dev.parent = dev;
- host->class_dev.class = &mmc_host_class;
- device_initialize(&host->class_dev);
-
- init_waitqueue_head(&host->wq);
- INIT_DELAYED_WORK(&host->detect, mmc_rescan);
- INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
- setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
-
- return host;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
mmc_priv函数:
- static inline void *mmc_priv(struct mmc_host *host)
- {
- return (void *)host->private;
- }
mmc_host结构体:
- struct mmc_host {
- struct device *parent;
- struct device class_dev;
- const struct mmc_host_ops *ops;
- unsigned int f_min;
- unsigned int f_max;
- unsigned int f_init;
- ......
- unsigned long private[0] ____cacheline_aligned;
- }
回到probe函数,看sdhci_priv函数:
- static inline void *sdhci_priv(struct sdhci_host *host)
- {
- return host->private;
- }
sdhci_host结构:
- struct sdhci_host {
- /* Data set by hardware interface driver */
- const char *hw_name; /* Hardware bus name */
- const struct sdhci_ops *ops; /* Low level hw interface */
- /* Internal data */
- struct mmc_host *mmc; /* MMC structure */
- struct mmc_host_ops mmc_host_ops; /* MMC host ops */
- ......
- unsigned long private[0] ____cacheline_aligned;
- }
回到probe函数,继续往下看:
sdhci_get_of_property函数:
- void sdhci_get_of_property(struct platform_device *pdev)
- {
- struct device_node *np = pdev->dev.of_node;
- struct sdhci_host *host = platform_get_drvdata(pdev);
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- u32 bus_width;
-
- if (of_get_property(np, "sdhci,auto-cmd12", NULL))
- host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
-
- if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
- (of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
- bus_width == 1))
- host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
-
- if (sdhci_of_wp_inverted(np))
- host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
-
- if (of_get_property(np, "broken-cd", NULL))
- host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
-
- if (of_get_property(np, "no-1-8-v", NULL))
- host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
-
- ......
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
mmc_of_parse函数:
- mmc_of_parse
- device_property_read_u32(dev, "bus-width", &bus_width);
- ......
- device_property_read_u32(dev, "max-frequency", &host->f_max);
- if (device_property_read_bool(dev, "cap-sd-highspeed"))
- host->caps |= MMC_CAP_SD_HIGHSPEED;
- ......
sdhci_tcc803x_set_core_clock函数:
- sdhci_tcc803x_set_core_clock
- //成员变量mmc的数据类型是struct mmc_host*
- clk_set_rate(pltfm_host->clk, host->mmc->f_max);
- clk_core_set_rate_nolock
- clk_change_rate
- core->ops->set_rate(core->hw, core->new_rate, best_parent_rate);
- static struct clk_ops tcc_peri_ops = {
- .set_rate = tcc_peri_set_rate,
- ......
- };
- tcc_peri_set_rate
- //rate传到安全核设置
- arm_smccc_smc(SIP_CLK_SET_PCLKCTRL, tcc->id, 1, rate, vflags, 0, 0, 0, &res);
sdhci_add_host函数:
- int sdhci_add_host(struct sdhci_host *host)
- {
- sdhci_setup_host(host);
- __sdhci_add_host(host);
- return 0;
- }
sdhci_setup_host函数:
- sdhci_setup_host
- sdhci_read_caps(host);
- ......
- if (host->caps & SDHCI_CAN_DO_HISPD)
- mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
-
- ......
-
- //host->quirks2见上面pdata有定义
- if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
- host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
- }
-
- if (host->caps1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50))
- mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
-
- if (host->caps1 & SDHCI_SUPPORT_SDR104) {
- mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
- } else if (host->caps1 & SDHCI_SUPPORT_SDR50) {
- mmc->caps |= MMC_CAP_UHS_SDR50;
- }
-
- if ((host->caps1 & SDHCI_SUPPORT_DDR50) &&!(host->quirks2 &
- SDHCI_QUIRK2_BROKEN_DDR50))
- mmc->caps |= MMC_CAP_UHS_DDR50;
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
sdhci_read_caps函数:
- sdhci_read_caps
- __sdhci_read_caps(host, NULL, NULL, NULL);
- of_property_read_u64(mmc_dev(host->mmc)->of_node,"sdhci-caps-mask",
- &dt_caps_mask);
- of_property_read_u64(mmc_dev(host->mmc)->of_node,"sdhci-caps", &dt_caps);
- ......
- host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
- host->caps &= ~lower_32_bits(dt_caps_mask);
- host->caps |= lower_32_bits(dt_caps);
- host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
- host->caps1 &= ~upper_32_bits(dt_caps_mask);
- host->caps1 |= upper_32_bits(dt_caps);
__sdhci_add_host函数:
- int __sdhci_add_host(struct sdhci_host *host)
- {
- struct mmc_host *mmc = host->mmc;
-
- .....
-
- sdhci_init(host, 0);
-
- request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
- IRQF_SHARED, mmc_hostname(mmc), host);
-
- .....
-
- mmc_add_host(mmc);
-
- ......
-
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
sdhc主控制器的中断处理函数分为上半部分sdhci_irq和下半部分sdhci_thread_irq。
sdhci_irq:
- static irqreturn_t sdhci_irq(int irq, void *dev_id)
- {
- irqreturn_t result = IRQ_NONE;
- struct sdhci_host *host = dev_id;
- u32 intmask, mask, unexpected = 0;
- int max_loops = 16;
-
- intmask = sdhci_readl(host, SDHCI_INT_STATUS);
- if (!intmask || intmask == 0xffffffff) {
- result = IRQ_NONE;
- goto out;
- }
-
- do {
-
- if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
- u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT;
- ......
- result = IRQ_WAKE_THREAD;
- }
-
- if (intmask & SDHCI_INT_CMD_MASK){
- sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, &intmask);
- }
- ......
- cont:
- if (result == IRQ_NONE)
- result = IRQ_HANDLED;
-
- intmask = sdhci_readl(host, SDHCI_INT_STATUS);
- } while (intmask && --max_loops);
- out:
- return result;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
sdhci_cmd_irq函数:
- static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
- {
- ......
- //#define SDHCI_INT_RESPONSE 0x00000001
- if (intmask & SDHCI_INT_RESPONSE){
- sdhci_finish_command(host);
- }
- }
中断状态寄存器:
auto-cmd12、auto-cmd23补充:
软件设置控制器相关寄存器,让控制器自行发 stop transfer的命令,即 Auto CMD23或 Auto CMD12。
sdhci_set_transfer_mode函数解析 - xxxdk's blog
struct mmc_request的变量 sbc,即SET_BLOCK_COUNT。
mmc_add_host函数:
- mmc_add_host
- mmc_start_host
- mmc_gpiod_request_cd_irq
- _mmc_detect_change
- //对应上面的INIT_DELAYED_WORK(&host->detect, mmc_rescan);
- mmc_schedule_delayed_work(&host->detect, delay);
mmc_rescan函数:该函数是detect工作队列的处理函数,该工作队列只在两个地方被调用,一个是在函数_mmc_detect_change里被调用(该函数虽然可能会被多个地方调用,但是正常时候只在初始化的时候被mmc_sart_host函数调用一次),一个是在函数mmc_rescan里递归调用。
- void mmc_rescan(struct work_struct *work)
- {
- struct mmc_host *host = container_of(work, struct mmc_host, detect.work);
- int i;
-
- if (host->rescan_disable)
- return;
-
- /* If there is a non-removable card registered, only scan once */
- if (!mmc_card_is_removable(host) && host->rescan_entered)
- return;
- host->rescan_entered = 1;
-
- .....
-
- /* if there is a _removable_ card registered, check whether it is still present*/
- if (host->bus_ops && !host->bus_dead && mmc_card_is_removable(host))
- host->bus_ops->detect(host);
-
- ......
-
- for (i = 0; i < ARRAY_SIZE(freqs); i++) {
- if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
- break;
- if (freqs[i] <= host->f_min)
- break;
- }
-
- .....
-
- out:
- if (host->caps & MMC_CAP_NEEDS_POLL) //只有SD卡主控制器会递归调用轮询扫描
- mmc_schedule_delayed_work(&host->detect, HZ);
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
mmc_rescan_try_freq函数:
- mmc_rescan_try_freq
- mmc_attach_sdio
- mmc_sdio_init_card
- mmc_set_clock(host, mmc_sdio_get_max_clock(card));
mmc_set_clock函数:设置实际频率,除了sdio设备,emmc也是调用该函数设置时钟频率。
- void mmc_set_clock(struct mmc_host *host, unsigned int hz)
- {
- //hz:最高频率,host->f_max:实际设置的频率,在设备树中定义,
- //对应max-frequency属性,上面有对该属性值获取
- if (hz > host->f_max)
- hz = host->f_max;
-
- host->ios.clock = hz; //注意:是ios.clock
- mmc_set_ios(host);
- }
mmc_set_ios函数:该函数除了被mmc_set_clock函数调用,还在mmc_rescan扫描设备的时候和更前面mmc_start_host->mmc_power_up的时候被调用设置时钟,这时的频率:host->f_init = freq;
- mmc_set_ios
- host->ops->set_ios(host, ios);
-
-
- static const struct mmc_host_ops sdhci_ops = {
- .set_ios = sdhci_set_ios,
- ......
- };
-
- sdhci_set_ios
- //注意:第一次设置的是ios->clock,第二次设置的是host->clock
- // host->version == SDHCI_SPEC_300
- // host->preset_enabled == 0
- if (!ios->clock || ios->clock != host->clock) {
- host->ops->set_clock(host, ios->clock);
- host->clock = ios->clock;
- }
-
- host->ops->set_uhs_signaling(host, ios->timing);
- host->timing = ios->timing;
- if (host->version >= SDHCI_SPEC_300) {
- //set_clock对应sdhci_tcc_set_clock函数
- host->ops->set_clock(host, host->clock);
- }
-
- sdhci_tcc_set_clock
- sdhci_set_clock(host, clock);
- host->mmc->actual_clock = 0;
-
- sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
- //如果传入的clock等于0,返回
- if (clock == 0){
- return;
- }
- clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
- sdhci_enable_clk(host, clk);
-
- sdhci_calc_clk
- int div = 0;
- int real_div = div, clk_mul = 1; //注意:clk_mul和host->clk_mul不一样
- u16 clk = 0;
- bool switch_base_clk = false;
- // host->version == SDHCI_SPEC_300
- //host->preset_enabled == 0
- //host->clk_mul == 0
- if (host->version >= SDHCI_SPEC_300) {
- if (!host->clk_mul || switch_base_clk) {
- //1、如果max_clk小于等于clock,设置div等参数,使能该频率;
- //2、否则对max_clk分频,分频后的频率小于clock,设置div等参数,使能该频率;
- if (host->max_clk <= clock){
- div = 1;
- }
- else {
- for (div = 2; div < SDHCI_MAX_DIV_SPEC_300;div += 2) {
- if ((host->max_clk / div) <= clock)
- break;
- }
- }
- real_div = div;
- div >>= 1;
- }
-
- if (real_div){
- *actual_clock = (host->max_clk * clk_mul) / real_div;
- }
- clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
- clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
- << SDHCI_DIVIDER_HI_SHIFT;
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
注:
quirks: 怪癖的意思,也就是说它某种特性与通常的某种设备不相同。
如果设置3.3V、高速,只要在sdhc主控制器节点上添加“no-1-8-v;”和“cap-sd-highspeed;”
SDR模式下:一个时钟采样一次,一次4bit,即0.5Byte,50MHZ,传输速度25MB/s。
卡容量:
1) 标准容量卡(SDSC):不超于2GB
2) 高容量卡 (SDHC):大于2GB又不超过32GB
3) 扩展容量卡(SDXC):大于32GB又不超过2TB的卡
工作电压范围:2.7V~3.6V
总线速率:(SDR-single Data Rate) DDR(Double Data Rate)
1) 默认速率模式:3.3V 信号,高达 25MHz,数据速率 12.5MB/S
2) 高速率模式:3.3V 信号,高达 50MHz,数据速率 25MB/S
3) SDR12:1.8V 信号,高达 25MHz,数据速率 12.5MB/S
4) SDR25:1.8V 信号,高达 50MHz,数据速率 25MB/S
5) SDR50:1.8V 信号,高达 100MHz,数据速率 50MB/S
6) SDR104:1.8V 信号,高达 208MHz,数据速率 104MB/S
7) DDR50:1.8V 信号,高达 50MHz,双时钟沿采样数据,数据速率 50MB/S
ocr:
- u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
- {
- int bit;
-
- //低7位是reserve位,清零
- if (ocr & 0x7F) {
- dev_warn(mmc_dev(host),
- "card claims to support voltages below defined range\n");
- ocr &= ~0x7F;
- }
- ......
- }
参考:
第12章 SD卡和SDIO接口(一)
[mmc]Linux下MMC/SD/SDIO的识别与操作_anxuan3201的博客-CSDN博客
UHS:Ultra High-Speed
超高速和超高清接口是下一代实现 SDHC 和 SDXC 卡高速数据传输的的总线接口。
UHS 总线接口现在共有三个版本:UHS-I 、UHS-II、UHS-III。
CCCR全称是Card Common Control Registers。
CIS:card infomation structure,在CCCR寄存器中。
参考:二,sdio总线简介之Commond_sdio cmd_初雨细无声的博客-CSDN博客
其它:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。