这些字符设备的操作函数集在文件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;
}