三:SPI设备驱动程序
在板文件中添加SPI设备
<span style="color:#444444"><strong>static </strong> <strong>struct</strong> spi_board_info s3c_spi_devs [] __initdata
[ <span style="color:#880000">0</span> ] = {
.modalias = <span style="color:#880000">“spidev”</span>, <span style="color:#888888">/ *设备节点名称* /</span>
.mode = SPI_MODE_0, <span style="color:#888888">/ * CPOL = 0,CPHA = 0 * /</span>
.max_speed_hz = <span style="color:#880000">10000000</span>,
<span style="color:#888888">/ *连接到SPI-1作为第1个从站* /</span>
.bus_num = <span style="color:#880000">1</span>,
.irq = IRQ_SPI1,
.chip_select = <span style="color:#880000">0</span>,
.controller_data =&smdk_spi1_csi [SMDK_MMCSPI_CS],
},
}</span>
设备的片选信息
<span style="color:#444444">/ *
static struct s3c64xx_spi_csinfo smdk_spi0_csi [] = {
[SMDK_MMCSPI_CS] = {
.line = S5PV210_GPB(1),
.set_level = gpio_ <span style="color:#1f811f">set</span> _value,
.fb_delay = 0x0,
},
}; * /</span>
设置平台信息
<span style="color:#444444"> <strong>if</strong>(!gpio_request(S5PV210_GPB(1),<span style="color:#880000">“SPICS0”</span>)){
gpio_direction_output(S5PV210_GPB(1),1);
s3c_gpio_cfgpin(S5PV210_GPB(1),S3C_GPIO_SFN(1));
s3c_gpio_setpull(S5PV210_GPB(1),S3C_GPIO_PULL_UP);
s5pv210_spi_ <span style="color:#1f811f">set</span> _info(0,S5PV210_SPI_SRCCLK_PCLK,
ARRAY_SIZE(smdk_spi0_csi));
}</span>
<span style="color:#444444">s5pv210_spi_ <span style="color:#1f811f">set</span> _info(0,S5PV210_SPI_SRCCLK_PCLK,
ARRAY_SIZE(smdk_spi0_csi));</span>
<span style="color:#444444"><strong>void</strong> __ init <span style="color:#880000"><strong>s5pv210_spi_set_info </strong></span>(<strong>int</strong> cntrlr,<strong>int</strong> src_clk_nr,<strong>int</strong> num_cs)
{
<strong>struct</strong> s3c64xx_spi_info * pd;
<span style="color:#888888">/ *拒绝无效配置* / </span>
<strong>if</strong>(!num_cs || src_clk_nr < <span style="color:#880000">0</span>
|| src_clk_nr> S5PV210_SPI_SRCCLK_SCLK){
printk(KERN_ERR <span style="color:#880000">“%s:SPI配置无效\ n”</span>,__ func__);
<strong>回归</strong> ;
}
<strong>switch</strong>(cntrlr){
<strong>案例</strong> <span style="color:#880000">0</span>:
pd =&s5pv210_spi0_pdata;
<strong>打破</strong> ;
<strong>案例</strong> <span style="color:#880000">1</span>:
pd =&s5pv210_spi1_pdata;
<strong>打破</strong> ;
<strong>默认值</strong>:
printk(KERN_ERR <span style="color:#880000">“%s:无效的SPI控制器(%d)\ n”</span>,
__func __,cntrlr);
<strong>回归</strong> ;
}
pd-> num_cs = num_cs;
pd-> src_clk_nr = src_clk_nr;
pd-> src_clk_name = spi_src_clks [src_clk_nr];
}</span>
<span style="color:#444444">pd =&s5pv210_spi0_pdata;</span>
<span style="color:#444444"><strong>static </strong> <strong>struct</strong> s3c64xx_spi_info s5pv210_spi0_pdata = {
.cfg_gpio = s5pv210_spi_cfg_gpio,
.fifo_lvl_mask = <span style="color:#880000">0x1ff</span>,
.rx_lvl_offset = <span style="color:#880000">15</span>,
.high_speed = <span style="color:#880000">1</span>,
};</span>
将该设备注册到系统中
<span style="color:#444444">spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));</span>
板文件设置完了,就是设备驱动了
驱动结构体
<span style="color:#444444"><strong>struct</strong> spidev_data {
<strong>dev_t</strong> devt;
<strong>spinlock_t</strong> spi_lock;
<strong>struct</strong> spi_device * spi;
<strong>struct</strong> list_head device_entry;
<span style="color:#888888">/ * buffer为NULL,除非此设备已打开(users> 0)* / </span>
<strong>struct</strong> mutex buf_lock;
<strong>未签名的</strong> 用户;
u8 *缓冲;
};</span>
注册驱动
S5PV210会在/ dev /下生成几个视频相关的设备节点,分别为/ dev / video0,/ dev / video1,/ dev / video2 / dev / video14,/ dev / video21,/ dev / video22 video0,video1, v
<span style="color:#444444"><strong>static </strong> <strong>struct</strong> spi_driver spidev_spi_driver = {
.driver = {
.name = <span style="color:#880000">“spidev”</span>,
.owner = THIS_MODULE,
},
.probe = spidev_probe,
.remove = __devexit_p(spidev_remove),
<span style="color:#888888">/ * <strong>注意:</strong> 这里不需要暂停/恢复方法。
*除了将请求传递给/从之外,我们不做任何事情
*底层控制器。冰箱把手
*大多数问题; 控制器驱动程序处理其余部分。
* /</span>
};</span>
<span style="color:#444444"><strong>static </strong> <strong>int</strong> __ init <span style="color:#880000"><strong>spidev_init </strong></span>(<strong>void</strong>)
{
<strong>int</strong> status;
<span style="color:#888888">/ *声明我们的256个预留设备号。然后注册一个课程
*将密钥udev / mdev添加/删除/ dev节点。最后,注册
*管理这些设备号的驱动程序。
* /</span>
BUILD_BUG_ON(N_SPI_MINORS> <span style="color:#880000">256</span>);
status = register_chrdev(SPIDEV_MAJOR,<span style="color:#880000">“spi”</span>,&spidev_fops); <span style="color:#888888">//注册字符驱动,方便文件io操作</span>
<strong>if</strong>(status < <span style="color:#880000">0</span>)
<strong>返回</strong>状态;
spidev_class = class_create(THIS_MODULE,<span style="color:#880000">“spidev”</span>); <span style="color:#888888">//用与自动创建节点,在驱动探测到后,就会创建节点</span>
<strong>if</strong>(IS_ERR(spidev_class)){
unregister_chrdev(SPIDEV_MAJOR,spidev_spi_driver.driver.name);
<strong>返回</strong> PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi_driver); <span style="color:#888888">//注册spi驱动</span>
<strong>if</strong>(status < <span style="color:#880000">0</span>){
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR,spidev_spi_driver.driver.name);
}
<strong>返回</strong>状态;
}
宏module_init(spidev_init);</span>
当驱动探测到后
<span style="color:#444444"><strong>static</strong> int __devinit spidev_probe(struct spi_device * spi)
{
struct spidev_data * spidev;
int状态;
未签名的长期未成年人
<span style="color:#888888">/ *分配驱动数据* /</span>
spidev = kzalloc(sizeof(* spidev),GFP_KERNEL); <span style="color:#888888">//分配spi驱动数据</span>
<strong>if</strong>(!spidev)
<strong>return</strong> -ENOMEM;
<span style="color:#888888">/ *初始化驱动程序数据* / </span><span style="color:#888888">//驱动赋值</span>
spidev-> spi = spi;
spin_lock_init(spidev-> spi_lock);
调用mutex_init(spidev-> buf_lock);
INIT_LIST_HEAD(spidev-> device_entry); <span style="color:#888888">//初始化链表</span>
<span style="color:#888888">/ *如果我们可以分配一个次要号码,请挂钩此设备。
*只要udev或mdev工作,重复使用未成年人就可以了。
* /</span>
的mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors,N_SPI_MINORS);
<strong>if</strong>(minor <N_SPI_MINORS){
struct device * dev;
spidev-> devt = MKDEV(SPIDEV_MAJOR,minor);
dev = device_create(spidev_class,&spi-> dev,spidev-> devt,
spidev,<span style="color:#880000">“spidev%d。%d”</span>,
spi-> master-> bus_num,spi-> chip_select); <span style="color:#888888">//创建设备节点</span>
status = IS_ERR(dev)?PTR_ERR(dev):<span style="color:#880000">0</span> ;
} <strong>else</strong> {
dev_dbg(&spi-> dev,<span style="color:#880000">“没有次要号码!\ n”</span>);
status = -ENODEV;
}
<strong>if</strong>(status == <span style="color:#880000">0</span>){
set_bit(未成年人,未成年人);
list_add(&spidev-> device_entry,&device_list);
}
mutex_unlock(&device_list_lock);
<strong>if</strong>(status == <span style="color:#880000">0</span>)
spi_set_drvdata(spi,spidev); <span style="color:#888888">//保存驱动数据到SPI的私有数据。用于后面的读写操作</span>
<strong>其他</strong>
kfree(spidev);
<strong>返回</strong>状态;
}</span>
到这里,,驱动就成功匹配到设备了
驱动的数据写流程:
当驱动打开后,应用层调用写函数
,在驱动中的过程如下
<span style="color:#444444"><strong>static</strong> ssize_t
<span style="color:#880000"><strong>spidev_write </strong></span>(<strong>struct</strong> file * filp,<strong>const </strong> <strong>char</strong> __user * buf,
size_t count,loff_t * f_pos)
{
<strong> struct</strong> spidev_data * spidev;
<strong>ssize_t</strong> status =<span style="color:#880000"> 0</span> ;
<strong>未签名的</strong> <strong>长期</strong> 失踪;
<span style="color:#888888">/ * chipselect仅在操作开始或结束时切换* / </span>
<strong>if</strong>(count> bufsiz)
<strong>返回</strong> -EMSGSIZE;
spidev = filp-> private_data; <span style="color:#888888">//从SPI私有数据获取驱动信息</span>
的mutex_lock(&spidev-> buf_lock);
missing = copy_from_user(spidev-> buffer,buf,count); <span style="color:#888888">//从用户层获取数据</span>
<strong>if</strong>(missing == <span style="color:#880000">0</span>){
status = spidev_sync_write(spidev,count); <span style="color:#888888">//成功,则调用该函数</span>
} <strong>else</strong>
status = -EFAULT;
mutex_unlock(&spidev-> buf_lock);
<strong>返回</strong>状态;
}</span>
<span style="color:#444444"><strong>static</strong> inline ssize_t
spidev_sync_write(struct spidev_data * spidev,size_t len)
{ <span style="color:#888888">// spi发送数据所需要的两个结构体,要发送的数据保存在</span></span>
// struct spi_transfer中 struct spi_transfer t = { .tx_buf = spidev-> buffer, .len = len, }; struct spi_message m; spi_message_init(米); spi_message_add_tail(&t,&m); // spi是一个消息队列来发送的 返回 spidev_sync(spidev,&m); //再调用此函数 }
static ssize_t
spidev_sync(struct spidev_data * spidev,struct spi_message * message)
{
DECLARE_COMPLETION_ONSTACK(完成);
int状态;
message-> complete = spidev_complete; //同步
message-> context =&done;
spin_lock_irq(&spidev-> spi_lock);
if(spidev-> spi == NULL)
status = -ESHUTDOWN;
else
status = spi_async(spidev-> spi,message); //调用SPI核心层的发送函数
spin_unlock_irq(spidev-> spi_lock);
if(status == 0){
wait_for_completion(完成);
status = message-> status;
if(status == 0)
status = message-> actual_length;
}
返回状态;
}
int spi_async(struct spi_device * spi,struct spi_message * message)
{
struct spi_master * master = spi-> master; //取出该设备的控制器
/ *半双工链接包括原始MicroWire和带有的
*只有一个数据引脚,如SPI_3WIRE(开关方向)或其中
*缺少MOSI或MISO。他们也可能是由
*软件限制。
* /
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)
返回 -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 = s3c64xx_spi_transfer;
static int s3c64xx_spi_transfer(struct spi_device * spi,
struct spi_message * msg)
{
struct s3c64xx_spi_driver_data * sdd;
无符号长旗;
sdd = spi_master_get_devdata(spi-> master);
spin_lock_irqsave(&sdd-> lock,flags);
printk(KERN_DEBUG “s3c64xx_spi_transfer \ n”);
if(sdd-> state&SUSPND){
spin_unlock_irqrestore(&sdd-> lock,flags);
返回 -ESHUTDOWN;
}
msg-> status = -EINPROGRESS;
msg-> actual_length = 0 ;
list_add_tail(&msg-> queue,&sdd-> queue); //将消息调价到消息链表尾部
queue_work(sdd-> workqueue,&sdd-> work); //添加到工作队列
spin_unlock_irqrestore(&sdd-> lock,flags);
返回 0 ;
}
接下来就是工作队列来发送数据
static void s3c64xx_spi_work(struct work_struct * work)
{
struct s3c64xx_spi_driver_data * sdd = container_of(work,
struct s3c64xx_spi_driver_data,work);
无符号长旗;
printk(KERN_DEBUG “s3c64xx_spi_work \ n”);
/ *获取DMA通道* /
while(!acquire_dma(sdd))
msleep(10);
spin_lock_irqsave(&sdd-> lock,flags);
while(!list_empty(&sdd-> queue)
&&!(sdd-> state&SUSPND)){
struct spi_message * msg;
msg = container_of(sdd-> queue.next,struct spi_message,queue);
list_del_init(MSG->队列);
/ *设置Xfer忙标志* /
sdd-> state | = SPIBUSY;
spin_unlock_irqrestore(&sdd-> lock,flags);
handle_msg(sdd,msg); //发送数据
spin_lock_irqsave(&sdd-> lock,flags);
sdd-> state&= ~SPIBUSY;
}
spin_unlock_irqrestore(&sdd-> lock,flags);
/ *免费DMA频道* /
s3c2410_dma_free(sdd-> tx_dmach,&s3c64xx_spi_dma_client);
s3c2410_dma_free(sdd-> rx_dmach,&s3c64xx_spi_dma_client);
}
static int s3c64xx_spi_transfer(struct spi_device * spi,
struct spi_message * msg)
{
struct s3c64xx_spi_driver_data * sdd;
无符号长旗;
sdd = spi_master_get_devdata(spi-> master);
spin_lock_irqsave(&sdd-> lock,flags);
printk(KERN_DEBUG “s3c64xx_spi_transfer \ n”);
if(sdd-> state&SUSPND){
spin_unlock_irqrestore(&sdd-> lock,flags);
返回 -ESHUTDOWN;
}
msg-> status = -EINPROGRESS;
msg-> actual_length = 0 ;
list_add_tail(&msg-> queue,&sdd-> queue);
queue_work(sdd-> workqueue,&sdd-> work);
spin_unlock_irqrestore(&sdd-> lock,flags);
返回 0 ;
}
static void handle_msg(struct s3c64xx_spi_driver_data * sdd,
struct spi_message * msg)
{
struct s3c64xx_spi_info * sci = sdd-> cntrlr_info;
struct spi_device * spi = msg-> spi;
struct s3c64xx_spi_csinfo * cs = spi-> controller_data;
struct spi_transfer * xfer;
int status = 0,cs_toggle = 0 ;
u32速度;
u8 bpw;
int i;
printk(KERN_DEBUG “handle_msg \ n”);
/ *如果Master(控制器)状态与Slave * /
if 所需的状态不同(sdd-> cur_speed!= spi-> max_speed_hz
|| sdd-> cur_mode!= spi->模式
|| sdd-> cur_bpw!= spi-> bits_per_word){
sdd-> cur_bpw = spi-> bits_per_word;
sdd-> cur_speed = spi-> max_speed_hz;
sdd-> cur_mode = spi-> mode;
s3c64xx_spi_config(SDD); //主的传输方式跟从设备不同时,重新配置
}
/ *如果需要,映射所有传输* /
if(s3c64xx_spi_map_mssg(sdd,msg)){
dev_err(SPI->开发,
“Xfer:无法映射消息缓冲区!\ n”);
status = -ENOMEM;
转出;
}
/ *配置反馈延迟* /
writel(cs-> fb_delay&0x3,sdd-> regs + S3C64XX_SPI_FB_CLK);
list_for_each_entry(xfer,&msg-> transfers,transfer_list){
无符号长旗;
int use_dma;
INIT_COMPLETION(sdd-> xfer_completion);
/ *只有BPW和速度可能会在转移中发生变化* /
bpw = xfer-> bits_per_word?:spi-> bits_per_word;
speed = xfer-> speed_hz?:spi-> max_speed_hz;
if(bpw!= sdd-> cur_bpw || speed!= sdd-> cur_speed){
sdd-> cur_bpw = bpw;
sdd-> cur_speed =速度;
s3c64xx_spi_config(SDD);
}
/ * xfers的轮询方法不大于FIFO容量* /
if(xfer-> len <=((sci-> fifo_lvl_mask >> 1)+ 1))//这里可以看芯片手册,主要是判断用dma方式传输还是fifo,下面有截图
use_dma = 0 ;
否则
use_dma = 1 ;
spin_lock_irqsave(&sdd-> lock,flags);
/ *仅待处理* /
sdd-> state&= ~RXBUSY;
sdd-> state&= ~TXBUSY;
char * tx_tmp =(char *)xfer-> tx_buf;
for(i = 0 ; i <xfer-> len; i ++)
printk(KERN_DEBUG “xfer-> len =%d,xfer-> tx_buf [%d] =%x \ n”,xfer-> len,i,tx_tmp [i]);
enable_datapath(sdd,spi,xfer,use_dma);
/ *奴隶选择* /
enable_cs(sdd,spi);
/ *启动信号* /
S3C64XX_SPI_ACT(SDD);
spin_unlock_irqrestore(&sdd-> lock,flags);
status = wait_for_xfer(sdd,xfer,use_dma); //等待完成
/ *静音信号* /
S3C64XX_SPI_DEACT(SDD);
if(status){
dev_err(&spi-> dev,“I / O错误:”
“rx-%d tx-%d res:rx-%c tx-%c len-%d \ n”,
xfer-> rx_buf?1:0,xfer-> tx_buf?1:0,
(sdd-> state&RXBUSY)?'f':'p',
(sdd-> state&TXBUSY)?'f':'p',
xfer-> LEN);
if(use_dma){
if(xfer-> tx_buf!= NULL
&&(sdd-> state&TXBUSY))
s3c2410_dma_ctrl(sdd-> tx_dmach,
S3C2410_DMAOP_FLUSH);
if(xfer-> rx_buf!= NULL
&&(sdd-> state&RXBUSY))
s3c2410_dma_ctrl(sdd-> rx_dmach,
S3C2410_DMAOP_FLUSH);
}
转出;
}
if(xfer-> delay_usecs)
udelay的(xfer-> delay_usecs);
if(xfer-> cs_change){
/ *暗示下一个mssg会是
对于相同的设备* /
if(list_is_last(&xfer-> transfer_list,
&MSG->转移))
cs_toggle = 1 ;
其他
disable_cs(sdd,spi);
}
msg-> actual_length + = xfer-> len;
FLUSH_FIFO(SDD);
}
出:
if(!cs_toggle || status)
disable_cs(sdd,spi);
其他
sdd-> tgl_spi = spi;
s3c64xx_spi_unmap_mssg(sdd,msg);
msg-> status = status;
if(msg->完成)
MSG->完成(MSG->上下文);
}
上面选择DMA还是FIFO的截图:
static void enable_datapath(struct s3c64xx_spi_driver_data * sdd,
struct spi_device * spi,
struct spi_transfer * xfer,int dma_mode)
{
struct s3c64xx_spi_info * sci = sdd-> cntrlr_info;
void __iomem * regs = sdd-> regs;
u32 modecfg,chcfg;
printk(KERN_DEBUG “enable_datapath \ n”);
//配置寄存器
modecfg = readl(regs + S3C64XX_SPI_MODE_CFG);
modecfg&=〜(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
chcfg = readl(regs + S3C64XX_SPI_CH_CFG);
chcfg&= ~S3C64XX_SPI_CH_TXCH_ON;
if(dma_mode){
chcfg&= ~S3C64XX_SPI_CH_RXCH_ON;
} else {
/ *总是在FIFO中移入数据,即使xfer只是Tx,
*这有助于设置PCKT_CNT值以生成时钟
*完全需要。
* /
chcfg | = S3C64XX_SPI_CH_RXCH_ON;
writel(((xfer-> len * 8 / sdd-> cur_bpw)&0xffff)
| S3C64XX_SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT);
}
if(xfer-> tx_buf!= NULL){
sdd-> state | = TXBUSY;
chcfg | = S3C64XX_SPI_CH_TXCH_ON;
if(dma_mode){
printk(KERN_DEBUG “DMA_MODE \ n”);
modecfg | = S3C64XX_SPI_MODE_TXDMA_ON;
s3c2410_dma_config(sdd-> tx_dmach,1);
s3c2410_dma_enqueue(sdd-> tx_dmach,(void *)sdd,
xfer-> tx_dma,xfer-> len);
s3c2410_dma_ctrl(sdd-> tx_dmach,S3C2410_DMAOP_START);
} else {
unsigned char * buf =(unsigned char *)xfer-> tx_buf;
int i = 0 ;
而(i <xfer-> len)
writeb(buf [i ++],regs + S3C64XX_SPI_TX_DATA); //将数据写到发送寄存器
}
}
if(xfer-> rx_buf!= NULL){
sdd-> state | = RXBUSY;
if(sci-> high_speed && sdd-> cur_speed> = 30000000 UL
&&!(sdd-> cur_mode&SPI_CPHA))
chcfg | = S3C64XX_SPI_CH_HS_EN;
if(dma_mode){
modecfg | = S3C64XX_SPI_MODE_RXDMA_ON;
chcfg | = S3C64XX_SPI_CH_RXCH_ON;
writel(((xfer-> len * 8 / sdd-> cur_bpw)&0xffff)
| S3C64XX_SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT);
s3c2410_dma_config(sdd-> rx_dmach,1);
s3c2410_dma_enqueue(sdd-> rx_dmach,(void *)sdd,
xfer-> rx_dma,xfer-> len);
s3c2410_dma_ctrl(sdd-> rx_dmach,S3C2410_DMAOP_START);
}
}
writel(modecfg,regs + S3C64XX_SPI_MODE_CFG);
writel(chcfg,regs + S3C64XX_SPI_CH_CFG);
}
static int wait_for_xfer(struct s3c64xx_spi_driver_data * sdd,
struct spi_transfer * xfer,int dma_mode)
{
struct s3c64xx_spi_info * sci = sdd-> cntrlr_info;
void __iomem * regs = sdd-> regs;
无符号长val;
int ms;
printk(KERN_DEBUG “wait_for_xfer \ n”);
/ * millisecs到xfer'len'字节@'cur_speed'* /
ms = xfer-> len * 8 * 1000 / sdd-> cur_speed;
ms + = 5 ; / *一些公差* /
if(dma_mode){
val = msecs_to_jiffies(ms)+ 10 ;
val = wait_for_completion_timeout(&sdd-> xfer_completion,val);
} else {
u32状态;
val = msecs_to_loops(ms);
做 {
status = readl(regs + S3C64XX_SPI_STATUS);读状态寄存器
printk(KERN_DEBUG “status =%d \ n”,状态);
} while(RX_FIFO_LVL(status,sci)<xfer-> len && --val);
}
printk(KERN_DEBUG “VAL =%d \ n”,val);
if(!val)
return -EIO;
if(dma_mode){
u32状态;
/ *
*简单地在FIFO中写入数据后,DmaTx返回,
*没有等待总线上的实际传输完成。
* DmaRx仅在Dma从FIFO读取数据后才返回
*需要总线传输完成,所以我们不担心
* Xfer涉及Rx(有或没有Tx)。
* /
if(xfer-> rx_buf == NULL){
val = msecs_to_loops(10);
status = readl(regs + S3C64XX_SPI_STATUS);
while((TX_FIFO_LVL(status,sci)
|| !S3C64XX_SPI_ST_TX_DONE(status,sci))
&& --val){
cpu_relax();
status = readl(regs + S3C64XX_SPI_STATUS);
}
if(!val)
return -EIO;
}
} else {
unsigned char * buf;
int i;
/ *如果它只是Tx * /
if(xfer-> rx_buf == NULL){
sdd-> state&= ~TXBUSY;
返回 0 ;
}
i = 0 ;
buf = xfer-> rx_buf;
而(i <xfer-> len)
buf [i ++] = readb(regs + S3C64XX_SPI_RX_DATA);
sdd-> state&= ~RXBUSY;
}
返回 0 ;
}