Linux SD卡/SDIO驅動開發2-host

一、struct mmc_host 結構體

struct dw_mci {
	spinlock_t		lock;
	spinlock_t		irq_lock;
	void __iomem		*regs;
	void __iomem		*fifo_reg;

	struct scatterlist	*sg;
	struct sg_mapping_iter	sg_miter;

	struct dw_mci_slot	*cur_slot;
	struct mmc_request	*mrq;
	struct mmc_command	*cmd;
	struct mmc_data		*data;
	struct mmc_command	stop_abort;
	unsigned int		prev_blksz;
	unsigned char		timing;

	/* DMA interface members*/
	int			use_dma;
	int			using_dma;
	int			dma_64bit_address;

	dma_addr_t		sg_dma;
	void			*sg_cpu;
	const struct dw_mci_dma_ops	*dma_ops;
	/* For idmac */
	unsigned int		ring_size;

	/* For edmac */
	struct dw_mci_dma_slave *dms;
	/* Registers's physical base address */
	resource_size_t		phy_regs;

	u32			cmd_status;
	u32			data_status;
	u32			stop_cmdr;
	u32			dir_status;
	struct tasklet_struct	tasklet;
	unsigned long		pending_events;
	unsigned long		completed_events;
	enum dw_mci_state	state;
	struct list_head	queue;

	u32			bus_hz;
	u32			current_speed;
	u32			num_slots;
	u32			fifoth_val;
	u16			verid;
	struct device		*dev;
	struct dw_mci_board	*pdata;
	const struct dw_mci_drv_data	*drv_data;
	void			*priv;
	struct clk		*biu_clk;
	struct clk		*ciu_clk;
	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];

	/* FIFO push and pull */
	int			fifo_depth;
	int			data_shift;
	u8			part_buf_start;
	u8			part_buf_count;
	union {
		u16		part_buf16;
		u32		part_buf32;
		u64		part_buf;
	};
	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);

	/* Workaround flags */
	u32			quirks;

	bool			vqmmc_enabled;
	unsigned long		irq_flags; /* IRQ flags */
	int			irq;

	int			sdio_id0;

	struct timer_list       cmd11_timer;
	struct timer_list       cto_timer;
	struct timer_list       dto_timer;
	struct timer_list	xfer_timer;
};

struct mmc_host(linux/include/linux/mmc/host.h)用於與core層的命令請求,數據 傳輸等信息,這裏代碼較長,展示部分

struct mmc_host 
{
	const struct mmc_host_ops *ops;     // SD卡主控制器的操作函數,即該控制器所具備的驅動能力
	const struct mmc_bus_ops *bus_ops; // SD總線驅動的操作函數,即SD總線所具備的驅動能力
	struct mmc_ios  ios;  // 配置時鐘、總線、電源、片選、時序等
	struct mmc_card  *card;  // 連接到此主控制器的SD卡設備
	... ...
};

結構體詳解可以參考:http://blog.chinaunix.net/uid-28414100-id-5754672.html

二、SD控制器之初始化

dw_mci_probe 初始化dw_mci結構體相關內容,解析dts的數據,創建mmc_host對象

struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
	int err;
	struct mmc_host *host;

	host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
	if (!host)
		return NULL;

	/* scanning will be enabled when we're ready */
	host->rescan_disable = 1;
	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();
	if (err < 0) {
		kfree(host);
		return NULL;
	}

	dev_set_name(&host->class_dev, "mmc%d", host->index);

	host->parent = dev;
	host->class_dev.parent = dev;
	host->class_dev.class = &mmc_host_class;
	device_initialize(&host->class_dev); //初始化host->class_dev,這個日後會通過device_register註冊進系統。

	if (mmc_gpio_alloc(host)) {
		put_device(&host->class_dev);
		return NULL;
	}

	spin_lock_init(&host->lock);
	init_waitqueue_head(&host->wq);
	INIT_DELAYED_WORK(&host->detect, mmc_rescan);
	#ifdef CONFIG_PM
	host->pm_notify.notifier_call = mmc_pm_notify;
	#endif
	setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);

	/*
	 * By default, hosts do not support SGIO or large requests.
	 * They have to set these according to their abilities.
	 */
	host->max_segs = 1;
	host->max_seg_size = PAGE_CACHE_SIZE;

	host->max_req_size = PAGE_CACHE_SIZE;
	host->max_blk_size = 512;
	host->max_blk_count = PAGE_CACHE_SIZE / 512;

	return host;
}

三、設備驅動功能函數

static const struct mmc_host_ops dw_mci_ops = {
    .request        = dw_mci_request,  //下面解釋。
    .pre_req        = dw_mci_pre_req, 
    .post_req       = dw_mci_post_req,
    .set_ios        = dw_mci_set_ios,  //設置SD卡控制器,前面我們所見到的設置控制器時鐘,數據線寬度等等一系列操作最終就是通過他來實現的。
    .set_sdio_status    = dw_mci_set_sdio_status,
    .get_ro         = dw_mci_get_ro,    //獲取卡的寫保護狀態,前面所過,SD卡初始化完成以後,我們進行的一個最後的工作便是檢測卡的寫保護狀態,其實就是調用get_ro方法。
    .get_cd         = dw_mci_get_cd,    //檢測卡是否在卡槽之中
    .enable_sdio_irq    = dw_mci_enable_sdio_irq, //開啓sdio中斷,這個是對sdio卡而言的
    .execute_tuning     = dw_mci_execute_tuning,
    .card_busy      = dw_mci_card_busy,
    .start_signal_voltage_switch = dw_mci_switch_voltage,
    .init_card      = dw_mci_init_card,
    .prepare_hs400_tuning   = dw_mci_prepare_hs400_tuning,
}; 
dw_mci_request
    dw_mci_queue_request
        host->state == STATE_WAITING_CMD11_DONE //判斷電壓是否正確
        dw_mci_start_request
            __dw_mci_start_request
                dw_mci_submit_data //實現數據的傳輸
                dw_mci_start_command //實現指令的傳輸,需要sd卡主控芯片手冊
  • response類型

    根據SD卡的協議,當SD卡收到從控制器發來的cmd指令後,SD卡會發出response相應,而response的類型分爲R1,R1b,R2,R3,R6,R7,這些類型分別對應不同的指令,各自的數據包結構也不同(具體內容參考SD卡協議)。這裏,通過RSP_TYPE對指令cmd的opcode的解析得到相對應的reponse類型,再通過swich賦給寄存器MMC_CMDAT對應的[1:0]位。

  • 將指令和參數寫入寄存器

    mci_writel()是整個SD卡主控制器設備驅動的實質,通過對主控制器芯片寄存器MMC_CMD,MMC_ARGH,MMC_ARGL,MMC_CMDAT的設置,實現主控制器發送指令到SD卡的功能。

四、中斷

有兩個中斷,一個爲SD主控制器芯片內電路固有的內部中斷,另一個爲探測引腳的探測到外部有SD卡插拔引起的中斷

devm_request_irq(host->dev, host->irq, dw_mci_interrupt,host->irq_flags, “dw-mci”, host);
當有SD卡插入或拔出時,硬件主控制器芯片的探測pin腳產生外部中斷,進入中斷處理函數,執行工作隊列裏的mmc_rescan,掃描SD總線,對插入或拔出SD卡作相應的處理。

enable_sdio_irq = dw_mci_enable_sdio_irq,
當調用(*request),即host->ops->request(host, mrq),即上文中的_request()後控制器與SD卡之間開始進行一次指令或數據傳輸,通信完畢後,主控芯片將產生一個內部中斷,
以告知此次指令或數據傳輸完畢。這個中斷的具體值將保存在一個名爲MMC_I_REG的中斷寄存器中以供讀取,中斷寄存器MMC_I_REG中相關描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章