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フレームワークのプライベートデータは、主にいくつかのコールバックインタフェースを含み、インタフェースを通じて特定のチップmmcコントローラのいくつかのパラメータにアクセスします.
このインタフェースは、特定のmmcコントローラによって駆動されて呼び出され、dw_として登録される.mci互換フレームワークの駆動、このような駆動アクセスレジスタ、クロックパラメータはすべて同じで、互いに互換性があります.
2、dw_mci-rockchip駆動
三、core層
1、ホスト層をドッキングするインタフェース
この構造は、mmcカード初期化、requestなどのmmcコントローラが実現する必要があるコールバックインタフェースを記述する.
これはmmcコントローラ構造で、コントローラのコールバックインタフェースが含まれています
2、core層インタフェース
このインタフェースはmmcコントローラへのアクセスを実現する
2、ドッキングcard層のインタフェース
4、mmcバスインタフェース
この関数は、mmcバスをシステムに登録し、後でバスにmmcデバイスを追加し、mmcドライバを登録できるようにします.
四、card層
card層ブロック設備の実現を重点的に説明する
mmcブロックデバイス駆動ioctlがcore層を介して最終的にhost層インタフェースを呼び出してmmcデバイスの動作を実現する方法を重点的に分析する.
五、タイミングチャート
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
本稿では,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