Linux SPI初始化及接口函數代碼細究

2012-01-08 22:11:38數據結構

      目的:我須要掌握spi驅動相關數據結構關係,及在哪部分函數中把這些數值進行底層寄存器賦值的。結合應用層函數完成spi驅動的代碼測試。已達到靈活修改的目的。app

按順序看probe函數中async

if (!pdata->set_cs)ide

則              hw->set_cs = s3c24xx_spi_gpiocs;函數

                  gpio_direction_output(pdata->pin_cs, 1);測試

因爲個人platform_device.platform_data沒設置set_cs。默認設置gpio片選。而且把pin_cs腳設置爲輸出。接着指針

s3c24xx_spi_initialsetup(hw);函數裏面有設置寄存器操做,設置默認值,代碼以下:調試

        /* for the moment, permanently enable the clock */rest

        clk_enable(hw->clk);orm

        /* program defaults into the registers */

        writeb(0xff, hw->regs + S3C2410_SPPRE);

        writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);

        writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);

再接着就是spi_add_device函數中調用s3c24xx_spi_setup,裏面有設置寄存器。

        if (!spi->bits_per_word)

                  spi->bits_per_word = 8;

因爲個人spi->bits_per_word以前都沒有定義過。因此爲0,那麼默認設置spi->bits_per_word = 8;

接着調用s3c24xx_spi_setupxfer函數第一句就是struct s3c24xx_spi *hw = to_hw(spi);分析一下。

仔細看了下to_hw就是說spi把spi_device結構轉換爲s3c24xx_spi結構。

方法就是spi(spi_device結構)先指向父指針master(spi_master結構)。

接着就是指向dev(spi_master的device成員),再接着指向driver_data(device結構的driver_data成員)爲何說這就是s3c24xx_spi的類型哪?看probe一開始的幾句代碼便可。

struct s3c24xx_spi *hw;

hw = spi_master_get_devdata(master);

memset(hw, 0, sizeof(struct s3c24xx_spi));

接着回到s3c24xx_spi_setupxfer函數下面的代碼是

        bpw = t ? t->bits_per_word : spi->bits_per_word;

        hz  = t ? t->speed_hz : spi->max_speed_hz;

因爲t傳進來是NULL,因此bpw= spi->bits_per_word;剛纔分析過了s3c24xx_spi_setup中把它設置爲默認值8。hz  = spi->max_speed_hz;在spi_add_device函數以前的spi_new_device有賦值proxy->max_speed_hz = chip->max_speed_hz;就是spi_board_info裏的賦值,我本身設置的值。至於要寫入寄存器是在接下來的hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE)中調用hw->set_cs(hw->pdata, spi->chip_select, cspol^1);(probe中hw->set_cs = s3c24xx_spi_gpiocs裏定義的) s3c24xx_spi_gpiocs函數,設置cspol值爲0。

此函數也調用div = clk_get_rate(hw->clk) / hz;設置波特率寄存器。

===========================================================bitbang_work函數中有調用bitbang->chipselect(spi, BITBANG_CS_ACTIVE);

s3c24xx_spi_probe中有定義       hw->bitbang.chipselect     = s3c24xx_spi_chipsel;

s3c24xx_spi_chipsel函數裏面有設置spi模式。數據來源是spi->mode。spi->mode又是在那裏賦值的呢?帶着問題作了以下探索

bitbang_work是在spi_bitbang_start中調用。

s3c24xx_spi_probe->spi_bitbang_start->bitbang_work但此時,spi-mode並無賦值。

賦值是在哪裏呢?換個方法,按順序查找。

s3c24xx_spi_probe->spi_bitbang_start->spi_register_master->scan_boardinfo->spi_new_device經過從頭至尾的方式查找,惟一首先出現spi_master與spi_device關係的是spi_new_device函數。進入其中調用的spi_alloc_device函數,查看註釋發現。

* Caller is responsible to call spi_add_device() on the returned

* spi_device structure to add it to the SPI master.

能夠肯定,spi->mode的首次賦值就是詞句代碼

proxy->mode = chip->mode;在bitbang_work函數中spi-mode並無賦值可是還在用是爲何呢?再仔細看代碼,原來while有個判斷條件。以前沒看到,搜索錯了方向。

在spi_new_device函數中proxy是spi_device結構。Spi->mode等都是在以下這裏賦值

        proxy->chip_select = chip->chip_select;

        proxy->max_speed_hz = chip->max_speed_hz;

        proxy->mode = chip->mode; //s3c2410_spi1_board中沒定義,則默認爲0

        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;

========================

接着就是spi_add_device函數,裏面有一句if (spi->chip_select >= spi->master->num_chipselect) 他們分別是什麼值呢?spi->master->num_chipselect在哪裏?帶着問題,開始了以下探索歷程

 

spi->chip_select在spi_board_info s3c2410_spi1_board[]結構賦值中爲0。

static struct spi_board_info s3c2410_spi1_board[] = {

        [0] = {

                 .modalias = "spidev",

                .bus_num        = 1,                 .chip_select        = 0,

                .irq = IRQ_EINT9,

                .max_speed_hz         = 2000*1000,

        },

};

在scan_boardinfo 中調用函數時候傳遞的參數是(void) spi_new_device(master, chip);master是spi_master結構。再以前的spi_register_master函數中有對spi_master的num_chipselect成員賦值的核對。代碼以下:

        if (master->num_chipselect == 0)

                  return -EINVAL;再往前找應該就能找到賦值了。

spi_bitbang_start中有對spi_register_master的調用。代碼以下:

status = spi_register_master(bitbang->master);

再往前看。s3c24xx_spi_probe函數中調用spi_bitbang_start,往上看到了master->num_chipselect的賦值語句了。代碼以下:

master->num_chipselect = hw->pdata->num_cs;

而hw->pdata = pdata = pdev->dev.platform_data;

而struct platform_device *pdev是platform_device中的platform_data結構,已經賦值了以下

static struct s3c2410_spi_info s3c2410_spi1_platdata = {

        .pin_cs = S3C2410_GPG3,

        .num_cs = 1,

        .bus_num = 1,

};

因此chip_select= 0, spi->master->num_chipselect值爲1.

經過倒序來找,從spi_add_device一直找到了s3c24xx_spi_probe,按順序寫下:

s3c24xx_spi_probe->spi_bitbang_start->spi_register_master->scan_boardinfo->spi_new_device->spi_add_device

一個接口對應一個master,一個master對應一條SPI總線,一條總線上可能掛有多個設備,num_chipselect 就表示該總線上的設備, chip_select表示該SPI設備在該條SPI總線上的設備號的惟一標識。

====================================================probe中函數綁定與調用分析

        hw->bitbang.master         = hw->master;

        hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;// s3c24xx_spi_setup中調用ret = s3c24xx_spi_setupxfer(spi, NULL);

        hw->bitbang.chipselect     = s3c24xx_spi_chipsel;//s3c24xx_spi_setupxfer中調用hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);

        hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx;// bitbang_work中調用status = bitbang->txrx_bufs(spi, t);

        hw->bitbang.master->setup  = s3c24xx_spi_setup;// spi_add_device中調用status = spi->master->setup(spi);

按probe初始化順序,則spi_add_device函數中調用

status = spi->master->setup(spi);//(s3c24xx_spi_setup)

接着s3c24xx_spi_setup函數中調用

ret = s3c24xx_spi_setupxfer(spi, NULL);

接着s3c24xx_spi_setupxfer函數中調用

hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE) 

關於如上圖的問題,怎麼HZ數一開始不是我默認設置的呢?原來是由於,用戶層函數調用的時候先是從octl函數開始的不是從probe函數開始的。

probe函數打印出來的信息徹底正確。以下圖:

 

先調用spidev_message如何調用到bitbang_work的?

spi_bitbang_start->bitbang_work接着就沒方向了,不可能到spidev_message了。因而查了網上的資料,是spi_bitbang_transfer 中的代碼以下:queue_work(bitbang->workqueue, &bitbang->work);  調用了bitbang_work函數。爲何bitbang->work指示的就是bitbang_work函數呢?原來在       spi_bitbang_start函數中的第一句代碼INIT_WORK(&bitbang->work, bitbang_work);就說明了。好了,這樣就有方向繼續摸索了。關係流程以下:

spidev_message ->spidev_sync->spi_async【spi->master->transfer(spi, message);  定義在spi_bitbang_start函數中bitbang->master->transfer = spi_bitbang_transfer;】 -> spi_bitbang_transfer->bitbang_work

===============================================================

bitbang_work函數中有句代碼

setup_transfer = bitbang->setup_transfer;

                             if (setup_transfer) {

                                       status = setup_transfer(spi, t);

bitbang->setup_transfer(spi, t)又是調用哪一個具體函數呢?

很面熟,想起來以前好像看到過在bitbang相關函數中,因而找到了spi_bitbang_start函數中有,代碼以下:

if (!bitbang->master->setup) {

                             if (!bitbang->setup_transfer)

                                       bitbang->setup_transfer =                                               spi_bitbang_setup_transfer;

原來是調用準備spi_bitbang_setup_transfer函數,先設置指針,傳遞進來的參數是(spi, t); 接着就在bitbang_work函數中調用status = setup_transfer(spi, t);即spi_bitbang_setup_transfer函數

其中與修改頻率相關的代碼以下:t是指向用戶傳遞來的數值,若是應用層函數沒有傳遞Hz值,則使用spi->max_speed_hz;就是spi_board_info中我本身定義的值。終於找到了賦值的元兇。代碼以下:

        /* nsecs = (clock period)/2 */

        if (!hz)

                  hz = spi->max_speed_hz;

        if (hz) {

                  cs->nsecs = (1000000000/2) / hz;

                  if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))

                             return -EINVAL;

        }

從打印的信息看出spi_bitbang_setup_transfer會調用s3c24xx_spi_setupxfer函數?原來以前分析錯了status = setup_transfer(spi, t);調用的probe中的s3c24xx_spi_setupxfer。由於這個if (!bitbang->master->setup) 條件不成了,因此bitbang.setup_transfer = s3c24xx_spi_setupxfer。這樣就能進入s3c24xx_spi_setupxfer函數了,而且這時候t不等於NULL。

bitbang_work函數中的struct spi_transfer  *t = NULL;那麼究竟是哪一句爲t賦值的呢?再調試。應該是list_for_each_entry (t, &m->transfers, transfer_list) 句。

繼續分析串口信息,又進入了s3c24xx_spi_setupxfer啊!bitbang_work有調用嗎?繼續看,代碼以下

                  

/* restore speed and wordsize */                    

if (setup_transfer)

                             setup_transfer(spi, NULL);

                  if (!(status == 0 && cs_change)) {

                             ndelay(nsecs);

                             bitbang->chipselect(spi, BITBANG_CS_INACTIVE);

                             ndelay(nsecs);

                  }

天呢!原來回複數據。也就是說個人這次spi自收發,用的不是初始化的值。緣由是在應用層函數中傳入了if (t->speed_hz || t->bits_per_word) 判斷條件中的2個值。若是不傳這2個值,那麼應該就用的是我在kernel中設置的默認值了。試一下,名稱爲spidev_test5

見下圖,效果和想象的是一致的。speed_hz與bits_per_word我在應用層註釋掉了。就沒調用以下代碼段,途中的「ready to change freq」信息應該寫成finish to change freq and other比較好,呵呵。 

                             if (t->speed_hz || t->bits_per_word) {

                                       printk("go bitbang_work 1\n");//by apple

                                       setup_transfer = bitbang->setup_transfer;

                                       if (!setup_transfer) {

                                                  status = -ENOPROTOOPT;

                                                  break;

                                       }

                             }

                             if (setup_transfer) {

                                       printk("setup_transfer\n"); //by apple

                                       printk("NULL addr is 0x%2x,t addr is 0x%2x ",NULL,t); //by apple

                                       status = setup_transfer(spi, t);

                                       if (status < 0)

                                                  break;

                             }

備註:個人spidev_test函數的主函數中write和read函數都被我註釋掉了。直接先transfer(fd);接着在read 2個寄存器後打印出來mode和speed。


上圖1:

 

 

 上圖2:

 

 

 上圖3:

 

相關文章
相關標籤/搜索