SPI驱动文件解析

三: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 Android创建的视频设备

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

猜你喜欢

转载自blog.csdn.net/qq_22079023/article/details/82426529