網上已經有很多文章寫了Linux SD/MMC的驅動的分析了,尤其是SAMSUNG系列的,估計用汗牛充棟來描寫都不過分。俺只能說點我自己寫的基於CBP的EVB板子的SD/MMC controller的驅動了,這個驅動沒有采用DMA,雖然那是我最擅長的。使用的是pio模式,其實就是CPU讀寫了,但是竟然也沒有使用tasklet,也就沒有中斷的上下半部的說法了,應該不是一個很好的習慣,雖然tasklet其實就是啓動一個內核運行隊列,去運行pio的讀寫等等,本質上沒有大的區別,但沒有使用就是沒有使用,實事求是而已。
那就說說Linux SD/MMC驅動的工作過程吧:
首先按照platform驅動的方式,註冊probe函數,然後系統就會調用probe函數,具體方法和原理請參考本人寫的以及轉載的plateform的文章,就不在贅述了。
在probe裏面首先分配一個mmc_host的結構,Linux以後發起各種操作的時候,參數都會通過這個結構傳遞。
mmc=mmc_alloc_host(sizeof(struct cbpmci_host), &pdev->dev);
mmc_alloc_host這個函數比較有意思,其中傳入的第一個參數是一個長度,這個長度是在mmc_hos後面額外連續分配的一塊數據,我這句的意思就是緊跟着mmc_host分配了一個cbpmci_host的結構,看mmc_host的定義如下:
struct mmc_host {
struct device *parent;
………省略若干行………………
unsigned long private[0] ____cacheline_aligned;
};
其實cbpmci_host的位置就是private[0]的位置,巧妙吧?經常看看Linux代碼能學到不少Linux大牛們的方法。這樣就可以通過這個函數來從mmc_host中得到自己帶入的私有數據了:
static inline void *mmc_priv(struct mmc_host *host)
{
return (void *)host->private;
}
按照Linux驅動的慣例,此時應該註冊SD/MMC的操作函數了,SD/MMC的操作函數恐怕是Linux驅動裏面最少的之一了,只有如下幾個:
struct mmc_host_ops {
int (*enable)(struct mmc_host *host);
int (*disable)(struct mmc_host *host, int lazy);
void (*request)(struct mmc_host *host, struct mmc_request *req);
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
int (*get_ro)(struct mmc_host *host);
int (*get_cd)(struct mmc_host *host);
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
};
我的定義:
static struct mmc_host_ops cbpmci_ops = {
.request = cbpmci_request,
.set_ios = cbpmci_set_ios,
.get_ro = cbpmci_get_ro,
.get_cd = cbpmci_card_present,
.enable_sdio_irq = cbpmci_enable_sdio_irq,
};
將cbpmci_ops 賦值給mmc->ops即可
mmc->ops = &cbpmci_ops;
爲啥連讀寫都沒有呢,因爲他是通過request裏面的參數來進行標識的。Request函數主要處理Linux發到驅動的命令請求,包括帶數據的和不帶數據的,帶數據的一般就是read/write, 其實ext CSD之類的東東也是通過讀來完成的。
其中的set_ios是設置一些控制參數的,比如時鐘頻率,電源開/關,總線寬度等等。
get_ro是獲取SD/MMC的寫保護標誌的,就是SD卡上的那個小開關的狀態,如果返回0則是可寫的,不然爲只讀。
get_cd是獲取卡的插入狀態的,返回0則有卡,不然返回1。
接着當然是要申請對應的SD的中斷了,在中斷函數中將處理命令完成,CRC錯誤等等中斷。
然後再初始化一些mmc_host的參數就OK了,比如最大最小頻率,總線寬度,最大的block count等等。然後調用 mmc_add_host將host驅動加入。
ret = mmc_add_host(mmc);
如果驅動加入沒有錯誤,系統將會調用相應的初始化過程,好像寫的有點長了,還是下回分解吧!