linux下SPI驱动----OK6410(一)

       SPI是同步外设接口,由摩托罗拉公司开发的全双工同步串行总线,接口有MISO、MOSI、SCK和SS四线组成。在这里不具体介绍SPI的工作原理了,相信学SPI驱动的同学已经在单片机上实现过了SPI的通讯。

      学习SPI驱动首先必须要建立一个分层的设计思想,分层设计不光在SPI中体现,在linux内核中都是分层的思想,使得linux具有强大的适应性。分层设计思想在linux的Input、RTC、MTD、IIC、TTY、USB、SPI等很多设备驱动中得到体现。那么SPI驱动分了哪几个层呢?

      SPI在linux中分为3层分别为主机控制器驱动、核心层驱动和外设驱动:

       在OK6410中     主机控制器驱动:spi_s3c64xx.c--------------这个文件将s3c6410的硬件spi控制器驱动。

                                   核心层驱动        :spi.c---------------------------这个文件是实现了spi的注册和注销的函数等,连接了主机控制器和外设驱动,起到了桥梁的作用。

                                   外设驱动            :spidev.c----------------------内核中的一个通用的外设驱动。


学习底层驱动免不了大量的结构体,下面就来看一些重要的结构体:

为了看起来可以有调理性,首先从主机控制器的结构体开始:

在include\linux\spi下有spi.h

struct spi_master {
	struct device	dev;				//内嵌标准dev结构
	struct list_head list;	
	s16			bus_num;				//总线编号
	u16			num_chipselect;			//芯片支持的片选数量
	u16			dma_alignment;			//采用dma时的对齐要求

        /* spi_device.mode flags understood by this controller driver */
	u16			mode_bits;

	/* other constraints relevant to this driver */
	u16			flags;
#define SPI_MASTER_HALF_DUPLEX	BIT(0)		/* can't do full duplex */
#define SPI_MASTER_NO_RX	BIT(1)		/* can't do buffer read */
#define SPI_MASTER_NO_TX	BIT(2)		/* can't do buffer write */

	/* lock and mutex for SPI bus locking */
	spinlock_t		bus_lock_spinlock;					//自旋锁定义
	struct mutex		bus_lock_mutex;					//互斥信号量定义

	/* flag indicating that the SPI bus is locked for exclusive use */
	bool			bus_lock_flag;

	//改变spi_device的特性如:传输模式,字长,时钟频率  
	int			(*setup)(struct spi_device *spi);		

	 /*添加消息到队列的方法,这个函数不可睡眠,他的任务是安排发生的传送并且调用注册的回调函数complete()*/ 
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	/* called on release() to free memory provided by spi_master */
	void			(*cleanup)(struct spi_device *spi);				//释放内存
};
在linux中,使用 spi_master结构体来描述一个SPI的主机控制器驱动,结构体主要完成主机控制器的序列号(s3c6410中有两个spi主机控制器)、片选数量、SPI模式和时钟以及一些传输函数的实现。

上面这个结构体通过一下3个函数来注册和注销:

struct spi_master *spi_alloc_master(struct device *host, unsigned size);      //自动分配内存
int spi_register_master(struct spi_master *master);                           //注册spi控制器
void spi_unregister_master(struct spi_master *master);                        //注销spi控制器

以上的结构体和函数最终在spi_s3c64xx.c中得到实现,有兴趣的可以看一下里面的代码很不错,但是也不好理解,因为6410的spi驱动采用了DMA传输。


SPI的核心层,作为桥梁的功能,里面主要注册了一个spi_bus_type的spi总线和spi_master的class类。通过注册在我们的系统中sys/bus中产生了一个spi的目录和sys/class下产生spi_master目录。


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;
};
spi_driver这个结构体和platform_driver结构体非常的相似。

在驱动中传输使用的结构体:

struct spi_transfer {

	const void	*tx_buf;			//要写入设备的数据(必须是dma_safe),或者为NULL
	void		*rx_buf;				//要读取的数据缓冲(必须是dma_safe),或者为NULL  
	unsigned	len;					 //tx和rx的大小(字节数),这里不是指它的和,而是各自的长度,他们总是相等的  

	dma_addr_t	tx_dma;			//如果spi_message.is_dma_mapped是真,这个是tx的dma地址 
	dma_addr_t	rx_dma;			//如果spi_message.is_dma_mapped是真,这个是rx的dma地址  

	unsigned	cs_change:1;		//影响此次传输之后的片选,指示本次tranfer结束之后是否要重新片选并调用setup改变设置,这个标志可以较少系统开销u8      
	u8		bits_per_word;		 //每个字长的比特数,如果是0,使用默认值  
	u16		delay_usecs;		//此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息  
	u32		speed_hz;			//通信时钟。如果是0,使用默认值  

	struct list_head transfer_list;	        //用来连接的双向链表节点
};

struct spi_message {
	struct list_head	transfers;			//此次消息的传输队列,一个消息可以包含多个传输段

	struct spi_device	*spi;				//传输的目的设备  

	unsigned		is_dma_mapped:1;		//如果为真,此次调用提供dma和cpu虚拟地址  

	/* completion is reported through a callback */
	void			(*complete)(void *context);		//异步调用完成后的回调函数  
	void			*context;						//回调函数的参数  
	unsigned		actual_length;					//此次传输的实际长度  
	int			status;							//执行的结果,成功被置0,否则是一个负的错误码  

	struct list_head	queue;
	void			*state;
};
通过
static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
初始化spi_message,进而将spi_transfer添加到spi_message中,发起传输有两种方式分别是同步传输和异步传输。

extern int spi_sync(struct spi_device *spi, struct spi_message *message);    -------------同步传输
extern int spi_async(struct spi_device *spi, struct spi_message *message);   -------------异步传输

下面介绍两个通用的SPI传输函数:

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;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);
	return spidev_sync(spidev, &m);
}

static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len)
{
	struct spi_transfer	t = {
			.rx_buf		= spidev->buffer,
			.len		= len,
		};
	struct spi_message	m;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);
	return spidev_sync(spidev, &m);
}







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值