• Linux MMC介绍


    1. 介绍

    Linux中,将包括MMC、SD、SDIO统称为MMC子系统

    MMC子系统从功能上可分为三个层次

    - card层:  Card驱动, 或称client驱动
    - core层:  MMC的核心层, 完成不同协议和规范的实现, 为host层和设备驱动层提供接口函数
    - host层:  Host驱动, 针对不同主机端的SDHC、MMC控制器的驱动

    MMCStructure

    2. 数据结构

    MMC中包含的主要数据结构如下

    - mmc_host      表示一个mmc host控制器
    - mmc_card      表示一个mmc设备
    - mmc_ios       IO总线相关设置
    - mmc_driver    表示一个card drive
    - mmc_bus_ops   总线操作函数集, 有mmc、sd、sdio三种
    - mmc_host_ops  Host Controller操作函数集
    - mmc_command   表示一个mmc命令
    - mmc_data      表示一个mmc数据
    - mmc_request   表示一个mmc请求
    - sdio_func     表示一个SDIO功能设备

    mmc_host主要字段如下

    struct mmc_host {
        int            index;
        const struct mmc_host_ops *ops;
        u32            ocr_avail;
        u32            ocr_avail_sdio;        /* SDIO-specific OCR */
        u32            ocr_avail_sd;          /* SD-specific OCR */
        u32            ocr_avail_mmc;         /* MMC-specific OCR */
        u32            caps;                  /* Host能力标志*/
        u32            caps2;                 /* Host更多能力标志*/
        struct mmc_ios         ios;           /* current io bus settings */
        int            rescan_disable;        /* disable card detection */
        int            rescan_entered;        /* used with nonremovable devices */
        struct mmc_card        *card;         /* device attached to this host */
        struct delayed_work    detect;
        int            detect_change;         /* card检测标志 */
        struct mmc_slot        slot;
        const struct mmc_bus_ops *bus_ops;    /* current bus driver */
        struct mmc_supply      supply;
        unsigned int           slotno;        /* used for sdio acpi binding */
        int                    dsr_req;       /* DSR value is valid */
        u32                    dsr;           /* optional driver stage (DSR) value */
        unsigned long  private[0];
    };

    mmc_card主要字段如下

    struct mmc_card {
        struct mmc_host     *host;        /* the host this device belongs to */
        struct device       dev;          /* the device */
        u32                 ocr;          /* the current OCR setting */
        unsigned int        rca;          /* relative card address of device */
        unsigned int        type;         /* Card类型: MMC、SD、SDIO、COMBO */
        unsigned int        state;        /* (our) card state */
        unsigned int        quirks;       /* card quirks */
        struct mmc_cid      cid;          /* card identification */
        struct mmc_csd      csd;          /* card specific */
        struct mmc_ext_csd  ext_csd;      /* mmc v4 extended card specific */
        struct sd_scr       scr;          /* extra SD information */
        struct sd_ssr       ssr;          /* yet more SD information */
        struct sd_switch_caps sw_caps;    /* switch (CMD6) caps */
        unsigned int        sdio_funcs;   /* number of SDIO functions */
        struct sdio_cccr    cccr;         /* common card info */
        struct sdio_cis     cis;          /* common tuple info */
        struct sdio_func    *sdio_func[7];     /* SDIO functions (devices) */
        unsigned int        sd_bus_speed;      /* Bus Speed Mode set for the card */
        unsigned int        mmc_avail_type;    /* supported device type by both host and card */
        unsigned int        drive_strength;    /* for UHS-I, HS200 or HS400 */
    };

    mmc_ios字段如下

    struct mmc_ios {
        unsigned int     clock;             /* 时钟频率 */
        unsigned short   vdd;
        unsigned char    bus_mode;          /* 命令输出模式: 开漏模式、上拉模式 */
        unsigned char    chip_select;       /* SPI片选: DONTCARE、HIGH、LOW */
        unsigned char    power_mode;        /* 电源供应状态: UNDEFINED、OFF、UP、ON */
        unsigned char    bus_width;         /* 数据总线宽度: 1、4、8 */
        unsigned char    timing;            /* 总线速度模式: DS、HS、SDR12、SDR25... */
        unsigned char    signal_voltage;    /* 信号电压值: 3.3V、1.8V、1.2V */
        unsigned char    drv_type;          /* 驱动类型: A, B, C, D */
        bool enhanced_strobe;               /* hs400es选择 */
    };

    mmc_driver字段如下

    struct mmc_driver {
        struct device_driver drv;
        int (*probe)(struct mmc_card *);
        void (*remove)(struct mmc_card *);
        void (*shutdown)(struct mmc_card *);
    };

    mmc_bus_ops字段如下

    struct mmc_bus_ops {
        void (*remove)(struct mmc_host *);
        void (*detect)(struct mmc_host *);
        int (*pre_suspend)(struct mmc_host *);
        int (*suspend)(struct mmc_host *);
        int (*resume)(struct mmc_host *);
        int (*runtime_suspend)(struct mmc_host *);
        int (*runtime_resume)(struct mmc_host *);
        int (*power_save)(struct mmc_host *);
        int (*power_restore)(struct mmc_host *);
        int (*alive)(struct mmc_host *);
        int (*shutdown)(struct mmc_host *);
        int (*reset)(struct mmc_host *);
    };

    mmc_host_ops字段如下

    struct mmc_host_ops {
        void    (*post_req)(struct mmc_host *, struct mmc_request *, int err);
        void    (*pre_req)(struct mmc_host *, struct mmc_request *, bool is_first_req);
        void    (*request)(struct mmc_host *host, struct mmc_request *req);
        void    (*set_ios)(struct mmc_host *, struct mmc_ios *);
        int     (*get_ro)(struct mmc_host *);
        int     (*get_cd)(struct mmc_host *);
        void    (*enable_sdio_irq)(struct mmc_host *, int enable);
        void    (*init_card)(struct mmc_host *, struct mmc_card *card);
        int     (*start_signal_voltage_switch)(struct mmc_host *, struct mmc_ios *);
        int     (*card_busy)(struct mmc_host *);
        int     (*execute_tuning)(struct mmc_host *, u32 opcode);
        int     (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
        void    (*hs400_enhanced_strobe)(struct mmc_host *, struct mmc_ios *);
        int     (*select_drive_strength)(struct mmc_card *card,
                      unsigned int max_dtr, int host_drv, int card_drv, int *drv_type);
        void    (*hw_reset)(struct mmc_host *);
        void    (*card_event)(struct mmc_host *);
        int     (*multi_io_quirk)(struct mmc_card *, unsigned int direction, int blk_size);
    };

    3. MMC接口

    3.1 Host API

    Host相关接口, 供Host Controller使用

    /* 设备树解析 */
    int mmc_of_parse(struct mmc_host *);
    
    /* 分配/释放host结构体 */
    struct mmc_host *mmc_alloc_host(int extra, struct device *);
    void   mmc_free_host(struct mmc_host *);
    
    /* 添加/移除host设备 */
    int  mmc_add_host(struct mmc_host *);
    void mmc_remove_host(struct mmc_host *);

    mmc_alloc_host主要流程如下

    mmc_alloc_host
      kzalloc(sizeof(struct mmc_host) + extra)
      /* 分配mmc_host结构体 */
      mmc_host::rescan_disable = 1
      /* 禁用扫描*/
      ida_pre_get
      ida_get_new
      /* ida相关 */
      dev_set_name
      /* 设置设备名称 */
      device_initialize(mmc_host::class_dev)
      /* 初始化设备结构体 */
      mmc_gpio_alloc
      /* 分配mmc_gpio结构体 */
      init_waitqueue_head(mmc_host::wq)
      /* 初始化等待队列 */
      INIT_DELAYED_WORK(mmc_host::detect, mmc_rescan);
      /* 初始化工作队列 */

    mmc_add_host主要流程如下

    mmc_add_host
      device_add(mmc_host::class_dev)
      /* 添加设备 */
      mmc_start_host
      /* 启用该Host */
        mmc_claim_host
        /* 占用Host控制器 */
        mmc_power_up
        /* 给Host供电 */
          mmc_host::mmc_ios::power_mode = MMC_POWER_UP
          /* 设置power状态为正在上电 */
          mmc_set_initial_state
          /* 设置初始化状态 */
            mmc_host::mmc_ios::bus_width = MMC_BUS_WIDTH_1
            /* 设置总线宽带为1bit */
            mmc_host::mmc_ios::timing = MMC_TIMING_LEGACY
            /* 设置总线速度模式为DS模式 */
            mmc_set_ios
            /* 将IO总线设置写入Host驱动, 调用mmc_host::mmc_host_ops::set_ios */
          __mmc_set_signal_voltage
          /* 设置信号电压, 依次尝试3.3V、1.8V、1.2V */
          mmc_host::mmc_ios::clock = mmc_host::f_init;
          /* 设置时钟频率 */ 
          mmc_host::mmc_ios::power_mode = MMC_POWER_ON
          /* 设置power状态为正常供电 */ 
          mmc_set_ios
          /* 将io相关设置写入Host驱动 */
        mmc_release_host
        /* 释放Host控制器 */
        mmc_gpiod_request_cd_irq
        /* 释放Host控制器 */
        _mmc_detect_change
        /* Card扫描 */
          mmc_host::detect_change = 1
          /* 设置Card检测标志 */
          mmc_schedule_delayed_work(mmc_host::detect);
          /* 调度工作队列, 调度函数为mmc_rescan */

    3.2 Card API

    /* 注册/注销MMC Card驱动 */
    int  mmc_register_driver(struct mmc_driver *);
    void mmc_unregister_driver(struct mmc_driver *);
    
    /* 注册/注销SDIO Card驱动 */
    int  sdio_register_driver(struct sdio_driver *);
    void sdio_unregister_driver(struct sdio_driver *);

    3.3 General API

    /* Card检测 */
    void mmc_detect_change(struct mmc_host *host, unsigned long delay);

    3.4 Command API

    cmd0 - mmc_go_idle
    cmd1 - mmc_send_op_cond
    cmd2 - mmc_all_send_cid

    4. MMC启动

    MMC在启动时会进行相应的初始化

    mmc_init
      mmc_register_bus
        bus_register                     /* 注册了mmc总线 */
      mmc_register_host_class
        class_register                   /* 注册'mmc_host'设备类 */
      sdio_register_bus
        bus_register                     /* 注册了sdio总线 */
    
    mmc_blk_init
      register_blkdev                    /* 注册mmc块设备 */
      mmc_register_driver
        driver_register                  /* 注册mmc card驱动 */

    5. MMC扫描

    MMC设备的发现通过mmc_rescan函数来实现,通过_mmc_detect_change/mmc_detect_change来触发
    mmc_rescan主要流程如下

    mmc_rescan
      mmc_host::mmc_host_ops::card_event
      mmc_host::mmc_bus_ops::detect
      mmc_host::mmc_host_ops::get_cd
      mmc_rescan_try_freq
      /* 使用不同时钟频率进行初始化 */
        mmc_power_up
        /* 设备供电 */
        mmc_hw_reset_for_init
        /* 针对部分eMMC(VCCQ一直为高) */
          mmc_host::mmc_host_ops::hw_reset
        sdio_reset
        /* 仅针对SDIO设备 */
        mmc_go_idle
        /* 发送CMD0命令 */
        mmc_send_if_cond
        /* 仅针对SD设备 */
        mmc_attach_sdio
        /* SDIO设备初始化 */
        mmc_attach_sd
        /* SD设备初始化 */
        mmc_attach_mmc
        /* MMC设备初始化 */
        mmc_power_off
        /* 当上述设备都不是则停止供电 */

    其中SDIO、SD、MMC的初始化流程各不相同

    mmc_attach_sdio主要流程如下

    /* SDIO Card初始化 */
    mmc_attach_sdio
      mmc_send_io_op_cond
      /* 发送SD_IO_SEND_OP_COND, 获取??? */
      mmc_attach_bus(mmc_sdio_ops)
      /* 将SDIO总线操作集分配给Host */
      host->ocr_avail = host->ocr_avail_sdio;
      /* 设置SDIO的OCR */
      mmc_select_voltage
      /* 选择合适的电压值 */
      mmc_sdio_init_card
      /* 识别和初始化SDIO Card */
        ......
      pm_runtime_set_active
      /* 设置Card运行时PM状态为活跃, 仅针对支持MMC_CAP_POWER_OFF_CARD特性的Host */
      pm_runtime_enable
      /* 使能Card运行时PM, 仅针对支持MMC_CAP_POWER_OFF_CARD特性的Host */
      ...... /* sdio functions related */
      mmc_add_card(mmc_host::mmc_card)
      /* 注册SDIO Card */
      sdio_add_func(mmc_host::mmc_card::sdio_func)
      /* 注册SDIO function */

    mmc_attach_sd主要流程如下

    /* SD Card初始化 */
    mmc_attach_sd
      mmc_send_app_op_cond
      /* 发送SD_APP_OP_COND, 获取??? */
      mmc_attach_bus(mmc_sd_ops)
      /* 将SD总线操作集分配给Host */
      host->ocr_avail = host->ocr_avail_sd;
      /* 设置SD的OCR */
      mmc_host_is_spi
      ->
        mmc_go_idle
        /* 发送CMD0 */
        mmc_spi_read_ocr
        /* 发送MMC_SPI_READ_OCR, 读取??? */
      mmc_select_voltage
      /* 选择合适的电压值 */
      mmc_sd_init_card 
      /* 识别和初始化SD Card */
        ......
      mmc_add_card(mmc_host::mmc_card)
      /* 注册SD Card */

    mmc_attach_mmc主要流程如下

    /* MMC Card初始化 */
    mmc_attach_mmc 
      mmc_send_op_cond
      /* 发送MMC_SEND_OP_COND, 获取??? */
      mmc_attach_bus(mmc_ops)
      /* 将MMC总线操作集分配给Host */
      host->ocr_avail = host->ocr_avail_mmc;
      /* 设置MMC的OCR */
      mmc_host_is_spi
      ->
        mmc_spi_read_ocr
        /* 发送MMC_SPI_READ_OCR, 读取??? */
      mmc_select_voltage
      /* 选择合适的电压值 */
      mmc_init_card 
      /* 识别和初始化MMC Card */
        ......
      mmc_add_card(mmc_host::mmc_card)
      /* 注册MMC Card */

    6. Host驱动

    Host驱动的编写主要步骤如下:

    1. 通过mmc_alloc_host分配一个mmc_host结构体
    2. 定义实现mmc_host_ops数据结构, 并赋值给上面的mmc_host::mmc_host_ops成员变量
    3. 给mmc_host成员变量赋值, 如ocr_avail、caps等成员变量
    4. 调用mmc_add_host注册该Host

    7. Card驱动

    对于Memory Card,内核实现了块设备驱动; 对于SDIO设备(比如WiFi), 则需要厂商实现(比如BCM的bcmdhd)

    参考:
    <ooonebook mmc>
    <WF111 Datasheet>
    <BCM4330 Datasheet>
    <MMC/SD卡驱动实例开发讲解>
    <SDIO Simplified Specification>

  • 相关阅读:
    【20220508】连岳摘抄
    Python PyWin32 模块
    C/C++ 反汇编:数据类型与常量
    Python 多线程实现爬取图片
    C/C++ 反汇编:流程控制与循环结构
    C/C++ 反汇编:函数与结构体
    PE结构:VA&RVA&FOA 转换复习
    理论:第五章:Spring和SpringMVC,MyBatis以及SpringBoot的注解分别有哪些?SpringMVC的工作原理,SpringBoot框架的优点,MyBatis框架的优点
    达梦数据库:第一章:MySQL数据库与达梦数据库的区别
    理论:第二章:Spring的AOP和IOC是什么?使用场景有哪些?Spring事务与数据库事务,传播行为,数据库隔离级别
  • 原文地址:https://www.cnblogs.com/hzl6255/p/9642451.html
Copyright © 2020-2023  润新知