Edit online

关键流程设计

Edit online

初始化流程

MMC 子系统的初始化包括 MMC 块设备、MMC 子系统、MMC 控制器驱动、card 设备等几条线,初始化顺序:

  1. MMC 核心初始化
  2. MC 控制器驱动初始化完成后才会对 card 设备进行初始化
  3. MMC 块设备初始化没有严格的先后顺序

MMC 块设备驱动初始化

MMC 在使用中,会将其抽象成一个块设备挂载到通用块层当中,通过 module_init(mmc_blk_init) 完成注册和初始化的操作,主要步骤如下:

  1. 注册总线 (bus_register)。
  2. 将块设备名 ”mmc” 和主设备注册到块层中 (register_blkdev)。
  3. 将 driver 设备驱动注册到驱动模型中 (mmc_register_driver)。
  4. 块设备的初始化及磁盘分区的注册 (mmc_blk_probe)。

MMC 子系统核心初始化

MMC 子系统的核心层负责处理 block 下达的请求,其中关于 MMC 协议的逻辑主要在此实现,通过 subsys_initcall(mmc_init) 完成初始化,其步骤如下:

  1. MMC 类型总线注册 (mmc_register_bus)。
  2. 为控制器设备注册一个类 (mmc_register_host_class)。
  3. SDIO 类型总线类型注册 (sdio_register_b)。

card 设备注册与初始化

MMC 驱动的访问对象为外设,在子系统中会将外设抽象成一个 card 设备,在每次探测外设的时候都会判断该设备是否需要被注册,所以 card 设备注册介绍分为探测时机和注册过程两部分:

  1. 探测时机:
    • mmc 控制器启动时
    • 热插拔时
    • mmc 控制器从 suspend 转为 resume 时
    • 上述三种情况均会进行一次探测,都会调用到函数 mmc_detect_change
  2. 注册过程:
    在探测时调用的函数 mmc_detect_change,该函数会调用 card 设备的注册函数 mmc_rescan,以 SD 卡为例,其注册和初始化过程如下:
    • 判断当前卡是否被注册
    • 若卡已经注册,则确认卡是否存在,存在则提前跳出,若不存在则释放相关资源
    • 若卡未注册,则启动控制器进行卡的初始化步骤
    • 为控制器绑定具体总线的操作函数(mmc_attach_bus(host, &mmc_sd_ops))
    • 适配卡的工作电压 (mmc_select_voltage)
    • 根据 MMC 协议初始化卡,使卡进入传输模式化 (mmc_sd_init_card)
    • 注册卡设备 (mmc_add_card)

控制器驱动注册与初始化

MMC 控制器驱动通过对控制器进行操作完成核心层的请求,控制器驱动也是实现和外设进行通信的软件最底层驱动,该层驱动根据厂商不同而不同,D211 的 SDMC 模块的控制器驱动通过 module_platform_driver(artinchip_mmc_aic_pltfm_driver) 实现,其主要步骤如下:

  1. 使能时钟 (artinchip_mmc_clk_enable)。
  2. 初始化计时机制,该机制实现发送命令和数据传输的 timeout 机制 (timer_setup)。
  3. 初始化保护锁 (spin_lock_init)。
  4. 初始化 tasklet,在驱动中很多流程的处理会在 tasklet 中 (tasklet_init)。
  5. 初始化 DMA(artinchip_mmc_init_dma)。
  6. 中断初始化和注册 (devm_request_irq)。
  7. 注册具体的控制器 (mmc_alloc_host + mmc_add_host)。
  8. 初始化具体控制器,包括接口函数、工作电压、传输能力等。
Edit online

中断处理流程

在触发中断后,需要根据目前的中断状态进行处理,其中主要为错误处理和传输处理,这些处理主要在 tasklet 中,并且基于一些状态变量来控制处理的流程。

  1. 状态变量。在流程的控制上,主要通过几个状态变量来控制:
    • host-> state:表示当前的操作状态,例如发送数据,发送 CMD 等等

    • host->pending_events:当前中断发生的状态

    • host->completed_events:当前完成的状态,例如 CMD 完成,DATA 完成等

    • host->cmd_status:发送 CMD 时中断的状态

    • host->data_status:传输数据时中断的状态

  2. 传输处理
    • 当 CMD 发送完成中断触发后,会在 tasklet 中调用函数 complete,该函数中会读取外部 SD 设备返回给控制器的 response 数据,再根据当前的 CMD 状态对 CMD 的结果进行赋值。

    • 如果使用的是 PIO 的方式,当 TX/RX FIFO 请求中断响应后,会调用对应的函数对 FIFO 进行读写操作。

    • 若是采用 DMA 的方式,则在中断函数中读取内部 DMA 状态,然后释放 DMA 传输的资源,再根据 DMA 的状态,在 tsaklet 中调用 artinchip_mmc_data_complete 函数,该函数会根据目前的数据传输情况对传输结果进行赋值。

  3. 错误处理。目前 SDMC 支持的错误中断类型包括:
    • CMD 错误中断

      当出现 CMD 错误中断后,在中断处理函数中,会将当前中断寄存器的状态保存,然后设置 cmd 的状态为已经完成,最后在 complete 函数中将 CMD 的结果进行赋值。

    • DATA 错误中断

      当出现 DATA 中断后,在中断处理函数中会将当前的中断状态保存,然后设置 data 的状态为 DATA 错误,然后切入到 tasklet 函数中,在该函数中,根据 DATA 错误的状态,停止 dma,如果有需求,就发送 stop 命令。

Edit online

请求处理流程

对于应用程序,通过读写的接口访问文件系统,文件系统访问块设备,MMC 设备在内核中被注册为一个块设备,当读写的操作传入到 MMC 块设备后,通过 MMC 子系统处理相关操作,对于 MMC 子系统其处理皆以请求的方式实现。

块层以上系统读写调用流程

在块层以上,通常是用户空间调用读写接口访问 MMC 设备,主要流程如下:


blkdev_access_flow

1. 通用块设备的访问流程
  1. 在用户空间,应用程序调用 read/write 接口
  2. 然后通过虚拟文件系统
  3. 调用通用块层的接口对块设备进行 IO 请求
  4. IO 调度层负责使用特定算法对这些请求进行调度
  5. 块设备驱动层调用具体的块设备接口访问设备

MMC 子系统请求处理流程

MMC 子系统被抽象成一个块设备,通用块层将 IO 请求调用到具体的块设备驱动层,在 MMC 块设备驱动中的请求处理流程如下:


mmc_request_flow

2. MMC 数据请求的处理流程
  1. 由于会有多个请求,在 block 中以队列的形式处理,在请求到达时,唤醒 mmc_queue_thread。
  2. 调用 block 的请求处理,发出 request。
  3. block 的 request 会由 core 来实现。
  4. core 层会根据当前 host 驱动调用对应 host 的 ops 中的 request 接口去操作 controller。

函数调用关系:

mmc_wait_for_req
|--__mmc_start_req
|--init_completion
            |--mmc_start_request
                |--mmc_mrq_prep
                |--__mmc_start_request
                    |--trace_mmc_request_start
                    |--host->ops->request (即 request)

Host 层驱动请求处理流程

在访问 MMC 外设时,都是通过发送 CMD 的方式,在 host 层驱动中需要通过操作 controller 去实现 core 层的 request,主要流程如下:


host_request_flow

3. Host 层驱动的请求处理流程
  1. 检测卡设备,需要判断当前卡设备是否被拔出。
  2. 判断传输状态,如果当前传输状态不是 idle,那么将会将该请求放在请求队列里。
  3. 处理 data,如果当前请求需要处理数据,则将数据先行处理,如果不需要处理数据则跳过。
  4. 发送 CMD,解析请求中的 CMD 和参数,将其写入寄存器,然后触发 CMD 的发送。
  5. 中断处理,在发送完 CMD 后,后续的工作需要等待中断的触发,在中断处理中会对外设返回的数据和状态进行处理。
  6. 如果需要,发送 stop 命令,结束该次传输。
Host 层函数调用关系:
artinchip_mmc_request
|--artinchip_mmc_get_cd
|--artinchip_mmc_queue_request
|--artinchip_mmc_start_request
        |--artinchip_mmc_prepare_command
        |--artinchip_mmc_submit_data
        |--artinchip_mmc_start_command
        |--artinchip_mmc_prep_stop_abort