三: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 ; }