最近用到了海思的3519的SPI读写,做一下简单的记录
SPI驱动包括主机master驱动,SPI core 和SPI设备驱动,linux驱动有很多这样的结构,I2C也是如此,这么做的目的是为了主机和设备分离。
spi定义的一些结构体在include/linux/spi/spi.h文件当中。
描述spi master
struct spi_master {
struct device dev; //设备模型使用
struct list_head list;
s16 bus_num; //总线编号
u16 num_chipselect; //片选数量,该控制器能挂载的最大的从设备数量
u16 dma_alignment;
u16 mode_bits; //
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
/* 最大最小传输速度 */
u32 min_speed_hz;
u32 max_speed_hz;
/* other constraints relevant to this driver */
u16 flags;
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
#define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
/* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
/* flag indicating that the SPI bus is locked for exclusive use */
bool bus_lock_flag;
/* 设置模式,时钟分频等,可能会被多次调用.需要被实现
*/
int (*setup)(struct spi_device *spi);
/* 数据传输要用的函数
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
/* called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device *spi);
/*
* Used to enable core support for DMA handling, if can_dma()
* exists and returns true then the transfer will be mapped
* prior to transfer_one() being called. The driver should
* not modify or store xfer and dma_tx and dma_rx must be set
* while the device is prepared.
*/
bool (*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);
/*
* These hooks are for drivers that want to use the generic
* master transfer queueing mechanism. If these are used, the
* transfer() function above must NOT be specified by the driver.
* Over time we expect SPI drivers to be phased over to this API.
*/
bool queued;
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work pump_messages;
spinlock_t queue_lock;
struct list_head queue;
struct spi_message *cur_msg;
bool busy;
bool running;
bool rt;
bool auto_runtime_pm;
bool cur_msg_prepared;
bool cur_msg_mapped;
struct completion xfer_completion;
size_t max_dma_len;
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
void (*set_cs)(struct spi_device *spi, bool enable);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
/* gpio chip select */
int *cs_gpios;
/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
/* dummy data for full duplex devices */
void *dummy_rx;
void *dummy_tx;
};
描述设备的结构体
struct spi_device {
struct device dev;
struct spi_master *master; //所挂载的master
u32 max_speed_hz; //设备工作的最高频率
u8 chip_select; //所用的片选信号
u8 bits_per_word; //bit数目
u16 mode; //模式
#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)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
驱动结构体
struct spi_driver {
const struct spi_device_id *id_table;
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
int (*resume)(struct spi_device *spi);
struct device_driver driver;
};
这个跟其他的驱动一样,都是这几个函数。
我们来看看数据的传输,传输时和I2C类似,一条消息定义一次传输,看一下消息结构体
struct spi_message {
struct list_head transfers;
struct spi_device *spi; //spi设备
unsigned is_dma_mapped:1;
/* 传输完成时的回调函数 */
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
/* for optional use by whatever driver currently owns the
* spi_message ... between calls to spi_async and then later
* complete(), that's the spi_master controller driver.
*/
struct list_head queue;
void *state;
};
一条消息由多个事物来构成,传输事务的结构体,由于SPI是全双工的,需要一边发送数据,一边接收数据,所以结构体中有发送和接收缓冲区
struct spi_transfer {
const void *tx_buf; //发送buf
void *rx_buf; //接收buf
unsigned len;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned cs_change:1;
unsigned tx_nbits:3;
unsigned rx_nbits:3;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};
消息message中传输事务transfer是以链表的形式组织在一起,linux提供一系列API
static inline void spi_message_init(struct spi_message *m)
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
spi_transfer_del(struct spi_transfer *t)
spi_sync(struct spi_device *spi, struct spi_message *message);
1、消息的初始化
2、将传输事务t加入到消息m中的链表当中
3、从链表中删除事务t
4、发起spi设备上的事务传输,同步等待所有事务完成
对于应用层,用户可以通过ioctl来进行spi配置以及spi的读写
static long
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;
/* 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.
*/
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);
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_MODE32:
retval = __put_user(spi->mode & SPI_MODE_MASK,
(__u32 __user *)arg);
break;
case SPI_IOC_RD_LSB_FIRST:
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:
case SPI_IOC_WR_MODE32:
if (cmd == SPI_IOC_WR_MODE)
retval = __get_user(tmp, (u8 __user *)arg);
else
retval = __get_user(tmp, (u32 __user *)arg);
if (retval == 0) {
u32 save = spi->mode;
if (tmp & ~SPI_MODE_MASK) {
retval = -EINVAL;
break;
}
tmp |= spi->mode & ~SPI_MODE_MASK;
spi->mode = (u16)tmp;
retval = spi_setup(spi);
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "spi mode %x\n", tmp);
}
break;
case SPI_IOC_WR_LSB_FIRST:
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u32 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 first\n",
tmp ? 'l' : 'm');
}
break;
case SPI_IOC_WR_BITS_PER_WORD: //设置数据位宽
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 word\n", tmp);
}
break;
case SPI_IOC_WR_MAX_SPEED_HZ: //设置传输速度
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: //spi的读写
/* segmented and/or full-duplex I/O request */
if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
|| _IOC_DIR(cmd) != _IOC_WRITE) {
retval = -ENOTTY;
break;
}
tmp = _IOC_SIZE(cmd); //读写数据的大小
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
retval = -EINVAL;
break;
}
n_ioc = tmp / sizeof(struct spi_ioc_transfer);
if (n_ioc == 0)
break;
/* copy into scratch area */
ioc = kmalloc(tmp, GFP_KERNEL);
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;
}
spi_message()是处理发送的消息
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 *tx_buf, *rx_buf;
int status = -EFAULT;
spi_message_init(&msg); //消息初始化
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
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.
*/ tx_buf = spidev->tx_buffer;
rx_buf = spidev->rx_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;
total += k_tmp->len;
/* Check total length of transfers. Also check each
* transfer length to avoid arithmetic overflow.
*/ if (total > bufsiz || k_tmp->len > bufsiz) { status = -EMSGSIZE;
goto done; }
if (u_tmp->rx_buf) { k_tmp->rx_buf = rx_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 = tx_buf;
if (copy_from_user(tx_buf, (const u8 __user *) (uintptr_t) u_tmp->tx_buf, u_tmp->len)) goto done; }
tx_buf += k_tmp->len;
rx_buf += k_tmp->len;
k_tmp->cs_change = !!u_tmp->cs_change;
k_tmp->tx_nbits = u_tmp->tx_nbits;
k_tmp->rx_nbits = u_tmp->rx_nbits;
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(&spidev->spi->dev, " xfer len %zd %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,
u_tmp->rx_buf ? "rx " : "",
u_tmp->tx_buf ? "tx " : "",
u_tmp->cs_change ? "cs " : "",
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
u_tmp->delay_usecs,
u_tmp->speed_hz ? : spidev->spi->max_speed_hz); #endif spi_message_add_tail(k_tmp, &msg); }
status = spidev_sync(spidev, &msg); //同步发送
if (status < 0) goto done;
/* copy any rx data out of bounce buffer */
rx_buf = spidev->rx_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, rx_buf,
u_tmp->len)) { status = -EFAULT;
goto done; } }
rx_buf += u_tmp->len; }
status = total;
done: kfree(k_xfers);
return status; }
向下继续追,直到
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
message->spi = spi;
trace_spi_message_submit(message);
return master->transfer(spi, message);
}
这里就是我们之前说过的master中的transfer方法,方法是在哪里初始化的呢
static int spi_master_initialize_queue(struct spi_master *master)
{
int ret;
master->transfer = spi_queued_transfer; //transfer方法的实现
if (!master->transfer_one_message)
master->transfer_one_message = spi_transfer_one_message;
/* Initialize and start queue */
ret = spi_init_queue(master);
if (ret) {
dev_err(&master->dev, "problem initializing queue\n");
goto err_init_queue;
}
master->queued = true;
ret = spi_start_queue(master);
if (ret) {
dev_err(&master->dev, "problem starting queue\n");
goto err_start_queue;
}
return 0;
err_start_queue:
spi_destroy_queue(master);
err_init_queue:
return ret;
}
static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
{
struct spi_master *master = spi->master;
unsigned long flags;
spin_lock_irqsave(&master->queue_lock, flags);
if (!master->running) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return -ESHUTDOWN;
}
msg->actual_length = 0;
msg->status = -EINPROGRESS;
list_add_tail(&msg->queue, &master->queue);
if (!master->busy)
queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
return 0;
}
实际上,transfer方法只是将消息挂载到了队列上,发送的调度是以kthread_worker和kthread_work机制实现的,通过创建一个内核线程来执行kthread_work的func
spi_init_queue实现了队列和工作线程的初始化
static int spi_init_queue(struct spi_master *master)
{
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
INIT_LIST_HEAD(&master->queue);
spin_lock_init(&master->queue_lock);
master->running = false;
master->busy = false;
init_kthread_worker(&master->kworker);
master->kworker_task = kthread_run(kthread_worker_fn,
&master->kworker, "%s",
dev_name(&master->dev));
if (IS_ERR(master->kworker_task)) {
dev_err(&master->dev, "failed to create message pump task\n");
return -ENOMEM;
}
init_kthread_work(&master->pump_messages, spi_pump_messages);
/*
* Master config will indicate if this controller should run the
* message pump with high (realtime) priority to reduce the transfer
* latency on the bus by minimising the delay between a transfer
* request and the scheduling of the message pump thread. Without this
* setting the message pump thread will remain at default priority.
*/
if (master->rt) {
dev_info(&master->dev,
"will run message pump with realtime priority\n");
sched_setscheduler(master->kworker_task, SCHED_FIFO, ¶m);
}
return 0;
}
入队之后,出对操作由kthread_wokrer中的spi_pump_message完成
/**
* spi_pump_messages - kthread work function which processes spi message queue
* @work: pointer to kthread work struct contained in the master struct
*
* This function checks if there is any spi message in the queue that
* needs processing and if so call out to the driver to initialize hardware
* and transfer each message.
*
*/
static void spi_pump_messages(struct kthread_work *work)
{
struct spi_master *master =
container_of(work, struct spi_master, pump_messages);
unsigned long flags;
bool was_busy = false;
int ret;
/* Lock queue and check for queue work */
spin_lock_irqsave(&master->queue_lock, flags);
if (list_empty(&master->queue) || !master->running) {
if (!master->busy) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
master->busy = false;
spin_unlock_irqrestore(&master->queue_lock, flags);
kfree(master->dummy_rx);
master->dummy_rx = NULL;
kfree(master->dummy_tx);
master->dummy_tx = NULL;
if (master->unprepare_transfer_hardware &&
master->unprepare_transfer_hardware(master))
dev_err(&master->dev,
"failed to unprepare transfer hardware\n");
if (master->auto_runtime_pm) {
pm_runtime_mark_last_busy(master->dev.parent);
pm_runtime_put_autosuspend(master->dev.parent);
}
trace_spi_master_idle(master);
return;
}
/* Make sure we are not already running a message */
if (master->cur_msg) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
/* Extract head of queue */
master->cur_msg =
list_first_entry(&master->queue, struct spi_message, queue);
list_del_init(&master->cur_msg->queue);
if (master->busy)
was_busy = true;
else
master->busy = true;
spin_unlock_irqrestore(&master->queue_lock, flags);
if (!was_busy && master->auto_runtime_pm) {
ret = pm_runtime_get_sync(master->dev.parent);
if (ret < 0) {
dev_err(&master->dev, "Failed to power device: %d\n",
ret);
return;
}
}
if (!was_busy)
trace_spi_master_busy(master);
if (!was_busy && master->prepare_transfer_hardware) {
ret = master->prepare_transfer_hardware(master);
if (ret) {
dev_err(&master->dev,
"failed to prepare transfer hardware\n");
if (master->auto_runtime_pm)
pm_runtime_put(master->dev.parent);
return;
}
}
trace_spi_message_start(master->cur_msg);
if (master->prepare_message) {
ret = master->prepare_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev,
"failed to prepare message: %d\n", ret);
master->cur_msg->status = ret;
spi_finalize_current_message(master);
return;
}
master->cur_msg_prepared = true;
}
ret = spi_map_msg(master, master->cur_msg);
if (ret) {
master->cur_msg->status = ret;
spi_finalize_current_message(master);
return;
}
//处理发送该消息
ret = master->transfer_one_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev,
"failed to transfer one message from queue\n");
return;
}
}
该方法spi_master_initialize_queue根据spi控制器的实现有所不同,在spi_transfer_one_message函数有所体现,如果主控制器中实现了该方法,就会执行主控制器的方法,若未实现,就赋值为spi_transfer_one_message函数。
if (!master->transfer_one_message)
master->transfer_one_message = spi_transfer_one_message;
海思使用的是ARM提供的SPI的IP PL022.spi-pl022.c中实现了该方法
static int pl022_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{
struct pl022 *pl022 = spi_master_get_devdata(master);
/* Initial message state */
pl022->cur_msg = msg;
msg->state = STATE_START;
pl022->cur_transfer = list_entry(msg->transfers.next,
struct spi_transfer, transfer_list);
/* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(msg->spi);
pl022->cur_cs = pl022->chipselects[msg->spi->chip_select];
restore_state(pl022);
flush(pl022);
if (pl022->cur_chip->xfer_type == POLLING_TRANSFER)
do_polling_transfer(pl022);
else
do_interrupt_dma_transfer(pl022);
return 0;
}
海思使用的是轮询的方式传输数据的
static void do_polling_transfer(struct pl022 *pl022)
{
struct spi_message *message = NULL;
struct spi_transfer *transfer = NULL;
struct spi_transfer *previous = NULL;
struct chip_data *chip;
unsigned long time, timeout;
chip = pl022->cur_chip;
message = pl022->cur_msg;
while (message->state != STATE_DONE) {
/* Handle for abort */
if (message->state == STATE_ERROR)
break;
transfer = pl022->cur_transfer;
/* Delay if requested at end of transfer */
if (message->state == STATE_RUNNING) {
previous =
list_entry(transfer->transfer_list.prev,
struct spi_transfer, transfer_list);
if (previous->delay_usecs)
udelay(previous->delay_usecs);
if (previous->cs_change)
pl022_cs_control(pl022, SSP_CHIP_SELECT);
} else {
/* STATE_START */
message->state = STATE_RUNNING;
if (!pl022->next_msg_cs_active)
pl022_cs_control(pl022, SSP_CHIP_SELECT);
}
/* Configuration Changing Per Transfer */
if (set_up_next_transfer(pl022, transfer)) {
/* Error path */
message->state = STATE_ERROR;
break;
}
/* Flush FIFOs and enable SSP */
flush(pl022);
writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
SSP_CR1(pl022->virtbase));
dev_dbg(&pl022->adev->dev, "polling transfer ongoing ...\n");
timeout = jiffies + msecs_to_jiffies(SPI_POLLING_TIMEOUT);
while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end) {
time = jiffies;
readwriter(pl022); //底层的读写函数
if (time_after(time, timeout)) {
dev_warn(&pl022->adev->dev,
"%s: timeout!\n", __func__);
message->state = STATE_ERROR;
goto out;
}
cpu_relax();
}
/* Update total byte transferred */
message->actual_length += pl022->cur_transfer->len;
if (pl022->cur_transfer->cs_change)
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
/* Move to next transfer */
message->state = next_transfer(pl022);
}
out:
/* Handle end of message */
if (message->state == STATE_DONE)
message->status = 0;
else
message->status = -EIO;
giveback(pl022);
return;
}
底层的读写函数readwriter(),进入函数后,会首先查看read fifo中是否还有数据,有的话都读走。然后再write,write会尽量将write fifo写满(若总共发送的数据没有fifo大,就不同填满了),写完之后,再将read fifo中全读走。这个函数执行完毕后,write fifo应该为满,read fifo中应为空
/**
* This will write to TX and read from RX according to the parameters
* set in pl022.
*/
static void readwriter(struct pl022 *pl022)
{
/*
* The FIFO depth is different between primecell variants.
* I believe filling in too much in the FIFO might cause
* errons in 8bit wide transfers on ARM variants (just 8 words
* FIFO, means only 8x8 = 64 bits in FIFO) at least.
*
* To prevent this issue, the TX FIFO is only filled to the
* unused RX FIFO fill length, regardless of what the TX
* FIFO status flag indicates.
*/
dev_dbg(&pl022->adev->dev,
"%s, rx: %p, rxend: %p, tx: %p, txend: %p\n",
__func__, pl022->rx, pl022->rx_end, pl022->tx, pl022->tx_end);
/* Read as much as you can */
while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
&& (pl022->rx < pl022->rx_end)) {
switch (pl022->read) {
case READING_NULL:
readw(SSP_DR(pl022->virtbase));
break;
case READING_U8:
*(u8 *) (pl022->rx) =
readw(SSP_DR(pl022->virtbase)) & 0xFFU;
break;
case READING_U16:
*(u16 *) (pl022->rx) =
(u16) readw(SSP_DR(pl022->virtbase));
break;
case READING_U32:
*(u32 *) (pl022->rx) =
readl(SSP_DR(pl022->virtbase));
break;
}
pl022->rx += (pl022->cur_chip->n_bytes);
pl022->exp_fifo_level--;
}
/*
* Write as much as possible up to the RX FIFO size
*/
while ((pl022->exp_fifo_level < pl022->vendor->fifodepth)
&& (pl022->tx < pl022->tx_end)) {
switch (pl022->write) {
case WRITING_NULL:
writew(0x0, SSP_DR(pl022->virtbase));
break;
case WRITING_U8:
writew(*(u8 *) (pl022->tx), SSP_DR(pl022->virtbase));
break;
case WRITING_U16:
writew((*(u16 *) (pl022->tx)), SSP_DR(pl022->virtbase));
break;
case WRITING_U32:
writel(*(u32 *) (pl022->tx), SSP_DR(pl022->virtbase));
break;
}
pl022->tx += (pl022->cur_chip->n_bytes);
pl022->exp_fifo_level++;
/*
* This inner reader takes care of things appearing in the RX
* FIFO as we're transmitting. This will happen a lot since the
* clock starts running when you put things into the TX FIFO,
* and then things are continuously clocked into the RX FIFO.
*/
while ((readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RNE)
&& (pl022->rx < pl022->rx_end)) {
switch (pl022->read) {
case READING_NULL:
readw(SSP_DR(pl022->virtbase));
break;
case READING_U8:
*(u8 *) (pl022->rx) =
readw(SSP_DR(pl022->virtbase)) & 0xFFU;
break;
case READING_U16:
*(u16 *) (pl022->rx) =
(u16) readw(SSP_DR(pl022->virtbase));
break;
case READING_U32:
*(u32 *) (pl022->rx) =
readl(SSP_DR(pl022->virtbase));
break;
}
pl022->rx += (pl022->cur_chip->n_bytes);
pl022->exp_fifo_level--;
}
}
/*
* When we exit here the TX FIFO should be full and the RX FIFO
* should be empty
*/
}