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

和菜鸟一起学linux总线驱动之初识spi驱动数据传输流程【转】

2016-08-04 14:39 579 查看
转自:http://blog.csdn.net/eastmoon502136/article/details/7921846

对于SPI的一些结构体都有所了解之后呢,那么再去瞧瞧SPI的那些长见的操作的函数了。

首先看一下本人画的比较挫的数据流了,仅供参考,如有不对,不吝赐教



接下来看看各个函数吧还是:

SPI write

[html] view plain copy

/**

* spi_write - SPI synchronous write

* @spi: device to which data will be written

* @buf: data buffer

* @len: data buffer size

* Context: can sleep

*

* This writes the buffer and returns zero or a negative error code.

* Callable only from contexts that can sleep.

*/

static inline int

spi_write(struct spi_device *spi, const void *buf, size_t len)

{

struct spi_transfer t = {

.tx_buf = buf,

.len = len,

};

struct spi_message m;

spi_message_init(&m);

spi_message_add_tail(&t, &m);

return spi_sync(spi, &m);

}

SPI发送函数,数据放在buf中,然后把要发送的数据放在工作队列中

SPI read

[html] view plain copy

/**

* spi_read - SPI synchronous read

* @spi: device from which data will be read

* @buf: data buffer

* @len: data buffer size

* Context: can sleep

*

* This reads the buffer and returns zero or a negative error code.

* Callable only from contexts that can sleep.

*/

static inline int

spi_read(struct spi_device *spi, void *buf, size_t len)

{

struct spi_transfer t = {

.rx_buf = buf,

.len = len,

};

struct spi_message m;

spi_message_init(&m);

spi_message_add_tail(&t, &m);

return spi_sync(spi, &m);

}

SPI接收函数,数据放在buf中,然后把要发送的数据放在工作队列中,发送出去

SPI write 8 bits read 8 bits

[html] view plain copy

/* this copies txbuf and rxbuf data; for small transfers only! */

extern int spi_write_then_read(struct spi_device *spi,

const void *txbuf, unsigned n_tx,

void *rxbuf, unsigned n_rx);

/**

* spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read

* @spi: device with which data will be exchanged

* @cmd: command to be written before data is read back

* Context: can sleep

*

* This returns the (unsigned) eight bit number returned by the

* device, or else a negative error code. Callable only from

* contexts that can sleep.

*/

static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)

{

ssize_t status;

u8 result;

status = spi_write_then_read(spi, &cmd, 1, &result, 1);

/* return negative errno or unsigned value */

return (status < 0) ? status : result;

}

SPI write 8 bit read 16 bits

[html] view plain copy

/**

* spi_w8r16 - SPI synchronous 8 bit write followed by 16 bit read

* @spi: device with which data will be exchanged

* @cmd: command to be written before data is read back

* Context: can sleep

*

* This returns the (unsigned) sixteen bit number returned by the

* device, or else a negative error code. Callable only from

* contexts that can sleep.

*

* The number is returned in wire-order, which is at least sometimes

* big-endian.

*/

static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)

{

ssize_t status;

u16 result;

status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2);

/* return negative errno or unsigned value */

return (status < 0) ? status : result;

}

[html] view plain copy

int spi_write_then_read(struct spi_device *spi,

const void *txbuf, unsigned n_tx,

void *rxbuf, unsigned n_rx)

{

static DEFINE_MUTEX(lock);

int status;

struct spi_message message;

struct spi_transfer x[2];

u8 *local_buf;

/* Use preallocated DMA-safe buffer. We can't avoid copying here,

* (as a pure convenience thing), but we can keep heap costs

* out of the hot path ...

*/

if ((n_tx + n_rx) > SPI_BUFSIZ)

return -EINVAL;

spi_message_init(&message);

memset(x, 0, sizeof x);

if (n_tx) {

x[0].len = n_tx;

spi_message_add_tail(&x[0], &message);

}

if (n_rx) {

x[1].len = n_rx;

spi_message_add_tail(&x[1], &message);

}

/* ... unless someone else is using the pre-allocated buffer */

if (!mutex_trylock(&lock)) {

local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);

if (!local_buf)

return -ENOMEM;

} else

local_buf = buf;

memcpy(local_buf, txbuf, n_tx);

x[0].tx_buf = local_buf;

x[1].rx_buf = local_buf + n_tx;

/* do the i/o */

status = spi_sync(spi, &message);

if (status == 0)

memcpy(rxbuf, x[1].rx_buf, n_rx);

if (x[0].tx_buf == buf)

mutex_unlock(&lock);

else

kfree(local_buf);

return status;

}

SPI sync

读写都会调用到spi_sync

[html] view plain copy

int spi_sync(struct spi_device *spi, struct spi_message *message)

{

return __spi_sync(spi, message, 0);

}

接着调用了__spi_sync

[html] view plain copy

static int __spi_sync(struct spi_device *spi, struct spi_message *message,

int bus_locked)

{

DECLARE_COMPLETION_ONSTACK(done);

int status;

struct spi_master *master = spi->master;

message->complete = spi_complete;

message->context = &done;

if (!bus_locked)

mutex_lock(&master->bus_lock_mutex);

status = spi_async_locked(spi, message);

if (!bus_locked)

mutex_unlock(&master->bus_lock_mutex);

if (status == 0) {

wait_for_completion(&done);

status = message->status;

}

message->context = NULL;

return status;

}

然后就是spi_async

[html] view plain copy

int spi_async(struct spi_device *spi, struct spi_message *message)

{

struct spi_master *master = spi->master;

int ret;

unsigned long flags;

spin_lock_irqsave(&master->bus_lock_spinlock, flags);

if (master->bus_lock_flag)

ret = -EBUSY;

else

ret = __spi_async(spi, message);

spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);

return ret;

}

最后调用__spi_async

[html] view plain copy

static int __spi_async(struct spi_device *spi, struct spi_message *message)

{

struct spi_master *master = spi->master;

/* Half-duplex links include original MicroWire, and ones with

* only one data pin like SPI_3WIRE (switches direction) or where

* either MOSI or MISO is missing. They can also be caused by

* software limitations.

*/

if ((master->flags & SPI_MASTER_HALF_DUPLEX)

|| (spi->mode & SPI_3WIRE)) {

struct spi_transfer *xfer;

unsigned flags = master->flags;

list_for_each_entry(xfer, &message->transfers, transfer_list) {

if (xfer->rx_buf && xfer->tx_buf)

return -EINVAL;

if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)

return -EINVAL;

if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)

return -EINVAL;

}

}

message->spi = spi;

message->status = -EINPROGRESS;

return master->transfer(spi, message);

}

返回了master->transfer(spi, message);那么就是控制器里去工作了。

我用的是gpio模拟的spi,所以那用gpio模拟的那个控制器去看控制器的处理了。

先还是看一下probe函数

[html] view plain copy

static int __init spi_gpio_probe(struct platform_device *pdev)

{

int status;

struct spi_master *master;

struct spi_gpio *spi_gpio;

struct spi_gpio_platform_data *pdata;

u16 master_flags = 0;

pdata = pdev->dev.platform_data;

#ifdef GENERIC_BITBANG

if (!pdata || !pdata->num_chipselect)

return -ENODEV;

#endif

status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags);

if (status < 0)

return status;

master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio);

if (!master) {

status = -ENOMEM;

goto gpio_free;

}

spi_gpio = spi_master_get_devdata(master);

platform_set_drvdata(pdev, spi_gpio);

spi_gpio->pdev = pdev;

if (pdata)

spi_gpio->pdata = *pdata;

master->flags = master_flags;

master->bus_num = pdev->id;

master->num_chipselect = SPI_N_CHIPSEL;

master->setup = spi_gpio_setup;

master->cleanup = spi_gpio_cleanup;

spi_gpio->bitbang.master = spi_master_get(master);

spi_gpio->bitbang.chipselect = spi_gpio_chipselect;

if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {

spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;

spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;

spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;

spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;

} else {

spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;

spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;

spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;

spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;

}

spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;

spi_gpio->bitbang.flags = SPI_CS_HIGH;

status = spi_bitbang_start(&spi_gpio->bitbang);

if (status < 0) {

spi_master_put(spi_gpio->bitbang.master);

gpio_free:

if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)

gpio_free(SPI_MISO_GPIO);

if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)

gpio_free(SPI_MOSI_GPIO);

gpio_free(SPI_SCK_GPIO);

spi_master_put(master);

}

return status;

}

主要看下下面三个函数

[html] view plain copy

spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;

spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;

status = spi_bitbang_start(&spi_gpio->bitbang);

spi_gpio_txrx_word_mode0;就是最后调用到的先放一边,spi_bitbang_start,看一下这个函数

[html] view plain copy

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)

bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;

if (!bitbang->master->transfer)

bitbang->master->transfer = spi_bitbang_transfer;

if (!bitbang->txrx_bufs) {

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)

return -EINVAL;

if (bitbang->master->transfer == spi_bitbang_transfer &&

!bitbang->setup_transfer)

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;

}

看到这个函数指针了吧:

[html] view plain copy

if (!bitbang->master->transfer)

bitbang->master->transfer = spi_bitbang_transfer;

那么设备驱动调用的master->transfer(spi, message);就是调用到了spi_bitbang_transfer了,

[html] view plain copy

/**

* spi_bitbang_transfer - default submit to transfer queue

*/

int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)

{

struct spi_bitbang *bitbang;

unsigned long flags;

int status = 0;

m->actual_length = 0;

m->status = -EINPROGRESS;

bitbang = spi_master_get_devdata(spi->master);

spin_lock_irqsave(&bitbang->lock, flags);

if (!spi->max_speed_hz)

status = -ENETDOWN;

else {

list_add_tail(&m->queue, &bitbang->queue);

queue_work(bitbang->workqueue, &bitbang->work);

}

spin_unlock_irqrestore(&bitbang->lock, flags);

return status;

}

这里是把信息加到了bitbang->workqueue,然后在bitbang->work里处理

再来看下bitbang->work做了什么

[html] view plain copy

static void bitbang_work(struct work_struct *work)

{

struct spi_bitbang *bitbang =

container_of(work, struct spi_bitbang, work);

unsigned long flags;

spin_lock_irqsave(&bitbang->lock, flags);

bitbang->busy = 1;

while (!list_empty(&bitbang->queue)) {

struct spi_message *m;

struct spi_device *spi;

unsigned nsecs;

struct spi_transfer *t = NULL;

unsigned tmp;

unsigned cs_change;

int status;

int do_setup = -1;

m = container_of(bitbang->queue.next, struct spi_message,

queue);

list_del_init(&m->queue);

spin_unlock_irqrestore(&bitbang->lock, flags);

/* FIXME this is made-up ... the correct value is known to

* word-at-a-time bitbang code, and presumably chipselect()

* should enforce these requirements too?

*/

nsecs = 100;

spi = m->spi;

tmp = 0;

cs_change = 1;

status = 0;

list_for_each_entry (t, &m->transfers, transfer_list) {

/* override speed or wordsize? */

if (t->speed_hz || t->bits_per_word)

do_setup = 1;

/* init (-1) or override (1) transfer params */

if (do_setup != 0) {

status = bitbang->setup_transfer(spi, t);

if (status < 0)

break;

if (do_setup == -1)

do_setup = 0;

}

/* set up default clock polarity, and activate chip;

* this implicitly updates clock and spi modes as

* previously recorded for this device via setup().

* (and also deselects any other chip that might be

* selected ...)

*/

if (cs_change) {

bitbang->chipselect(spi, BITBANG_CS_ACTIVE);

ndelay(nsecs);

}

cs_change = t->cs_change;

if (!t->tx_buf && !t->rx_buf && t->len) {

status = -EINVAL;

break;

}

/* transfer data. the lower level code handles any

* new dma mappings it needs. our caller always gave

* us dma-safe buffers.

*/

if (t->len) {

/* REVISIT dma API still needs a designated

* DMA_ADDR_INVALID; ~0 might be better.

*/

if (!m->is_dma_mapped)

t->rx_dma = t->tx_dma = 0;

status = bitbang->txrx_bufs(spi, t);

}

if (status > 0)

m->actual_length += status;

if (status != t->len) {

/* always report some kind of error */

if (status >= 0)

status = -EREMOTEIO;

break;

}

status = 0;

/* protocol tweaks before next transfer */

if (t->delay_usecs)

udelay(t->delay_usecs);

if (!cs_change)

continue;

if (t->transfer_list.next == &m->transfers)

break;

/* sometimes a short mid-message deselect of the chip

* may be needed to terminate a mode or command

*/

ndelay(nsecs);

bitbang->chipselect(spi, BITBANG_CS_INACTIVE);

ndelay(nsecs);

}

m->status = status;

m->complete(m->context);

/* normally deactivate chipselect ... unless no error and

* cs_change has hinted that the next message will probably

* be for this chip too.

*/

if (!(status == 0 && cs_change)) {

ndelay(nsecs);

bitbang->chipselect(spi, BITBANG_CS_INACTIVE);

ndelay(nsecs);

}

spin_lock_irqsave(&bitbang->lock, flags);

}

bitbang->busy = 0;

spin_unlock_irqrestore(&bitbang->lock, flags);

}

当队列非空的时候就一直去取队列的数据,然后会执行到

[html] view plain copy

status = bitbang->setup_transfer(spi, t);

这个函数,因为在spi_bitbang_start中

[html] view plain copy

if (!bitbang->txrx_bufs) {

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;

}

}

所以就调用了spi_bitbang_setup_transfer;

[html] view plain copy

int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t)

{

struct spi_bitbang_cs *cs = spi->controller_state;

u8 bits_per_word;

u32 hz;

if (t) {

bits_per_word = t->bits_per_word;

hz = t->speed_hz;

} else {

bits_per_word = 0;

hz = 0;

}

/* spi_transfer level calls that work per-word */

if (!bits_per_word)

bits_per_word = spi->bits_per_word;

if (bits_per_word <= 8)

cs->txrx_bufs = bitbang_txrx_8;

else if (bits_per_word <= 16)

cs->txrx_bufs = bitbang_txrx_16;

else if (bits_per_word <= 32)

cs->txrx_bufs = bitbang_txrx_32;

else

return -EINVAL;

/* nsecs = (clock period)/2 */

if (!hz)

hz = spi->max_speed_hz;

if (hz) {

cs->nsecs = (1000000000/2) / hz;

if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))

return -EINVAL;

}

return 0;

}

这里主要是根据bits_per_word选择传输的方式,分8、16,、32三种模式,ads7843touchscreen是用bits_per_word默认没有,选择bitbang_txrx_8的。

[html] view plain copy

static unsigned bitbang_txrx_8(

struct spi_device *spi,

u32 (*txrx_word)(struct spi_device *spi,

unsigned nsecs,

u32 word, u8 bits),

unsigned ns,

struct spi_transfer *t

) {

unsigned bits = t->bits_per_word ? : spi->bits_per_word;

unsigned count = t->len;

const u8 *tx = t->tx_buf;

u8 *rx = t->rx_buf;

while (likely(count > 0)) {

u8 word = 0;

if (tx)

word = *tx++;

word = txrx_word(spi, ns, word, bits);

if (rx)

*rx++ = word;

count -= 1;

}

return t->len - count;

}

这里word = txrx_word(spi, ns, word, bits);会调用到哪里呢?,首先看下这个函数的指针指向哪里。

在spi_bitbang_start中,bitbang->master->setup = spi_bitbang_setup;

然后在spi_bitbang_setup 中有

[html] view plain copy

cs->txrx_word = bitbang->txrx_word[spi->mode & (SPI_CPOL|SPI_CPHA)];

所以,这个最终还是调用到了spi_gpio.c文件中的spi_gpio_spec_txrx_word_mode0

[html] view plain copy

static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi,

unsigned nsecs, u32 word, u8 bits)

{

unsigned flags = spi->master->flags;

return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);

}

然后这个函数就调用了bitbang_txrx_be_cpha0,这个函数在spi-bitbang-txrx.h中

[html] view plain copy

static inline u32

bitbang_txrx_be_cpha0(struct spi_device *spi,

unsigned nsecs, unsigned cpol, unsigned flags,

u32 word, u8 bits)

{

/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */

/* clock starts at inactive polarity */

for (word <<= (32 - bits); likely(bits); bits--) {

/* setup MSB (to slave) on trailing edge */

if ((flags & SPI_MASTER_NO_TX) == 0)

setmosi(spi, word & (1 << 31));

spidelay(nsecs); /* T(setup) */

setsck(spi, !cpol);

spidelay(nsecs);

/* sample MSB (from slave) on leading edge */

word <<= 1;

if ((flags & SPI_MASTER_NO_RX) == 0)

word |= getmiso(spi);

setsck(spi, cpol);

}

return word;

}

这里就是gpio模拟的spi总线的协议过程了。这样,从最上面设备程序调用到控制器的整个数据流就结束了。

注:这里有一个很恶心的东东,就是在bitbang_txrx_16,bitbang_txrx_32中的

[html] view plain copy

const u8 *tx = t->tx_buf;

u8 *rx = t->rx_buf;

这里是强制转换的,由于大小端的问题,可能导致数据相反,从而传输会出现问题的,如果是8bit的,那么就没有任何问题了。

一段小插曲,也是用逻辑分析仪抓到的数据才发现的,如果没有这玩意儿,估计现在还纠结着。

OK,至此,linux的SPI的数据传输就到这里了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: