Linux SPI框架

水平有限,描述不当之处还请指出,转载请注明出处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就完全一样了
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;  
}
-
 
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框架已分析完毕。

 

posted @ 2014-06-03 17:43  Merlin-magic  阅读(442)  评论(0编辑  收藏  举报