SPI设备驱动模型与I2C设备驱动模型基本一样.

SPI的控制器驱动由平台设备与平台驱动来实现. 驱动后用spi_master对象来描述.在设备驱动中就可以通过函数spi_write, spi_read, spi_w8r16, spi_w8r8, spi_write_then_read来调用控制器.

注意:上面的函数不可以同时收发数据。如需要同时收发可用下面方法调用控制器:

    struct spi_message msg; //spi控制器的传输操作是以消息来提交. 一条消息里可有多个传输操作
    struct spi_transfer x = {  //每个spi_transfer对象来描述收、发、收发操作 
        .tx_buf = txbuf,  //指定发出数据的缓冲区地址
        .rx_buf = rxbuf,  //指定接收数据的缓冲区地址
        .len = strlen(txbuf),  //收发数据的长度
    };  

    spi_message_init(&msg);  //初始化消息对象
    spi_message_add_tail(&x, &msg); //把传输对象增加到消息对象里
	spi_sync(spi, &msg); //提交消息对象到控制器

如需要先读然后再写, 可以用一个消息对象,两个传输对象(一写一读)先后加入消息对象提交即可.
///
spi的设备也是先用spi_board_info来描述,在spi_master对象注册时再生成相应的spi_device对象. spi设备驱动由spi_driver对象描述. spi_device对象与spi_driver都是挂载到spi总线上的.

描述spi设备的结构体:

struct spi_device {
        struct device           dev; //基于device成员扩展, 在/sys/bus/spi/devie目录有相应的子目录(名为spi%d.%d)
        struct spi_master       *master; //spi控制器对象的地址
        u32                     max_speed_hz; //设备工作时钟最大多少HZ
        u8                      chip_select; 
        u8                      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 */
#define SPI_NO_CS       0x40                    /* 1 dev/bus, no chipselect */
#define SPI_READY       0x80                    /* slave pulls low to pause */
        u8                      bits_per_word; //传输的数据以多少位为单位
        int                     irq;    //中断号
        void                    *controller_state;
        void                    *controller_data; //给控制器的驱动使用 
	char                    modalias[SPI_NAME_SIZE]; //spi设备的名字
};
// spi设备驱动类型
struct spi_driver {
    const struct spi_device_id *id_table;
    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; //基于device_driver扩展, 驱动模型
};


extern int spi_register_driver(struct spi_driver *sdrv);
static inline void spi_unregister_driver(struct spi_driver *sdrv);

/

spi总线:
struct bus_type spi_bus_type = {
    .name       = "spi",
    .dev_attrs  = spi_dev_attrs,
    .match      = spi_match_device,
    .uevent     = spi_uevent,
    .pm     = &spi_pm,
};
EXPORT_SYMBOL_GPL(spi_bus_type);

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
    const struct spi_device *spi = to_spi_device(dev);
    const struct spi_driver *sdrv = to_spi_driver(drv);
	...
    if (sdrv->id_table)
        return !!spi_match_id(sdrv->id_table, spi); //如设备驱动有id_table则按id_table里指定的设备名来匹配 

    return strcmp(spi->modalias, drv->name) == 0; //如设备驱动没有id_table则按名字来匹配 
}


声明spi设备有两种方法:

  1. 在script.bin里声明spi设备. 这种方法只适用于SOC里spi控制器, 不适用基于GPIO口的控制器.
[spi_devices]
spi_dev_num = 1   //表示spi设备的个数
  
[spi_board0]  //第0个spi设备
modalias = "spidev"  //设备名
max_speed_hz = 33000000 //设备最高的工作时钟
bus_num = 0            //此设备是连接在编号为0的控制器上的
chip_select = 0        //使用控制器的第0个片选 
mode = 0               //工作时序的方式
full_duplex = 1        //全双工, 表示MISO,MOSI都用上
manual_cs = 0          //表示片选线是由控制器自动控制的

  1. 在内核源码声明spi_board_info对象, 再内核初始化函数里spi_register_board_info注册设备信息.
struct spi_board_info myspi_info = {
        .modalias = "spidev",
        .controller_data = GPIOA(20), //注意这里是指定设备的片选脚, 需查看控制器驱动代码里对controller_data成员的使用。
        .max_speed_hz = 100000, //100Khz
        .bus_num = 0,           //控制器的编号
        .chip_select = 0,      //第0个片选
        .mode = SPI_MODE_2,
};

/
应用程序调用spi控制器的方法.

在内核里有实现好一个spi的设备驱动,用于供应用程序调用.

make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
Device Drivers  --->
	[*] SPI support  --->
		<*>   User mode SPI device driver support //spi设备驱动,供应用程序调用控制器用

源码在"drivers/spi/spidev.c"

static int __init spidev_init(void)
{
    int status;

    status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); // cdev字符设备初始化
 	...

    spidev_class = class_create(THIS_MODULE, "spidev");
 	...

    status = spi_register_driver(&spidev_spi_driver); //注册spi设备驱动
 	...
}
module_init(spidev_init);

//

static struct spi_driver spidev_spi_driver = {
    .driver = {
        .name =     "spidev", //只可与名为"spidev"的spi设备匹配
        .owner =    THIS_MODULE,
    },
    .probe =    spidev_probe,
	...
};

static int __devinit spidev_probe(struct spi_device *spi)
{
    struct spidev_data  *spidev;
    int         status;
    unsigned long       minor;

    
    spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
	...    

    spidev->spi = spi;
    	...

    minor = find_first_zero_bit(minors, N_SPI_MINORS);
    if (minor < N_SPI_MINORS) {
        struct device *dev;
        spidev->devt = MKDEV(SPIDEV_MAJOR, minor); //准备设备号
        dev = device_create(spidev_class, &spi->dev, spidev->devt,
                    spidev, "spidev%d.%d", //创建出设备文件, 第1个%d指定控制器的编号,第2指定第几个片选 
                    spi->master->bus_num, spi->chip_select);
        status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
   	...
    }
	...
}

///
全志h3的script.bin里默认增加了一个名为"spidev"的spi设备, 只要内核编译项选上"spidev"设备驱动, 在系统的/dev目录下应出现设备文件"spidev0.0".

可使用内核源码目录下的"Documentation/spi/spidev_test.c"测试spi控制器程序.
只要把spi控制器的MISO与MOSI短接起来,执行spidev_test.c应会接到收到程序发出的数据.

Logo

更多推荐