Linux 2.6.38
S3C2440
SDIO驅動(8)Host驅動實現第8行,mmc_alloc_host創建mmc host,並進行通用部分的初始化。第1個參數指定mmc_host對象中private數據佔用的內存大小,這裏把struct s3cmci_host對象設置爲了private數據。mmc_alloc_host函數很好看的:
/**
* mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure
* @dev: pointer to host device model structure
*
* Initialise the per-host structure.
*/
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
int err;
struct mmc_host *host;
if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
return NULL;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
if (!host)
return NULL;
spin_lock(&mmc_host_lock);
err = idr_get_new(&mmc_host_idr, host, &host->index);
spin_unlock(&mmc_host_lock);
if (err)
goto free;
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);
mmc_host_clk_init(host);
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
#endif
/*
* 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;
free:
kfree(host);
return NULL;
}
13~26行,idr_*函數用於從kernel獲取一個id,用於區分不同的host;28~31行初始化host這個設備並設置類別(class)爲“mmc_host”,設備添加後在class的mmc_host類別下可以看到這個設備:/sys/class/mmc_host/。
33行mmc_host_clk_init(host)用於設置bus的門控時鐘:
/**
* mmc_host_clk_init - set up clock gating code
* @host: host with potential clock to control
*/
static inline void mmc_host_clk_init(struct mmc_host *host)
{
host->clk_requests = 0;
/* Hold MCI clock for 8 cycles by default */
host->clk_delay = 8;
host->clk_gated = false;
INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
spin_lock_init(&host->clk_lock);
}
clock gate通過下圖就一目瞭然:
啓用了門控時鐘的功能我們就可以控制host的時鐘輸出,這是個涉及硬件的操作,所以具體實現還是由host controller決定(host成員mmc_host_ops的set_ios)。
mmc_alloc_host餘下的代碼初始化成員,其中host->detect用於卡掃描的一個work,host->disable禁用該host,notifier_call,休眠發生時向host發送通知。
14~24獲取host連接外部卡的pin。
26行開篇mmc_host爲對象private分配的內存用上了吧,內核中類似的使用比比皆是,之後通過struct s3cmci_host *host = mmc_priv(mmc)可以方便的獲取s3cmci_host進行操作。
37行host->pio_tasklet任務用來讀寫數據,是種下半部機制。中斷髮生後,中斷的handler只處理短小、緊急的事務(比如清除中斷標誌,此爲上半部);接着使用tasklet處理好事的事情,這樣提高了中斷的吞吐率。
39~43設置host controller的中斷/數據寄存器和分頻係數。
52~95獲取“ SDIO驅動(1)Host驅動框架”中定義好的mem、irq資源,中斷處理函數s3cmci_irq!驅動probe過程中,disable中斷功能。
97~122如果設置了card監測引腳,有卡插拔時引發一次scan動作(host->detect, mmc_rescan)。
124~132如果設置了寫保護引腳,寫數據是需要查詢host->pdata->gpio_wprotect的pin狀態,確定是否可以寫數據。
136~149獲取dma資源。
151~165行,host和card之間時鐘設置。
167行mmc->ops,host的操作接口,這裏:
static struct mmc_host_ops s3cmci_ops = {
.request = s3cmci_request,
.set_ios = s3cmci_set_ios,
.get_ro = s3cmci_get_ro,
.get_cd = s3cmci_card_present,
.enable_sdio_irq = s3cmci_enable_sdio_irq,
};
簡單來說,request,一次請求的實現;set_ios,設置bus的參數;get_ro,獲取card的讀寫狀態(是一張read/write卡or read-only卡);get_cd,檢測卡是否存在;enable_sdio_irq,通過寄存器控制sdio中斷使能、禁止。這幾個函數可以sleep,有的耗時較長,用時小心。
mmc->ocr_avail,該host可支持的操作電壓範圍。
mmc->caps,該host支持的功能特性,比如這裏的數據位4bit,支持sdio中斷。
mmc->f_min、f_max該host支持的時鐘頻率範圍,最小頻率、最大頻率。
198行主角,mmc_add_host:
/**
* mmc_add_host - initialise host hardware
* @host: mmc host
*
* Register the host with the driver model. The host must be
* prepared to start servicing requests before this function
* completes.
*/
int mmc_add_host(struct mmc_host *host)
{
int err;
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
!host->ops->enable_sdio_irq);
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
err = device_add(&host->class_dev);
if (err)
return err;
#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif
mmc_start_host(host);
register_pm_notifier(&host->pm_notify);
return 0;
}
主要做兩件事:host設備註冊(18行),啓動host並進行一次掃描(26行)。所以註釋提到,在調用這個函數之前要做好準備工作。204行,debugfs文件系統相關的東東,如果系統支持debugfs,在/sys/kernel/debug/下有host的目錄,可以查詢host的信息。目錄名稱就是dev_set_name(&host->class_dev, "mmc%d", host->index)設置的device name,比如:
# ls /sys/kernel/debug/mmc0/
clock
completed_events
ios
pending_events
regs
req
state
# cat /sys/kernel/debug/mmc0/ios <
clock: 52000000 Hz
vdd: 23 (3.5 ~ 3.6 V)
bus mode: 2 (push-pull)
chip select: 0 (don't care)
power mode: 2 (on)
bus width: 3 (8 bits)
timing spec: 1 (mmc high-speed)
signal voltage: 0 (3.30 V)