最近用到了海思的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 */ }