RK 3399ベースのLinux駆動開発--EMMC駆動フレームワーク

75231 ワード

文書ディレクトリ
  • 一、概説
  • 二、host層
  • 1、dw_mciフレーム
  • `dw_mci_drv_data`
  • `dw_mci_dma_ops`
  • `dw_mci`
  • `dw_mci_pltfm_register`
  • `dw_mci_probe`
  • `dw_mci_init_slot`
  • `dw_mci_init_dma`
  • 2、dw_mci-rockchip駆動
  • `dw_mci_rockchip_probe`
  • 三、core層
  • 1、ホスト層をドッキングするインターフェース
  • `mmc_host_ops`
  • `mmc_host`
  • `mmc_alloc_host`
  • `mmc_add_host`
  • 2、core層インタフェース
  • `mmc_start_host`
  • `_mmc_detect_change`
  • `mmc_rescan`
  • `mmc_rescan_try_freq`
  • `mmc_wait_for_req`
  • `__mmc_start_req`
  • `mmc_start_request`
  • `__mmc_start_request`
  • 2、ドッキングcard層のインタフェース
  • `mmc_attach_mmc`
  • `mmc_init_card`
  • 4、mmcバスインタフェース
  • `mmc_register_bus`
  • `mmc_add_card`
  • `mmc_register_driver`
  • 四、card層
  • `mmc_blk_init`
  • `mmc_blk_probe`
  • `mmc_blk_alloc`
  • `mmc_blk_alloc_req`
  • `mmc_blk_ioctl`
  • `mmc_blk_ioctl_cmd`
  • `__mmc_blk_ioctl_cmd`
  • 五、タイミング図
  • 1、mmcブロックデバイス駆動初期化
  • 2、mmc host駆動初期化
  • 3、mmcデバイススキャン、
  • 追加
  • 4、mmcブロックデバイス登録
  • 5、mmcブロックデバイス動作
  • 一、概説
    本稿では,EMMCの駆動フレームワークをソースコードで解析し,host駆動を登録しcard層のブロックデバイス駆動インタフェースとcore層関連インタフェースを介してhost駆動インタフェースにアクセスし,emmcデバイスにアクセスする方法を説明する.
    二、host層
    1、dw_mciフレームワークdw_mci_drv_data
    これはdw_mciフレームワークのプライベートデータは、主にいくつかのコールバックインタフェースを含み、インタフェースを通じて特定のチップmmcコントローラのいくつかのパラメータにアクセスします.
    struct dw_mci_drv_data {
    	unsigned long	*caps;
    	int		(*init)(struct dw_mci *host);
    	int		(*setup_clock)(struct dw_mci *host);
    	void		(*prepare_command)(struct dw_mci *host, u32 *cmdr);
    	void		(*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
    	int		(*parse_dt)(struct dw_mci *host);
    	int		(*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
    	int		(*prepare_hs400_tuning)(struct dw_mci *host,
    						struct mmc_ios *ios);
    	int		(*switch_voltage)(struct mmc_host *mmc,
    					  struct mmc_ios *ios);
    };
    
    dw_mci_dma_ops
    struct dw_mci_dma_ops {
    	int (*init)(struct dw_mci *host);
    	int (*start)(struct dw_mci *host, unsigned int sg_len);
    	void (*complete)(void *host);
    	void (*stop)(struct dw_mci *host);
    	void (*cleanup)(struct dw_mci *host);
    	void (*exit)(struct dw_mci *host);
    };
    
    dw_mci
    struct dw_mci {
    	int			use_dma;
    	const struct dw_mci_dma_ops	*dma_ops;
    	struct device		*dev;
    	const struct dw_mci_drv_data	*drv_data;
    	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
    };
    
    dw_mci_pltfm_register
    このインタフェースは、特定のmmcコントローラによって駆動されて呼び出され、dw_として登録される.mci互換フレームワークの駆動、このような駆動アクセスレジスタ、クロックパラメータはすべて同じで、互いに互換性があります.
    int dw_mci_pltfm_register(struct platform_device *pdev,
    			  const struct dw_mci_drv_data *drv_data)
    {
    	struct dw_mci *host;
    
    	host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
    	/*   host   */
    	return dw_mci_probe(host);
    }
    
    dw_mci_probe
    int dw_mci_probe(struct dw_mci *host)
    {
    	if (!host->pdata) {
    		/*      dts   mmc        */
    		host->pdata = dw_mci_parse_dt(host); 
    	}
    	
    	/*      */
    	host->biu_clk = devm_clk_get(host->dev, "biu");
    	host->ciu_clk = devm_clk_get(host->dev, "ciu");
    	
    	/*     mmc         */
    	if (drv_data && drv_data->init) {
    	}
    	if (drv_data && drv_data->setup_clock) {
    	}
    
    	host->dma_ops = host->pdata->dma_ops;
    	dw_mci_init_dma(host);
    	
    	for (i = 0; i < host->num_slots; i++) {
    		/*   mmc       slot,  rk3399   mmc   ,   sdio0,   sdmmc */
    		ret = dw_mci_init_slot(host, i);
    	}
    }
    
    dw_mci_init_slot
    /*     mmc         */
    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,
    	.set_sdio_status	= dw_mci_set_sdio_status,
    	.get_ro			= dw_mci_get_ro,
    	.get_cd			= dw_mci_get_cd,
    	.enable_sdio_irq	= dw_mci_enable_sdio_irq,
    	.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,
    };
    
    static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
    {
    	struct mmc_host *mmc;
    	
    	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); 
    	/*     ,  core   ,  request、card_busy  */
    	mmc->ops = &dw_mci_ops;
    	ret = mmc_add_host(mmc);
    }
    
    dw_mci_init_dma
    static const struct dw_mci_dma_ops dw_mci_idmac_ops = {
    	.init = dw_mci_idmac_init,
    	.start = dw_mci_idmac_start_dma,
    	.stop = dw_mci_idmac_stop_dma,
    	.complete = dw_mci_dmac_complete_dma,
    	.cleanup = dw_mci_dma_cleanup,
    };
    
    static const struct dw_mci_dma_ops dw_mci_edmac_ops = {
    	.init = dw_mci_edmac_init,
    	.exit = dw_mci_edmac_exit,
    	.start = dw_mci_edmac_start_dma,
    	.stop = dw_mci_edmac_stop_dma,
    	.complete = dw_mci_dmac_complete_dma,
    	.cleanup = dw_mci_dma_cleanup,
    };
    
    static void dw_mci_init_dma(struct dw_mci *host)
    {
    	if (host->use_dma == TRANS_MODE_IDMAC) {
    		host->dma_ops = &dw_mci_idmac_ops;
    	} else {
    		host->dma_ops = &dw_mci_edmac_ops;
    	}
    }
    

    2、dw_mci-rockchip駆動dw_mci_rockchip_probe
    /*      dm_mci        */
    static const struct dw_mci_drv_data rk3288_drv_data = {
    	.caps			= dw_mci_rk3288_dwmmc_caps,
    	.prepare_command        = dw_mci_rockchip_prepare_command,
    	.set_ios		= dw_mci_rk3288_set_ios,
    	.execute_tuning		= dw_mci_rk3288_execute_tuning,
    	.parse_dt		= dw_mci_rk3288_parse_dt,
    	.setup_clock    = dw_mci_rk3288_setup_clock,
    	.init			= dw_mci_rockchip_init,
    };
    
    static const struct of_device_id dw_mci_rockchip_match[] = {
    	{ .compatible = "rockchip,rk2928-dw-mshc",
    		.data = &rk2928_drv_data },
    	{ .compatible = "rockchip,rk3288-dw-mshc",
    		.data = &rk3288_drv_data },
    	{},
    };
    
    static int dw_mci_rockchip_probe(struct platform_device *pdev)
    {
    	const struct dw_mci_drv_data *drv_data;
    	const struct of_device_id *match;
    
    	if (!pdev->dev.of_node)
    		return -ENODEV;
    
    	match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
    	drv_data = match->data;
    	
    	/*   dw_mci         dw_mci   */
    	return dw_mci_pltfm_register(pdev, drv_data);
    }
    

    三、core層
    1、ホスト層をドッキングするインタフェースmmc_host_ops
    この構造は、mmcカード初期化、requestなどのmmcコントローラが実現する必要があるコールバックインタフェースを記述する.
    struct mmc_host_ops {
    	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);
    	int	(*get_ro)(struct mmc_host *host);
    	int	(*get_cd)(struct mmc_host *host);
    	void	(*init_card)(struct mmc_host *host, struct mmc_card *card);	
    }
    
    mmc_host
    これはmmcコントローラ構造で、コントローラのコールバックインタフェースが含まれています
    struct mmc_host {
    	struct device		*parent;
    	struct device		class_dev;
    	/* mmc            */
    	const struct mmc_host_ops *ops; 
    }
    
    mmc_alloc_host
    struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
    {
    	struct mmc_host *host;
    
    	host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
    	
    	/*           core      mmc    mmc  ,     mmc       */
    	INIT_DELAYED_WORK(&host->detect, mmc_rescan);
    	
    	return host;
    }
    
    mmc_add_host
    int mmc_add_host(struct mmc_host *host)
    {
    	int err;
    
    	err = device_add(&host->class_dev);
    	/*   mmc   ,     */
    	mmc_start_host(host);
    }
    

    2、core層インタフェースmmc_start_host
    void mmc_start_host(struct mmc_host *host)
    {
    	_mmc_detect_change(host, 0, false);
    }
    
    _mmc_detect_change
    static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
    				bool cd_irq)
    {
    	host->detect_change = 1;
    	/*       ,    mmc       */
    	mmc_schedule_delayed_work(&host->detect, delay);
    }
    
    mmc_rescan
    void mmc_rescan(struct work_struct *work)
    {
    	struct mmc_host *host =
    		container_of(work, struct mmc_host, detect.work);
    	
    	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
    		if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
    			break;
    		if (freqs[i] <= host->f_min)
    			break;
    	}
    }
    
    mmc_rescan_try_freq
    static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
    {
    #ifdef MMC_STANDARD_PROBE
    	/*     card     */
    	if (!mmc_attach_mmc(host)) 
    		return 0;
    #else
    	if ((host->restrict_caps & RESTRICT_CARD_TYPE_EMMC) &&
    	     !mmc_attach_mmc(host))
    		return 0;	
    #endif	
    }
    
    mmc_wait_for_req
    このインタフェースはmmcコントローラへのアクセスを実現する
    void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
    {
    	__mmc_start_req(host, mrq);
    	mmc_wait_for_req_done(host, mrq);
    }
    
    __mmc_start_req
    static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
    {
    	int err;
    	
    	err = mmc_start_request(host, mrq);
    }
    
    mmc_start_request
    static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
    {
    	__mmc_start_request(host, mrq);
    }
    
    __mmc_start_request
    static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
    {
    	if (mmc_is_io_op(mrq->cmd->opcode) && host->ops->card_busy) {
    		/*   host mmc_host_ops card_busy   */
    		while (host->ops->card_busy(host) && --tries) 
    			mmc_delay(1);
    	}
    	/*   host mmc_host_ops request   */
    	host->ops->request(host, mrq);
    }
    

    2、ドッキングcard層のインタフェースmmc_attach_mmc
    static const struct mmc_bus_ops mmc_ops = {
    	.remove = mmc_remove,
    	.detect = mmc_detect,
    	.suspend = mmc_suspend,
    	.resume = mmc_resume,
    	.runtime_suspend = mmc_runtime_suspend,
    	.runtime_resume = mmc_runtime_resume,
    	.alive = mmc_alive,
    	.shutdown = mmc_shutdown,
    	.reset = mmc_reset,
    };
    
    int mmc_attach_mmc(struct mmc_host *host)
    {
    	int err;
    	
    	mmc_attach_bus(host, &mmc_ops);
    	
    	err = mmc_init_card(host, rocr, NULL);
    	err = mmc_add_card(host->card);
    }
    
    mmc_init_card
    static int mmc_init_card(struct mmc_host *host, u32 ocr,
    	struct mmc_card *oldcard)
    {
    	struct mmc_card *card;
    	int err;
    	u32 cid[4];
    	
    	if (oldcard) {
    		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
    		}
    		card = oldcard;
    	} else {
    		card = mmc_alloc_card(host, &mmc_type);
    		card->ocr = ocr;
    		card->type = MMC_TYPE_MMC;
    		card->rca = 1;
    		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
    	}
    	
    	/*   mmc        emmc */
    	if (host->ops->init_card)
    		host->ops->init_card(host, card);	
    
    	if (!oldcard) {
    		/*
    		 * Fetch CSD from card.
    		 */
    		err = mmc_send_csd(card, card->raw_csd);
    		err = mmc_decode_csd(card);
    		err = mmc_decode_cid(card);
    	}
    }
    

    4、mmcバスインタフェースmmc_register_bus
    この関数は、mmcバスをシステムに登録し、後でバスにmmcデバイスを追加し、mmcドライバを登録できるようにします.
    static struct bus_type mmc_bus_type = {
    	.name		= "mmc",
    	.dev_groups	= mmc_dev_groups,
    	.match		= mmc_bus_match,
    	.uevent		= mmc_bus_uevent,
    	.probe		= mmc_bus_probe,
    	.remove		= mmc_bus_remove,
    	.shutdown	= mmc_bus_shutdown,
    	.pm		= &mmc_bus_pm_ops,
    };
    
    int mmc_register_bus(void)
    {
    	/*      mmc   */
    	return bus_register(&mmc_bus_type);
    }
    
    mmc_add_card
    int mmc_add_card(struct mmc_card *card)
    {
    	int ret;
    	
    	/*    mmc     mmc   ,           */ 
    	ret = device_add(&card->dev);
    }
    
    mmc_register_driver
    int mmc_register_driver(struct mmc_driver *drv)
    {
    	drv->drv.bus = &mmc_bus_type;
    	/*         mmc   ,                ,         */
    	return driver_register(&drv->drv);
    }
    

    四、card層
    card層ブロック設備の実現を重点的に説明するmmc_blk_init
    static struct mmc_driver mmc_driver = {
    	.drv		= {
    		.name	= "mmcblk",
    		.pm	= &mmc_blk_pm_ops,
    	},
    	.probe		= mmc_blk_probe,
    	.remove		= mmc_blk_remove,
    	.shutdown	= mmc_blk_shutdown,
    };
    
    static int __init mmc_blk_init(void)
    {
    	int res;
    	
    	/*        */
    	res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
    	
    	/*  mmc       */
    	res = mmc_register_driver(&mmc_driver);
    }
    
    mmc_blk_probe
    static int mmc_blk_probe(struct mmc_card *card)
    {
    	struct mmc_blk_data *md, *part_md;
    	
    	md = mmc_blk_alloc(card);
    	if (mmc_blk_alloc_parts(card, md))
    		goto out;
    	if (mmc_add_disk(md))
    		goto out;	
    	list_for_each_entry(part_md, &md->part, part) {
    		if (mmc_add_disk(part_md))
    			goto out;
    	}	
    }
    
    mmc_blk_alloc
    static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
    {
    	sector_t size;
    	
    	return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
    					MMC_BLK_DATA_AREA_MAIN);
    }
    
    mmc_blk_alloc_req
    static const struct block_device_operations mmc_bdops = {
    	.open			= mmc_blk_open,
    	.release		= mmc_blk_release,
    	.getgeo			= mmc_blk_getgeo,
    	.owner			= THIS_MODULE,
    	.ioctl			= mmc_blk_ioctl,
    #ifdef CONFIG_COMPAT
    	.compat_ioctl		= mmc_blk_compat_ioctl,
    #endif
    };
    
    static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
    					      struct device *parent,
    					      sector_t size,
    					      bool default_ro,
    					      const char *subname,
    					      int area_type)
    {
    	struct mmc_blk_data *md;
    
    	md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
    	md->disk = alloc_disk(perdev_minors);
    	/*                */
    	md->disk->fops = &mmc_bdops;
    	md->disk->private_data = md;	
    }					      
    
    mmc_blk_ioctl
    mmcブロックデバイス駆動ioctlがcore層を介して最終的にhost層インタフェースを呼び出してmmcデバイスの動作を実現する方法を重点的に分析する.
    static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
    	unsigned int cmd, unsigned long arg)
    {
    	switch (cmd) {
    	case MMC_IOC_CMD:
    		/*     */
    		return mmc_blk_ioctl_cmd(bdev,
    				(struct mmc_ioc_cmd __user *)arg);
    	case MMC_IOC_MULTI_CMD:
    		return mmc_blk_ioctl_multi_cmd(bdev,
    				(struct mmc_ioc_multi_cmd __user *)arg);
    	default:
    		return -EINVAL;
    	}
    }
    
    mmc_blk_ioctl_cmd
    static int mmc_blk_ioctl_cmd(struct block_device *bdev,
    			     struct mmc_ioc_cmd __user *ic_ptr)
    {
    	struct mmc_blk_ioc_data *idata;
    	struct mmc_blk_data *md;
    	struct mmc_card *card;
    	int err = 0, ioc_err = 0;
    
    	idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
    	md = mmc_blk_get(bdev->bd_disk);
    	card = md->queue.card;
    	
    	mmc_get_card(card);
    	/*     */
    	ioc_err = __mmc_blk_ioctl_cmd(card, md, idata);
    	mmc_put_card(card);
    
    	err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
    }
    
    __mmc_blk_ioctl_cmd
    static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
    			       struct mmc_blk_ioc_data *idata)
    {
    	struct mmc_request mrq = {NULL};
    	/*      core     */
    	mmc_wait_for_req(card->host, &mrq);
    }
    

    五、タイミングチャート
    1、mmcブロックデバイス駆動初期化
    card/block genhd core/bus register_blkdev登録ブロックデバイス駆動mmc_register_driver mmcバスcard/block genhd core/busに登録
    2、mmc host駆動初期化
    dw_mmc_rockchip platform dw_mmc_pltfm dw_mmc core/host platform_driver_registerはplatformドライバを登録しdtsノードとdw_を一致させるmci_pltfm_register dtsノードマッチング後、dw_に登録mciフレームワーク、完全な駆動dw_を実現mci_probe解析dts,初期化クロック,割り込みサービスなどdw_mci_init_slot設定操作hostのコールバックmmc_alloc_hostは、mm c_を呼び出すために遅延ワークキューを設定するrescan mmc_add_hostスケジューリング遅延ワークキュー呼び出しmmc_rescan dw_mmc_rockchip platform dw_mmc_pltfm dw_mmc core/host
    3、mmc設備のスキャンと追加
    ホストドライバを完全に初期化した後、スケジューリング遅延ワークキューを介して、ホストに接続されたmmcデバイスを発見するためにデバイススキャンフェーズに進む
    core/host core/core core/mmc dw_mmc core/bus mmc_rescanは遅延ワークキューによってmmc_をトリガするrescan_try_freq mmc_attach_mmc mmc_init_card初期化mmc card dw_mci_init_cardコールバックmmc_host_opのinit_cardメソッドmmc_add_cardはmm cバスにデバイスを追加します.ここで、バスの駆動がデバイスと一致してcore/host core/core core/mmc dw_を操作することをトリガーします.mmc core/bus
    4、mmcブロック設備登録
    card/block genhd mmc_blk_alloc/mmc_blk_alloc_partsはmmcデバイススキャン結果に基づいてブロックデバイスファイルエントリmmc_を割り当てるadd_disk add_diskは汎用ファイルシステムにディスクを追加し、ファイル操作ポインタcard/block genhdを設定する
    5、mmcブロック設備の操作
    block/ioctl card/block core/core dw_mmc blkdev_ioctl mmc_blk_ioctlコールバックblock_dev ice_operati onsポインタのioctlメソッドmmc_blk_ioctl_cmd __mmc_blk_ioctl_cmd mmc_wait_for_req mmc_wait_for_req_done __mmc_start_request dw_mci_requestコールバックmmc_ho st_opsポインタのrequestメソッドblock/ioctl card/block core/core dw_mmc