您的位置:首页 > 运维架构 > Linux

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

2017-07-19 17:30 597 查看
经过了前面两节的学习,现在到了这个环节了,spi驱动的完整工作过程渐渐明朗起来

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

首先,还是先唠叨以下,以方便接下来对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的关系也可以从图中看出。



图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驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spi