经过了前面两节的学习,现在到了这个环节了,spi驱动的完整工作过程渐渐明朗起来
不多说废话了,直接进主题,大家共同学习,共同进步
首先,还是先唠叨以下,以方便接下来对bitbang机制的学习,那就是spi 的工作时序,这里直接转载自己看到的一篇文章
这里也同时感谢楼主的分享
SPI时序图详解
(2009-10-18 21:49)
SPI接口在模式0下输出第一位数据的时刻
SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序,
时序与CPOL、CPHL的关系也可以从图中看出。
图1
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,
空闲电平为高电平。CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
由于我使用的器件工作在模式0这种时序(CPOL=0,CPHA=0),所以将图1简化为图2,
只关注模式0的时序。
图2
我们来关注SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿,第一个时钟沿),
在时钟的后沿输出数据(下降沿,第二个时钟沿)。首先来看主器件,主器件的输出口(MOSI)输出的数据bit1,
在时钟的前沿被从器件采样,那主器件是在何时刻输出bit1的呢?bit1的输出时刻实际上在SCK信号有效以前,
比 SCK的上升沿还要早半个时钟周期。bit1的输出时刻与SSEL信号没有关系。再来看从器件,
主器件的输入口MISO同样是在时钟的前沿采样从器件输出的bit1的,那从器件又是在何时刻输出bit1的呢。
从器件是在SSEL信号有效后,立即输出bit1,尽管此时SCK信号还没有起效。关于上面的主器件
和从器件输出bit1位的时刻,可以从图3、4中得到验证。
图3
注意图3中,CS信号有效后(低电平有效,注意CS下降沿后发生的情况),故意用延时程序
延时了一段时间,之后再向数据寄存器写入了要发送的数据,来观察主器件输出bit1的情况(MOSI)。
可以看出,bit1(值为1)是在SCK信号有效之前的半个时钟周期的时刻开始输出的(与CS信号无关),
到了SCK的第一个时钟周期的上升沿正好被从器件采样。
图4
图4中,注意看CS和MISO信号。我们可以看出,CS信号有效后,从器件立刻输出了bit1(值为1)。
通常我们进行的spi操作都是16位的。图5记录了第一个字节和第二个字节间的相互衔接的过程。
第一个字节的最后一位在SCK的上升沿被采样,随后的SCK下降沿,从器件就输出了第二个字节的第一位。
图5
熟悉了spi 的工作时序,接着往下说
注意:
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,空闲电平为高电平。
CPHA是用来决定采样时刻的,
CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
linux的SPI模型中重要的有如下几个结构体,位置include/linux/spi/spi.h
struct spi_device {} //Master side proxy for an SPI slave device
struct spi_driver {} //Host side "protocol" driver
struct spi_master {} //interface to SPI master controller
struct spi_transfer{} //a read/write buffer pair
在这几个结构体中,我们只注意一下device结构体
-
struct spi_device
-
{
-
.....
-
#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)
-
.....
-
}
注意如上定义的 SPI_CPHA和SPI_CPOL
这个和一中的CPHA,以及CPOL是对应的,
然后在次基础上定义了MODE?,到此,你是否能相像出SPI接口的数据传输过程?
下面才是刚刚进入真正的主题
以下是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;
-
-
/* 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);
-
};
这里我们只对加底纹部分做以下说明:
struct work_struct work;
这个让我们想入菲菲--bit bang是按照workqueue队列的形式来工作的吗?
worqueue这个是什么?简单地说,他就是一个队列,里面的每一个work节点代表着一个需要调度的工作。
具体可以百度,这个和tasklet有点相似的。以前自己有用过,经常用于中断的顶半部,底半部机制
既然是worqueue,那么我们可以猜想,spi是在每一个work中实现bit bang的。
事实上确实如此,在/driver/spi/spi_bitbang.c#L267中,我们可以看到如下函数
static void bitbang_work(struct work_struct *work){},
在/driver/spi/spi_bitbang.c中,我们可以发现
int spi_bitbang_start(struct spi_bitbang *bitbang);
这里还是有必要看一下这个方法的实现过程,看他都干了些什么:
-
int spi_bitbang_start(struct spi_bitbang *bitbang)
-
{
-
int status;
-
-
if (!bitbang->master || !bitbang->chipselect)
-
return -EINVAL;
-
-
INIT_WORK(&bitbang->work, bitbang_work);
-
spin_lock_init(&bitbang->lock);
-
INIT_LIST_HEAD(&bitbang->queue);
-
-
if (!bitbang->master->mode_bits)//此处在s3c24xx_spi_probe方法中已定义,采用s3c24xx_spi_probe的设置
-
bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
-
-
if (!bitbang->master->transfer)//此处在s3c24xx_spi_probe方法中没有定义,所以使用这里设置的方法
-
bitbang->master->transfer = spi_bitbang_transfer;//之后发送数据的时候会用到这个接口
-
-
if (!bitbang->txrx_bufs) {//此处在s3c24xx_spi_probe方法中已定义,采用s3c24xx_spi_probe的设置
-
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)//此处在s3c24xx_spi_probe方法中已定义,从这里看来bitbang->master->setup必须定义,不可遗漏
-
return -EINVAL;
-
-
/* this task is the only thing to touch the SPI bits */
-
bitbang->busy = 0;
-
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
-
*/
-
status = spi_register_master(bitbang->master);
-
if (status < 0)
-
goto err2;
-
-
return status;
-
-
err2:
-
destroy_workqueue(bitbang->workqueue);
-
err1:
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(spi_bitbang_start);
看看
spi_register_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)
-
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);//添加设备
-
if (status < 0)
-
goto done;
-
dev_dbg(dev, "registered master %s%sn", dev_name(&master->dev),
-
dynamic ? " (dynamic)" : "");
-
-
/* populate children from any spi device tables */
-
scan_boardinfo(master);
-
status = 0;
-
done:
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(spi_register_master);
看看
scan_boardinfo原型:
-
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
-
*/
-
(void) spi_new_device(master, chip);
-
}
-
}
-
mutex_unlock(&board_lock);
-
}
-
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).
-
*/
-
-
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;
-
}
-
EXPORT_SYMBOL_GPL(spi_new_device);
-
struct spi_device *spi_alloc_device(struct spi_master *master)
-
{
-
struct spi_device *spi;
-
struct device *dev = master->dev.parent;
-
-
if (!spi_master_get(master))
-
return NULL;
-
-
spi = kzalloc(sizeof *spi, GFP_KERNEL);
-
if (!spi) {
-
dev_err(dev, "cannot alloc spi_devicen");
-
spi_master_put(master);
-
return NULL;
-
}
-
-
spi->master = master;
-
spi->dev.parent = dev;
-
spi->dev.bus = &spi_bus_type;
-
spi->dev.release = spidev_release;
-
device_initialize(&spi->dev);
-
return spi;
-
}
-
EXPORT_SYMBOL_GPL(spi_alloc_device);
-
int spi_add_device(struct spi_device *spi)
-
{
-
static DEFINE_MUTEX(spi_add_lock);
-
struct device *dev = spi->master->dev.parent;
-
int status;
-
-
/* Chipselects are numbered 0..max; validate. */
-
if (spi->chip_select >= spi->master->num_chipselect) {
-
dev_err(dev, "cs%d >= max %dn",
-
spi->chip_select,
-
spi->master->num_chipselect);
-
return -EINVAL;
-
}
-
-
/* Set the bus ID string */
-
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
-
spi->chip_select);
-
-
-
/* We need to make sure there's no other device with this
-
* chipselect **BEFORE** we call setup(), else we'll trash
-
* its configuration. Lock against concurrent add() calls.
-
*/
-
mutex_lock(&spi_add_lock);
-
-
if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))
-
!= NULL) {
-
dev_err(dev, "chipselect %d already in usen",
-
spi->chip_select);
-
status = -EBUSY;
-
goto done;
-
}
-
-
/* Drivers may modify this initial i/o setup, but will
-
* normally rely on the device being setup. Devices
-
* using SPI_CS_HIGH can't coexist well otherwise...
-
*/
-
status = spi_setup(spi);
-
if (status < 0) {
-
dev_err(dev, "can't %s %s, status %dn",
-
"setup", dev_name(&spi->dev), status);
-
goto done;
-
}
-
-
/* Device may be bound to an active driver when this returns */
-
status = device_add(&spi->dev);
-
if (status < 0)
-
dev_err(dev, "can't %s %s, status %dn",
-
"add", dev_name(&spi->dev), status);
-
else
-
dev_dbg(dev, "registered child %sn", dev_name(&spi->dev));
-
-
done:
-
mutex_unlock(&spi_add_lock);
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(spi_add_device);
-
int spi_setup(struct spi_device *spi)
-
{
-
unsigned bad_bits;
-
int status;
-
-
/* help drivers fail *cleanly* when they need options
-
* that aren't supported with their current master
-
*/
-
bad_bits = spi->mode & ~spi->master->mode_bits;
-
if (bad_bits) {
-
dev_dbg(&spi->dev, "setup: unsupported mode bits %xn",
-
bad_bits);
-
return -EINVAL;
-
}
-
-
if (!spi->bits_per_word)
-
spi->bits_per_word = 8;
-
-
status = spi->master->setup(spi);//这里其实调用的就是s3c24xx_spi_probe里填充的spi->master->setup
-
-
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"
-
"%u bits/w, %u Hz max --> %dn",
-
(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
-
(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
-
(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
-
(spi->mode & SPI_3WIRE) ? "3wire, " : "",
-
(spi->mode & SPI_LOOP) ? "loopback, " : "",
-
spi->bits_per_word, spi->max_speed_hz,
-
status);
-
-
return status;
-
}
-
EXPORT_SYMBOL_GPL(spi_setup);
-
static int s3c24xx_spi_setup(struct spi_device *spi)
-
{
-
struct s3c24xx_spi_devstate *cs = spi->controller_state;
-
struct s3c24xx_spi *hw = to_hw(spi);
-
int ret;
-
-
/* allocate settings on the first call */
-
if (!cs) {
-
cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL);
-
if (!cs) {
-
dev_err(&spi->dev, "no memory for controller staten");
-
return -ENOMEM;
-
}
-
-
cs->spcon = SPCON_DEFAULT;
-
cs->hz = -1;
-
spi->controller_state = cs;
-
}
-
-
/* initialise the state from the device */
-
ret = s3c24xx_spi_update_state(spi, NULL);
-
if (ret)
-
return ret;
-
-
spin_lock(&hw->bitbang.lock);
-
if (!hw->bitbang.busy) {
-
hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
-
/* need to ndelay for 0.5 clocktick ? */
-
}
-
spin_unlock(&hw->bitbang.lock);
-
-
return 0;
-
}
好了,至此spi主控制器(驱动)和板上spi设备注册完毕,以后要使用spi来传输数据的话,只要先获得spi设备结构,然后就可以利用它来和spi驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)
具体的数据传输过程将在下一节进行,待续.....
阅读(1289) | 评论(0) | 转发(0) |