当前位置:   article > 正文

linux mmc驱动_linux mmc驱动怎么触发init

linux mmc驱动怎么触发init

插曲:

因为使用的平台是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主控制器:

  1. static int sdhci_tcc_probe(struct platform_device *pdev)
  2. {
  3. const struct of_device_id *match;
  4. const struct sdhci_tcc_soc_data *soc_data;
  5. struct sdhci_host *host;
  6. struct sdhci_pltfm_host *pltfm_host;
  7. struct sdhci_tcc *tcc = NULL;
  8. match = of_match_device(sdhci_tcc_of_match_table, &pdev->dev);
  9. soc_data = match->data;
  10. host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tcc));
  11. pltfm_host = sdhci_priv(host);
  12. tcc = sdhci_pltfm_priv(pltfm_host);
  13. sdhci_get_of_property(pdev);
  14. mmc_of_parse(host->mmc);
  15. sdhci_tcc_parse_configs(pdev, host);
  16. //设置SDMMC1的基础时钟,即PCLKCTL_IO2,输出到外设SDMMC1的时钟
  17. tcc->soc_data->set_core_clock(host);
  18. sdhci_add_host(host);
  19. }

sdhci_tcc_of_match_table表:

  1. static const struct of_device_id sdhci_tcc_of_match_table[] = {
  2. { .compatible = "telechips,tcc803x-sdhci,module-only", .data = &soc_data_tcc803x},
  3. {}
  4. };

表里的soc_data_tcc803x结构:

  1. static const struct sdhci_tcc_soc_data soc_data_tcc803x = {
  2. .pdata = &sdhci_tcc803x_pdata,
  3. .parse_channel_configs = sdhci_tcc803x_parse_channel_configs,
  4. .set_channel_configs = sdhci_tcc803x_set_channel_configs,
  5. .set_core_clock = sdhci_tcc803x_set_core_clock,
  6. .sdhci_tcc_quirks = 0,
  7. };

sdhci_tcc803x_pdata平台数据结构:

  1. static const struct sdhci_pltfm_data sdhci_tcc803x_pdata = {
  2. .ops = &sdhci_tcc803x_ops,
  3. //重点关注quirks,表示异于通常特性
  4. .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
  5. .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
  6. SDHCI_QUIRK2_STOP_WITH_TC,
  7. };

sdhci_tcc803x_ops操作集:

  1. static const struct sdhci_ops sdhci_tcc803x_ops = {
  2. .get_max_clock = sdhci_tcc803x_clk_get_max_clock,
  3. .set_clock = sdhci_tcc_set_clock,
  4. .set_bus_width = sdhci_set_bus_width,
  5. .reset = sdhci_tcc_reset,
  6. .hw_reset = sdhci_tcc_hw_reset,
  7. .set_uhs_signaling = sdhci_set_uhs_signaling,
  8. .get_ro = sdhci_tcc_get_ro,
  9. };

sdhci_ops与主控制硬件打交道,所以是再host主控制器驱动中实现的。

重新回到probe函数,看sdhci_pltfm_init函数:

  1. struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
  2. const struct sdhci_pltfm_data *pdata,
  3. size_t priv_size)
  4. {
  5. struct sdhci_host *host;
  6. //分配一个struct sdhci_host结构空间,附带分配一个struct sdhci_pltfm_host结构空间
  7. //和一个priv_size大小的空间
  8. host = sdhci_alloc_host(&pdev->dev,sizeof(struct sdhci_pltfm_host) + priv_size);
  9. if (pdata && pdata->ops)
  10. host->ops = pdata->ops; //对应上面定义的struct sdhci_ops sdhci_tcc803x_ops变量
  11. return host;
  12. }

sdhci_alloc_host函数:

  1. struct sdhci_host *sdhci_alloc_host(struct device *dev,
  2. size_t priv_size)
  3. {
  4. struct mmc_host *mmc;
  5. struct sdhci_host *host;
  6. //在分配一个mmc_host内存空间的同时也分配了一个sdhci_host内存空间以及sdhci_host附属空间
  7. mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
  8. host = mmc_priv(mmc);
  9. host->mmc = mmc;
  10. host->mmc_host_ops = sdhci_ops;
  11. mmc->ops = &host->mmc_host_ops;
  12. return host;
  13. }

mmc_alloc_host函数:分配一个mmc_host结构体内存空间,也分配了其它以外的内存空间。

  1. struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
  2. {
  3. int err;
  4. struct mmc_host *host;
  5. //除了分配mmc_host内存空间,也分配了其它以外的内存空间
  6. host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
  7. /* scanning will be enabled when we're ready */
  8. host->rescan_disable = 1;
  9. dev_set_name(&host->class_dev, "mmc%d", host->index);
  10. //mmc_host的父设备是probe函数层层传下来的pdev,就是sdhc主控制器设备,即对应sdhc设备节点
  11. host->parent = dev;
  12. host->class_dev.parent = dev;
  13. host->class_dev.class = &mmc_host_class;
  14. device_initialize(&host->class_dev);
  15. init_waitqueue_head(&host->wq);
  16. INIT_DELAYED_WORK(&host->detect, mmc_rescan);
  17. INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
  18. setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
  19. return host;
  20. }

mmc_priv函数:

  1. static inline void *mmc_priv(struct mmc_host *host)
  2. {
  3. return (void *)host->private;
  4. }

mmc_host结构体:

  1. struct mmc_host {
  2. struct device *parent;
  3. struct device class_dev;
  4. const struct mmc_host_ops *ops;
  5. unsigned int f_min;
  6. unsigned int f_max;
  7. unsigned int f_init;
  8. ......
  9. unsigned long private[0] ____cacheline_aligned;
  10. }

回到probe函数,看sdhci_priv函数:

  1. static inline void *sdhci_priv(struct sdhci_host *host)
  2. {
  3. return host->private;
  4. }

sdhci_host结构:

  1. struct sdhci_host {
  2. /* Data set by hardware interface driver */
  3. const char *hw_name; /* Hardware bus name */
  4. const struct sdhci_ops *ops; /* Low level hw interface */
  5. /* Internal data */
  6. struct mmc_host *mmc; /* MMC structure */
  7. struct mmc_host_ops mmc_host_ops; /* MMC host ops */
  8. ......
  9. unsigned long private[0] ____cacheline_aligned;
  10. }

回到probe函数,继续往下看:

sdhci_get_of_property函数:

  1. void sdhci_get_of_property(struct platform_device *pdev)
  2. {
  3. struct device_node *np = pdev->dev.of_node;
  4. struct sdhci_host *host = platform_get_drvdata(pdev);
  5. struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
  6. u32 bus_width;
  7. if (of_get_property(np, "sdhci,auto-cmd12", NULL))
  8. host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
  9. if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
  10. (of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
  11. bus_width == 1))
  12. host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
  13. if (sdhci_of_wp_inverted(np))
  14. host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
  15. if (of_get_property(np, "broken-cd", NULL))
  16. host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
  17. if (of_get_property(np, "no-1-8-v", NULL))
  18. host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
  19. ......
  20. }

mmc_of_parse函数:

  1. mmc_of_parse
  2. device_property_read_u32(dev, "bus-width", &bus_width);
  3. ......
  4. device_property_read_u32(dev, "max-frequency", &host->f_max);
  5. if (device_property_read_bool(dev, "cap-sd-highspeed"))
  6. host->caps |= MMC_CAP_SD_HIGHSPEED;
  7. ......

sdhci_tcc803x_set_core_clock函数:

  1. sdhci_tcc803x_set_core_clock
  2. //成员变量mmc的数据类型是struct mmc_host*
  3. clk_set_rate(pltfm_host->clk, host->mmc->f_max);
  4. clk_core_set_rate_nolock
  5. clk_change_rate
  6. core->ops->set_rate(core->hw, core->new_rate, best_parent_rate);
  1. static struct clk_ops tcc_peri_ops = {
  2. .set_rate = tcc_peri_set_rate,
  3. ......
  4. };
  1. tcc_peri_set_rate
  2. //rate传到安全核设置
  3. arm_smccc_smc(SIP_CLK_SET_PCLKCTRL, tcc->id, 1, rate, vflags, 0, 0, 0, &res);

 sdhci_add_host函数:

  1. int sdhci_add_host(struct sdhci_host *host)
  2. {
  3. sdhci_setup_host(host);
  4. __sdhci_add_host(host);
  5. return 0;
  6. }

sdhci_setup_host函数:

  1. sdhci_setup_host
  2. sdhci_read_caps(host);
  3. ......
  4. if (host->caps & SDHCI_CAN_DO_HISPD)
  5. mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
  6. ......
  7. //host->quirks2见上面pdata有定义
  8. if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
  9. host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
  10. }
  11. if (host->caps1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50))
  12. mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
  13. if (host->caps1 & SDHCI_SUPPORT_SDR104) {
  14. mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
  15. } else if (host->caps1 & SDHCI_SUPPORT_SDR50) {
  16. mmc->caps |= MMC_CAP_UHS_SDR50;
  17. }
  18. if ((host->caps1 & SDHCI_SUPPORT_DDR50) &&!(host->quirks2 &
  19. SDHCI_QUIRK2_BROKEN_DDR50))
  20. mmc->caps |= MMC_CAP_UHS_DDR50;

sdhci_read_caps函数:

  1. sdhci_read_caps
  2. __sdhci_read_caps(host, NULL, NULL, NULL);
  3. of_property_read_u64(mmc_dev(host->mmc)->of_node,"sdhci-caps-mask",
  4. &dt_caps_mask);
  5. of_property_read_u64(mmc_dev(host->mmc)->of_node,"sdhci-caps", &dt_caps);
  6. ......
  7. host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
  8. host->caps &= ~lower_32_bits(dt_caps_mask);
  9. host->caps |= lower_32_bits(dt_caps);
  10. host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
  11. host->caps1 &= ~upper_32_bits(dt_caps_mask);
  12. host->caps1 |= upper_32_bits(dt_caps);

__sdhci_add_host函数:

  1. int __sdhci_add_host(struct sdhci_host *host)
  2. {
  3. struct mmc_host *mmc = host->mmc;
  4. .....
  5. sdhci_init(host, 0);
  6. request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
  7. IRQF_SHARED, mmc_hostname(mmc), host);
  8. .....
  9. mmc_add_host(mmc);
  10. ......
  11. return 0;
  12. }

sdhc主控制器的中断处理函数分为上半部分sdhci_irq和下半部分sdhci_thread_irq。

sdhci_irq:

  1. static irqreturn_t sdhci_irq(int irq, void *dev_id)
  2. {
  3. irqreturn_t result = IRQ_NONE;
  4. struct sdhci_host *host = dev_id;
  5. u32 intmask, mask, unexpected = 0;
  6. int max_loops = 16;
  7. intmask = sdhci_readl(host, SDHCI_INT_STATUS);
  8. if (!intmask || intmask == 0xffffffff) {
  9. result = IRQ_NONE;
  10. goto out;
  11. }
  12. do {
  13. if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
  14. u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT;
  15. ......
  16. result = IRQ_WAKE_THREAD;
  17. }
  18. if (intmask & SDHCI_INT_CMD_MASK){
  19. sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, &intmask);
  20. }
  21. ......
  22. cont:
  23. if (result == IRQ_NONE)
  24. result = IRQ_HANDLED;
  25. intmask = sdhci_readl(host, SDHCI_INT_STATUS);
  26. } while (intmask && --max_loops);
  27. out:
  28. return result;
  29. }

 sdhci_cmd_irq函数:

  1. static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
  2. {
  3. ......
  4. //#define SDHCI_INT_RESPONSE 0x00000001
  5. if (intmask & SDHCI_INT_RESPONSE){
  6. sdhci_finish_command(host);
  7. }
  8. }

 中断状态寄存器:

 

 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函数:

  1. mmc_add_host
  2. mmc_start_host
  3. mmc_gpiod_request_cd_irq
  4. _mmc_detect_change
  5. //对应上面的INIT_DELAYED_WORK(&host->detect, mmc_rescan);
  6. mmc_schedule_delayed_work(&host->detect, delay);

mmc_rescan函数:该函数是detect工作队列的处理函数,该工作队列只在两个地方被调用,一个是在函数_mmc_detect_change里被调用(该函数虽然可能会被多个地方调用,但是正常时候只在初始化的时候被mmc_sart_host函数调用一次),一个是在函数mmc_rescan里递归调用。

  1. void mmc_rescan(struct work_struct *work)
  2. {
  3. struct mmc_host *host = container_of(work, struct mmc_host, detect.work);
  4. int i;
  5. if (host->rescan_disable)
  6. return;
  7. /* If there is a non-removable card registered, only scan once */
  8. if (!mmc_card_is_removable(host) && host->rescan_entered)
  9. return;
  10. host->rescan_entered = 1;
  11. .....
  12. /* if there is a _removable_ card registered, check whether it is still present*/
  13. if (host->bus_ops && !host->bus_dead && mmc_card_is_removable(host))
  14. host->bus_ops->detect(host);
  15. ......
  16. for (i = 0; i < ARRAY_SIZE(freqs); i++) {
  17. if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
  18. break;
  19. if (freqs[i] <= host->f_min)
  20. break;
  21. }
  22. .....
  23. out:
  24. if (host->caps & MMC_CAP_NEEDS_POLL) //只有SD卡主控制器会递归调用轮询扫描
  25. mmc_schedule_delayed_work(&host->detect, HZ);
  26. }

mmc_rescan_try_freq函数:

  1. mmc_rescan_try_freq
  2. mmc_attach_sdio
  3. mmc_sdio_init_card
  4. mmc_set_clock(host, mmc_sdio_get_max_clock(card));

 mmc_set_clock函数:设置实际频率,除了sdio设备,emmc也是调用该函数设置时钟频率。

  1. void mmc_set_clock(struct mmc_host *host, unsigned int hz)
  2. {
  3. //hz:最高频率,host->f_max:实际设置的频率,在设备树中定义,
  4. //对应max-frequency属性,上面有对该属性值获取
  5. if (hz > host->f_max)
  6. hz = host->f_max;
  7. host->ios.clock = hz; //注意:是ios.clock
  8. mmc_set_ios(host);
  9. }

mmc_set_ios函数:该函数除了被mmc_set_clock函数调用,还在mmc_rescan扫描设备的时候和更前面mmc_start_host->mmc_power_up的时候被调用设置时钟,这时的频率:host->f_init = freq;

  1. mmc_set_ios
  2. host->ops->set_ios(host, ios);
  3. static const struct mmc_host_ops sdhci_ops = {
  4. .set_ios = sdhci_set_ios,
  5. ......
  6. };
  7. sdhci_set_ios
  8. //注意:第一次设置的是ios->clock,第二次设置的是host->clock
  9. // host->version == SDHCI_SPEC_300
  10. // host->preset_enabled == 0
  11. if (!ios->clock || ios->clock != host->clock) {
  12. host->ops->set_clock(host, ios->clock);
  13. host->clock = ios->clock;
  14. }
  15. host->ops->set_uhs_signaling(host, ios->timing);
  16. host->timing = ios->timing;
  17. if (host->version >= SDHCI_SPEC_300) {
  18. //set_clock对应sdhci_tcc_set_clock函数
  19. host->ops->set_clock(host, host->clock);
  20. }
  21. sdhci_tcc_set_clock
  22. sdhci_set_clock(host, clock);
  23. host->mmc->actual_clock = 0;
  24. sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
  25. //如果传入的clock等于0,返回
  26. if (clock == 0){
  27. return;
  28. }
  29. clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
  30. sdhci_enable_clk(host, clk);
  31. sdhci_calc_clk
  32. int div = 0;
  33. int real_div = div, clk_mul = 1; //注意:clk_mul和host->clk_mul不一样
  34. u16 clk = 0;
  35. bool switch_base_clk = false;
  36. // host->version == SDHCI_SPEC_300
  37. //host->preset_enabled == 0
  38. //host->clk_mul == 0
  39. if (host->version >= SDHCI_SPEC_300) {
  40. if (!host->clk_mul || switch_base_clk) {
  41. //1、如果max_clk小于等于clock,设置div等参数,使能该频率;
  42. //2、否则对max_clk分频,分频后的频率小于clock,设置div等参数,使能该频率;
  43. if (host->max_clk <= clock){
  44. div = 1;
  45. }
  46. else {
  47. for (div = 2; div < SDHCI_MAX_DIV_SPEC_300;div += 2) {
  48. if ((host->max_clk / div) <= clock)
  49. break;
  50. }
  51. }
  52. real_div = div;
  53. div >>= 1;
  54. }
  55. if (real_div){
  56. *actual_clock = (host->max_clk * clk_mul) / real_div;
  57. }
  58. clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
  59. clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
  60. << SDHCI_DIVIDER_HI_SHIFT;

注:

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 

 参考:SD协议简介_工作使我快乐的博客-CSDN博客

ocr:

 

  1. u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
  2. {
  3. int bit;
  4. //7位是reserve位,清零
  5. if (ocr & 0x7F) {
  6. dev_warn(mmc_dev(host),
  7. "card claims to support voltages below defined range\n");
  8. ocr &= ~0x7F;
  9. }
  10. ......
  11. }

参考:

第12章 SD卡和SDIO接口(一)
[mmc]Linux下MMC/SD/SDIO的识别与操作_anxuan3201的博客-CSDN博客

 UHS:Ultra High-Speed

超高速和超高清接口是下一代实现 SDHC 和 SDXC 卡高速数据传输的的总线接口。

UHS 总线接口现在共有三个版本:UHS-I 、UHS-II、UHS-III。

 参考:Linux SD/MMC/SDIO驱动分析

CCCR全称是Card Common Control Registers。

CIS:card infomation structure,在CCCR寄存器中。
 

 参考:二,sdio总线简介之Commond_sdio cmd_初雨细无声的博客-CSDN博客

其它:

SDIO Card传输分析 - TJ的技术博客

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号