生活随笔
收集整理的這篇文章主要介紹了
Linux驱动修炼之道-SPI驱动框架源码分析(中)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
來自:http://blog.csdn.NET/woshixingaaa/article/details/6574220
這篇來分析spi子系統(tǒng)的建立過程。
嵌入式微處理器訪問SPI設(shè)備有兩種方式:使用GPIO模擬SPI接口的工作時序或者使用SPI控制器。使用GPIO模擬SPI接口的工作時序是非常容易實現(xiàn)的,但是會導(dǎo)致大量的時間耗費在模擬SPI接口的時序上,訪問效率比較低,容易成為系統(tǒng)瓶頸。這里主要分析使用SPI控制器的情況。
這個是由sys文件系統(tǒng)導(dǎo)出的spi子系統(tǒng)在內(nèi)核中的視圖了。
首先了解一下Linux內(nèi)核中的幾個文件:spi.c也就是spi子系統(tǒng)的核心了,spi_s3c24xx.c是s3c24xx系列芯片的SPI controller驅(qū)動,它向更上層的SPI核心層(spi.c)提供接口用來控制芯片的SPI controller,是一個被其他驅(qū)動使用的驅(qū)動。而spidev.c是在核心層基礎(chǔ)之上將SPI controller模擬成一個字符型的驅(qū)動,向文件系統(tǒng)提供標準的文件系統(tǒng)接口,用來操作對應(yīng)的SPI controller。
下面我們來看看spi子系統(tǒng)是怎么注冊進內(nèi)核的:
view plain
static?int?__init?spi_init(void)?? {?? ????int?status;?? ????buf?=?kmalloc(SPI_BUFSIZ,?GFP_KERNEL);?? ????if?(!buf)?{?? ????????status?=?-ENOMEM;?? ????????goto?err0;?? ????}?? ????status?=?bus_register(&spi_bus_type);?? ????if?(status?<?0)?? ????????goto?err1;?? ????status?=?class_register(&spi_master_class);?? ????if?(status?<?0)?? ????????goto?err2;?? ????return?0;?? err2:?? ????bus_unregister(&spi_bus_type);?? err1:?? ????kfree(buf);?? ????buf?=?NULL;?? err0:?? ????return?status;?? }?? postcore_initcall(spi_init);??
這里注冊了一個spi_bus_type,也就是一個spi總線,和一個spi_master的class。分別對應(yīng)上圖中sys/bus/下的spi目錄和sys/class/下的spi_master目錄。
下面來分析SPI controller驅(qū)動的注冊與初始化過程,首先執(zhí)行的是s3c24xx_spi_init。
view plain
static?int?__init?s3c24xx_spi_init(void)?? {?? ????????return?platform_driver_probe(&s3c24xx_spi_driver,?s3c24xx_spi_probe);?? }??
platform_driver_probe中完成了s3c24xx_spi_driver這個平臺驅(qū)動的注冊,相應(yīng)的平臺設(shè)備在devs.c中定義,在smdk2440_devices中添加&s3c_device_spi0,&s3c_device_spi1,這就生成了圖中所示的s3c24xx-spi.0與s3c24xx-spi.1,當然了這圖是在網(wǎng)上找的,不是我畫的,所以是6410的。這里s3c24xx-spi.0表示s3c2440的spi controller的0號接口,s3c24xx-spi.1表示s3c2440的spi controller的1號接口。注冊了s3c24xx_spi_driver后,賦值了平臺驅(qū)動的probe函數(shù)為s3c24xx_spi_probe。所以當match成功后,調(diào)用s3c24xx_spi_probe,這里看其實現(xiàn):
view plain
<span?style="font-size:18px;">static?int?__init?s3c24xx_spi_probe(struct?platform_device?*pdev)?? {?? ????struct?s3c2410_spi_info?*pdata;?? ????struct?s3c24xx_spi?*hw;?? ????struct?spi_master?*master;?? ????struct?resource?*res;?? ????int?err?=?0;?? ?????? ????master?=?spi_alloc_master(&pdev->dev,?sizeof(struct?s3c24xx_spi));?? ????if?(master?==?NULL)?{?? ????????dev_err(&pdev->dev,?"No?memory?for?spi_master\n");?? ????????err?=?-ENOMEM;?? ????????goto?err_nomem;?? ????}?? ?????? ????hw?=?spi_master_get_devdata(master);?? ????memset(hw,?0,?sizeof(struct?s3c24xx_spi));?? ?? ?? ????hw->master?=?spi_master_get(master);?? ?????? ????hw->pdata?=?pdata?=?pdev->dev.platform_data;?? ????hw->dev?=?&pdev->dev;?? ?? ?? ????if?(pdata?==?NULL)?{?? ????????dev_err(&pdev->dev,?"No?platform?data?supplied\n");?? ????????err?=?-ENOENT;?? ????????goto?err_no_pdata;?? ????}?? ?????? ????platform_set_drvdata(pdev,?hw);?? ????init_completion(&hw->done);?? ?? ?? ?????? ?????? ????master->num_chipselect?=?hw->pdata->num_cs;?? ???????? ????master->bus_num?=?pdata->bus_num;?? ?? ?? ?????? ?????? ????hw->bitbang.master?????????=?hw->master;?? ????hw->bitbang.setup_transfer?=?s3c24xx_spi_setupxfer;?? ????hw->bitbang.chipselect?????=?s3c24xx_spi_chipsel;?? ????hw->bitbang.txrx_bufs??????=?s3c24xx_spi_txrx;?? ????hw->bitbang.master->setup??=?s3c24xx_spi_setup;?? ?? ?? ????dev_dbg(hw->dev,?"bitbang?at?%p\n",?&hw->bitbang);?? ??????? ????。。。。。。。。。。。。。。。。。。。。。。。。?? ?????? ?????? ????s3c24xx_spi_initialsetup(hw);?? ?? ?? ?????? ?? ?? ????err?=?spi_bitbang_start(&hw->bitbang);?? ????????。。。。。。。。。。。。。。。。。。。。。?? }?? spi?controller的register在spi_bitbang_start函數(shù)中實現(xiàn):?? int?spi_bitbang_start(struct?spi_bitbang?*bitbang)?? {?? ????int?status;?? ?? ?? ????if?(!bitbang->master?||?!bitbang->chipselect)?? ????????return?-EINVAL;?? ?????? ????INIT_WORK(&bitbang->work,?bitbang_work);?? ????spin_lock_init(&bitbang->lock);?? ????INIT_LIST_HEAD(&bitbang->queue);?? ?????? ????if?(!bitbang->master->transfer)?? ????????bitbang->master->transfer?=?spi_bitbang_transfer;?? ????if?(!bitbang->txrx_bufs)?{?? ????????bitbang->use_dma?=?0;?? ?????????? ????????bitbang->txrx_bufs?=?spi_bitbang_bufs;?? ????????if?(!bitbang->master->setup)?{?? ????????????if?(!bitbang->setup_transfer)?? ????????????????bitbang->setup_transfer?=?? ?????????????????????spi_bitbang_setup_transfer;?? ?????????????? ????????????bitbang->master->setup?=?spi_bitbang_setup;?? ????????????bitbang->master->cleanup?=?spi_bitbang_cleanup;?? ????????}?? ????}?else?if?(!bitbang->master->setup)?? ????????return?-EINVAL;?? ?? ?? ?????? ????bitbang->busy?=?0;?? ????/調(diào)用create_singlethread_workqueue創(chuàng)建單個工作線程/?? ????bitbang->workqueue?=?create_singlethread_workqueue(?? ????????????dev_name(bitbang->master->dev.parent));?? ????if?(bitbang->workqueue?==?NULL)?{?? ????????status?=?-EBUSY;?? ????????goto?err1;?? ????}?? ????status?=?spi_register_master(bitbang->master);?? ????if?(status?<?0)?? ????????goto?err2;?? ????return?status;?? err2:?? ????destroy_workqueue(bitbang->workqueue);?? err1:?? ????return?status;?? }</span>??
然后看這里是怎樣注冊spi主機控制器驅(qū)動的:
view plain
int?spi_register_master(struct?spi_master?*master)?? {?? ????。。。。。。。。。。。。。。。。?? ?????? ????dev_set_name(&master->dev,?"spi%u",?master->bus_num);?? ????status?=?device_add(&master->dev);?? ????scan_boardinfo(master);?? }??
這里跟蹤scan_boardinfo函數(shù):
view plain
static?void?scan_boardinfo(struct?spi_master?*master)?? {?? ????struct?boardinfo????*bi;?? mutex_lock(&board_lock);?? ?????? ????list_for_each_entry(bi,?&board_list,?list)?{?? ????????struct?spi_board_info???*chip?=?bi->board_info;?? ????????unsigned????n;?? ?????????? ????????for?(n?=?bi->n_board_info;?n?>?0;?n--,?chip++)?{?? ????????????if?(chip->bus_num?!=?master->bus_num)?? ????????????????continue;?? ????????????(void)?spi_new_device(master,?chip);?? ????????}?? ????}?? ????mutex_unlock(&board_lock);?? }??
在移植的時候我們會在mach-smdk2440.c中的smdk2440_machine_init中添加spi_register_board_info
這個函數(shù)完成了將spi_board_info交由boardinfo管理,并把boardinfo掛載到board_list鏈表上。也就是說在系統(tǒng)初始化的時候?qū)pi_device交由到掛在board_list上的boardinfo管理,在spi controller的driver注冊的時候不但注冊這個主機控制器的驅(qū)動,還要遍歷這個主機控制器的總線上的spi_device,將總線上的spi_device全部注冊進內(nèi)核。當注冊進內(nèi)核并且spi_driver已經(jīng)注冊的時候,如果總線match成功,則會調(diào)用spi_driver的probe函數(shù),這個將在后邊進行分析。
view plain
<span?style="font-size:18px;">int?__init?? spi_register_board_info(struct?spi_board_info?const?*info,?unsigned?n)?? {?? ????struct?boardinfo????*bi;?? ?? ?? ????bi?=?kmalloc(sizeof(*bi)?+?n?*?sizeof?*info,?GFP_KERNEL);?? ????if?(!bi)?? ????????return?-ENOMEM;?? ????bi->n_board_info?=?n;?? ????memcpy(bi->board_info,?info,?n?*?sizeof?*info);?? ?? ?? ????mutex_lock(&board_lock);?? ????list_add_tail(&bi->list,?&board_list);?? ????mutex_unlock(&board_lock);?? ????return?0;?? }</span>??
看一下創(chuàng)建新設(shè)備的函數(shù):
view plain
<span?style="font-size:18px;">struct?spi_device?*spi_new_device(struct?spi_master?*master,?? ??????????????????struct?spi_board_info?*chip)?? {?? ????struct?spi_device???*proxy;?? ????int?????????status;?? ????proxy?=?spi_alloc_device(master);?? ????if?(!proxy)?? ????????return?NULL;?? ?? ?? ????WARN_ON(strlen(chip->modalias)?>=?sizeof(proxy->modalias));?? ?????? ????proxy->chip_select?=?chip->chip_select;?? ????proxy->max_speed_hz?=?chip->max_speed_hz;?? ????proxy->mode?=?chip->mode;?? ????proxy->irq?=?chip->irq;?? ?????? ????strlcpy(proxy->modalias,?chip->modalias,?sizeof(proxy->modalias));?? ????proxy->dev.platform_data?=?(void?*)?chip->platform_data;?? ????proxy->controller_data?=?chip->controller_data;?? ????proxy->controller_state?=?NULL;?? ?????? ????status?=?spi_add_device(proxy);?? ????if?(status?<?0)?{?? ????????spi_dev_put(proxy);?? ????????return?NULL;?? ????}?? ?? ?? ????return?proxy;?? }</span>??
下面來看分配spi_alloc_device的函數(shù),主要完成了分配spi_device,并初始化spi->dev的一些字段。
view plain
struct?spi_device?*spi_alloc_device(struct?spi_master?*master)?? {?? ????struct?spi_device???*spi;?? ????struct?device???????*dev?=?master->dev.parent;?? ????if?(!spi_master_get(master))?? ????????return?NULL;?? ????spi?=?kzalloc(sizeof?*spi,?GFP_KERNEL);?? ????if?(!spi)?{?? ????????dev_err(dev,?"cannot?alloc?spi_device\n");?? ????????spi_master_put(master);?? ????????return?NULL;?? ????}?? ????spi->master?=?master;?? ????spi->dev.parent?=?dev;?? ?????? ????spi->dev.bus?=?&spi_bus_type;?? ????spi->dev.release?=?spidev_release;?? ????device_initialize(&spi->dev);?? ????return?spi;?? }??
下面來看分配的這個spi_device是怎樣注冊進內(nèi)核的:
view plain
int?spi_add_device(struct?spi_device?*spi)?? {?? ????static?DEFINE_MUTEX(spi_add_lock);?? ????struct?device?*dev?=?spi->master->dev.parent;?? ????int?status;?? ?????? ????if?(spi->chip_select?>=?spi->master->num_chipselect)?{?? ????????dev_err(dev,?"cs%d?>=?max?%d\n",?? ????????????spi->chip_select,?? ????????????spi->master->num_chipselect);?? ????????return?-EINVAL;?? ????}?? ?????? ????dev_set_name(&spi->dev,?"%s.%u",?dev_name(&spi->master->dev),?? ????????????spi->chip_select);?? ????mutex_lock(&spi_add_lock);?? ?????? ????if?(bus_find_device_by_name(&spi_bus_type,?NULL,?dev_name(&spi->dev))?? ????????????!=?NULL)?{?? ????????dev_err(dev,?"chipselect?%d?already?in?use\n",?? ????????????????spi->chip_select);?? ????????status?=?-EBUSY;?? ????????goto?done;?? ????}?? ????/對spi_device的時鐘等進行設(shè)置/?? ????status?=?spi->master->setup(spi);?? ????if?(status?<?0)?{?? ????????dev_err(dev,?"can't?%s?%s,?status?%d\n",?? ????????????????"setup",?dev_name(&spi->dev),?status);?? ????????goto?done;?? ????}?? ?????? ????status?=?device_add(&spi->dev);?? ????if?(status?<?0)?? ????????dev_err(dev,?"can't?%s?%s,?status?%d\n",?? ????????????????"add",?dev_name(&spi->dev),?status);?? ????else?? ????????dev_dbg(dev,?"registered?child?%s\n",?dev_name(&spi->dev));?? ?? ?? done:?? ????mutex_unlock(&spi_add_lock);?? ????return?status;?? }?? ?? ?? static?int?s3c24xx_spi_setup(struct?spi_device?*spi)?? {?? ????。。。。。。。。。。。。。。?? ????ret?=?s3c24xx_spi_setupxfer(spi,?NULL);?? ????。。。。。。。。。。。。。。?? }?? ?? ?? static?int?s3c24xx_spi_setupxfer(struct?spi_device?*spi,?? ?????????????????struct?spi_transfer?*t)?? {?? ????struct?s3c24xx_spi?*hw?=?to_hw(spi);?? ????unsigned?int?bpw;?? ????unsigned?int?hz;?? ????unsigned?int?div;?? ?????? ????bpw?=?t???t->bits_per_word?:?spi->bits_per_word;?? ????hz??=?t???t->speed_hz?:?spi->max_speed_hz;?? ?? ?? ????if?(bpw?!=?8)?{?? ????????dev_err(&spi->dev,?"invalid?bits-per-word?(%d)\n",?bpw);?? ????????return?-EINVAL;?? ????}?? ?????? ????div?=?clk_get_rate(hw->clk)?/?hz;?? ?? ?? ????? ?? ?? ?? ????div?/=?2;?? ?? ?? ????if?(div?>?0)?? ????????div?-=?1;?? ?? ?? ????if?(div?>?255)?? ????????div?=?255;?? ?? ?? ????dev_dbg(&spi->dev,?"setting?pre-scaler?to?%d?(hz?%d)\n",?div,?hz);?? ????writeb(div,?hw->regs?+?S3C2410_SPPRE);?? ?? ?? ????spin_lock(&hw->bitbang.lock);?? ????if?(!hw->bitbang.busy)?{?? ????????hw->bitbang.chipselect(spi,?BITBANG_CS_INACTIVE);?? ?????????? ????}?? ????spin_unlock(&hw->bitbang.lock);?? ?? ?? ????return?0;?? }??
下面來看這個spi_driver是怎樣注冊的,又是與spi_device怎樣match上的。
在spidev.c中:
view plain
static?int?__init?spidev_init(void)?? {?? ????int?status;?? ????BUILD_BUG_ON(N_SPI_MINORS?>?256);?? ????status?=?register_chrdev(SPIDEV_MAJOR,?"spi",?&spidev_fops);?? ????if?(status?<?0)?? ????????return?status;?? ????spidev_class?=?class_create(THIS_MODULE,?"spidev");?? ????if?(IS_ERR(spidev_class))?{?? ????????unregister_chrdev(SPIDEV_MAJOR,?spidev_spi.driver.name);?? ????????return?PTR_ERR(spidev_class);?? ????}?? ????status?=?spi_register_driver(&spidev_spi);?? ????if?(status?<?0)?{?? ????????class_destroy(spidev_class);?? ????????unregister_chrdev(SPIDEV_MAJOR,?spidev_spi.driver.name);?? ????}?? ????return?status;?? }??
注冊了名為”spi”的字符驅(qū)動,然后注冊了spidev_spi驅(qū)動,這個就是圖中sys/Bus/Spi/Drivers/下的spidev。
view plain
static?struct?spi_driver?spidev_spi?=?{?? ????.driver?=?{?? ????????.name?=?????"spidev",?? ????????.owner?=????THIS_MODULE,?? ????},?? ????.probe?=????spidev_probe,?? ????.remove?=???__devexit_p(spidev_remove),?? };??
view plain
static?struct?spi_driver?spidev_spi?=?{?? ????.driver?=?{?? ????????.name?=?????"spidev",?? ????????.owner?=????THIS_MODULE,?? ????},?? ????.probe?=????spidev_probe,?? ????.remove?=???__devexit_p(spidev_remove),?? };??
這里來看__driver_attach這個函數(shù),其中分別調(diào)用了driver_match_device,driver_probe_device函數(shù)。如果匹配成果調(diào)用probe函數(shù),否則返回。
view plain
static?int?__driver_attach(struct?device?*dev,?void?*data)???? {???? ????struct?device_driver?*drv?=?data;???? ????if?(!driver_match_device(drv,?dev))???? ????????return?0;???? ???? ????if?(dev->parent)????? ????????down(&dev->parent->sem);???? ????down(&dev->sem);???? ????if?(!dev->driver)???? ????????driver_probe_device(drv,?dev);???? ????up(&dev->sem);???? ????if?(dev->parent)???? ????????up(&dev->parent->sem);???? ???? ????return?0;???? }????
匹配的時候調(diào)用的bus的match函數(shù)。
view plain
struct?bus_type?spi_bus_type?=?{?? ???????.name?????????????=?"spi",?? ???????.dev_attrs???????=?spi_dev_attrs,?? ???????.match???????????=?spi_match_device,?? ???????.uevent???????????=?spi_uevent,?? ???????.suspend??=?spi_suspend,?? ???????.resume??????????=?spi_resume,?? };?? static?int?spi_match_device(struct?device?*dev,?struct?device_driver?*drv)?? {?? ????const?struct?spi_device?*spi?=?to_spi_device(dev);?? ?? ?? ????return?strcmp(spi->modalias,?drv->name)?==?0;?? }??
可以看到這里根據(jù)驅(qū)動和設(shè)備的名字進行匹配,匹配成功后調(diào)用驅(qū)動的probe函數(shù)。
view plain
static?int?spi_drv_probe(struct?device?*dev)?? {?? ????const?struct?spi_driver?????*sdrv?=?to_spi_driver(dev->driver);?? ?? ?? ????return?sdrv->probe(to_spi_device(dev));?? }??
可以看大調(diào)用了具體的probe函數(shù),這里實現(xiàn)了把spidev添加到device_list,這樣這個虛擬的字符驅(qū)動就注冊并初始化完畢。
view plain
static?int?spidev_remove(struct?spi_device?*spi)?? {?? ????struct?spidev_data??*spidev?=?spi_get_drvdata(spi);?? ?? ?? ?????? ????spin_lock_irq(&spidev->spi_lock);?? ????spidev->spi?=?NULL;?? ????spi_set_drvdata(spi,?NULL);?? ????spin_unlock_irq(&spidev->spi_lock);?? ?? ?? ?????? ????mutex_lock(&device_list_lock);?? ????list_del(&spidev->device_entry);?? ????device_destroy(spidev_class,?spidev->devt);?? ????clear_bit(MINOR(spidev->devt),?minors);?? ????if?(spidev->users?==?0)?? ????????kfree(spidev);?? ????mutex_unlock(&device_list_lock);?? ?? ?? ????return?0;?? }??
在spidev的注冊函數(shù)中注冊了文件操作集合file_operations,為用戶空間提供了操作SPI controller的接口。
view plain
static?struct?file_operations?spidev_fops?=?{?? ????.owner?=????THIS_MODULE,?? ????? ? ? ?? ????.write?=????spidev_write,?? ????.read?=?????spidev_read,?? ????.unlocked_ioctl?=?spidev_ioctl,?? ????.open?=?????spidev_open,?? ????.release?=??spidev_release,?? };??
到此為止spi子系統(tǒng)與spi_master,spi_device,spi_driver這個Linux設(shè)備驅(qū)動模型已經(jīng)建立完了。
總結(jié)
以上是生活随笔為你收集整理的Linux驱动修炼之道-SPI驱动框架源码分析(中)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。