版权声明:本文为博主原创文章,未经博主允许不得转载。
Allein.Cao原创作品,转载请注明出处:
http://blog.csdn.net/alleincao/article/details/7522418
内核版本:2.6.32.2
硬件:S3C2440
SPI总线是一种比较通用的数据传输总线,遵从主从模式,由主设备发起通讯请求,通常工作于全双工模式,由4条数据时钟线组成,下面这段话摘自s3c2440数据手册:
There are 4 I/O pin signals associated with SPItransfers: SCK (SPICLK0,1), MISO (SPIMISO0,1) data line, MOSI (SPIMOSI0,1) dataline and active low /SS (nSS0,1) pin (input).
- 确定驱动文件
SPI作为Linux里面比较小的一个子系统,其驱动程序位于/drivers/spi/*目录,首先,我们可以通过Makefile及Kconfig来确定我们需要看的源文件:
Makefile:
[html]
view plain
copy
- <pre name="code" class="csharp">#
- # Makefile for kernel SPI drivers.
- #
- ……………………………………………….
- # small core, mostly translating board-specific
- # config declarations into driver model code
- obj-$(CONFIG_SPI_MASTER) += spi.o
- # SPI master controller drivers (bus)
- …………………………………………………
- obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
- …………………………………………………
- obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
- obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
- …………………………………………………
- # ... add above this line ...
- # SPI protocol drivers (device/link on bus)
- obj-$(CONFIG_SPI_SPIDEV) += spidev.o
- …………………………………………………
- # ... add above this line ...</pre>
- <pre></pre>
- <p></p>
- <p>通过以上分析我们知道,spi驱动由三部分组成,分别是core(spi.c),master controller driver (spi_s3c24xx.c or spi_s3c24xx_gpio.)以及SPIprotocol drivers (spidev.c),这里spi_s3c24xx_gpio.c文件是用普通的io口来模拟spi时序,具体见Kconfig描述:</p>
- <pre name="code" class="csharp">config SPI_S3C24XX_GPIO
- tristate "Samsung S3C24XX series SPI by GPIO"
- depends on ARCH_S3C2410 && EXPERIMENTAL
- select SPI_BITBANG
- help
- SPI driver for Samsung S3C24XX series ARM SoCs using
- GPIO lines to provide the SPI bus. This can be used where
- the inbuilt hardware cannot provide the transfer mode, or
- where the board is using non hardware connected pins.
- </pre>
- <p>在此,我们以spi控制器方式进行分析。</p>
- <p></p>
- <ul>
- <li>数据结构</li></ul>
- <p></p>
- <p>Spi驱动涉及的<a href="http://lib.csdn.net/base/datastructure" class="replace_word" title="算法与数据结构知识库" target="_blank" style="color:#df3434; font-weight:bold;">数据结构</a>主要位于/include/<a href="http://lib.csdn.net/base/linux" class="replace_word" title="Linux知识库" target="_blank" style="color:#df3434; font-weight:bold;">linux</a>/spi.h,对各个结构有比较详细的解释,限于篇幅,简单介绍如下:</p>
- <p>1、Spi_device代表一个外围spi设备,由master controller driver注册完成后扫描BSP中注册设备产生的设备链表并向spi_bus注册产生。</p>
- <p></p>
- <pre name="code" class="csharp">struct spi_device {
- struct device dev; //设备模型使用
- struct spi_master *master; //设备使用的master结构
- u32 max_speed_hz; //通讯时钟
- u8 chip_select; //片选号,每个master支持多个spi_device
- u8 mode; //设备支持的模式,如片选是高or低?
- #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]; //设备名称
- };
- </pre>
- <p>2、 spi_driver代表一个SPI protocol drivers,即外设驱动。</p>
- <p></p>
- <pre name="code" class="csharp">struct spi_driver {
- const struct spi_device_id *id_table; //支持的spi_device设备表
- int (*probe)(struct spi_device *spi); //probe函数
- 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; //设备模型使用
- };
- </pre>
- <p>3、spi_master代表一个主机控制器,此处即S3C2440中的SPI控制器</p>
- <p></p>
- <pre name="code" class="csharp">struct spi_master {
- struct device dev; //设备模型使用
- s16 bus_num; //master编号,s3c2440有2个spi控制器,编号为0 1
- u16 num_chipselect; //支持的片选的数量,从设备的片选号不能大于这个数量
- u16 dma_alignment;
- /* spi_device.mode flags understood by this controller driver */
- u16 mode_bits; //master支持的设备模式
- /* 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 */
- int (*setup)(struct spi_device *spi); //设置模式、时钟等
- 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);
- };
- </pre>
- <p>4、spi_transfer代表一个读写缓冲对,包含接收缓冲区及发送缓冲区,其实,spi_transfer的发送是通过构建spi_message实现,通过将spi_transfer中的链表transfer_list链接到spi_message中的transfers,再以spi_message形势向底层发送数据。<span style="color:red">每个</span><span style="color:red">spi_transfer</span><span style="color:red">都可以对传输的一些参数进行设置,使得</span><span style="color:red">master
- controller</span><span style="color:red">按照它要求的参数进行数据发送。</span></p>
- <p></p>
- <pre name="code" class="csharp">struct spi_transfer {
- const void *tx_buf; //发送缓冲区
- void *rx_buf; //接收缓冲区
- unsigned len; //缓冲区长度
- dma_addr_t tx_dma;
- dma_addr_t rx_dma;
- unsigned cs_change:1; // 当前spi_transfer发送完成之后重新片选?
- u8 bits_per_word; //每个字长的比特数,0代表使用Spi_device中的默认值
- u16 delay_usecs; //发送完成一个spi_transfer后延时时间
- u32 speed_hz; //速率
- struct list_head transfer_list; //用于链接到spi_message
- };<span style="color:#ff0000;">
- </span></pre>5、spi_message代表spi消息,由多个spi_ transfer段组成:
- <p>struct spi_message {</p>
- <pre name="code" class="csharp"> struct list_head transfers; // spi_transfer链表队列
- struct spi_device *spi; //该消息的目标设备
- unsigned is_dma_mapped:1;
- /* completion is reported through a callback */
- void (*complete)(void *context); //消息完成后调用的回调函数
- void *context; //回调函数参数
- unsigned actual_length; //实际传输的数据长度
- int status; //该消息的发送结果,0:成功
- struct list_head queue; //用于添加到bitbang的list
- void *state;
- };</pre><pre name="code" class="csharp"></pre><pre name="code" class="csharp" style="text-align: left;">6、s3c24xx_spi代表具体的s3c2440中的spi控制器,包含了控制器的信息,如中断,寄存器等信息,定义于/drivers/spi/spi_s3c24xx.c(因为该结构与具体的硬件有关,属于linux里面的非通用代码)</pre><pre name="code" class="csharp"><pre name="code" class="csharp"><div style="text-align: left;">
- </div><pre></pre><pre name="code" class="csharp"></pre><pre name="code" class="csharp">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;
- };
- </pre>7、struct spi_bitbang是具体的负责信息传输的数据结构,它维护一个workqueue_struct,每收到一个消息,都会向其中添加一个work_struct,由内核守护进程在将来的某个时间调用该work_struct中的function进行消息发送。
- <p></p>
- <p></p>
- <p align="left"></p>
- <pre name="code" class="csharp">struct spi_bitbang {
- struct workqueue_struct *workqueue; //工作队列头,spi master初始化时建立
- struct work_struct work; //spi master初始化时初始化
- spinlock_t lock;
- struct list_head queue; //挂接spi_message
- u8 busy; //忙标志
- u8 use_dma;
- u8 flags; /* extra spi->mode support */
- struct spi_master *master; //对应的spi_master
- /* setup_transfer() changes clock and/or wordsize to match settings
- * for this transfer; zeroes restore defaults from spi_device.
- */
- 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
- /* txrx_bufs() may handle dma mapping for transfers that don't
- * already have one (transfer.{tx,rx}_dma is zero), or use PIO
- */
- 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);
- };
- </pre><br>
- <br>
- <p></p>
- <pre></pre>
- <pre name="code" class="csharp"></pre><pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- </pre></pre>