轉載出處:http://blog.csdn.net/vanbreaker/article/details/7733476
一 、
Linux的SPI子系統採用主機驅動和外設驅動分離的思想,首先主機SPI控制器是一種平臺設備,因此它以platform的方式註冊進內核,外設的信息是以boardinfo形式靜態定義的,在創建spi_master時,會根據外設的bus_num和主機的bus_num是否相等,來選擇是否將該外設掛接在該SPI主控制器下。先看SPI子系統中幾個關鍵的數據結構:
struct spi_master用來描述一個SPI主控制器
- struct spi_master {
- struct device dev;
- s16 bus_num; /*總線編號*/
- u16 num_chipselect;/*支持的外設數量*/
- u16 dma_alignment;
- int (*transfer)(struct spi_device *spi, struct spi_message *mesg);/*用於將消息添加到隊列*/
- void (*cleanup)(struct spi_device *spi);
- };
struct spi_device用來描述一個SPI從設備
- struct spi_device {
- struct device dev;
- struct spi_master *master; /*從設備所屬的SPI主控器*/
- u32 max_speed_hz; /*最大傳輸頻率*/
- u8 chip_select; /*片選號,用於區別其他從設備*/
- u8 mode; /*傳輸模式*/
- /*各個mode的定義*/
- #define SPI_CPHA 0x01 /* clock phase */
- #define SPI_CPOL 0x02 /* clock polarity */
- #define SPI_MODE_0 (0|0) /* (original MicroWire) */
- #define SPI_MODE_1 (0|SPI_CPHA)
- #define SPI_MODE_2 (SPI_CPOL|0)
- #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
- #define SPI_CS_HIGH 0x04 /* chipselect active high? */
- #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
- #define SPI_3WIRE 0x10 /* SI/SO signals shared */
- #define SPI_LOOP 0x20 /* loopback mode */
- u8 bits_per_word; /*每個字的比特數*/
- int irq; /*所使用的中斷*/
- void *controller_state;
- void *controller_data;
- char modalias[32]; /*設備名,在和從設備驅動匹配時會用到*/
- };
struct spi_driver用來描述一個SPI從設備的驅動,它的形式和struct platform_driver是一致的
- struct spi_driver {
- int (*probe)(struct spi_device *spi);
- int (*remove)(struct spi_device *spi);
- void (*shutdown)(struct spi_device *spi);
- int (*suspend)(struct spi_device *spi, pm_message_t mesg);
- int (*resume)(struct spi_device *spi);
- struct device_driver driver;
- };
SPI子系統初始化的第一步就是將SPI總線註冊進內核,並且在/sys下創建一個spi_master的類,以後註冊的從設備都將掛接在該總線下
- 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);//註冊SPI總線
- if (status < 0)
- goto err1;
- status = class_register(&spi_master_class);//註冊spi_master類
- if (status < 0)
- goto err2;
- return 0;
- err2:
- bus_unregister(&spi_bus_type);
- err1:
- kfree(buf);
- buf = NULL;
- err0:
- return status;
- }
我們來看spi_bus_type的定義
- 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,
- };
來看掛接在SPI總線下的從設備和從設備驅動是如何匹配的,也就是spi_match_device函數
- 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;
- }
這裏可以看到是將struct device_driver中的name字段與struct spi_device中的modalias字段進行匹配
這裏已經完成了SPI子系統初始化的第一步,也就是註冊SPI總線,這一步是和平臺無關的,第二步是和平臺相關的初始化。
二、
上面介紹了SPI子系統中的一些重要數據結構和SPI子系統初始化的第一步,也就是註冊SPI總線。這節介紹針對於s3c24xx平臺的SPI子系統初始化,在看具體的代碼之前,先上一張自己畫的圖,幫助理清初始化的主要步驟
顯然,SPI是一種平臺特定的資源,所以它是以platform平臺設備的方式註冊進內核的,因此它的struct platform_device結構是已經靜態定義好了的,現在只待它的struct platform_driver註冊,然後和platform_device匹配。
初始化的入口:
- static int __init s3c24xx_spi_init(void)
- {
- return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
- }
platform_driver_probe()會調用platform_driver_register()來註冊驅動,然後在註冊的過程中尋求匹配的platform_device,一旦匹配成功,便會調用probe函數,也就是s3c24xx_spi_probe(),在看這個函數之前,還得介紹幾個相關的數據結構。
struct s3c2410_spi_info是一個板級結構,也是在移植時就定義好的,在初始化spi_master時用到,platform_device-->dev-->platform_data會指向這個結構。
- struct s3c2410_spi_info {
- int pin_cs; /* simple gpio cs */
- unsigned int num_cs; /* total chipselects */
- int bus_num;/* bus number to use. */
- void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);
- void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
- };
struct s3c24xx_spi用來具體描述s3c24xx平臺上一個SPI控制器
- struct s3c24xx_spi {
- /* bitbang has to be first */
- struct spi_bitbang bitbang;
- struct completion done;
- void __iomem *regs;
- int irq;
- int len;
- int count;
- void (*set_cs)(struct s3c2410_spi_info *spi,
- int cs, int pol);
- /* data buffers */
- const unsigned char *tx;
- unsigned char *rx;
- struct clk *clk;
- struct resource *ioarea;
- struct spi_master *master;
- struct spi_device *curdev;
- struct device *dev;
- struct s3c2410_spi_info *pdata;
- };
struct spi_bitbang用於控制實際的數據傳輸
- struct spi_bitbang {
- struct workqueue_struct *workqueue; /*工作隊列*/
- struct work_struct work;
- spinlock_t lock;
- struct list_head queue;
- u8 busy;
- u8 use_dma;
- u8 flags; /* extra spi->mode support */
- struct spi_master *master; /*bitbang所屬的master*/
- /*用於設置設備傳輸時的時鐘,字長等*/
- int (*setup_transfer)(struct spi_device *spi,
- struct spi_transfer *t);
- void (*chipselect)(struct spi_device *spi, int is_on);
- #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
- #define BITBANG_CS_INACTIVE 0
- /*針對於平臺的傳輸控制函數*/
- int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);
- /* txrx_word[SPI_MODE_*]() just looks like a shift register */
- u32 (*txrx_word[4])(struct spi_device *spi,
- unsigned nsecs,
- u32 word, u8 bits);
- };
下面來看s3c24xx_spi_probe()函數的實現
- 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;
- /*創建spi_master,並將spi_master->private_data指向s3c24xx_spi*/
- 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);//獲取s3c24xx_spi
- 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);
- /* setup the master state. */
- /*片選數和SPI主控制器編號是在platform_data中已經定義好了的*/
- master->num_chipselect = hw->pdata->num_cs;
- master->bus_num = pdata->bus_num;
- /* setup the state for the bitbang driver */
- /*設置bitbang的所屬master和控制傳輸的相關函數*/
- 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);
- /* find and map our resources */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
- err = -ENOENT;
- goto err_no_iores;
- }
- hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,
- pdev->name);
- if (hw->ioarea == NULL) {
- dev_err(&pdev->dev, "Cannot reserve region\n");
- err = -ENXIO;
- goto err_no_iores;
- }
- /*映射SPI控制寄存器*/
- hw->regs = ioremap(res->start, (res->end - res->start)+1);
- if (hw->regs == NULL) {
- dev_err(&pdev->dev, "Cannot map IO\n");
- err = -ENXIO;
- goto err_no_iomap;
- }
- /*獲取中斷號*/
- hw->irq = platform_get_irq(pdev, 0);
- if (hw->irq < 0) {
- dev_err(&pdev->dev, "No IRQ specified\n");
- err = -ENOENT;
- goto err_no_irq;
- }
- /*註冊中斷*/
- err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);
- if (err) {
- dev_err(&pdev->dev, "Cannot claim IRQ\n");
- goto err_no_irq;
- }
- hw->clk = clk_get(&pdev->dev, "spi");
- if (IS_ERR(hw->clk)) {
- dev_err(&pdev->dev, "No clock for device\n");
- err = PTR_ERR(hw->clk);
- goto err_no_clk;
- }
- /* setup any gpio we can */
- if (!pdata->set_cs) {
- if (pdata->pin_cs < 0) {
- dev_err(&pdev->dev, "No chipselect pin\n");
- goto err_register;
- }
- err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));
- if (err) {
- dev_err(&pdev->dev, "Failed to get gpio for cs\n");
- goto err_register;
- }
- hw->set_cs = s3c24xx_spi_gpiocs;//設定片選函數
- gpio_direction_output(pdata->pin_cs, 1);
- } else
- hw->set_cs = pdata->set_cs;
- s3c24xx_spi_initialsetup(hw);
- /* register our spi controller */
- /* 註冊主機SPI控制器 */
- err = spi_bitbang_start(&hw->bitbang);
- if (err) {
- dev_err(&pdev->dev, "Failed to register SPI master\n");
- goto err_register;
- }
- return 0;
- err_register:
- if (hw->set_cs == s3c24xx_spi_gpiocs)
- gpio_free(pdata->pin_cs);
- clk_disable(hw->clk);
- clk_put(hw->clk);
- err_no_clk:
- free_irq(hw->irq, hw);
- err_no_irq:
- iounmap(hw->regs);
- int spi_bitbang_start(struct spi_bitbang *bitbang)
- {
- int status;
- if (!bitbang->master || !bitbang->chipselect)
- return -EINVAL;
- /*初始化一個struct work,處理函數爲bitbang_work*/
- INIT_WORK(&bitbang->work, bitbang_work);
- spin_lock_init(&bitbang->lock);
- INIT_LIST_HEAD(&bitbang->queue);
- /*檢測bitbang中的函數是否都定義了,如果沒定義,則默認使用spi_bitbang_xxx*/
- 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;
- /* this task is the only thing to touch the SPI bits */
- bitbang->busy = 0;
- /*創建bitbang的工作隊列*/
- bitbang->workqueue = create_singlethread_workqueue(
- dev_name(bitbang->master->dev.parent));
- if (bitbang->workqueue == NULL) {
- status = -EBUSY;
- goto err1;
- }
- /* driver may get busy before register() returns, especially
- * if someone registered boardinfo for devices
- */
- /*註冊spi_master*/
- status = spi_register_master(bitbang->master);
- if (status < 0)
- goto err2;
- return status;
- err2:
- destroy_workqueue(bitbang->workqueue);
- err1:
- return status;
- }
下一個關鍵函數就是spi_register_master(),用於註冊spi_master
- int spi_register_master(struct spi_master *master)
- {
- static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
- struct device *dev = master->dev.parent;
- int status = -ENODEV;
- int dynamic = 0;
- if (!dev)
- return -ENODEV;
- /* even if it's just one always-selected device, there must
- * be at least one chipselect
- */
- if (master->num_chipselect == 0)//片選數不能爲0
- return -EINVAL;
- /* convention: dynamically assigned bus IDs count down from the max */
- if (master->bus_num < 0) {
- /* FIXME switch to an IDR based scheme, something like
- * I2C now uses, so we can't run out of "dynamic" IDs
- */
- master->bus_num = atomic_dec_return(&dyn_bus_id);
- dynamic = 1;
- }
- /* register the device, then userspace will see it.
- * registration fails if the bus ID is in use.
- */
- dev_set_name(&master->dev, "spi%u", master->bus_num);
- status = device_add(&master->dev);//添加spi_master設備
- if (status < 0)
- goto done;
- dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
- dynamic ? " (dynamic)" : "");
- /* populate children from any spi device tables */
- scan_boardinfo(master);//遍歷板級信息,尋找可以掛接在該spi_master下的從設備
- status = 0;
- done:
- return status;
- }
- 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;
- /* NOTE: this relies on spi_new_device to
- * issue diagnostics when given bogus inputs
- */
- /*bus_num相等則創建新設備*/
- (void) spi_new_device(master, chip);
- }
- }
- mutex_unlock(&board_lock);
- }
spi_board_info是板級信息,是在移植時就寫好的,並且要將其註冊
- struct spi_board_info {
- char modalias[32]; /*名字*/
- const void *platform_data;
- void *controller_data;
- int irq; /*中斷號*/
- u32 max_speed_hz; /*最高傳輸速率*/
- u16 bus_num; /*所屬的spi_master編號*/
- u16 chip_select; /*片選號*/
- u8 mode; /*傳輸模式*/
- };
最後一步就是將相應的從設備註冊進內核
- struct spi_device *spi_new_device(struct spi_master *master,
- struct spi_board_info *chip)
- {
- struct spi_device *proxy;
- int status;
- /* NOTE: caller did any chip->bus_num checks necessary.
- *
- * Also, unless we change the return value convention to use
- * error-or-pointer (not NULL-or-pointer), troubleshootability
- * suggests syslogged diagnostics are best here (ugh).
- */
- /*創建SPI_device*/
- 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;
- }
三、
最後以spidev設備驅動爲例,來闡述SPI數據傳輸的過程。spidev是內核中一個通用的設備驅動,我們註冊的從設備都可以使用該驅動,只需在註冊時將從設備的modalias字段設置爲"spidev",這樣才能和spidev驅動匹配成功。我們要傳輸的數據有時需要分爲一段一段的(比如先發送,後讀取,就需要兩個字段),每個字段都被封裝成一個transfer,N個transfer可以被添加到message中,作爲一個消息包進行傳輸。當用戶發出傳輸數據的請求時,message並不會立刻傳輸到從設備,而是由之前定義的transfer()函數將message放入一個等待隊列中,這些message會以FIFO的方式有workqueue調度進行傳輸,這樣能夠避免SPI從設備同一時間對主SPI控制器的競爭。和之前一樣,還是習慣先畫一張圖來描述數據傳輸的主要過程。
在使用spidev設備驅動時,需要先初始化spidev. spidev是以字符設備的形式註冊進內核的。
- static int __init spidev_init(void)
- {
- int status;
- /* Claim our 256 reserved device numbers. Then register a class
- * that will key udev/mdev to add/remove /dev nodes. Last, register
- * the driver which manages those device numbers.
- */
- BUILD_BUG_ON(N_SPI_MINORS > 256);
- /*將spidev作爲字符設備註冊*/
- status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
- if (status < 0)
- return status;
- /*創建spidev類*/
- 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);
- }
- /*註冊spidev的driver,可與modalias字段爲"spidev"的spi_device匹配*/
- status = spi_register_driver(&spidev_spi);
- if (status < 0) {
- class_destroy(spidev_class);
- unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
- }
- return status;
- }
與相應的從設備匹配成功後,則調用spidev中的probe函數
- static int spidev_probe(struct spi_device *spi)
- {
- struct spidev_data *spidev;
- int status;
- unsigned long minor;
- /* Allocate driver data */
- spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
- if (!spidev)
- return -ENOMEM;
- /* Initialize the driver data */
- spidev->spi = spi;//設定spi
- spin_lock_init(&spidev->spi_lock);
- mutex_init(&spidev->buf_lock);
- INIT_LIST_HEAD(&spidev->device_entry);
- /* If we can allocate a minor number, hook up this device.
- * Reusing minors is fine so long as udev or mdev is working.
- */
- mutex_lock(&device_list_lock);
- minor = find_first_zero_bit(minors, N_SPI_MINORS);//尋找沒被佔用的次設備號
- if (minor < N_SPI_MINORS) {
- struct device *dev;
- /*計算設備號*/
- spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
- /*在spidev_class下創建設備*/
- dev = device_create(spidev_class, &spi->dev, spidev->devt,
- spidev, "spidev%d.%d",
- spi->master->bus_num, spi->chip_select);
- status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
- } else {
- dev_dbg(&spi->dev, "no minor number available!\n");
- status = -ENODEV;
- }
- if (status == 0) {
- set_bit(minor, minors);//將minors的相應位置位,表示該位對應的次設備號已被佔用
- list_add(&spidev->device_entry, &device_list);//將創建的spidev添加到device_list
- }
- mutex_unlock(&device_list_lock);
- if (status == 0)
- spi_set_drvdata(spi, spidev);
- else
- kfree(spidev);
- return status;
- }
然後就可以利用spidev模塊提供的接口來實現主從設備之間的數據傳輸了。我們以spidev_write()函數爲例來分析數據傳輸的過程,實際上spidev_read()和其是差不多的,只是前面的一些步驟不一樣,可以參照上圖。
- static ssize_t
- spidev_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos)
- {
- struct spidev_data *spidev;
- ssize_t status = 0;
- unsigned long missing;
- /* chipselect only toggles at start or end of operation */
- if (count > bufsiz)
- return -EMSGSIZE;
- spidev = filp->private_data;
- mutex_lock(&spidev->buf_lock);
- //將用戶要發送的數據拷貝到spidev->buffer
- missing = copy_from_user(spidev->buffer, buf, count);
- if (missing == 0) {//全部拷貝成功,則調用spidev_sysn_write()
- status = spidev_sync_write(spidev, count);
- } else
- status = -EFAULT;
- mutex_unlock(&spidev->buf_lock);
- return status;
- }
- static inline ssize_t
- spidev_sync_write(struct spidev_data *spidev, size_t len)
- {
- struct spi_transfer t = {//設置傳輸字段
- .tx_buf = spidev->buffer,
- .len = len,
- };
- struct spi_message m;//創建message
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);//將transfer添加到message中
- return spidev_sync(spidev, &m);
- }
我們來看看struct spi_transfer和struct spi_message是如何定義的
- struct spi_transfer {
- /* it's ok if tx_buf == rx_buf (right?)
- * for MicroWire, one buffer must be null
- * buffers must work with dma_*map_single() calls, unless
- * spi_message.is_dma_mapped reports a pre-existing mapping
- */
- const void *tx_buf;//發送緩衝區
- void *rx_buf;//接收緩衝區
- unsigned len; //傳輸數據的長度
- dma_addr_t tx_dma;
- dma_addr_t rx_dma;
- unsigned cs_change:1; //該位如果爲1,則表示當該transfer傳輸完後,改變片選信號
- u8 bits_per_word;//字比特數
- u16 delay_usecs; //傳輸後的延時
- u32 speed_hz; //指定的時鐘
- struct list_head transfer_list;//用於將該transfer鏈入message
- };
- struct spi_message {
- struct list_head transfers;//用於鏈接spi_transfer
- struct spi_device *spi; //指向目的從設備
- unsigned is_dma_mapped:1;
- /* REVISIT: we might want a flag affecting the behavior of the
- * last transfer ... allowing things like "read 16 bit length L"
- * immediately followed by "read L bytes". Basically imposing
- * a specific message scheduling algorithm.
- *
- * Some controller drivers (message-at-a-time queue processing)
- * could provide that as their default scheduling algorithm. But
- * others (with multi-message pipelines) could need a flag to
- * tell them about such special cases.
- */
- /* completion is reported through a callback */
- void (*complete)(void *context);//用於異步傳輸完成時調用的回調函數
- void *context; //回調函數的參數
- unsigned actual_length; //實際傳輸的長度
- int status;
- /* for optional use by whatever driver currently owns the
- * spi_message ... between calls to spi_async and then later
- * complete(), that's the spi_master controller driver.
- */
- struct list_head queue; //用於將該message鏈入bitbang等待隊列
- void *state;
- };
繼續跟蹤源碼,進入spidev_sync(),從這一步開始,read和write就完全一樣了
- <span style="font-size:12px;">static ssize_t
- spidev_sync(struct spidev_data *spidev, struct spi_message *message)
- {
- DECLARE_COMPLETION_ONSTACK(done);
- int status;
- message->complete = spidev_complete;//設置回調函數
- message->context = &done;
- spin_lock_irq(&spidev->spi_lock);
- if (spidev->spi == NULL)
- status = -ESHUTDOWN;
- else
- status = spi_async(spidev->spi, message);//調用spi核心層的函數spi_async()
- spin_unlock_irq(&spidev->spi_lock);
- if (status == 0) {
- wait_for_completion(&done);
- status = message->status;
- if (status == 0)
- status = message->actual_length;
- }
- return status;
- }</span>
- static inline int
- spi_async(struct spi_device *spi, struct spi_message *message)
- {
- message->spi = spi;
- /*調用master的transfer函數將message放入等待隊列*/
- return spi->master->transfer(spi, message);
- }
s3c24xx平臺下的transfer函數是在bitbang_start()函數中定義的,爲bitbang_transfer()
- int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
- {
- struct spi_bitbang *bitbang;
- unsigned long flags;
- int status = 0;
- m->actual_length = 0;
- m->status = -EINPROGRESS;
- bitbang = spi_master_get_devdata(spi->master);
- spin_lock_irqsave(&bitbang->lock, flags);
- if (!spi->max_speed_hz)
- status = -ENETDOWN;
- else {
- list_add_tail(&m->queue, &bitbang->queue);//將message添加到bitbang的等待隊列
- queue_work(bitbang->workqueue, &bitbang->work);//調度運行work
- }
- spin_unlock_irqrestore(&bitbang->lock, flags);
- return status;
- }
這裏可以看到transfer函數不負責實際的數據傳輸,而是將message添加到等待隊列中。同樣在spi_bitbang_start()中,有這樣一個定義INIT_WORK(&bitbang->work, bitbang_work);因此bitbang_work()函數會被調度運行,類似於底半部機制
- static void bitbang_work(struct work_struct *work)
- {
- struct spi_bitbang *bitbang =
- container_of(work, struct spi_bitbang, work);//獲取bitbang
- unsigned long flags;
- spin_lock_irqsave(&bitbang->lock, flags);
- bitbang->busy = 1;
- while (!list_empty(&bitbang->queue)) {//等待隊列不爲空
- struct spi_message *m;
- struct spi_device *spi;
- unsigned nsecs;
- struct spi_transfer *t = NULL;
- unsigned tmp;
- unsigned cs_change;
- int status;
- int (*setup_transfer)(struct spi_device *,
- struct spi_transfer *);
- /*取出等待隊列中的的第一個message*/
- m = container_of(bitbang->queue.next, struct spi_message,
- queue);
- list_del_init(&m->queue);//將message從隊列中刪除
- spin_unlock_irqrestore(&bitbang->lock, flags);
- /* FIXME this is made-up ... the correct value is known to
- * word-at-a-time bitbang code, and presumably chipselect()
- * should enforce these requirements too?
- */
- nsecs = 100;
- spi = m->spi;
- tmp = 0;
- cs_change = 1;
- status = 0;
- setup_transfer = NULL;
- /*遍歷message中的所有傳輸字段,逐一進行傳輸*/
- list_for_each_entry (t, &m->transfers, transfer_list) {
- /* override or restore speed and wordsize */
- if (t->speed_hz || t->bits_per_word) {
- setup_transfer = bitbang->setup_transfer;
- if (!setup_transfer) {
- status = -ENOPROTOOPT;
- break;
- }
- }
- /*調用setup_transfer根據transfer中的信息進行時鐘、字比特數的設定*/
- if (setup_transfer) {
- status = setup_transfer(spi, t);
- if (status < 0)
- break;
- }
- /* set up default clock polarity, and activate chip;
- * this implicitly updates clock and spi modes as
- * previously recorded for this device via setup().
- * (and also deselects any other chip that might be
- * selected ...)
- */
- if (cs_change) {//使能外設的片選
- bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
- ndelay(nsecs);
- }
- cs_change = t->cs_change;//這裏確定進行了這個字段的傳輸後是否要改變片選狀態
- if (!t->tx_buf && !t->rx_buf && t->len) {
- status = -EINVAL;
- break;
- }
- /* transfer data. the lower level code handles any
- * new dma mappings it needs. our caller always gave
- * us dma-safe buffers.
- */
- if (t->len) {
- /* REVISIT dma API still needs a designated
- * DMA_ADDR_INVALID; ~0 might be better.
- */
- if (!m->is_dma_mapped)
- t->rx_dma = t->tx_dma = 0;
- /*調用針對於平臺的傳輸函數txrx_bufs*/
- status = bitbang->txrx_bufs(spi, t);
- }
- if (status > 0)
- m->actual_length += status;
- if (status != t->len) {
- /* always report some kind of error */
- if (status >= 0)
- status = -EREMOTEIO;
- break;
- }
- status = 0;
- /* protocol tweaks before next transfer */
- /*如果要求在傳輸完一個字段後進行delay,則進行delay*/
- if (t->delay_usecs)
- udelay(t->delay_usecs);
- if (!cs_change)
- continue;
- /*最後一個字段傳輸完畢了,則跳出循環*/
- if (t->transfer_list.next == &m->transfers)
- break;
- /* sometimes a short mid-message deselect of the chip
- * may be needed to terminate a mode or command
- */
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
- m->status = status;
- m->complete(m->context);
- /* restore speed and wordsize */
- if (setup_transfer)
- setup_transfer(spi, NULL);
- /* normally deactivate chipselect ... unless no error and
- * cs_change has hinted that the next message will probably
- * be for this chip too.
- */
- if (!(status == 0 && cs_change)) {
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
- spin_lock_irqsave(&bitbang->lock, flags);
- }
- bitbang->busy = 0;
- spin_unlock_irqrestore(&bitbang->lock, flags);
- }
只要bitbang->queue等待隊列不爲空,就表示相應的SPI主控制器上還有傳輸任務沒有完成,因此bitbang_work()會被不斷地調度執行。 bitbang_work()中的工作主要是兩個循環,外循環遍歷等待隊列中的message,內循環遍歷message中的transfer,在bitbang_work()中,傳輸總是以transfer爲單位的。當選定了一個transfer後,便會調用transfer_txrx()函數,進行實際的數據傳輸,顯然這個函數是針對於平臺的SPI控制器而實現的,在s3c24xx平臺中,該函數爲s3c24xx_spi_txrx();
- static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
- {
- struct s3c24xx_spi *hw = to_hw(spi);
- dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
- t->tx_buf, t->rx_buf, t->len);
- hw->tx = t->tx_buf;//獲取發送緩衝區
- hw->rx = t->rx_buf;//獲取讀取緩存區
- hw->len = t->len; //獲取數據長度
- hw->count = 0;
- init_completion(&hw->done);//初始化完成量
- /* send the first byte */
- /*只發送第一個字節,其他的在中斷中發送(讀取)*/
- writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
- wait_for_completion(&hw->done);
- return hw->count;
- }
- static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
- {
- /*如果tx不爲空,也就是說當前是從主機向從機發送數據,則直接將tx[count]發送過去,
- 如果tx爲空,也就是說當前是從從機向主機發送數據,則向從機寫入0*/
- return hw->tx ? hw->tx[count] : 0;
- }
負責SPI數據傳輸的中斷函數:
- static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
- {
- struct s3c24xx_spi *hw = dev;
- unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);
- unsigned int count = hw->count;
- /*衝突檢測*/
- if (spsta & S3C2410_SPSTA_DCOL) {
- dev_dbg(hw->dev, "data-collision\n");
- complete(&hw->done);
- goto irq_done;
- }
- /*設備忙檢測*/
- if (!(spsta & S3C2410_SPSTA_READY)) {
- dev_dbg(hw->dev, "spi not ready for tx?\n");
- complete(&hw->done);
- goto irq_done;
- }
- hw->count++;
- if (hw->rx)//讀取數據到緩衝區
- hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
- count++;
- if (count < hw->len)//向從機寫入數據
- writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
- else//count == len,一個字段發送完成,喚醒完成量
- complete(&hw->done);
- irq_done:
- return IRQ_HANDLED;
- }
這裏可以看到一點,即使tx爲空,也就是說用戶申請的是從從設備讀取數據,也要不斷地向從設備寫入數據,只不過寫入從設備的是無效數據(0),這樣做得目的是爲了維持SPI總線上的時鐘。至此,SPI框架已分析完畢。