zoukankan      html  css  js  c++  java
  • 8. [mmc subsystem] host(第二章)——sdhci

    一、sdhci core说明

    1、sdhci说明

    具体参考《host(第一章)——概述》

    SDHC:Secure Digital(SD) Host Controller,是指一套sd host控制器的设计标准,其寄存器偏移以及意义都有一定的规范,并且提供了对应的驱动程序,方便vendor进行host controller的开发。

    vendor按照这套标准设计host controller之后,可以直接使用sdhci driver来实现host controller的使用,(qcom和samsung都使用了这套标准)。而vendor只需要实现平台相关的部分、如clock、pinctrl、power等等的部分即可。

    关于这个标准,我们可以参考《SDHC_Ver3.00_Final_110225》。

    注意,强调一下,这是一种mmc host controller的设计标准,其本质上还是属于mmc host。并且,其兼容mmc type card,而不是说只能使用于sd type card。

    2、sdhci core

    因为sdhci driver并不是某个特定host的driver,而是提供了一些接口和操作集方法给对应的host driver使用。

    因此,我们将sdhci.c的代码部分称之为sdhci core用以和host driver区分。

    其主要功能如下:

    • 为host driver提供分配、释放sdhci_host的接口
    • 为host driver提供注册、卸载sdhci_host的接口
    • 实现sdhci_host和mmc_host的对接(也就是mmc core的对接)
    • 实现host关于SDHCI标准的通用操作(sdhci_ops)
    • 实现host的通用电源管理操作

    注意,clock和pinctrl是由host driver自己管理,sdhci core并不参与。

    3、代码位置

    drivers/mmc/host/sdhci.c 
    drivers/mmc/host/sdhci.h
    

    二、数据结构

    1、struct sdhci_host

    sdhci core将host抽象出struct sdhci_host来进行管理和维护。

    数据结构如下:

    struct sdhci_host {
        /* Data set by hardware interface driver */
        const char *hw_name;    /* Hardware bus name */      // 名称
        unsigned int quirks;    /* Deviations from spec. */         // 癖好,可以理解为硬件sdhci controller和标准sdhci规范不符合的地方。
        unsigned int quirks2;   /* More deviations from spec. */   // 癖好2,可以理解为硬件sdhci controller和标准sdhci规范不符合的地方。
    
        int irq;        /* Device IRQ */      // sdhci的中断
        void __iomem *ioaddr;   /* Mapped address */   // sdhci寄存器的基地址
        const struct sdhci_ops *ops;    /* Low level hw interface */      // 底层硬件的操作接口
    
        struct regulator *vmmc;     /* Power regulator (vmmc) */       // sdhci core的LDO
        struct regulator *vqmmc;    /* Signaling regulator (vccq) */      // 给sdhci io供电的LDO
    
        /* Internal data */
        struct mmc_host *mmc;   /* MMC structure */      // struct mmc_host,用于注册到mmc subsystem中
        u64 dma_mask;       /* custom DMA mask */
    
        spinlock_t lock;    /* Mutex */      // 自旋锁
        int flags;      /* Host attributes */   // sdhci的一些标识
        unsigned int version;   /* SDHCI spec. version */   // 当前sdhci的硬件版本
        unsigned int max_clk;   /* Max possible freq (MHz) */   // 该sdhci支持的最大电压
        unsigned int timeout_clk;   /* Timeout freq (KHz) */   // 超时频率
        unsigned int clk_mul;   /* Clock Muliplier value */   // 当前倍频值
        unsigned int clock; /* Current clock (MHz) */      // 当前工作频率
        u8 pwr;         /* Current voltage */   // 当前工作电压
        bool runtime_suspended; /* Host is runtime suspended */      // 是否处于runtime suspend状态
        struct mmc_request *mrq;    /* Current request */      // 当前正在处理的请求
        struct mmc_command *cmd;    /* Current command */   // 当前的命令请求
        struct mmc_data *data;  /* Current data request */      // 当前的数据请求
        unsigned int data_early:1;  /* Data finished before cmd */   // 表示在CMD处理完成前,data已经处理完成
    
        struct sg_mapping_iter sg_miter;    /* SG state for PIO */
        unsigned int blocks;    /* remaining PIO blocks */
        int sg_count;       /* Mapped sg entries */
        u8 *adma_desc;      /* ADMA descriptor table */
        u8 *align_buffer;   /* Bounce buffer */
        unsigned int adma_desc_sz; /* ADMA descriptor table size */
        unsigned int adma_desc_line_sz; /* ADMA descriptor line size */
        unsigned int align_buf_sz; /* Bounce buffer size */
        unsigned int align_bytes; /* Alignment bytes (4/8 for 32-bit/64-bit) */
        unsigned int adma_max_desc; /* Max ADMA descriptos (max sg segments) */
        dma_addr_t adma_addr;   /* Mapped ADMA descr. table */
        dma_addr_t align_addr;  /* Mapped bounce buffer */
    
        struct tasklet_struct card_tasklet; /* Tasklet structures */      // card tasklet,用于处理card的插入或者拔出事件
        struct tasklet_struct finish_tasklet;      // finsh tasklet,用来通知上层一个请求处理完成(包括出错的情况)
    
        struct timer_list timer;    /* Timer for timeouts */   // 超时定时器链表
    
        u32 caps;       /* Alternative CAPABILITY_0 */   // 表示该sdhci controller的属性
        u32 caps1;      /* Alternative CAPABILITY_1 */   // 表示该sdhci controller的属性
    
        unsigned int            ocr_avail_sdio; /* OCR bit masks */   // 在该sdhci controller上可用的sdio card的ocr值掩码(代表了其可用电压)
        unsigned int            ocr_avail_sd;   // 在该sdhci controller上可用的sd card的ocr值掩码(代表了其可用电压) 
        unsigned int            ocr_avail_mmc;   /// 在该sdhci controller上可用的mmc card的ocr值掩码(代表了其可用电压) 
    
    /* 以下和mmc的tuning相关 */
        wait_queue_head_t   buf_ready_int;  /* Waitqueue for Buffer Read Ready interrupt */
        unsigned int        tuning_done;    /* Condition flag set when CMD19 succeeds */
        unsigned int        tuning_count;   /* Timer count for re-tuning */
        unsigned int        tuning_mode;    /* Re-tuning mode supported by host */
    #define SDHCI_TUNING_MODE_1 0
        struct timer_list   tuning_timer;   /* Timer for tuning */
    
    /* 以下和sdhci的qos相关 */
        struct sdhci_host_qos host_qos[SDHCI_QOS_MAX_POLICY];
        enum sdhci_host_qos_policy last_qos_policy;
        bool host_use_default_qos;  
        unsigned int pm_qos_timeout_us;         /* timeout for PM QoS request */
        struct device_attribute pm_qos_tout;
        struct delayed_work pm_qos_work;
    
        struct sdhci_next next_data;
        ktime_t data_start_time;
        struct mutex ios_mutex;
        enum sdhci_power_policy power_policy;
    
        bool irq_enabled; /* host irq status flag */      // 表示中断是否使能?
        bool async_int_supp;  /* async support to rxv int, when clks are off */
        bool disable_sdio_irq_deferred; /* status of disabling sdio irq */
        u32 auto_cmd_err_sts;
        struct ratelimit_state dbg_dump_rs;
        int reset_wa_applied; /* reset workaround status */
        ktime_t reset_wa_t; /* time when the reset workaround is applied */
        int reset_wa_cnt; /* total number of times workaround is used */
    
        unsigned long private[0] ____cacheline_aligned;      // 私有数据指针
    };
    
    • 癖好1(sdhci_host->quirks)各个位意义如下:
    /* Controller doesn't honor resets unless we touch the clock register */
    #define SDHCI_QUIRK_CLOCK_BEFORE_RESET            (1<<0)
    /* Controller has bad caps bits, but really supports DMA */
    #define SDHCI_QUIRK_FORCE_DMA                (1<<1)
    /* Controller doesn't like to be reset when there is no card inserted. */
    #define SDHCI_QUIRK_NO_CARD_NO_RESET            (1<<2)
    /* Controller doesn't like clearing the power reg before a change */
    #define SDHCI_QUIRK_SINGLE_POWER_WRITE            (1<<3)
    /* Controller has flaky internal state so reset it on each ios change */
    #define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS        (1<<4)
    /* Controller has an unusable DMA engine */
    #define SDHCI_QUIRK_BROKEN_DMA                (1<<5)
    /* Controller has an unusable ADMA engine */
    #define SDHCI_QUIRK_BROKEN_ADMA                (1<<6)
    /* Controller can only DMA from 32-bit aligned addresses */
    #define SDHCI_QUIRK_32BIT_DMA_ADDR            (1<<7)
    /* Controller can only DMA chunk sizes that are a multiple of 32 bits */
    #define SDHCI_QUIRK_32BIT_DMA_SIZE            (1<<8)
    /* Controller can only ADMA chunks that are a multiple of 32 bits */
    #define SDHCI_QUIRK_32BIT_ADMA_SIZE            (1<<9)
    /* Controller needs to be reset after each request to stay stable */
    #define SDHCI_QUIRK_RESET_AFTER_REQUEST            (1<<10)
    /* Controller needs voltage and power writes to happen separately */
    #define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER        (1<<11)
    /* Controller provides an incorrect timeout value for transfers */
    #define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL            (1<<12)
    /* Controller has an issue with buffer bits for small transfers */
    #define SDHCI_QUIRK_BROKEN_SMALL_PIO            (1<<13)
    /* Controller does not provide transfer-complete interrupt when not busy */
    #define SDHCI_QUIRK_NO_BUSY_IRQ                (1<<14)
    /* Controller has unreliable card detection */
    #define SDHCI_QUIRK_BROKEN_CARD_DETECTION        (1<<15)
    /* Controller reports inverted write-protect state */
    #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT        (1<<16)
    /* Controller has nonstandard clock management */
    #define SDHCI_QUIRK_NONSTANDARD_CLOCK            (1<<17)
    /* Controller does not like fast PIO transfers */
    #define SDHCI_QUIRK_PIO_NEEDS_DELAY            (1<<18)
    /* Controller losing signal/interrupt enable states after reset */
    #define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET        (1<<19)
    /* Controller has to be forced to use block size of 2048 bytes */
    #define SDHCI_QUIRK_FORCE_BLK_SZ_2048            (1<<20)
    /* Controller cannot do multi-block transfers */
    #define SDHCI_QUIRK_NO_MULTIBLOCK            (1<<21)
    /* Controller can only handle 1-bit data transfers */
    #define SDHCI_QUIRK_FORCE_1_BIT_DATA            (1<<22)
    /* Controller needs 10ms delay between applying power and clock */
    #define SDHCI_QUIRK_DELAY_AFTER_POWER            (1<<23)
    /* Controller uses SDCLK instead of TMCLK for data timeouts */
    #define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK        (1<<24)
    /* Controller reports wrong base clock capability */
    #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN        (1<<25)
    /* Controller cannot support End Attribute in NOP ADMA descriptor */
    #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC        (1<<26)
    /* Controller is missing device caps. Use caps provided by host */
    #define SDHCI_QUIRK_MISSING_CAPS            (1<<27)
    /* Controller uses Auto CMD12 command to stop the transfer */
    #define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12        (1<<28)
    /* Controller doesn't have HISPD bit field in HI-SPEED SD card */
    #define SDHCI_QUIRK_NO_HISPD_BIT            (1<<29)
    /* Controller treats ADMA descriptors with length 0000h incorrectly */
    #define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC        (1<<30)
    /* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
    #define SDHCI_QUIRK_UNSTABLE_RO_DETECT            (1<<31)
    
    • 癖好2(sdhci_host->quirks2)各个位意义如下:
    #define SDHCI_QUIRK2_HOST_OFF_CARD_ON           (1<<0)
    #define SDHCI_QUIRK2_HOST_NO_CMD23          (1<<1)
    /* The system physically doesn't support 1.8v, even if the host does */
    #define SDHCI_QUIRK2_NO_1_8_V               (1<<2)
    #define SDHCI_QUIRK2_PRESET_VALUE_BROKEN        (1<<3)
    /*
     * Read Transfer Active/ Write Transfer Active may be not
     * de-asserted after end of transaction. Issue reset for DAT line.
     */
    #define SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT         (1<<4)
    /*
     * Slow interrupt clearance at 400KHz may cause
     * host controller driver interrupt handler to
     * be called twice.
     */
    #define SDHCI_QUIRK2_SLOW_INT_CLR           (1<<5)
    /*
     * If the base clock can be scalable, then there should be no further
     * clock dividing as the input clock itself will be scaled down to
     * required frequency.
     */
    #define SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK      (1<<6)
    /*
     * Dont use the max_discard_to in sdhci driver so that the maximum discard
     * unit gets picked by the mmc queue. Otherwise, it takes a long time for
     * secure discard kind of operations to complete.
     */
    #define SDHCI_QUIRK2_USE_MAX_DISCARD_SIZE       (1<<7)
    /*
     * Ignore data timeout error for R1B commands as there will be no
     * data associated and the busy timeout value for these commands
     * could be lager than the maximum timeout value that controller
     * can handle.
     */
    #define SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD     (1<<8)
    /*
     * The preset value registers are not properly initialized by
     * some hardware and hence preset value must not be enabled for
     * such controllers.
     */
    #define SDHCI_QUIRK2_BROKEN_PRESET_VALUE        (1<<9)
    /*
     * Some controllers define the usage of 0xF in data timeout counter
     * register (0x2E) which is actually a reserved bit as per
     * specification.
     */
    #define SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT       (1<<10)
    /*
     * This is applicable for controllers that advertize timeout clock
     * value in capabilities register (bit 5-0) as just 50MHz whereas the
     * base clock frequency is 200MHz. So, the controller internally
     * multiplies the value in timeout control register by 4 with the
     * assumption that driver always uses fixed timeout clock value from
     * capabilities register to calculate the timeout. But when the driver
     * uses SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK base clock frequency is directly
     * controller by driver and it's rate varies upto max. 200MHz. This new quirk
     * will be used in such cases to avoid controller mulplication when timeout is
     * calculated based on the base clock.
     */
    #define SDHCI_QUIRK2_DIVIDE_TOUT_BY_4 (1 << 11)
    /*
     * Some SDHC controllers are unable to handle data-end bit error in
     * 1-bit mode of SDIO.
     */
    #define SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR             (1<<12)
    
    /*
     * Some SDHC controllers do not require data buffers alignment, skip
     * the bounce buffer logic when preparing data
     */
    #define SDHCI_QUIRK2_ADMA_SKIP_DATA_ALIGNMENT             (1<<13)
    /* Some controllers doesn't have have any LED control */
    #define SDHCI_QUIRK2_BROKEN_LED_CONTROL (1 << 14)
    /* Use reset workaround in case sdhci reset timeouts */
    #define SDHCI_QUIRK2_USE_RESET_WORKAROUND (1 << 15)
    
    • sdhci host的一些标识(sdhci_host->flags)如下:
    #define SDHCI_USE_SDMA      (1<<0)  /* Host is SDMA capable */
    #define SDHCI_USE_ADMA      (1<<1)  /* Host is ADMA capable */
    #define SDHCI_REQ_USE_DMA   (1<<2)  /* Use DMA for this req. */
    #define SDHCI_DEVICE_DEAD   (1<<3)  /* Device unresponsive */
    #define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
    #define SDHCI_NEEDS_RETUNING    (1<<5)  /* Host needs retuning */
    #define SDHCI_AUTO_CMD12    (1<<6)  /* Auto CMD12 support */
    #define SDHCI_AUTO_CMD23    (1<<7)  /* Auto CMD23 support */
    #define SDHCI_PV_ENABLED    (1<<8)  /* Preset value enabled */
    #define SDHCI_SDIO_IRQ_ENABLED  (1<<9)  /* SDIO irq enabled */
    #define SDHCI_HS200_NEEDS_TUNING (1<<10)    /* HS200 needs tuning */
    #define SDHCI_USING_RETUNING_TIMER (1<<11)  /* Host is using a retuning timer for the card */
    #define SDHCI_HS400_NEEDS_TUNING (1<<12)    /* HS400 needs tuning */
    #define SDHCI_USE_ADMA_64BIT     (1<<13)/* Host is 64-bit ADMA capable */
    

    2、struct sdhci_ops结构体

    sdhci core只是提供了一些接口和符合mmc core的操作集方法给对应的host driver使用。由于各个host的硬件有所差异,所以实际和硬件交互的驱动部分还是在host driver中实现。

    所以sdhci core要求host提供标准的访问硬件的一些方法。而这些方法就被定义在了struct sdhci_ops结构体内部。

    结构体如下:

    struct sdhci_ops {
    #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS    
       // 表示host另外提供了一套访问寄存器的方法,没有定义的话,则说明使用通用的读写寄存器的方法
        u32        (*read_l)(struct sdhci_host *host, int reg);
        u16        (*read_w)(struct sdhci_host *host, int reg);
        u8        (*read_b)(struct sdhci_host *host, int reg);
        void        (*write_l)(struct sdhci_host *host, u32 val, int reg);
        void        (*write_w)(struct sdhci_host *host, u16 val, int reg);
        void        (*write_b)(struct sdhci_host *host, u8 val, int reg);
    #endif
    
        void    (*set_clock)(struct sdhci_host *host, unsigned int clock);    // 设置时钟频率
    
        int        (*enable_dma)(struct sdhci_host *host);    // 使能DMA
        unsigned int    (*get_max_clock)(struct sdhci_host *host);    // 获取支持的最大时钟频率
        unsigned int    (*get_min_clock)(struct sdhci_host *host);    // 获取支持的最小时钟频率
        unsigned int    (*get_timeout_clock)(struct sdhci_host *host);
        int        (*platform_bus_width)(struct sdhci_host *host, int width);  
        void (*platform_send_init_74_clocks)(struct sdhci_host *host,
                             u8 power_mode);
        unsigned int    (*get_ro)(struct sdhci_host *host);    // 获取
        void    (*platform_reset_enter)(struct sdhci_host *host, u8 mask);    // 进入平台复位的方法
        void    (*platform_reset_exit)(struct sdhci_host *host, u8 mask);    // 退出平台复位的方法
        int    (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);    // 设置uhs方式
        void    (*hw_reset)(struct sdhci_host *host);    // 硬件复位的方法
        void    (*platform_suspend)(struct sdhci_host *host);    // 平台host的suspend方法
        void    (*platform_resume)(struct sdhci_host *host);    // 平台host的resume方法
        void    (*adma_workaround)(struct sdhci_host *host, u32 intmask);
        void    (*platform_init)(struct sdhci_host *host);    // 平台host的初始化方法
        void    (*check_power_status)(struct sdhci_host *host, u32 req_type);    // 检测总线的电源状态
    #define REQ_BUS_OFF    (1 << 0)
    #define REQ_BUS_ON    (1 << 1)
    #define REQ_IO_LOW    (1 << 2)
    #define REQ_IO_HIGH    (1 << 3)
        int    (*execute_tuning)(struct sdhci_host *host, u32 opcode);    // 执行tuning操作的的方法
        void    (*toggle_cdr)(struct sdhci_host *host, bool enable);
        unsigned int    (*get_max_segments)(void);
        void    (*platform_bus_voting)(struct sdhci_host *host, u32 enable);    // 平台总线投票的方法
        void    (*disable_data_xfer)(struct sdhci_host *host);
        void    (*dump_vendor_regs)(struct sdhci_host *host);
        int    (*config_auto_tuning_cmd)(struct sdhci_host *host,
                          bool enable,
                          u32 type);
        int    (*enable_controller_clock)(struct sdhci_host *host);
        void    (*reset_workaround)(struct sdhci_host *host, u32 enable);
    };
    

    这个结构体也就是host driver要实现的核心内容。

    3、struct mmc_host_ops sdhci_ops

    注意:这里的sdhci_ops是一个变量名,和上述的struct sdhci_ops不是同一个概念。搞不懂为什么这么命名,容易混淆。

    sdhci core使用sdhci_ops作为sdhci host抽象出来的mmc host的操作集,所以其是一个struct mmc_host_ops结构体。

    后续mmc core关于这个host的操作也都是基于这个操作集上实现的,包括使能host(enable方法)、禁用host(disable方法)、发送请求(request方法)。

    具体参考《mmc core》系列。

    具体实现如下,具体意义参考《mmc core(第二章)——数据结构和宏定义说明》:

    static const struct mmc_host_ops sdhci_ops = {
            // post_req和pre_req是为了实现异步请求处理而设置的
            // 异步请求处理就是指,当另外一个异步请求还没有处理完成的时候,可以先准备另外一个异步请求而不必等待
            // 具体参考《mmc core主模块》
        .pre_req    = sdhci_pre_req,  
        .post_req   = sdhci_post_req,
        .request    = sdhci_request,    // host处理mmc请求的方法,在mmc_start_request中会调用
        .set_ios    = sdhci_set_ios,   // 设置host的总线的io setting
        .get_cd     = sdhci_get_cd,   // 检测host的卡槽中card的插入状态
        .get_ro     = sdhci_get_ro,  // 获取host上的card的读写属性
        .hw_reset   = sdhci_hw_reset,  // 硬件复位
        .enable_sdio_irq = sdhci_enable_sdio_irq,
        .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,   // 切换信号电压的方法
        .execute_tuning         = sdhci_execute_tuning,   // 执行tuning操作,为card选择一个合适的采样点
        .card_event         = sdhci_card_event,
        .card_busy  = sdhci_card_busy,   // 用于检测card是否处于busy状态
        .enable     = sdhci_enable, // 使能host,当host被占用时(第一次调用mmc_claim_host)调用
        .disable    = sdhci_disable,    // 禁用host,当host被释放时(第一次调用mmc_release_host)调用
        .stop_request = sdhci_stop_request,   // 停止请求处理的方法
        .get_xfer_remain = sdhci_get_xfer_remain,
        .notify_load    = sdhci_notify_load,
    };
    

    三、API总览

    1、sdhci_host分配和释放相关

    • sdhci_alloc_host & sdhci_free_host

    由底层host driver调用。

    sdhci_alloc_host为host driver分配一个sdhci_host和mmc_host,并实现其初始化,以及sdhci_host和mmc_host的关联。

    sdhci_free_host则是用来释放一个sdhci_host。

    原型:struct sdhci_host *sdhci_alloc_host(struct device *dev, size_t priv_size)
        参数说明:struct device *dev——》对应host的device结构体
                        size_t priv_size——》要分配的sdhci_host的私有数据的长度,一般是平台自己定制的host的长度。
    
        原型:void sdhci_free_host(struct sdhci_host *host)
    

    2、sdhci_host的注册和卸载相关

    • sdhci_add_host & sdhci_remove_host

    由底层host driver调用。

    sdhci_add_host用于向sdhci core注册一个sdhci_host。会根据sdhci的寄存器以及部分标识设置其mmc_host,最终将mmc_host注册到mmc core中。

    因此,在调用sdhci_add_host之前,必须准备好sdhci的所有硬件环境。

    sdhci_free_host则用于从sdhci core中卸载一个sdhci_host,对应的mmc_host也会从mmc core中被卸载。

        原型:int sdhci_add_host(struct sdhci_host *host);
        原型:void sdhci_remove_host(struct sdhci_host *host, int dead);
    

    四、接口代码说明

    1、sdhci_alloc_host

    struct sdhci_host *sdhci_alloc_host(struct device *dev,
        size_t priv_size)
    {
        struct mmc_host *mmc;
        struct sdhci_host *host;
    
        WARN_ON(dev == NULL);
    
    /* 实现mmc_host和sdhci_host的分配 */
        mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);   // 分配一个struct mmc_host
        // 分配mmc_host的同时也分配了sizeof(struct sdhci_host) + priv_size的私有数据空间,这部分就是作为sdhci_host及其私有数据使用的。
        // 具体参考《mmc core——host模块说明》
        if (!mmc)
            return ERR_PTR(-ENOMEM);
    
    /* 实现mmc_host和sdhci_host的关联操作 */
        host = mmc_priv(mmc);   // 将sdhci_host作为mmc_host的私有数据,mmc_host->private = sdhci_host
        host->mmc = mmc;   // 关联sdhci_host和mmc_host,sdhci_host->mmc = mmc_host
    
    /* sdhci_host的锁的初始化工作 */
        spin_lock_init(&host->lock);   // 初始化sdhci_host 的占有锁
        mutex_init(&host->ios_mutex);   // 初始化sdhci_host 设置io setting的互斥锁
    
        return host;   // 将struct sdhci_host 返回
    }
    

    综上,

    mmc_host->private = sdhci_host 
    sdhci_host->mmc = mmc_host
    

    2、sdhci_add_host

    (0)底层传上来的sdhci_host中应该包含什么信息

    • sdhci的寄存器的映射过后的基地址(sdhci_host->ioaddr)
    • sdhci的癖好quirks、quirks2(sdhci_host->quirks,sdhci_host->quirks2)
    • sdhci的中断号(sdhci_host->irq)
    • host提供给sdhci core用来操作硬件的操作集(sdhci_host->ops)

    (1)主要完成工作如下:

    • sdhci host复位

    调用sdhci_reset

    • 读取该host的sdhci的信息(从sdhci相关寄存器中读取)并设置sdhci_host相关成员

      • 版本(sdhci_host->version) : 从SDHCI_HOST_VERSION寄存器中读取
      • 支持的属性 : 从SDHCI_CAPABILITIES、SDHCI_CAPABILITIES_1寄存器中读取
      • 标识(sdhci_host->version) : 根据sdhci_host->quirks和quirks2来设置
      • 支持的最大频率和倍频(sdhci_host->max_clk & sdhci_host->clk_mul)

        对应SDHCI_CAPABILITIES寄存器中的SDHCI_CLOCK_BASE_SHIFT位

        对应SDHCI_CAPABILITIES寄存器中的SDHCI_CLOCK_MUL_SHIFT位
      • sdhci使用的regulator(sdhci_host->vqmmc)

        从节点中的命名为”vmmc”的regulator属性中获取
      • card插入状态发生变化时调用的tasklet(sdhci_host->card_tasklet)

        设置为sdhci_tasklet_card
      • 请求处理完成时调用的tasklet(sdhci_host->finish_tasklet)

        设置为sdhci_tasklet_finish
      • 请求的处理超时定时器(sdhci_host->timer)

        设置为sdhci_timeout_timer
      • qos处理的工作(sdhci_host->pm_qos_work)

        设置为sdhci_pm_qos_remove_work
    • 设置mmc_host的相关成员

      • 操作集(mmc_host->ops)

        设置为sdhci_ops,上面已经说明过了
      • 最大频率(mmc_host->f_max)

        用sdhci_host->max_clk的值来设置
      • host的属性(mmc_host->caps & mmc_host->caps2)

        通过sdhci_host->quirks和quirks2、以及SDHCI_CAPABILITIES、SDHCI_CAPABILITIES_1寄存器中的属性进行设置
      • 各个电压下的最大电流值(mmc_host->max_current_330 & mmc_host->max_current_300 & mmc_host->max_current_180)

        从SDHCI_MAX_CURRENT寄存器中读取
      • 可用电压(mmc->ocr_avail & mmc->ocr_avail_sdio & mmc->ocr_avail_sd & mmc->ocr_avail_mmc)

        从SDHCI_CAPABILITIES寄存器中的SDHCI_CAN_VDD_330、SDHCI_CAN_VDD_300、SDHCI_CAN_VDD_180位获取
      • 一些块和段size的设置
    • 中断的注册

      将sdhci_host的中断处理函数注册为sdhci_irq

    • sdhci host初始化

      调用sdhci_init

    • 注册mmc_host到mmc core中

      调用mmc_add_host

    • 使能card插入状态的检测

      调用sdhci_enable_card_detection

    (2)代码如下:

    int sdhci_add_host(struct sdhci_host *host)
    {
    // 以下变量要注意区分
    // host是指要注册的sdhci host
    // mmc是指要注册到mmc subsystem的host,封装在sdhci host中
        struct mmc_host *mmc;
        u32 caps[2] = {0, 0};
        u32 max_current_caps;
        unsigned int ocr_avail;
        int ret;
    
        WARN_ON(host == NULL);
        if (host == NULL)
            return -EINVAL;
    
        mmc = host->mmc;      // 获取struct mmc_host
    
    /* 执行复位操作 */
        sdhci_reset(host, SDHCI_RESET_ALL);
            // 执行reset操作,会调用到sdhci_host->ops->platform_reset_enter,msm并没有实现这个方法
    
    /********************************* 获取sdhci信息并设置sdhci_host的相应成员***********************/
    /* 获取sdhci controller版本号 */
        host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
        host->version = (host->version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
            // 获取sdhci host的硬件版本号
    
    /* 获取sdhci controller支持的属性 */
        caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : sdhci_readl(host, SDHCI_CAPABILITIES);
            // SDHCI_QUIRK_MISSING_CAPS:Controller is missing device caps. Use caps provided by host 
            // sdhci控制器没有devices属性的话,由底层host提供,否则,从sdhci controller的SDHCI_CAPABILITIES读取属性
    
        if (host->version >= SDHCI_SPEC_300)
            caps[1] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ?host->caps1 : sdhci_readl(host, SDHCI_CAPABILITIES_1);
            // 从sdhci controller的SDHCI_CAPABILITIES_1读取属性
    
    /* 设置sdhci_host->flags中和DMA相关的flag */
        if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
            host->flags |= SDHCI_USE_SDMA;
        else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
            DBG("Controller doesn't have SDMA capability
    ");
        else
            host->flags |= SDHCI_USE_SDMA;
            // SDHCI_QUIRK_FORCE_DMA : Controller has bad caps bits, but really supports DMA
            // 设置sdhci_host->flags中的SDHCI_USE_SDMA标识
            //............................
        if (host->flags & SDHCI_USE_ADMA) {
                    // sdhci_host ->adma_max_desc
                    // sdhci_host ->adma_desc_line_sz
                    // sdhci_host ->align_bytes
                    // sdhci_host ->adma_desc_sz
                    // sdhci_host ->align_buf_sz 
                    // sdhci_host ->adma_desc
                    // sdhci_host ->align_buffer
        }
    
        host->next_data.cookie = 1;
    
    /* 获取sdhci controller支持的最大频率以及倍频 */
        if (host->version >= SDHCI_SPEC_300)
            host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
                >> SDHCI_CLOCK_BASE_SHIFT;     // 从sdhci controller的SDHCI_CLOCK_V3_BASE_MASK读取最大clock(单位是MHZ)
        else
            host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
                >> SDHCI_CLOCK_BASE_SHIFT;
    
        host->max_clk *= 1000000;(转化为hz)
            // 设置sdhci_host->max_clk
        sdhci_update_power_policy(host, SDHCI_PERFORMANCE_MODE_INIT);
            // 设置sdhci_host->power_policy为SDHCI_PERFORMANCE_MODE_INIT
        if (host->max_clk == 0 || host->quirks & SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
            host->max_clk = host->ops->get_max_clock(host);   // 调用sdhci_host->ops->get_max_clock获得最大时钟
        }
    
        host->clk_mul = (caps[1] & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT;
        if (host->clk_mul)
            host->clk_mul += 1;
            // 设置sdhci_host->clk_mul,clock的倍频实行
    
    /*************************** 以下对mmc_host和sdhci_host进行设置操作 ***************************/
    /* 以下设置mmc_host,ops、f_max、f_min */
        mmc->ops = &sdhci_ops;   // 设置mmc_host的操作集为sdhci_ops
        mmc->f_max = host->max_clk;   // 设置最大时钟频率mmc_host->f_max
        if (host->ops->get_min_clock)
            mmc->f_min = host->ops->get_min_clock(host);   // 调用sdhci_host->ops->get_min_clock获得最小时钟频率mmc_host->f_min
    
        host->timeout_clk = (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
            // 从sdhci controller的SDHCI_TIMEOUT_CLK_MASK读取最大timeout
            // 设置到sdhci_host->timeout_clk
        if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
            host->timeout_clk = mmc->f_max / 1000;
    
        if (!(host->quirks2 & SDHCI_QUIRK2_USE_MAX_DISCARD_SIZE))
            mmc->max_discard_to = (1 << 27) / host->timeout_clk;
            // 设置mmc_host->max_discard_to
    
    /* 设置mmc_host->caps,也就是属性 */
        mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
        if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
            mmc->caps |= MMC_CAP_4_BIT_DATA;
        if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
            mmc->caps &= ~MMC_CAP_CMD23;
        if (caps[0] & SDHCI_CAN_DO_HISPD)
            mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
        if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
            !(host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
            (mmc_gpio_get_cd(host->mmc) < 0) &&
            !(host->mmc->caps2 & MMC_CAP2_NONHOTPLUG))
            mmc->caps |= MMC_CAP_NEEDS_POLL;
    
    /* 获取vqmmc regulater并使能 */
        /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
        host->vqmmc = regulator_get(mmc_dev(mmc), "vqmmc");
        if (IS_ERR_OR_NULL(host->vqmmc)) {
                ....
        } else {
            ret = regulator_enable(host->vqmmc);
            if (!regulator_is_supported_voltage(host->vqmmc, 1700000,1950000))
                caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
        }
        if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)
            caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
    
    /* 设置mmc_host->caps和传输模式相关的属性 */
        /* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
        if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
                   SDHCI_SUPPORT_DDR50))
            mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
    
        /* SDR104 supports also implies SDR50 support */
        if (caps[1] & SDHCI_SUPPORT_SDR104)
            mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
        else if (caps[1] & SDHCI_SUPPORT_SDR50)
            mmc->caps |= MMC_CAP_UHS_SDR50;
    
        if (caps[1] & SDHCI_SUPPORT_DDR50)
            mmc->caps |= MMC_CAP_UHS_DDR50;
    
    /* 设置sdhci_host->flags中和tuning相关的flag */
        /* Does the host need tuning for SDR50? */
        if (caps[1] & SDHCI_USE_SDR50_TUNING)
            host->flags |= SDHCI_SDR50_NEEDS_TUNING;
        /* Does the host need tuning for HS200? */
        if (mmc->caps2 & MMC_CAP2_HS200)
            host->flags |= SDHCI_HS200_NEEDS_TUNING;
        /* Does the host need tuning for HS400? */
        if (mmc->caps2 & MMC_CAP2_HS400)
            host->flags |= SDHCI_HS400_NEEDS_TUNING;
    
    /* 设置mmc_host->caps和驱动类型相关的属性 */
        /* Driver Type(s) (A, C, D) supported by the host */
        if (caps[1] & SDHCI_DRIVER_TYPE_A)
            mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
        if (caps[1] & SDHCI_DRIVER_TYPE_C)
            mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
        if (caps[1] & SDHCI_DRIVER_TYPE_D)
            mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
    
    /* 获取sdhci controller的tuning计数(tuning_count 、tuning_mode )*/
        host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
                      SDHCI_RETUNING_TIMER_COUNT_SHIFT;
        if (host->tuning_count)
            host->tuning_count = 1 << (host->tuning_count - 1);
    
        host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >> SDHCI_RETUNING_MODE_SHIFT;
    
        ocr_avail = 0;
    
    /* 获取vmmc regulater,设置caps[0]支持的电压值 */
        host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
    #ifdef CONFIG_REGULATOR
        /*
         * Voltage range check makes sense only if regulator reports
         * any voltage value.
         */
        if (host->vmmc && regulator_get_voltage(host->vmmc) > 0) {
            ret = regulator_is_supported_voltage(host->vmmc, 2700000,
                3600000);
            if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330)))
                caps[0] &= ~SDHCI_CAN_VDD_330;
            if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300)))
                caps[0] &= ~SDHCI_CAN_VDD_300;
            ret = regulator_is_supported_voltage(host->vmmc, 1700000,
                1950000);
            if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180)))
                caps[0] &= ~SDHCI_CAN_VDD_180;
        }
    #endif /* CONFIG_REGULATOR */
    
    /* 设置各个电压下的最大电流值(max_current_330、max_current_330 、max_current_180 )*/
    /* 设置可用电压域 */
        max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
        if (!max_current_caps && host->vmmc) {
            u32 curr = regulator_get_current_limit(host->vmmc);
                    //....................
        }
    
        if (caps[0] & SDHCI_CAN_VDD_330) {
            ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
    
            mmc->max_current_330 = ((max_current_caps &
                       SDHCI_MAX_CURRENT_330_MASK) >>
                       SDHCI_MAX_CURRENT_330_SHIFT) *
                       SDHCI_MAX_CURRENT_MULTIPLIER;
        }
            //.........
        mmc->ocr_avail = ocr_avail;
        mmc->ocr_avail_sdio = ocr_avail;
            // ......
    
    /*********************************** sdhci的初始化工作**************************************/
    /* 初始化sdhci工作过程中会使用到的tasklet */
        tasklet_init(&host->card_tasklet, sdhci_tasklet_card, (unsigned long)host);      // host上发生card插入或者拔出时调用
        tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host);   // 完成一个request时调用
    
        setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); // command的超时定时器
    
    /* 初始化qos处理的工作 */
        INIT_DELAYED_WORK(&host->pm_qos_work, sdhci_pm_qos_remove_work);
    
    /* 中断注册和使能 */
        ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,mmc_hostname(mmc), host);
        host->irq_enabled = true;
    
    /* 对该sdhci controller进行初始化 */
        sdhci_init(host, 0);
    
        mmiowb();
    /* sdhci关于qos的请求和操作的设置 */
        if (host->host_qos[SDHCI_QOS_READ_WRITE].cpu_dma_latency_us) {
                // .........
        }
    
    /*********************************** 将mmc_host注册到mmc subsystem中 *******************************/
        mmc_add_host(mmc);
    
    /*********************************** 开始使能sdhci和并且开始检测card状态******************************/
        sdhci_enable_card_detection(host);
    
        return 0;
    }
    

    重点关注如下几个部分:

    (1)sdhci_reset(host, SDHCI_RESET_ALL);
    (2)mmc->ops = &sdhci_ops;   // 设置mmc_host的操作集为sdhci_ops
    (3)host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
    (4)tasklet_init(&host->card_tasklet, sdhci_tasklet_card, (unsigned long)host); // host上发生card插入或者拔出时调用
    (5)tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host); // 完成一个request时调用的tasklet
    (6)ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,mmc_hostname(mmc), host);
    (7)sdhci_init(host, 0);    // 软初始化host
    (8)sdhci_enable_card_detection(host);    // 开始使能card插入状态的检测
    

    五、sdhci core内部代码简单说明

    1、sdhci_reset & sdhci_init & sdhci_enable_card_detection

    • sdhci_reset

      由sdhci core内部调用,用于复位host。
    • sdhci_init

      由sdhci core内部调用,用于初始化host
    • sdhci_enable_card_detection

      由sdhci core内部调用,使能card插入状态的检测,主要是设置SDHCI_INT_ENABLE、SDHCI_SIGNAL_ENABLE寄存器
    static irqreturn_t sdhci_irq(int irq, void *dev_id)
    {
        irqreturn_t result;
        struct sdhci_host *host = dev_id;
        u32 intmask, unexpected = 0;
        int cardint = 0, max_loops = 16;
    
        spin_lock(&host->lock);
    
     /* 从SDHCI_INT_STATUS寄存器中读取中断状态 */
        intmask = sdhci_readl(host, SDHCI_INT_STATUS);    // 从SDHCI_INT_STATUS寄存器中读取中断状态
    
    /* 确认是否有中断产生 */
        if (!intmask || intmask == 0xffffffff) {
            result = IRQ_NONE;
            goto out;
        }
    
    again:
        DBG("*** %s got interrupt: 0x%08x
    ",
            mmc_hostname(host->mmc), intmask);
    
     /* 以下是对card插入或者拔出的中断进行处理 */
        if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
            u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
                      SDHCI_CARD_PRESENT;
            sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
                            SDHCI_INT_CARD_REMOVE);
            sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
                              SDHCI_INT_CARD_INSERT);
    
            sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
                     SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);    // 重置这两个中断位
            intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
            tasklet_schedule(&host->card_tasklet);    // 执行host->card_tasklet,也就是sdhci_tasklet_card进行处理,后面说明
        }
    
     /* 以下是sdhci处理命令产生的中断进行处理,不一定是出错 */
        if (intmask & SDHCI_INT_CMD_MASK) {
            if (intmask & SDHCI_INT_AUTO_CMD_ERR)
                host->auto_cmd_err_sts = sdhci_readw(host,
                        SDHCI_AUTO_CMD_ERR);
            sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
                SDHCI_INT_STATUS);
            if ((host->quirks2 & SDHCI_QUIRK2_SLOW_INT_CLR) &&
                (host->clock <= 400000))
                udelay(40);
            sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);    // 在sdhci_cmd_irq中会执行host->finish_tasklet, 也就是sdhci_tasklet_finish来通知上层。后面说明。
        }
    
     /* 以下是sdhci处理数据产生的中断进行处理,不一定是出错 */
        if (intmask & SDHCI_INT_DATA_MASK) {
            sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
                SDHCI_INT_STATUS);
            if ((host->quirks2 & SDHCI_QUIRK2_SLOW_INT_CLR) &&
                (host->clock <= 400000))
                udelay(40);
            sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); // 在sdhci_data_irq中会执行host->finish_tasklet, 也就是sdhci_tasklet_finish来通知上层。
        }
    
        intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
    
        intmask &= ~SDHCI_INT_ERROR;
    
     /* 以下是对总线电源状态发生变化的中断的处理 */
        if (intmask & SDHCI_INT_BUS_POWER) {
            pr_err("%s: Card is consuming too much power!
    ",
                mmc_hostname(host->mmc));
            sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
        }
    
        intmask &= ~SDHCI_INT_BUS_POWER;
    
        if (intmask & SDHCI_INT_CARD_INT)
            cardint = 1;
    
        intmask &= ~SDHCI_INT_CARD_INT;
    
        if (intmask) {
            unexpected |= intmask;
            sdhci_writel(host, intmask, SDHCI_INT_STATUS);
        }
    
        result = IRQ_HANDLED;
    
    /* 可能不止有其他事件导致中断的产生,重复检测 */
        intmask = sdhci_readl(host, SDHCI_INT_STATUS);
        if (intmask && --max_loops)
            goto again;
    out:
        spin_unlock(&host->lock);
        return result;
    }
    

    3、sdhci_tasklet_card

    • 简单流程说明:
      • 当进行卡插入或者拔出的时候,sdhci controller(硬件)会检测到其状态发生变化
      • sdhci controller(硬件)会设置中断状态寄存器中SDHCI_INT_CARD_INSERT或者SDHCI_INT_CARD_REMOVE位
      • sdhci controller(硬件)触发中段
      • sdhci core中的中断处理函数sdhci_irq被调用(软件)
      • sdhci_irq(软件)去判断出中断状态寄存器中SDHCI_INT_CARD_INSERT或者SDHCI_INT_CARD_REMOVE位被设置
      • sdhci_irq执行host->card_tasklet,也就是我们这里的sdhci_tasklet_card进行相应处理。
    • sdhci_tasklet_card实现如下:
    static void sdhci_tasklet_card(unsigned long param)
    {
        struct sdhci_host *host = (struct sdhci_host*)param;   // 提取sdhci_host结构体
        sdhci_card_event(host->mmc);   // 发送事件,如果此时有mmc_request正在处理,则会复位数据线和命令线,终止mmc_request处理
        mmc_detect_change(host->mmc, msecs_to_jiffies(200));   
             // 调用mmc_detect_change通知mmc core卡槽状态发生了变化,剩下的就是mmc core的工作了
            // mmc_detect_change实现具体参考《mmc core主模块说明》
    }
    

    4、sdhci_tasklet_finish

    static void sdhci_tasklet_finish(unsigned long param)
    {
            //......过滤掉前面一些根据情况决定的复位操作
        mmc_request_done(host->mmc, mrq);   
            // 调用mmc_request_done来通知mmc core 说mrq这个mmc request已经处理完成,至于处理完成的结果由上层自己解决
            // mmc_request_done实现具体参考《mmc core主模块说明》
        sdhci_runtime_pm_put(host);
    }
    

    5、struct mmc_host_ops sdhci_ops各个方法简单说明

    static const struct mmc_host_ops sdhci_ops = {
            // post_req和pre_req是为了实现异步请求处理而设置的
            // 异步请求处理就是指,当另外一个异步请求还没有处理完成的时候,可以先准备另外一个异步请求而不必等待
            // 具体参考《mmc core主模块》
        .pre_req    = sdhci_pre_req, 
        .post_req    = sdhci_post_req,
        .request    = sdhci_request,    // host处理mmc请求的方法,在mmc_start_request中会调用
        .set_ios    = sdhci_set_ios,   // 设置host的总线的io setting
        .get_cd        = sdhci_get_cd,   // 检测host的卡槽中card的插入状态
        .get_ro        = sdhci_get_ro,  // 获取host上的card的读写属性
        .hw_reset    = sdhci_hw_reset,  // 硬件复位
        .enable_sdio_irq = sdhci_enable_sdio_irq,
        .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,   // 切换信号电压的方法
        .execute_tuning            = sdhci_execute_tuning,   // 执行tuning操作,为card选择一个合适的采样点
        .card_event            = sdhci_card_event,
        .card_busy    = sdhci_card_busy,   // 用于检测card是否处于busy状态
        .enable        = sdhci_enable, // 使能host,当host被占用时(第一次调用mmc_claim_host)调用
        .disable    = sdhci_disable,    // 禁用host,当host被释放时(第一次调用mmc_release_host)调用
        .stop_request = sdhci_stop_request,   // 停止请求处理的方法
        .get_xfer_remain = sdhci_get_xfer_remain,
        .notify_load    = sdhci_notify_load,
    };
    
  • 相关阅读:
    使用sublimeserver启动本地服务器进行调试
    echarts图表自适应盒子的大小(盒子的大小是动态改变的),大到需要全屏展示
    axios跨域问题
    reset.css
    Git初体验
    Window下的git配置文件在哪里【图文】
    使用better-scroll遇到的问题
    代码中特殊的注释技术 -- TODO、FIXME和XXX的用处
    vue饿了么学习笔记(1)vue-cli开启项目
    gulp4小demo
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/10813200.html
Copyright © 2011-2022 走看看