linux MMC framework(4) - mmc host driver

  • 了解mmc host driver.

1.host相关数据结构

1.1.struct mmc_host

  struct mmc_host是mmc core由host controller抽象出来的结构体,用于代表一个mmc host控制器。

struct mmc_host {
    struct device       *parent;   // 对应的host controller的device
    struct device       class_dev;   // mmc_host的device结构体,会挂在class/mmc_host下
    int         index;   // 该host的索引号
    const struct mmc_host_ops *ops; // 该host的操作集,由host controller设置,后面说明
    unsigned int        f_min;   // 该host支持的最低频率
    unsigned int        f_max;   // 该host支持的最大频率
    unsigned int        f_init;   // 该host使用的初始化频率
    u32         ocr_avail;   // 该host可用的ocr值(电压相关)
    u32         ocr_avail_sdio; /* SDIO-specific OCR */
    u32         ocr_avail_sd;   /* SD-specific OCR */
    u32         ocr_avail_mmc;  /* MMC-specific OCR */
    struct notifier_block   pm_notify;
    u32         max_current_330;   // 3.3V时的最大电流
    u32         max_current_300;   // 3.0V时的最大电流
    u32         max_current_180;   // 1.8V时的最大电流
    u32         caps;       /* Host capabilities */   // host属性
    u32         caps2;      /* More host capabilities */   // host属性2
    mmc_pm_flag_t       pm_caps;    /* supported pm features */   // 电源管理属性

    int         clk_requests;   /* internal reference counter */
    unsigned int        clk_delay;  /* number of MCI clk hold cycles */
    bool            clk_gated;  /* clock gated */
    struct delayed_work clk_gate_work; /* delayed clock gate */
    unsigned int        clk_old;    /* old clock value cache */
    spinlock_t      clk_lock;   /* lock for clk fields */
    struct mutex        clk_gate_mutex; /* mutex for clock gating */
    struct device_attribute clkgate_delay_attr;
    unsigned long           clkgate_delay;

    /* host specific block data */   
    unsigned int        max_seg_size;   /* see blk_queue_max_segment_size */
    unsigned short      max_segs;   /* see blk_queue_max_segments */
    unsigned short      unused;
    unsigned int        max_req_size;   /* maximum number of bytes in one req */
    unsigned int        max_blk_size;   /* maximum size of one mmc block */
    unsigned int        max_blk_count;  /* maximum number of blocks in one req */
    unsigned int        max_discard_to; /* max. discard timeout in ms */

    /* private data */
    spinlock_t      lock;       /* lock for claim and bus ops */   // host的bus使用的锁
    struct mmc_ios      ios;        /* current io bus settings */   // io setting,后续说明
    u32         ocr;        /* the current OCR setting */   // 当前使用的ocr的值

    /* group bitfields together to minimize padding */
    unsigned int        use_spi_crc:1;
    unsigned int        claimed:1;  /* host exclusively claimed */   // host是否已经被占用
    unsigned int        bus_dead:1; /* bus has been released */   // host的bus是否处于激活状态

    int         rescan_disable; /* disable card detection */   // 禁止rescan的标识,禁止搜索card
    int         rescan_entered; /* used with nonremovable devices */   // 是否已经rescan过的标识,对应不可移除的设备只能rescan一次

    struct mmc_card     *card;      /* device attached to this host */   // 和该host绑定在一起的card

    wait_queue_head_t   wq;
    struct task_struct  *claimer;   /* task that has host claimed */   // 该host的占有者进程
    struct task_struct  *suspend_task;
    int         claim_cnt;  /* "claim" nesting count */   // 占有者进程对该host的占用计数

    struct delayed_work detect;   // 检测卡槽变化的工作
    struct wake_lock    detect_wake_lock;   // 检测卡槽变化的工作使用的锁
    const char      *wlock_name;   // 锁名称
    int         detect_change;  /* card detect flag */ // 需要检测卡槽变化的标识
    struct mmc_slot     slot;   // 卡槽的结构体

    const struct mmc_bus_ops *bus_ops;  /* current bus driver */   // host的mmc总线的操作集,后面说明
    unsigned int        bus_refs;   /* reference counter */   // host的mmc总线的使用计数
    unsigned int        bus_resume_flags;   // host的mmc总线的resume标识

    mmc_pm_flag_t       pm_flags;   /* requested pm features */

#ifdef CONFIG_REGULATOR
    bool            regulator_enabled; /* regulator state */   // 代表regulator(LDO)的状态
#endif
    struct mmc_supply   supply;

    struct dentry       *debugfs_root;   // 对应的debug目录结构体

    struct mmc_async_req    *areq;      /* active async req */   // 当前正在处理的异步请求
    struct mmc_context_info context_info;   /* async synchronization info */ // 异步请求的信息

    unsigned int        actual_clock;   /* Actual HC clock rate */ // 实际的时钟频率
};
  • ocr值各个位代表的电压意义如下:
#define MMC_VDD_165_195     0x00000080  /* VDD voltage 1.65 - 1.95 */
#define MMC_VDD_20_21       0x00000100  /* VDD voltage 2.0 ~ 2.1 */
#define MMC_VDD_21_22       0x00000200  /* VDD voltage 2.1 ~ 2.2 */
#define MMC_VDD_22_23       0x00000400  /* VDD voltage 2.2 ~ 2.3 */
#define MMC_VDD_23_24       0x00000800  /* VDD voltage 2.3 ~ 2.4 */
#define MMC_VDD_24_25       0x00001000  /* VDD voltage 2.4 ~ 2.5 */
#define MMC_VDD_25_26       0x00002000  /* VDD voltage 2.5 ~ 2.6 */
#define MMC_VDD_26_27       0x00004000  /* VDD voltage 2.6 ~ 2.7 */
#define MMC_VDD_27_28       0x00008000  /* VDD voltage 2.7 ~ 2.8 */
#define MMC_VDD_28_29       0x00010000  /* VDD voltage 2.8 ~ 2.9 */
#define MMC_VDD_29_30       0x00020000  /* VDD voltage 2.9 ~ 3.0 */
#define MMC_VDD_30_31       0x00040000  /* VDD voltage 3.0 ~ 3.1 */
#define MMC_VDD_31_32       0x00080000  /* VDD voltage 3.1 ~ 3.2 */
#define MMC_VDD_32_33       0x00100000  /* VDD voltage 3.2 ~ 3.3 */
#define MMC_VDD_33_34       0x00200000  /* VDD voltage 3.3 ~ 3.4 */
#define MMC_VDD_34_35       0x00400000  /* VDD voltage 3.4 ~ 3.5 */
#define MMC_VDD_35_36       0x00800000  /* VDD voltage 3.5 ~ 3.6 */
  • host属性(mmc_host->caps)支持的属性如下:
#define MMC_CAP_4_BIT_DATA  (1 << 0)    /* Can the host do 4 bit transfers */
#define MMC_CAP_MMC_HIGHSPEED   (1 << 1)    /* Can do MMC high-speed timing */
#define MMC_CAP_SD_HIGHSPEED    (1 << 2)    /* Can do SD high-speed timing */
#define MMC_CAP_SDIO_IRQ    (1 << 3)    /* Can signal pending SDIO IRQs */
#define MMC_CAP_SPI     (1 << 4)    /* Talks only SPI protocols */
#define MMC_CAP_NEEDS_POLL  (1 << 5)    /* Needs polling for card-detection */
#define MMC_CAP_8_BIT_DATA  (1 << 6)    /* Can the host do 8 bit transfers */
#define MMC_CAP_NONREMOVABLE    (1 << 8)    /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9)    /* Waits while card is busy */
#define MMC_CAP_ERASE       (1 << 10)   /* Allow erase/trim commands */
#define MMC_CAP_1_8V_DDR    (1 << 11)   /* can support */
                        /* DDR mode at 1.8V */
#define MMC_CAP_1_2V_DDR    (1 << 12)   /* can support */
                        /* DDR mode at 1.2V */
#define MMC_CAP_HSDDR       (MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR)
#define MMC_CAP_POWER_OFF_CARD  (1 << 13)   /* Can power off after boot */
#define MMC_CAP_BUS_WIDTH_TEST  (1 << 14)   /* CMD14/CMD19 bus width ok */
#define MMC_CAP_UHS_SDR12   (1 << 15)   /* Host supports UHS SDR12 mode */
#define MMC_CAP_UHS_SDR25   (1 << 16)   /* Host supports UHS SDR25 mode */
#define MMC_CAP_UHS_SDR50   (1 << 17)   /* Host supports UHS SDR50 mode */
#define MMC_CAP_UHS_SDR104  (1 << 18)   /* Host supports UHS SDR104 mode */
#define MMC_CAP_UHS_DDR50   (1 << 19)   /* Host supports UHS DDR50 mode */
#define MMC_CAP_DRIVER_TYPE_A   (1 << 23)   /* Host supports Driver Type A */
#define MMC_CAP_DRIVER_TYPE_C   (1 << 24)   /* Host supports Driver Type C */
#define MMC_CAP_DRIVER_TYPE_D   (1 << 25)   /* Host supports Driver Type D */
#define MMC_CAP_CMD23       (1 << 30)   /* CMD23 supported. */
#define MMC_CAP_HW_RESET    (1 << 31)   /* Hardware reset */
  • host属性2(mmc_host->caps2)支持的属性如下:
#define MMC_CAP2_BOOTPART_NOACC (1 << 0)    /* Boot partition no access */
#define MMC_CAP2_CACHE_CTRL (1 << 1)    /* Allow cache control */
#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)   /* Notify poweroff supported */
#define MMC_CAP2_NO_MULTI_READ  (1 << 3)    /* Multiblock reads don't work */
#define MMC_CAP2_NO_SLEEP_CMD   (1 << 4)    /* Don't allow sleep command */
#define MMC_CAP2_HS200_1_8V_SDR (1 << 5)        /* can support */
#define MMC_CAP2_HS200_1_2V_SDR (1 << 6)        /* can support */
#define MMC_CAP2_HS200      (MMC_CAP2_HS200_1_8V_SDR | \
                 MMC_CAP2_HS200_1_2V_SDR)
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7)    /* Use the broken voltage */
#define MMC_CAP2_DETECT_ON_ERR  (1 << 8)    /* On I/O err check card removal */
#define MMC_CAP2_HC_ERASE_SZ    (1 << 9)    /* High-capacity erase size */
#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10)   /* Card-detect signal active high */
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11)   /* Write-protect signal active high */
#define MMC_CAP2_PACKED_RD  (1 << 12)   /* Allow packed read */
#define MMC_CAP2_PACKED_WR  (1 << 13)   /* Allow packed write */
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
                 MMC_CAP2_PACKED_WR)
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14)   /* Don't power up before scan */
#define MMC_CAP2_INIT_BKOPS     (1 << 15)   /* Need to set BKOPS_EN */
#define MMC_CAP2_PACKED_WR_CONTROL (1 << 16) /* Allow write packing control */
#define MMC_CAP2_CLK_SCALE  (1 << 17)   /* Allow dynamic clk scaling */
#define MMC_CAP2_STOP_REQUEST   (1 << 18)   /* Allow stop ongoing request */
/* Use runtime PM framework provided by MMC core */
#define MMC_CAP2_CORE_RUNTIME_PM (1 << 19)
#define MMC_CAP2_SANITIZE   (1 << 20)       /* Support Sanitize */
/* Allows Asynchronous SDIO irq while card is in 4-bit mode */
#define MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE (1 << 21)

#define MMC_CAP2_HS400_1_8V (1 << 22)        /* can support */
#define MMC_CAP2_HS400_1_2V (1 << 23)        /* can support */
#define MMC_CAP2_CORE_PM       (1 << 24)       /* use PM framework */
#define MMC_CAP2_HS400      (MMC_CAP2_HS400_1_8V | \
                 MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_NONHOTPLUG (1 << 25)   /*Don't support hotplug*/

1.2.struct mmc_host_ops

  mmc core将host需要提供的一些操作方法封装成struct mmc_host_ops。mmc core主模块的很多接口都是基于这里面的操作方法来实现的,通过这些方法来操作host硬件达到对应的目的。 所以struct mmc_host_ops也是host controller driver需要实现的核心部分。

struct mmc_host_ops {
    /*
     * 'enable' is called when the host is claimed and 'disable' is called
     * when the host is released. 'enable' and 'disable' are deprecated.
     */
    int (*enable)(struct mmc_host *host);   // 使能host,当host被占用时(第一次调用mmc_claim_host)调用
    int (*disable)(struct mmc_host *host);   // 禁用host,当host被释放时(第一次调用mmc_release_host)调用
    /*
     * It is optional for the host to implement pre_req and post_req in
     * order to support double buffering of requests (prepare one
     * request while another request is active).
     * pre_req() must always be followed by a post_req().
     * To undo a call made to pre_req(), call post_req() with
     * a nonzero err condition.
     */
        // post_req和pre_req是为了实现异步请求处理而设置的
        // 异步请求处理就是指,当另外一个异步请求还没有处理完成的时候,可以先准备另外一个异步请求而不必等待
    void    (*post_req)(struct mmc_host *host, struct mmc_request *req,
                int err);
    void    (*pre_req)(struct mmc_host *host, struct mmc_request *req,
               bool is_first_req);
    void    (*request)(struct mmc_host *host, struct mmc_request *req); // host处理mmc请求的方法,在mmc_start_request中会调用

    void    (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);   // 设置host的总线的io setting
    int (*get_ro)(struct mmc_host *host);   // 获取host上的card的读写属性
    int (*get_cd)(struct mmc_host *host);   // 检测host的卡槽中card的插入状态

    /* optional callback for HC quirks */
    void    (*init_card)(struct mmc_host *host, struct mmc_card *card);   // 初始化card的方法

    int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);   // 切换信号电压的方法

    /* Check if the card is pulling dat[0:3] low */
    int (*card_busy)(struct mmc_host *host);   // 用于检测card是否处于busy状态

    /* The tuning command opcode value is different for SD and eMMC cards */
    int (*execute_tuning)(struct mmc_host *host, u32 opcode);   // 执行tuning操作,为card选择一个合适的采样点
    int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);   // 选择信号的驱动强度
    void    (*hw_reset)(struct mmc_host *host);   // 硬件复位
    void    (*card_event)(struct mmc_host *host);   // 
    unsigned long (*get_max_frequency)(struct mmc_host *host); // 获取host支持的最大频率的方法
    unsigned long (*get_min_frequency)(struct mmc_host *host);   // 获取host支持的最小频率的方法
    int (*notify_load)(struct mmc_host *, enum mmc_load);
    int (*stop_request)(struct mmc_host *host);   // 停止请求处理的方法
    unsigned int    (*get_xfer_remain)(struct mmc_host *host);
};

1.3.mmc_host_class

  mmc_host_class代表了mmc_host这个类。其内容如下:

static struct class mmc_host_class = {
    .name       = "mmc_host",        // 添加到sys文件系统之后,会生成/sys/class/mmc_host这个目录
    .dev_release    = mmc_host_classdev_release,    // 从mmc_host这个class下release掉某个设备之后要做的对应操作
};

2.APIs

2.1.mmc host分配、释放

struct mmc_host *mmc_alloc_host(int extra, struct device *dev);
void mmc_free_host(struct mmc_host *host)
  • 用来分配或者释放一个struct mmc_host结构体,将其于mmc_host_class关联,并且做部分初始化操作。

2.2.mmc host 注册、卸载

int mmc_add_host(struct mmc_host *host);
void mmc_remove_host(struct mmc_host *host);
  • 注册或者卸载mmc_host到设备驱动中,添加到sys类下面,并设置相应的debug目录,然后启动mmc_host。

2.3.mmc host class 注册、卸载

int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
  • 注册或者卸载mmc_host class

2.4.mmc host 属性解析(关键函数)

void mmc_of_parse(struct mmc_host *host);
  • 底层host controller驱动调用,解析mmc_host的dtsi节点的部分属性。

3.Interface

3.1.mmc_register_host_class

int mmc_register_host_class(void)
{
    return class_register(&mmc_host_class);   // 以mmc_host_class为class创建一个class,关于mmc_host_class在上述数据结构已经说明过了
}
  • /sys/class/mmc_host

3.2.mmc_alloc_host

主要工作:

  • 分配内存空间
  • 初始化其class device(对应/sys/class/mmc0节点)
  • clock gate、锁、工作队列、wakelock、detect工作的初始化
  • 初始化detect成员(也就是检测工作)为mmc_rescan
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
    int err;
    struct mmc_host *host;

  	/* 分配内存空间,其中多分配了extra字节作为私有数据 */
    host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
        
	/* 因为只是分配了一个mmc_host,host还没有准备好,所以这里禁用rescan,也就是设置mmc_host->rescan_disable */
    host->rescan_disable = 1;  			 // mmc_start_host中会去使能
    
	/* 为该mmc_host分配一个唯一的id号,设置到host->index */
    idr_preload(GFP_KERNEL);
    spin_lock(&mmc_host_lock);
    err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);
    if (err >= 0)
        host->index = err;
    spin_unlock(&mmc_host_lock);
    idr_preload_end();

    dev_set_name(&host->class_dev, "mmc%d", host->index);   // 以mmc_host的id号构成mmc_host的name,例如mmc0、mmc1

    host->parent = dev;  
    host->class_dev.parent = dev;             
    host->class_dev.class = &mmc_host_class;
    // 将mmc_device(class_dev)的类设置为mmc_host_class
    // 注册到sysfs之后,会相应生成/sys/class/mmc_host/mmc0
   
    device_initialize(&host->class_dev);   // 初始化mmc_host->class_dev
    device_enable_async_suspend(&host->class_dev);
    
    spin_lock_init(&host->lock);
    init_waitqueue_head(&host->wq);
    
 	// 初始化detect工作为mmc_rescan,后续调度host->detect来检测是否有card插入时,就会调用到mmc_rescan。
    INIT_DELAYED_WORK(&host->detect, mmc_rescan);
    INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
    timer_setup(&host->retune_timer, mmc_retune_timer, 0);
    
    /* 一些size的初始化 */
    host->max_segs = 1;   // 初始化最大支持段(由host自己根据硬件进行修改),可以通过/sys/block/mmcblk0/queue/max_segments进行修改
    host->max_seg_size = PAGE_SIZE;   // 初始化段大小,(由host自己根据硬件进行修改)
    host->max_req_size = PAGE_SIZE;   // 一次MMC请求的最大字节数
    host->max_blk_size = 512;   // 一个块的最大字节数
    host->max_blk_count = PAGE_SIZE / 512; // 一次MMC请求的最大块数量

    return host;
}

重点如下:

mmc_alloc_host
	-> INIT_DELAYED_WORK(&host->detect, mmc_rescan);   //mmc_rescan    //用来做卡的检测

何时调用该work? 如下所示。

3.3.mmc_add_host实现

主要工作:

  • 将mmc_host的class_dev添加到设备驱动模型中,在sysfs中生成相应的节点
  • 初始化mmc_host相关的debug目录
  • 调用mmc_start_host启动host,调用mmc_rescan
int mmc_add_host(struct mmc_host *host)
{
    int err;

   /* 将mmc_host->class_dev添加到设备驱动模型中,在sys下生成相应节点 */
    err = device_add(&host->class_dev);
    
    led_trigger_register_simple(dev_name(&host->class_dev), &host->led);

	/*对应sys节点为/sys/kernel/debug/mmc0 */
#ifdef CONFIG_DEBUG_FS
    mmc_add_host_debugfs(host);
#endif
	
  mmc_start_host(host);
  mmc_register_pm_notifier(host); 
  return 0;
}

3.3.1.创建节点:

  • /sys/class/mmc_host/mmc0
  • /sys/kernel/debug/mmc0
  • /sys/bus/platform/devices/d4280000.sdhci/mmc_host/mmc0

3.3.2.mmc_start_host

  在mmc_alloc_host中初始化一个work,即mmc_rescan。
在这里插入图片描述
  其中初始化了一个work节点,mmc_rescan这个函数比较重要,用来做卡的检测。

  mmc_add_host向系统添加一个mmc_host,并调用mmc_start_host

void mmc_start_host(struct mmc_host *host)
{
	host->f_init = max(freqs[0], host->f_min);
	host->rescan_disable = 0;
	host->ios.power_mode = MMC_POWER_UNDEFINED;

	if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) {
		mmc_claim_host(host);
		mmc_power_up(host, host->ocr_avail);
		mmc_release_host(host);
	}

	mmc_gpiod_request_cd_irq(host);
	_mmc_detect_change(host, 0, false);
}

SD dts配置:

&sdhc_2 {
         #address-cells = <0>;
        interrupt-parent = <&sdhc_2>;
        interrupts = <0 1 2>;
        #interrupt-cells = <1>;
        interrupt-map-mask = <0xffffffff>;
        interrupt-map = <0 &intc 0 125 0
                        1 &intc 0 221 0
                        2 &msm_gpio 38 0>;
        interrupt-names = "hc_irq", "pwr_irq", "status_irq";
        vqmmc-supply = <&reg_eldo1>;
        cd-gpios = <&msm_gpio 38 0x1>;           //0x1 - 表示插卡低有效

电压设置:

vqmmc-supply = <&reg_eldo1>;

reg_eldo1:eldo1 {
	regulator-name = "vddio-sdmmc";
	regulator-min-mincrovolt=<1800000>;
	regulator-max-mincrovolt=<1800000>;
}
函数调用流程:
sdhci_setup_host:
->mmc_regulator_get_supply         //通过device tree获取vmmc和vqmmc

SD检卡中断检测:

Card detection:
	If no property below is supplied, host native card detect is used.
Only one of the properties in this section should be supplied:
	broken-cd: There is no card detection available; polling must be used.
	cd-gpios: Specify GPIOs for card detection, see gpio binding
	non-removable: non-removable slot (like eMMC); assume always present.
  • cd-gpios = <&msm_gpio 38 0x1>; // 最后一位,需要根据实际情况进行设置;
#define GPIO_ACTIVE_HIGH 0 
#define GPIO_ACTIVE_LOW 1

内核提供两种方式进行sd card detection:

  • 轮训方式(sdhci_setup_host)
/*   
 * Enable polling on when card detection is broken and no card detect
 * gpio is present.
  */
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
	    mmc_card_is_removable(mmc) &&
	    mmc_gpio_get_cd(host->mmc) < 0)
		mmc->caps |= MMC_CAP_NEEDS_POLL;
  • gpio中断检卡方式(mmc_of_parse):(cat /proc/interrupts )
	/* Parse Card Detection */
	if (device_property_read_bool(dev, "non-removable")) {
		host->caps |= MMC_CAP_NONREMOVABLE;
	} else {
		cd_cap_invert = device_property_read_bool(dev, "cd-inverted");

		if (device_property_read_bool(dev, "broken-cd"))
			host->caps |= MMC_CAP_NEEDS_POLL;

		ret = mmc_gpiod_request_cd(host, "cd", 0, true,
					   0, &cd_gpio_invert);
		if (!ret)
			dev_info(host->parent, "Got CD GPIO\n");
		else if (ret != -ENOENT && ret != -ENOSYS)
			return ret;

		if (cd_cap_invert ^ cd_gpio_invert)
			host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
	}

中断注册:mmc_gpiod_request_cd_irq:

void mmc_gpiod_request_cd_irq(struct mmc_host *host)
{
	struct mmc_gpio *ctx = host->slot.handler_priv;
	int ret, irq;

	if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
		return;

	irq = gpiod_to_irq(ctx->cd_gpio);

	if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
		irq = -EINVAL;

	if (irq >= 0) {
		if (!ctx->cd_gpio_isr)
			ctx->cd_gpio_isr = mmc_gpio_cd_irqt;
		ret = devm_request_threaded_irq(host->parent, irq,
			NULL, ctx->cd_gpio_isr,
			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
			ctx->cd_label, host);
		if (ret < 0)
			irq = ret;
	}

	host->slot.cd_irq = irq;

	if (irq < 0)
		host->caps |= MMC_CAP_NEEDS_POLL;
	else if ((host->caps & MMC_CAP_CD_WAKE) && !enable_irq_wake(irq))
		host->slot.cd_wake_enabled = true;
}

//中断处理mmc_gpio_cd_irqt:
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
{
        /* Schedule a card detection after a debounce timeout */
        struct mmc_host *host = dev_id;

        host->trigger_card_event = true;
        mmc_detect_change(host, msecs_to_jiffies(200));

        return IRQ_HANDLED;
}

  mmc_detect_change会调用_mmc_detect_change ,如下所示:
在这里插入图片描述
  mmc_schedule_delayed_work(&host->detect, delay);向work_queue提交一个work来检测卡,具体的检测函数就是mmc_rescan
在这里插入图片描述
  如果设置了MMC_CAP_NONREMOVABLE,表示host不支持热插拔卡,否则调用mmc_host_ops的get_cd检查是否有卡,如果有卡,则调用mmc_rescan_try_freq继续探测,如果探测到总线上挂有有效的设备,按照设备是否支持相应协议命令来区分sdio设备,SD卡和MMC卡。

  • 如果是SDID,调用mmc_attach_sdio;
  • 如果是SD,调用mmc_attach_sd;
  • 如果是MMC,调用mmc_attach_mmc;

调用如下所示:
在这里插入图片描述

2444 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
2445 {                                                                                                       
2446     host->f_init = freq;
2451     mmc_power_up(host, host->ocr_avail);
2452 
2453     /*
2454      * Some eMMCs (with VCCQ always on) may not be reset after power up, so
2455      * do a hardware reset if possible.
2456      */
2457     mmc_hw_reset_for_init(host);
2458 
2459     /*
2460      * sdio_reset sends CMD52 to reset card.  Since we do not know
2461      * if the card is being re-initialized, just send it.  CMD52
2462      * should be ignored by SD/eMMC cards.
2463      * Skip it if we already know that we do not support SDIO commands
2464      */
2465     if (!(host->caps2 & MMC_CAP2_NO_SDIO))
2466         sdio_reset(host);
2468     mmc_go_idle(host);
2469 
2470     if (!(host->caps2 & MMC_CAP2_NO_SD))
2471         mmc_send_if_cond(host, host->ocr_avail);
2472 
2473     /* Order's important: probe SDIO, then SD, then MMC */
2474     if (!(host->caps2 & MMC_CAP2_NO_SDIO))
2475         if (!mmc_attach_sdio(host))
2476             return 0;
2477 
2478     if (!(host->caps2 & MMC_CAP2_NO_SD))
2479         if (!mmc_attach_sd(host))
2480             return 0;
2481 
2482     if (!(host->caps2 & MMC_CAP2_NO_MMC))
2483         if (!mmc_attach_mmc(host))
2484             return 0;
2485 
2486     mmc_power_off(host);                                                                                
2487     return -EIO;
2488 }
  • mmc_go_idle函数:发送CMD0使卡进入IDLE状态
  • mmc_send_if_cond函数:发送CMD8,检查卡是否SD2.0。SD1.1是不支持CMD8的,因此在SD2.0 Spec中提出先发送CMD8,如响应为无效命令,则卡为SD1.1,否则就是SD2.0。
  • mmc_send_app_op_cond:循环发送CMD55、ACMD41给卡读取OCR寄存器。

3.4.调用mmc:mmc_attach_mmc

2196 int mmc_attach_mmc(struct mmc_host *host)
2197 {                                                                                                       
2198     int err;
2199     u32 ocr, rocr;
2202 
2203     /* Set correct bus mode for MMC before attempting attach */
2204     if (!mmc_host_is_spi(host))
2205         mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
2206 
2207     err = mmc_send_op_cond(host, 0, &ocr);
2210 
2211     mmc_attach_bus(host, &mmc_ops);
2212     if (host->ocr_avail_mmc)
2213         host->ocr_avail = host->ocr_avail_mmc;
2214 
2218     if (mmc_host_is_spi(host)) {
2219         err = mmc_spi_read_ocr(host, 1, &ocr);
2221             goto err;
2222     }
2223 
2224     rocr = mmc_select_voltage(host, ocr);
2237     err = mmc_init_card(host, rocr, NULL);
2240 
2241     mmc_release_host(host);                                                                             
2242     err = mmc_add_card(host->card);
2245 
2246     mmc_claim_host(host);
2247     return 0;
2248 }
  • 调用mmc_init_card,完成card的初始化;
  • 调用mmc_add_card将mmc_card注册到mmc bus,这样就触发了mmc_blk_probe函数。

3.5.调用sdcard:mmc_attach_sd

int mmc_attach_sd(struct mmc_host *host)
{
	int err;
	u32 ocr, rocr;
	
	err = mmc_send_app_op_cond(host, 0, &ocr);
	mmc_attach_bus(host, &mmc_sd_ops);
	if (host->ocr_avail_sd)
		host->ocr_avail = host->ocr_avail_sd;

	if (mmc_host_is_spi(host)) {
		mmc_go_idle(host);

		err = mmc_spi_read_ocr(host, 0, &ocr);
		if (err)
			goto err;
	}


	ocr &= ~0x7FFF;
	rocr = mmc_select_voltage(host, ocr);

	err = mmc_sd_init_card(host, rocr, NULL);
	mmc_release_host(host);
	err = mmc_add_card(host->card);

	mmc_claim_host(host);
	return 0;
}

3.5.1.代码分析:

  • mmc_attach_bus(host, &mmc_sd_ops);
// sd卡的总线操作 core/sd.c
static const struct mmc_bus_ops mmc_sd_ops = {
    .remove = mmc_sd_remove,
    .detect = mmc_sd_detect,
    .sysfs_add = mmc_sd_sysfs_add,
    .sysfs_remove = mmc_sd_sysfs_remove,
    .suspend = mmc_sd_suspend,
    .resume = mmc_sd_resume,
};

.detect,驱动程序调用此函数去检测mmc卡的状态,具体实现是发送CMD13命令,并读回响应,如果响应错误,则依次调用.remove、detach_bus来移除卡及释放总线。
  • mmc_sd_init_card

    • mmc_sd_get_cid(host, ocr, cid, &rocr); //发送 CMD2获取卡的身份信息,进入到身份状态
    • mmc_alloc_card(host, &sd_type); //分配一张 SD 类型的 card 结构
    • mmc_send_relative_addr(host, &card->rca); //获取卡的相对地址
    • mmc_sd_get_csd(host, card); //CMD9, 获取 CSD 寄存器的信息,包括 block 长度,卡容量等信息
    • mmc_select_card(card); //发送 CMD7, 选中目前 RADD 地址上的卡,任何时候总线上只有一张卡被选中,进入了传输状态
    • mmc_sd_setup_card(host, card, oldcard != NULL); //发送ACMD51获取SCR寄存器值,发送ACMD13获取SD卡状态信息,解析并填充card结构,SCR寄存器是对CSD的补充。
    • mmc_sd_init_uhs_card(card); //切换UHS-I mode
  • 调用mmc_add_card()把 mmc_card 挂载到 mmc_bus_type 总线去 。

3.5.2.mmc_sd_get_cid

 738 int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
   739 {                                                                                                     
   740     int err;
   741     u32 max_current;
   742     int retries = 10;
   743     u32 pocr = ocr;
   744 
   745 try_again:
   746     if (!retries) {
   747         ocr &= ~SD_OCR_S18R;
   748         pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
   749     }
   750 
   757     mmc_go_idle(host);   //发送CMD0
   758 
   765     err = mmc_send_if_cond(host, ocr);  //发送CMD8,
   766     if (!err)                                                                                         
   767         ocr |= SD_OCR_CCS;
   768 
   769     /*
   770      * If the host supports one of UHS-I modes, request the card
   771      * to switch to 1.8V signaling level. If the card has failed
    772      * repeatedly to switch however, skip this.
   773      */
   774     if (retries && mmc_host_uhs(host))  
   775         ocr |= SD_OCR_S18R;
   776 
   781     max_current = sd_get_host_max_current(host);
   782     if (max_current > 150)
   783         ocr |= SD_OCR_XPC;
   784 
   785     err = mmc_send_app_op_cond(host, ocr, rocr);     //发送ACMD41                                                   
   786     if (err)
   787         return err;
   788 
   789     /*
   790      * In case CCS and S18A in the response is set, start Signal Voltage
      791      * Switch procedure. SPI mode doesn't support CMD11.
   792      */
   793     if (!mmc_host_is_spi(host) && rocr &&
   794        ((*rocr & 0x41000000) == 0x41000000)) {
   795         err = mmc_set_uhs_voltage(host, pocr);  //发送CMD11切换,切换电压
   796         if (err == -EAGAIN) {
   797             retries--;
   798             goto try_again;
   799         } else if (err) {
   800             retries = 0;
   801             goto try_again;
   802         }
   803     }                                                                                                 
   804 
   805     err = mmc_send_cid(host, cid); /发送CMD2,获取cid
   806     return err;
   807 }

流程图如下所示:
在这里插入图片描述
重点分析如何切换1.8v电压:

mmc_sd_get_cid
  ->mmc_set_uhs_voltage
  -->先close clock
  -->mmc_set_signal_voltage
    ->start_signal_voltage_switch //驱动自定义,可参考sdhci_start_signal_voltage_switch
    ->再open clock

3.5.3.UFS-I mode

  UHS-I provides up to 104MB/sec performance on 4-bit SD bus with the single end driver interface. Card form factor is the same and existing connector can be used.

3.5.2.1.UHS-I Card Operation Modes
在这里插入图片描述
3.5.2.2.UHS-I Card 初始化流程
在这里插入图片描述
3.5.2.3.代码分析

static int mmc_sd_init_uhs_card(struct mmc_card *card)
{
    int err;
    u8 *status;

   //sd卡3.0版本才加入的UHS-I
    if (!card->scr.sda_spec3)
        return 0;
        
    //判断是否支持class10命令,CMD6属于该类
    if (!(card->csd.cmdclass & CCC_SWITCH))
        return 0;

    status = kmalloc(64, GFP_KERNEL);//CMD6的应答
    if (!status) {
        pr_err("%s: could not allocate a buffer for "
            "switch capabilities.\n", mmc_hostname(card->host));
        return -ENOMEM;
    }

    /* Set 4-bit bus width */
    if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&     //host是否支持4位数据线宽度
        (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {     //卡是否支持4位数据线宽度模式
        err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);  //发送ACMD6切换
        mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);   //host端的设置.
    }

    /*这个函数是找一个card和host都支持的速度,类似上面对数据线宽度的操作,期望找到一个host和card支持的最快的速度.*/
    sd_update_bus_speed_mode(card);

	/*driver strength在spec中没找到详细的说明,spec中流程图里有这个步骤.这个也是3.0才增加的,sd卡这边切换用的是CMD6, driver strength属于CMD6的function group 3*/
    err = sd_select_driver_type(card, status);

	/*这个函数里面注释比较清楚了,根据电压和速度模式,设置卡的最大功率,属于CMD6的function group 4*/
    err = sd_set_current_limit(card, status);

    /* Set bus speed mode of the card */
    err = sd_set_bus_speed_mode(card, status);/*设置速度,属于CMD6的function group 1*/

    /* SPI mode doesn't define CMD19 */
    if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) {
        mmc_host_clk_hold(card->host);
        err = card->host->ops->execute_tuning(card->host,
                              MMC_SEND_TUNING_BLOCK);
        mmc_host_clk_release(card->host);
    }

out:
    kfree(status);

    return err;
}

4.SD卡的启动过程

  根据SD2.0协议,SD卡的状态可分为两种模式:卡识别模式(card-identification mode)和数据传输模式(data-transfer mode)。这里,关注启动SD卡的卡识别模式。
在这里插入图片描述

  • 1.发送指令CMD0使卡设备处于idle状态;
  • 2.发送指令CMD8,如果卡设备有response,说明此卡为SD2.0以上;
  • 3.发送指令CMD55+ACMD41,该指令是用来探测卡设备的工作电压是否符合host端的要求;在发送ACMD41这类指令之前需要先发送CMD55指令,在SDIO中ACMD41指令被CMD5替代。
  • 4.发送指令CMD11转换工作电压到1.8V;
  • 5.发送指令CMD2获取CIA;
  • 6.发送指令CMD3获取RCA(relative card address)

5.mmc power sequence framework

add generic power sequence framework:
We have an well-known problem that the device needs to do some power
sequence before it can be recognized by related host, the typical
example like hard-wired mmc devices and usb devices. This power
sequence is hard to be described at device tree and handled by
related host driver, so we have created a common power sequence
framework to handle this requirement. The generic code is supplied
some common helpers from host driver, and individual power sequence
driver handles kinds of power sequence for devices.

Since the MMC has already done the similar things, and this power
sequence handling can be generic, we use mmc power sequence code
as base to create this framework.

This patch set is based on Krzysztof Kozlowski's RFC patch set (v4.7-rc1)
[1], and making some changes which can let it be generic. After that,
we create a generic power sequence driver for USB devices which handles
below things, it includes all input signals for devices I can consider.

- Clock and its frequencies
- GPIO for reset and the duration time
- GPIO for enable
- Regulator for power
  • drivers/mmc/core/pwrseq_emmc.c
  • https://lwn.net/Articles/691877/

小结:

mmc_rescan(struct work_struct *work)
  -> mmc_rescan_try_freq(host, max(freqs[i], host->f_min))
    -> mmc_attach_sdio(host)    /* 检测卡的类型 */
    -> mmc_attach_sd(host)
    -> mmc_attach_mmc(host)
      -> mmc_send_op_cond(host, 0, &ocr);         /* 发送卡的ID */
      -> mmc_init_card(host, host->ocr, NULL);    /* 初始化mmc_card */
        -> card = mmc_alloc_card(host, &mmc_type);
          -> device_initialize(&card->dev);
          -> card->dev.bus = &mmc_bus_type;   /* 设置总线为mmc_bus_type */
        -> card->type = MMC_TYPE_MMC;         /* 设置card结构体 */
      -> mmc_release_host(host);
      -> mmc_add_card(host->card);            /* 添加卡mmc_card */
        -> device_add(&card->dev);
      -> mmc_claim_host(host);                /* 使能host */

6.Debug

6.1.调试sd 卡插拔中断问题

1>.dts 添加如下:

cd-inverted;
cd-gpios = <&gpio 65 0x0>;       //最后一位配置为0,还是1,需要根据实际情况设置。      

2>.gpio65 作为sd 卡插拔检测引脚,原理图如下所示:
在这里插入图片描述
  TF_CD连接到GPIO_65,使用1.8v 上拉,即默认如果不插卡,D7电压测试点为1.8v,当插入卡后,D7测试点为0v。软件需要配置GPIO_65为pull up,如下所示:

config_pull_up:config_pull_up {
	bias-pull-up;
};

sd_cd: sd_cd {
	xxx,pins = <gpio65 1 &config_pull_up>;
}

refer to

  • http://www.codexiu.cn/linux/blog/7520/
  • https://www.cnblogs.com/lihuidashen/p/6112169.html
发布了161 篇原创文章 · 获赞 15 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_41028621/article/details/103787137
今日推荐