Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1432892
  • 博文数量: 1334
  • 博客积分: 645
  • 博客等级: 上士
  • 技术积分: 5762
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-25 16:56
文章分类

全部博文(1334)

文章存档

2014年(108)

2013年(1059)

2012年(169)

分类: 嵌入式

2013-06-05 14:26:55

原文地址:Linux SPI框架(上) 作者:enzo26

 水平有限,描述不当之处还请指出,转载请注明出处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 const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
                        const struct spi_device *sdev)
{
    while (id->name[0]) {
        if (!strcmp(sdev->modalias, id->name))
            return id;
        id++;
    }
    return NULL;
}

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

    /* Attempt an OF style match */
    if (of_driver_match_device(dev, drv))
        return 1;
    //如果驱动中定义id_table,查看driver结构体id_table->name与 平台设备结构体dev->modalias 是否相同
    if (sdrv->id_table)
        return !!spi_match_id(sdrv->id_table, spi);
    //如果驱动中没有定义id_table,则对比drv->name 与 dev->modalias是否相同
    return strcmp(spi->modalias, drv->name) == 0;
}


这里可以看到是将struct device_driver中的name字段与struct spi_device中的modalias字段进行匹配

 

这里已经完成了SPI子系统初始化的第一步,也就是注册SPI总线,这一步是和平台无关的,第二步是和平台相关的初始化,下一节再做介绍。

阅读(148) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~