Linux系統spi驅動程式分析---(一)
說明:本文將分析Linux-2.6.17原始碼中的spi驅動程式,其內容為本人閱讀原始碼後的一些理解。由於本人水平有限,所以內容可能比較雜亂零散,權當個人筆記記錄之用。而以下內容均以powerpc架構為例說明。
在Linux系統中,spi驅動的設計採用了分層設計模式的思想,將spi驅動分為三層:
- spi主控制器層:即對cpu所整合的spi控制器進行初始化設定的程式碼,所以對cpu的spi控制器的暫存器設定也在此程式碼中實現;
- spi通用層:提供一些spi主控制器層和spi從裝置層中所需的設備註冊函式,以及spi匯流排型別等資料結構;
- spi從裝置層:將從裝置作為字元裝置型別進行註冊,並提供操作從裝置的方法,以及從裝置spidev_data等資料結構。
首先分析spi主控制器層:
spi_mpc83xx.c檔案中有如下主要結構體:
/* SPI Controller registers */ struct mpc83xx_spi_reg { //u8 res1[0x20]; /* 此暫存器保留 */ __be32 mode; __be32 event; __be32 mask; __be32 command; __be32 transmit; __be32 receive; /* 以下暫存器對應四個從裝置的模式設定 */ __be32 res[2]; __be32 mode0; __be32 mode1; __be32 mode2; __be32 mode3; };
以上結構體為spi主控制器的幾個暫存器,對其進行賦值即可操作spi暫存器。
以上結構體為spi主控制器資料結構(spi_master)中cdev的私有資料,即可以認為其為spi主控制器資料結構中的私有資料。此結構體中包含了spi控制暫存器的基地址、傳輸和接受的buf、中斷號以及系統時鐘值。/* SPI Controller driver's private data. */ struct mpc83xx_spi { /* bitbang has to be first */ struct spi_bitbang bitbang; struct completion done; struct mpc83xx_spi_reg __iomem *base; /* rx & tx bufs from the spi_transfer */ const void *tx; void *rx; /* functions to deal with different sized buffers */ void (*get_rx) (u32 rx_data, struct mpc83xx_spi *); u32(*get_tx) (struct mpc83xx_spi *); unsigned int count; u32 irq; unsigned nsecs; /* (clock cycle time)/2 */ u32 sysclk; void (*activate_cs) (u8 cs, u8 polarity); void (*deactivate_cs) (u8 cs, u8 polarity); };
/* SPI platform_driver */
static struct platform_driver mpc83xx_spi_driver = {
.probe = mpc83xx_spi_probe,
.remove = __devexit_p(mpc83xx_spi_remove),
.driver = {
.name = "mpc83xx_spi",
},
};
以上結構體為spi的平臺驅動結構體,其代表在平臺總線上spi主控制器對應的驅動資料結構。probe函式用於探測總線上是否有註冊過的對應裝置。
以我的理解:在系統中註冊裝置,是指對裝置的資料結構進行初始化賦值,並呼叫相關函式進行註冊。而設備註冊過之後,其所初始化賦值的結構體就能被平臺驅動探測到,驅動從而找到對應的裝置結構體,再根據裝置結構體的初始化值來進行spi暫存器的相關設定。
spi_mpc83xx.c檔案中有如下主要函式:
static int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t);
static int mpc83xx_spi_setup(struct spi_device *spi);
static int __init mpc83xx_spi_probe(struct platform_device *dev);
static int __init mpc83xx_spi_init(void);
首先,在boot核心的過程中,系統會直接呼叫被module_init(mpc83xx_spi_init)過的mpc83xx_spi_init(void)函式,而此函式的定義如下所示:
static int __init mpc83xx_spi_init(void)
{
return platform_driver_register(&mpc83xx_spi_driver);
}
其呼叫平臺驅動註冊函式,將上文提到過的mpc83xx_spi_driver平臺驅動結構體註冊到平臺總線上。而platform_driver_register()函式又會把已初始化的platform_bus_type賦值給mpc83xx_spi_driver平臺驅動結構體中device的bus成員,以及呼叫driver_register(&mpc83xx_spi_driver.driver)來註冊驅動。platform_driver_register()函式定義如下:
/**
* platform_driver_register
* @drv: platform driver structure
*/
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type; /* 此bus中的match函式能檢測已經註冊的各個平臺裝置和此平臺驅動是否匹配 */
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
其中幾個if語句表示如果mpc83xx_spi_driver平臺驅動結構體中probe等函式指標已被賦值的話,則mpc83xx_spi_driver平臺驅動結構中的driver結構相應的函式指標也會被初始化。而platform_drv_probe()函式功能為呼叫mpc83xx_spi_driver平臺驅動結構體中的probe函式。
而driver_register(&drv->driver)函式將此平臺驅動結構中的裝置驅動新增到平臺bus總線上,其函式定義如下所示:
/**
* driver_register - register driver with bus
* @drv: driver to register
**/
int driver_register(struct device_driver * drv)
{
INIT_LIST_HEAD(&drv->devices);
init_completion(&drv->unloaded);
return bus_add_driver(drv);
}
之後將以此順序來依次呼叫接下來的這些函式:bus_add_driver(drv) -> driver_attach(drv) -> driver_probe_device(drv, dev) -> drv->probe
由以上函式的呼叫順序可知,當driver_probe_device(drv, dev)函式檢測到系統中有與平臺驅動相對應的平臺裝置時,將呼叫drv->probe函式。而drv->probe函式就是上文中提到的platform_drv_probe()函式,所以最終呼叫的函式是mpc83xx_spi_probe(struct platform_device *dev),其定義如下所示:
static int __init mpc83xx_spi_probe(struct platform_device *dev)
{
struct spi_master *master;
struct mpc83xx_spi *mpc83xx_spi;
struct fsl_spi_platform_data *pdata;
struct resource *r;
u32 regval;
int ret = 0;
/* Get resources(memory, IRQ) associated with the device */
master = spi_alloc_master(&dev->dev, sizeof(struct mpc83xx_spi));
if (master == NULL) {
ret = -ENOMEM;
goto err;
}
platform_set_drvdata(dev, master);
pdata = dev->dev.platform_data;
if (pdata == NULL) {
ret = -ENODEV;
goto free_master;
}
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (r == NULL) {
ret = -ENODEV;
goto free_master;
}
mpc83xx_spi = spi_master_get_devdata(master);
mpc83xx_spi->bitbang.master = spi_master_get(master);
mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect;
mpc83xx_spi->bitbang.setup_transfer = mpc83xx_spi_setup_transfer;
mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;
mpc83xx_spi->sysclk = pdata->sysclk;
mpc83xx_spi->activate_cs = pdata->activate_cs;
mpc83xx_spi->deactivate_cs = pdata->deactivate_cs;
mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8;
mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8;
mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup;
init_completion(&mpc83xx_spi->done);
mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1);
if (mpc83xx_spi->base == NULL) {
ret = -ENOMEM;
goto put_master;
}
mpc83xx_spi->irq = platform_get_irq(dev, 0);
if (mpc83xx_spi->irq < 0) {
ret = -ENXIO;
goto unmap_io;
}
/* Register for SPI Interrupt */
ret = request_irq(mpc83xx_spi->irq, mpc83xx_spi_irq,
0, "mpc83xx_spi", mpc83xx_spi);
if (ret != 0)
goto unmap_io;
master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->max_chipselect;
/* SPI controller initializations */
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode0, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->command, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, 0xffffffff);
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL;
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode0, regval);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0x8000100F);
ret = spi_bitbang_start(&mpc83xx_spi->bitbang);
if (ret != 0)
goto free_irq;
printk(KERN_INFO
"%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)\n",
dev->dev.bus_id, mpc83xx_spi->base, mpc83xx_spi->irq);
return ret;
free_irq:
free_irq(mpc83xx_spi->irq, mpc83xx_spi);
unmap_io:
iounmap(mpc83xx_spi->base);
put_master:
spi_master_put(master);
free_master:
kfree(master);
err:
return ret;
}
此函式的作用在於對映spi控制暫存器的基地址,註冊spi控制器的中斷號,填充mpc83xx_spi結構體以及初始化spi控制暫存器。而mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)與mpc83xx_spi_setup(struct spi_device *spi)函式在註冊spi從裝置時才被呼叫,其作用為根據從裝置傳入的引數來初始化spi控制暫存器,所以這兩個函式將放在spi從裝置驅動中進行講解。
spi主控制器驅動層基本就這些內容,而還有一些資料傳輸等函式並未進行分析,可能會再後面的文章中再進行詳細的分析。總之spi主控制器驅動層的主要功能在於註冊spi的平臺驅動,將spi的平臺驅動加入到平臺匯流排中,然後再進行spi控制暫存器的地址對映和初始化賦值。