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

linux spi驱动开发学习(四)-----spi驱动程序完整流程分析

2015-03-24 16:12 483 查看
所有的应用程序使用dev/目录下创建的设备,这些字符设备的操作函数集在文件spidev.c中实现。

点击(此处)折叠或打开

static const struct file_operations spidev_fops = {

.owner = THIS_MODULE,

/* REVISIT switch to aio primitives, so
that userspace

* gets more complete API coverage. It'll simplify things

* too, except for the locking.

*/

.write =
spidev_write,

.read =
spidev_read,

.unlocked_ioctl = spidev_ioctl,

.open =
spidev_open,

.release =
spidev_release,

};

以上是所有应用程序所能够做的所有操作,由此开始追踪spi 驱动程序的完整执行流程

其中,最重要的就是ioctl, 从这里开始先重点剖析inctl

点击(此处)折叠或打开

spidev_ioctl(struct file *filp, unsigned int cmd, unsigned
long arg)

{

int err = 0;

int retval = 0;

struct spidev_data *spidev;

struct spi_device *spi;

u32 tmp;

unsigned n_ioc;

struct spi_ioc_transfer *ioc;

//ioctl cmd 检查

/* Check type and command number */

if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)

return -ENOTTY;

/* Check access direction once here; don't
repeat below.

* IOC_DIR is from the user perspective, while access_ok is

* from the kernel perspective; so they look reversed.

*/

if (_IOC_DIR(cmd) & _IOC_READ)

err = !access_ok(VERIFY_WRITE,

(void __user *)arg, _IOC_SIZE(cmd));

if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)

err = !access_ok(VERIFY_READ,

(void __user *)arg, _IOC_SIZE(cmd));

if (err)

return -EFAULT;

/* guard against device removal before, or while,

* we issue this ioctl.

*/

//通过以下方式获取spi_device-----spi(是之后操作的基础)

spidev = filp->private_data;

spin_lock_irq(&spidev->spi_lock);

spi = spi_dev_get(spidev->spi);

spin_unlock_irq(&spidev->spi_lock);

if (spi == NULL)

return -ESHUTDOWN;

/* use the buffer lock here for triple duty:

* - prevent I/O (from
us) so calling spi_setup() is safe;

* - prevent concurrent SPI_IOC_WR_* from morphing

* data fields while SPI_IOC_RD_* reads them;

* - SPI_IOC_MESSAGE needs the buffer locked "normally".

*/

mutex_lock(&spidev->buf_lock);

//以上是进行check,检查命令有效性,以及进行初始化数据,这里不在多做说明

switch (cmd) {

/* read requests */

case SPI_IOC_RD_MODE://获取模式信息,将信息发送给用户

retval = __put_user(spi->mode & SPI_MODE_MASK,

(__u8 __user *)arg);

break;

case SPI_IOC_RD_LSB_FIRST://获取spi最低有效位

retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,

(__u8 __user *)arg);

break;

case SPI_IOC_RD_BITS_PER_WORD:

retval = __put_user(spi->bits_per_word, (__u8
__user *)arg);

break;

case SPI_IOC_RD_MAX_SPEED_HZ:

retval = __put_user(spi->max_speed_hz, (__u32
__user *)arg);

break;

/* write requests */

case SPI_IOC_WR_MODE://设置数据传输模式,这里只是把设置的数据保存在spi
中,但并没有对spi device做任何操作,对spi device的操作一并在最后进行

retval = __get_user(tmp, (u8
__user *)arg);

if (retval == 0) {

u8 save = spi->mode;

if (tmp & ~SPI_MODE_MASK) {

retval = -EINVAL;

break;

}

tmp |= spi->mode & ~SPI_MODE_MASK;

spi->mode = (u8)tmp;

retval = spi_setup(spi);

if (retval < 0)

spi->mode = save;

else

dev_dbg(&spi->dev, "spi
mode %02xn", tmp);

}

break;

case SPI_IOC_WR_LSB_FIRST://设置设置spi写最低有效位,同上

retval = __get_user(tmp, (__u8
__user *)arg);

if (retval == 0) {

u8 save = spi->mode;

if (tmp)

spi->mode |= SPI_LSB_FIRST;

else

spi->mode &= ~SPI_LSB_FIRST;

retval = spi_setup(spi);

if (retval < 0)

spi->mode = save;

else

dev_dbg(&spi->dev, "%csb
firstn",

tmp ? 'l' : 'm');

}

break;

case SPI_IOC_WR_BITS_PER_WORD://设置spi写每个字含多个个位,同上

retval = __get_user(tmp, (__u8
__user *)arg);

if (retval == 0) {

u8 save = spi->bits_per_word;

spi->bits_per_word = tmp;

retval = spi_setup(spi);

if (retval < 0)

spi->bits_per_word = save;

else

dev_dbg(&spi->dev, "%d
bits per wordn", tmp);

}

break;

case SPI_IOC_WR_MAX_SPEED_HZ://设置spi写最大速率,同上

retval = __get_user(tmp, (__u32
__user *)arg);

if (retval == 0) {

u32 save = spi->max_speed_hz;

spi->max_speed_hz = tmp;

retval = spi_setup(spi);

if (retval < 0)

spi->max_speed_hz = save;

else

dev_dbg(&spi->dev, "%d
Hz (max)n", tmp);

}

break;

default:

/* segmented and/or full-duplex
I/O request */

if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))//查看是否为数据write命令

|| _IOC_DIR(cmd) != _IOC_WRITE) {

retval = -ENOTTY;

break;

}

//pay more time on understanding below method

tmp = _IOC_SIZE(cmd);//从命令参数中解析出用户数据大小

if ((tmp % sizeof(struct
spi_ioc_transfer)) != 0) {//数据大小必须是struct
spi_ioc_transfer的整数倍

retval = -EINVAL;

break;

}

n_ioc = tmp / sizeof(struct spi_ioc_transfer);//将要传输的数据分成n个传输数据段

if (n_ioc == 0)

break;

/* copy into scratch area */

ioc = kmalloc(tmp, GFP_KERNEL);//获取n个数据段的数据管理结构体的内存空间

if (!ioc) {

retval = -ENOMEM;

break;

}

if (__copy_from_user(ioc, (void
__user *)arg, tmp)) {//从用户空间获取数据管理结构体的初始化值

kfree(ioc);

retval = -EFAULT;

break;

}

/* translate to spi_message, execute */

retval = spidev_message(spidev, ioc, n_ioc);//数据传输,这是整个流程中的核心

kfree(ioc);

break;

}

mutex_unlock(&spidev->buf_lock);

spi_dev_put(spi);

return retval;

}

****************************************************************************************华丽分割线****************************************************************************************

(1)

//通过调用函数spi->master->setup()来设置SPI模式。

static inline int

spi_setup(struct spi_device *spi)

{

return spi->master->setup(spi);

}

master->setup成员被初始化成函数s3c24xx_spi_setup()。这一工作是在函数

s3c24xx_spi_probe()中进行的

hw->bitbang.master->setup = s3c24xx_spi_setup;

函数s3c24xx_spi_setup()是在文件linux/drivers/spi/spi_s3c24xx.c中实现的。

点击(此处)折叠或打开

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);

//bitbang包含了数据传输函数,在数据传输时忙碌标识bitbang.busy置1,也就是在数据传输过程中不能改变数据传输模式。

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;

}

点击(此处)折叠或打开

static int s3c24xx_spi_update_state(struct spi_device *spi,

struct spi_transfer *t)

{

struct s3c24xx_spi *hw = to_hw(spi);

struct s3c24xx_spi_devstate *cs = spi->controller_state;

unsigned int bpw;

unsigned int hz;

unsigned int div;

unsigned long clk;

bpw = t ? t->bits_per_word : spi->bits_per_word;

hz = t ? t->speed_hz : spi->max_speed_hz;

if (!bpw)

bpw = 8;

if (!hz)

hz = spi->max_speed_hz;

if (bpw != 8) {

dev_err(&spi->dev, "invalid
bits-per-word (%d)n", bpw);

return -EINVAL;

}

if (spi->mode != cs->mode) {

u8 spcon = SPCON_DEFAULT;

if (spi->mode & SPI_CPHA)

spcon |= S3C2410_SPCON_CPHA_FMTB;

if (spi->mode & SPI_CPOL)

spcon |= S3C2410_SPCON_CPOL_HIGH;

cs->mode = spi->mode;

cs->spcon = spcon;

}

if (cs->hz != hz) {

clk = clk_get_rate(hw->clk);

div = DIV_ROUND_UP(clk, hz * 2) - 1;

if (div > 255)

div = 255;

dev_dbg(&spi->dev, "pre-scaler=%d
(wanted %d, got %ld)n",

div, hz, clk / (2 * (div + 1)));

cs->hz = hz;

cs->sppre = div;

}

return 0;

}

s3c24xx_spi_update_state 方法对设置项做了以下初始化和检查,并未做任何的有关于硬件的任何操作,

所有对硬件的设置是通过s3c24xx_spi_probe中填充好的s3c24xx_spi_chipsel方法实现的

点击(此处)折叠或打开

/* setup the state for the
bitbang driver */

hw->bitbang.master = hw->master;

hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;

hw->bitbang.chipselect = s3c24xx_spi_chipsel;

hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;

hw->master->setup = s3c24xx_spi_setup;

hw->master->cleanup = s3c24xx_spi_cleanup;

下面看一下这个方法的具体实现过程:

点击(此处)折叠或打开

static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)

{

struct s3c24xx_spi_devstate *cs = spi->controller_state;

struct s3c24xx_spi *hw = to_hw(spi);

unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;

/* change the chipselect state and the state of the spi engine clock */

switch (value) {

case BITBANG_CS_INACTIVE:

hw->set_cs(hw->pdata, spi->chip_select, cspol^1);

writeb(cs->spcon, hw->regs + S3C2410_SPCON);

break;

case BITBANG_CS_ACTIVE:

writeb(cs->spcon | S3C2410_SPCON_ENSCK,

hw->regs + S3C2410_SPCON);

hw->set_cs(hw->pdata, spi->chip_select, cspol);

break;

}

}

//上面对本函数的调用传递的参数是BITBANG_CS_INACTIVE。可以看出上层函数对数据传输模式的设置,

//只能改变spi->mode字段,而不能通过函数spi->master->setup(spi);的调用而将要设置的传输模式写入硬件。

//这或许是linux中的SPI子系统还不够完善吧。但spi->mode的值最终是会被写入SPI的控制寄存器中的,

// hw->bitbang.chipselect(spi, BITBANG_CS_ACTIVE); 的时候。在后面会遇到,这里是在s3c24xx_spi_probe 方法中填充的

**************************************************************************************华丽分割线************************************************************************************

(2)

在iotcl中, 除了(1) 中讲到的设置项,以下便是重点中的重点

retval = spidev_message(spidev, ioc, n_ioc);//数据传输

点击(此处)折叠或打开

static int spidev_message(struct spidev_data *spidev,

struct spi_ioc_transfer *u_xfers, unsigned n_xfers)

{

struct spi_message msg;

struct spi_transfer *k_xfers;

struct spi_transfer *k_tmp;

struct spi_ioc_transfer *u_tmp;

unsigned n, total;

u8 *buf;

int status = -EFAULT;

spi_message_init(&msg);

k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);//每个
spi_transfer代表一段要传输的数据

if (k_xfers == NULL)

return -ENOMEM;

/* Construct spi_message, copying any tx data to bounce
buffer.

* We walk the array of user-provided transfers, using each one

* to initialize a kernel version of the same transfer.

*/

buf = spidev->buffer;

total = 0;

for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;

n;

n--, k_tmp++, u_tmp++) {

k_tmp->len = u_tmp->len;

//将要传输的数据分成n个数据段每个数据段用一个spi_transfer管理,u_xfers为用户空间传来的数据段

total += k_tmp->len;//要传输的数据总量

if (total > bufsiz) {

status = -EMSGSIZE;

goto done;

}

if (u_tmp->rx_buf) {//需要接收则分给一段用于接收数据的内存

k_tmp->rx_buf = buf;

if (!access_ok(VERIFY_WRITE, (u8
__user *)

(uintptr_t) u_tmp->rx_buf,

u_tmp->len))

goto done;

}

if (u_tmp->tx_buf) {

k_tmp->tx_buf = buf;

if (copy_from_user(buf, (const u8
__user *)

(uintptr_t) u_tmp->tx_buf,

u_tmp->len))

goto done;

}

buf += k_tmp->len;//指向下一段内存

k_tmp->cs_change = !!u_tmp->cs_change;//双非操作取其逻辑值

k_tmp->bits_per_word = u_tmp->bits_per_word;

k_tmp->delay_usecs = u_tmp->delay_usecs;//一段数据的完全传输需要一定时间的等待

k_tmp->speed_hz = u_tmp->speed_hz;

#ifdef VERBOSE

dev_dbg(&spi->dev,

" xfer len %zd %s%s%s%dbits %u usec %uHzn",

u_tmp->len,

u_tmp->rx_buf ? "rx
" : "",

u_tmp->tx_buf ? "tx
" : "",

u_tmp->cs_change ? "cs
" : "",

u_tmp->bits_per_word ? : spi->bits_per_word,

u_tmp->delay_usecs,

u_tmp->speed_hz ? : spi->max_speed_hz);

#endif

spi_message_add_tail(k_tmp, &msg);//将用于数据传输的数据段挂在msg上

}

status = spidev_sync(spidev, &msg);//调用底层函数进行数据传输

if (status < 0)

goto done;

/* copy any rx data out of bounce buffer */

buf = spidev->buffer;

for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {

if (u_tmp->rx_buf) {

if (__copy_to_user((u8
__user *)

(uintptr_t) u_tmp->rx_buf, buf,

u_tmp->len)) {//分段向用户空间传输数据

status = -EFAULT;

goto done;

}

}

buf += u_tmp->len;

}

status = total;

done:

kfree(k_xfers);

return status;

}

点击(此处)折叠或打开

static ssize_t

spidev_sync(struct spidev_data *spidev, struct spi_message *message)

{

DECLARE_COMPLETION_ONSTACK(done);

int status;

message->complete = spidev_complete;

message->context = &done;//在底层的数据传输函数中会调用函数spidev_complete来通知数据传输完成,在此留一标记

spin_lock_irq(&spidev->spi_lock);

if (spidev->spi == NULL)

status = -ESHUTDOWN;

else

status = spi_async(spidev->spi, message);

spin_unlock_irq(&spidev->spi_lock);

if (status == 0) {

wait_for_completion(&done); //等待数据完成

status = message->status;

if (status == 0)

status = message->actual_length;

}

return status;

}

点击(此处)折叠或打开

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);

}

EXPORT_SYMBOL_GPL(spi_async);

函数spi->master->transfer()在函数spi_bitbang_start()中被初始化为函数spi_bitbang_transfer()如下

if (!bitbang->master->transfer)

bitbang->master->transfer = spi_bitbang_transfer;

函数spi_bitbang_transfer()又在函数s3c24xx_spi_probe()中被调用。

函数spi_bitbang_transfer()在文件spi_bitbang.c中实现。

点击(此处)折叠或打开

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 {

//将携带数据的结构体spi_message挂到bitbang->queue上。每一次数据传输都将要传输的数据包装成结构体spi_message传递

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

//将该传输任务添加到工作队列头bitbang->workqueue。接下来将调用任务处理函数进一步数据处理。

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

}

spin_unlock_irqrestore(&bitbang->lock, flags);

return status;

}

EXPORT_SYMBOL_GPL(spi_bitbang_transfer);

在此不得不讨论一下结构体,这是一个完成数据传输的重要结构体。

struct spi_bitbang {

struct workqueue_struct *workqueue; //工作队列头。

struct work_struct work; //每一次数据传输都传递下来一个spi_message,都向工作队列头添加一个任务。

。。。。。。

//挂接spi_message,如果上一次的spi_message还没处理完接下来的spi_message就挂接在 queue上等待处理。

struct list_head queue;

u8 busy; //忙碌标识。

。。。。。。

struct spi_master *master;

// 以下三个函数都是在函数s3c24xx_spi_probe()中被初始化的。

int (*setup_transfer)(struct spi_device *spi,struct spi_transfer *t); //设置数据传输波特率

void (*chipselect)(struct spi_device *spi, int is_on); //将数据传输模式写入控制寄存器。

//向SPTDAT中写入要传输的第一个数据,数据传输是通过中断方式完成的,只要进行一次中断触发,以后向SPTDAT中写数据

//的工作就在中断处理函数中进行。

int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);

。。。。。。

};

数据传输是SPI接口的任务,结构体master代表了一个接口,当一个spi_message从上层函数传递下来时,

master的成员函数bitbang->master->transfer将该数据传输任务添加到工作队列头。

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

函数bitbang->master->transfer()在上面已经讲解。

工作队列struct workqueue_struct *workqueue;的创建和 struct work_struct work的初始化都是在函数

spi_bitbang_start()中进行的。

INIT_WORK(&bitbang->work, bitbang_work); //bitbang_work是任务处理函数

bitbang->workqueue = create_singlethread_workqueue(dev_name(bitbang->master->dev.parent));

任务处理函数如下

点击(此处)折叠或打开

static void bitbang_work(struct work_struct *work)

{

struct spi_bitbang *bitbang =

container_of(work, struct spi_bitbang, work);

unsigned long flags;

int do_setup = -1;

int (*setup_transfer)(struct
spi_device *,

struct spi_transfer *);

setup_transfer = bitbang->setup_transfer;

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;

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) {

if (!setup_transfer) {

status = -ENOPROTOOPT;

break;

}

status = setup_transfer(spi, t);

if (status < 0)

break;

}

/* 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) {

//多段数据传输时只需第一段数据传输时调用以下模式设置,将spi->mode

//写入SPI的模式控制寄存器。并调用函数hw->set_cs,片选设置

//在函数讲解函数spidev_ioctl()中的模式设置时讲到过。在那里 调用的函数chipselect(spi, BITBANG_CS_INACTIVE);

//传递的传输是BITBANG_CS_INACTIVE是不能将数据传输模式spi->mode写入SPI控制寄存器的,不过在那里设置了spi->mode的值。

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)//多段数据传输时t->cs_change为0表示下面还有未传输数据否者判断遍历是否结束

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);//
通知一次数据传输完

/* restore speed and wordsize if it
was overridden */

if (do_setup == 1)

setup_transfer(spi, NULL);

do_setup = 0;

/* 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);

}

点击(此处)折叠或打开

static int s3c24xx_spi_txrx(struct spi_device *spi, struct
spi_transfer *t)

{

struct s3c24xx_spi *hw = to_hw(spi);

dev_dbg(&spi->dev, "txrx:
tx %p, rx %p, len %dn",

t->tx_buf, t->rx_buf, t->len);

hw->tx = t->tx_buf;

hw->rx = t->rx_buf;

hw->len = t->len;

hw->count = 0;

init_completion(&hw->done);

/* send the first byte */

writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);

wait_for_completion(&hw->done);

return hw->count;

}

********************************************************************************************************

函数hw_txbyte()的实现如下

static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)

{

return hw->tx ? hw->tx[count] : 0;

}

将要传输的数据段的第一个数据写入SPI数据寄存器S3C2410_SPTDAT。即便是数据接收也得向数据寄存器写入数据

才能触发一次数据的传输。只需将该数据段的第一个数据写入数据寄存器就可以触发数据传输结束中断,以后的数据

就在中断处理函数中写入数据寄存器。

********************************************************************************************************

数据传输结束中断

点击(此处)折叠或打开

static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)

{

struct s3c24xx_spi *hw = dev;

unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);

unsigned int count = hw->count;

if (spsta & S3C2410_SPSTA_DCOL) {

dev_dbg(hw->dev, "data-collisionn");

complete(&hw->done);

goto irq_done;

}

if (!(spsta & S3C2410_SPSTA_READY)) {//数据准备好

dev_dbg(hw->dev, "spi
not ready for tx?n");

complete(&hw->done);

goto irq_done;

}

hw->count++;

if (hw->rx)

hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);//接收数据

count++;

if (count < hw->len)

writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);//写入数据

else

complete(&hw->done);

irq_done:

return IRQ_HANDLED;

}

以上为记录运行流程,细节待修正补充

好了,稍微总结一下:

spi的读写请求通过:

spi_transfer->spi_message->spi_bitbang添加都bitbang->queue中,被bitbang->work反方向提取出来执行(后面会提到)。

通过queue_work(bitbang->workqueue, &bitbang->work)把bitbang-work加入bitbang->workqueue后,在某个合适的时间, bitbang->work将被调度运行,bitbang_work函数将被调用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: