linux spi驱动分析 三,linux spi驱动开发学习(三)-----spi_bitbang.c详解

经过了前面两节的学习,现在到了这个环节了,spi驱动的完整工作过程渐渐明朗起来

不多说废话了,直接进主题,大家共同学习,共同进步

首先,还是先唠叨以下,以方便接下来对bitbang机制的学习,那就是spi 的工作时序,这里直接转载自己看到的一篇文章

这里也同时感谢楼主的分享

SPI时序图详解

(2009-10-18 21:49)

SPI接口在模式0下输出第一位数据的时刻

SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序,

时序与CPOL、CPHL的关系也可以从图中看出。

1111551424.jpg

图1

CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,

空闲电平为高电平。CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,

CPHA=1,在每个周期的第二个时钟沿采样。

由于我使用的器件工作在模式0这种时序(CPOL=0,CPHA=0),所以将图1简化为图2,

只关注模式0的时序。

1111896066.jpg

图2

我们来关注SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿,第一个时钟沿),

在时钟的后沿输出数据(下降沿,第二个时钟沿)。首先来看主器件,主器件的输出口(MOSI)输出的数据bit1,

在时钟的前沿被从器件采样,那主器件是在何时刻输出bit1的呢?bit1的输出时刻实际上在SCK信号有效以前,

比 SCK的上升沿还要早半个时钟周期。bit1的输出时刻与SSEL信号没有关系。再来看从器件,

主器件的输入口MISO同样是在时钟的前沿采样从器件输出的bit1的,那从器件又是在何时刻输出bit1的呢。

从器件是在SSEL信号有效后,立即输出bit1,尽管此时SCK信号还没有起效。关于上面的主器件

和从器件输出bit1位的时刻,可以从图3、4中得到验证。

1111712099.jpg

图3

注意图3中,CS信号有效后(低电平有效,注意CS下降沿后发生的情况),故意用延时程序

延时了一段时间,之后再向数据寄存器写入了要发送的数据,来观察主器件输出bit1的情况(MOSI)。

可以看出,bit1(值为1)是在SCK信号有效之前的半个时钟周期的时刻开始输出的(与CS信号无关),

到了SCK的第一个时钟周期的上升沿正好被从器件采样。

1111247817.jpg

图4

图4中,注意看CS和MISO信号。我们可以看出,CS信号有效后,从器件立刻输出了bit1(值为1)。

通常我们进行的spi操作都是16位的。图5记录了第一个字节和第二个字节间的相互衔接的过程。

第一个字节的最后一位在SCK的上升沿被采样,随后的SCK下降沿,从器件就输出了第二个字节的第一位。

1111432607.jpg

图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驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)

具体的数据传输过程将在下一节进行,待续.....

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值