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>

posted @ 2018-09-13 19:12  北落不吉  阅读(7527)  评论(1编辑  收藏  举报