不多说废话了,直接进主题,大家共同学习,共同进步
首先,还是先唠叨以下,以方便接下来对bitbang机制的学习,那就是spi 的工作时序,这里直接转载自己看到的一篇文章
http://www.52rd.com/Blog/Detail_RD.Blog_yuwenxin_21678.html?#Flag_Comment
这里也同时感谢楼主的分享
SPI时序图详解
(2009-10-18 21:49)SPI接口在模式0下输出第一位数据的时刻
SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序,
时序与CPOL、CPHL的关系也可以从图中看出。
只关注模式0的时序。
![linux spi驱动开发学习(三)-----spi_bitbang.c详解-LMLPHP linux spi驱动开发学习(三)-----spi_bitbang.c详解-LMLPHP]()
我们来关注SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿,第一个时钟沿),
在时钟的后沿输出数据(下降沿,第二个时钟沿)。首先来看主器件,主器件的输出口(MOSI)输出的数据bit1,
在时钟的前沿被从器件采样,那主器件是在何时刻输出bit1的呢?bit1的输出时刻实际上在SCK信号有效以前,
比 SCK的上升沿还要早半个时钟周期。bit1的输出时刻与SSEL信号没有关系。再来看从器件,
主器件的输入口MISO同样是在时钟的前沿采样从器件输出的bit1的,那从器件又是在何时刻输出bit1的呢。
从器件是在SSEL信号有效后,立即输出bit1,尽管此时SCK信号还没有起效。关于上面的主器件
和从器件输出bit1位的时刻,可以从图3、4中得到验证。
![linux spi驱动开发学习(三)-----spi_bitbang.c详解-LMLPHP linux spi驱动开发学习(三)-----spi_bitbang.c详解-LMLPHP]()
注意图3中,CS信号有效后(低电平有效,注意CS下降沿后发生的情况),故意用延时程序
延时了一段时间,之后再向数据寄存器写入了要发送的数据,来观察主器件输出bit1的情况(MOSI)。
可以看出,bit1(值为1)是在SCK信号有效之前的半个时钟周期的时刻开始输出的(与CS信号无关),
到了SCK的第一个时钟周期的上升沿正好被从器件采样。
![linux spi驱动开发学习(三)-----spi_bitbang.c详解-LMLPHP linux spi驱动开发学习(三)-----spi_bitbang.c详解-LMLPHP]()
第一个字节的最后一位在SCK的上升沿被采样,随后的SCK下降沿,从器件就输出了第二个字节的第一位。
![linux spi驱动开发学习(三)-----spi_bitbang.c详解-LMLPHP linux spi驱动开发学习(三)-----spi_bitbang.c详解-LMLPHP]()
熟悉了spi 的工作时序,接着往下说SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序,
时序与CPOL、CPHL的关系也可以从图中看出。
图1
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,
空闲电平为高电平。CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
只关注模式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)。
第一个字节的最后一位在SCK的上升沿被采样,随后的SCK下降沿,从器件就输出了第二个字节的第一位。
图5
注意:
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,空闲电平为高电平。
CPHA是用来决定采样时刻的,
CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=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)
- .....
- }
这个和一中的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);
点击(此处)折叠或打开
- 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);
点击(此处)折叠或打开
- 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;
- }
具体的数据传输过程将在下一节进行,待续.....