linux SPI bitbang 小结

前段时间同事调试 SPI 驱动, 遇到个问题来问我, 帮忙澄清了并顺便看了下SPI bitbang的驱动代码流程, 小结在此. 内核版本 3.6.4

本文所有文字都是原创, 转发请注明出处.


她的问题是这样的" 如果bitbang结构体不是排在第一个的话, 会有kernel warning出现." 以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;

    struct fiq_handler     fiq_handler;
    enum spi_fiq_mode     fiq_mode;
    unsigned char         fiq_inuse;
    unsigned char         fiq_claimed;

    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;
};


如果不放在结构体中第一个成员的位置的话, 就会出kernel warning.

------------[ cut here ]------------
WARNING: at kernel/workqueue.c:991 __queue_work+0x338/0x36c()   
Modules linked in:
Call Trace:
[<806ac75c>] dump_stack+0x8/0x34
[<80123e6c>] warn_slowpath_common+0x78/0xa4
[<80123eb0>] warn_slowpath_null+0x18/0x24
[<80143714>] __queue_work+0x338/0x36c
[<801437b4>] queue_work_on+0x44/0x8c
[<803c230c>] spi_bitbang_transfer+0xa4/0xd8
[<803c02b0>] spi_async_locked+0x20/0x4c
[<803c0700>] __spi_sync+0x54/0xbc
[<803d3e1c>] mmc_spi_readbytes+0x54/0xc8
[<803d3ee8>] mmc_spi_skip+0x58/0xf4
[<803d40d0>] mmc_spi_set_ios+0x14c/0x434
[<803c6520>] mmc_power_up+0x1e0/0x268
[<803c68b8>] mmc_start_host+0x30/0x58
[<803c742c>] mmc_add_host+0x64/0xa0
[<803d5ec8>] mmc_spi_probe+0x3bc/0x60c
[<8038d5e4>] driver_probe_device+0x118/0x498
[<8038da20>] __driver_attach+0xbc/0xc4
[<8038b808>] bus_for_each_dev+0x70/0xac
[<8038c1e0>] bus_add_driver+0x1d0/0x2bc
[<8038e204>] driver_register+0x74/0x17c
[<80100440>] do_one_initcall+0x40/0x1e0

---[ end trace fe3505dcf96ad417 ]---

分析方式不外乎顺推和倒推, 从workqueue倒退, 检查这个flag, 但是这个flag根本不会被置起来, 说明不是workqueue机制的问题.
那就顺推, 检查spi_bitbang的驱动, 结果发现在spi-bitbang.c的驱动中, 函数spi_bitbang_setup和spi_bitbang_transfer都调用到了

spi_master_get_devdata 去得到spi_bitbang, 但是实际上在spi_master_set_devdata的时候, 是整个结构体的.


int spi_bitbang_setup(struct spi_device *spi)
{
    struct spi_bitbang_cs    *cs = spi->controller_state;
    struct spi_bitbang    *bitbang;
    int            retval;
    unsigned long        flags;

    bitbang = spi_master_get_devdata(spi->master);

    if (!cs) {
        cs = kzalloc(sizeof *cs, GFP_KERNEL);
        if (!cs)
            return -ENOMEM;
        spi->controller_state = cs;
    }
...

}


struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
    struct spi_master    *master;

    if (!dev)
        return NULL;

    master = kzalloc(size + sizeof *master, GFP_KERNEL);
    if (!master)
        return NULL;

    device_initialize(&master->dev);
    master->bus_num = -1;
    master->num_chipselect = 1;
    master->dev.class = &spi_master_class;
    master->dev.parent = get_device(dev);
    spi_master_set_devdata(master, &master[1]); // 这个master[1] 其实就是struct s3c24xx_spi, 所以要求bitbang是第一个成员

    return master;
}


static int __devinit 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;
    }

...

}


由于这个隐式转换的问题, 所以要求spi_bitbang是结构体内的第一个成员, 否则就会出各种意料不到的问题.

当然也可以自己修改下代码, 避免这个问题.


2. spi部分代码阅读
重要的结构体有: spi_device, spi_driver, spi_master, spi_transfer, spi_message
spi device和spi master的挂载是通过bus_num的匹配来实现的.


3. probe部分的流程
driver_probe_device
  real_probe
    spi_drv_probe(sdrv->driver.probe ) //是device_driver结构体里面的probe
      sdrv->probe (spidev_probe) //是spi_driver结构体里面的probe

device_release_driver
  __device_release_driver
    drv->remove(dev); //spi_drv_remove
      sdrv->remove //spidev_remove
      
4. transfer 流程
spi_read/spi_write
  spi_sync
    spi_async
      master->transfer //可以是自己的transfer, 也可以是spi_bitbang_transfer(驱动没有赋值的系统默认)
      
5. spi_bitbang_transfer 流程
     queue_work(bitbang->workqueue, &bitbang->work); //bitbang_work
       bitbang->txrx_bufs(spi, t);
         spi_bitbang_bufs //默认
           cs->txrx_bufs
             bitbang_txrx_8 // 16 //32
               cs->txrx_word //bitbang->txrx_word[spi->mode & (SPI_CPOL|SPI_CPHA)] 函数指针数组
                 spi_gpio_txrx_word_mode0
                   bitbang_txrx_be_cpha0

你可能感兴趣的:(Linux驱动)