精华内容
下载资源
问答
  • linux spi 驱动框架

    2021-02-24 08:28:00
    spi.c postcore_initcall(spi_init); static int __init spi_init(void) bus_register(&spi_bus_type); class_register(&spi_master_class); spidev.c module_init(spidev_init); static int __...

    spi.c

    postcore_initcall(spi_init);
    static int __init spi_init(void)
        bus_register(&spi_bus_type);
        class_register(&spi_master_class);
        
    

    spidev.c

    module_init(spidev_init);
    
    static int __init spidev_init(void)
        register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
        class_create(THIS_MODULE, "spidev"); 
        spi_register_driver(&spidev_spi_driver);

    static int spidev_probe(struct spi_device *spi) 

     

    展开全文
  • Linux SPI驱动框架

    2013-05-20 21:49:25
    http://yannik520.github.io/spi_driver_framework.html
    展开全文
  • Linux spi驱动框架分析(一) Linux spi驱动框架分析(二) Linux spi驱动框架分析(三) spi_master的消息队列机制 SPI数据传输可以有两种方式:同步方式和异步方式。所谓同步方式是指数据传输的发起者必须等待本次...

    系列文章
    Linux spi驱动框架分析(一)
    Linux spi驱动框架分析(二)
    Linux spi驱动框架分析(三)
    Linux spi驱动框架分析(四)

    spi_master的消息队列机制

    SPI数据传输可以有两种方式:同步方式和异步方式。所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能做其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返回。而异步方式则正好相反,数据传输的发起者无需等待传输的结束,数据传输期间还可以做其它事情,用代码来解释就是,调用传输的函数后,函数会立刻返回而不用等待数据传输完成,我们只需设置一个回调函数,传输完成后,该回调函数会被调用以通知发起者数据传送已经完成。同步方式简单易用,很适合处理那些少量数据的单次传输。但是对于数据量大、次数多的传输来说,异步方式就显得更加合适。

    对于SPI控制器来说,要支持异步方式必须要考虑以下两种状况:

    1. 对于同一个数据传输的发起者,既然异步方式无需等待数据传输完成即可返回,返回后,该发起者可以立刻又发起一个message,而这时上一个message还没有处理完。

    2. 对于另外一个不同的发起者来说,也有可能同时发起一次message传输请求。

    队列化正是为了为了解决以上的问题,所谓队列化,是指把等待传输的message放入一个队列中,发起一个传输操作,其实就是把对应的message按先后顺序放入一个队列中。内核会创建一个内核工作线程,通过线程来处理队列上的message。

    一个或者多个设备驱动程序可以同时向控制器驱动发起多个spi_message请求,这些spi_message也是以链表的形式被链接在spi_master结构体的queue成员里。

    spi_master,spi_message,spi_transfer这几个数据结构的关系可以用下图来描述:
    在这里插入图片描述
    如果spi控制器驱动要想支持消息队列机制的话,注册spi_master时,其transfer成员不能设置,具体细节如下代码所示:

    int spi_register_master(struct spi_master *master)
    {
    	static atomic_t		dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
    	struct device		*dev = master->dev.parent;
    	struct boardinfo	*bi;
    	int			status = -ENODEV;
    	int			dynamic = 0;
    
    	......
    
    	
    	if (master->transfer)
    		dev_info(dev, "master is unqueued, this is deprecated\n");
    	else {
    		//消息队列机制初始化,创建内核工作队列等
    		status = spi_master_initialize_queue(master);
    		if (status) {
    			device_del(&master->dev);
    			goto done;
    		}
    	}
    
    	......
    
    }
    

    spi_master_initialize_queue函数执行流程图如下所示:
    在这里插入图片描述

    进入spi_master_initialize_queue函数:

    static int spi_master_initialize_queue(struct spi_master *master)
    {
    	int ret;
    
    	//设置spi_master->transfer为spi_queued_transfer
    	master->transfer = spi_queued_transfer;
    
    	//若驱动未提供transfer_one_message则设置为spi_transfer_one_message
    	if (!master->transfer_one_message)
    		master->transfer_one_message = spi_transfer_one_message;
    
    	/* 创建工作线程等*/
    	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;
    }
    

    在分析spi_init_queue函数之前,先介绍下什么是工作线程。要完成工作的话,得有工人和工作。kthread_worker代表工人,而kthread_work代表工作,定义于include/linux/kthread.h。

    struct kthread_worker {
    	unsigned int		flags;
    
    	spinlock_t		lock;
    	
    	//kthread_work会链接在这链表上,相当于流水线
    	struct list_head	work_list;
    
    	struct list_head	delayed_work_list;
    
    	//为该kthread_worker执行任务的线程对于的task_struct	
    	struct task_struct	*task;
    
    	//当前正在处理的kthread_work 
    	struct kthread_work	*current_work;
    };
    
    struct kthread_work {
    	struct list_head	node;
    
    	//执行函数,该kthread_work所要做的事情
    	kthread_work_func_t	func;
    
    	//指向处理该kthread_work的kthread_worker 
    	struct kthread_worker	*worker;
    
    	/* Number of canceling calls that are running at the moment. */
    	int			canceling;
    };
    

    工作线程即是创建一个内线线程,内核线程执行kthread_worker_fn函数,在该函数会从kthread_worker的work_list链表里,取出每个kthread_work然后执行kthread_work里的func函数。
    spi_init_queue函数主要做创建线程,初始化kthread_worker等工作。

    static int spi_init_queue(struct spi_master *master)
    {
    	struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
    
    	master->running = false;
    	master->busy = false;
    
    	//初始化spi_master里的kthread_worker
    	kthread_init_worker(&master->kworker);
    
    	//创建一个内核线程,执行函数为kthread_worker_fn
    	master->kworker_task = kthread_run(kthread_worker_fn,
    					   &master->kworker, "%s",
    					   dev_name(&master->dev));
    	
    	......
    
    	//初始化spi_master里的kthread_work,把func设置为spi_pump_messages
    	kthread_init_work(&master->pump_messages, spi_pump_messages);
    
    	......
    		
    	
    }
    

    初始化完后,调用spi_start_queue函数开始工作:

    static int spi_start_queue(struct spi_master *master)
    {
    	unsigned long flags;
    
    	......
    
    	//把spi_master->kthread_work放入spi_master->kthread_worker的work_list链表,并唤醒工作线程
    	kthread_queue_work(&master->kworker, &master->pump_messages);
    
    	return 0;
    }
    

    工人和工作都有了,准备就绪,看看kthread_worker_fn函数:

    int kthread_worker_fn(void *worker_ptr)
    {
    	struct kthread_worker *worker = worker_ptr;
    	struct kthread_work *work;
    
    	......
    
    repeat:
    
    	......
    
    	work = NULL;
    	spin_lock_irq(&worker->lock);
    
    	//判断work_list链表是否空
    	if (!list_empty(&worker->work_list)) {
    
    		//不空,则取出第一个kthread_work 
    		work = list_first_entry(&worker->work_list,
    					struct kthread_work, node);
    		list_del_init(&work->node);
    	}
    	worker->current_work = work;
    	spin_unlock_irq(&worker->lock);
    
    	//如果存在kthread_work 
    	if (work) {
    		__set_current_state(TASK_RUNNING);
    		work->func(work);    //执行工作,调用spi_pump_messages函数
    	} else if (!freezing(current))
    		schedule();    //无工作则睡眠
    
    	try_to_freeze();
    	goto repeat;
    
    }
    

    存在kthread_work,调用里面的func函数,即spi_pump_messages:

    static void spi_pump_messages(struct kthread_work *work)
    {
    	struct spi_master *master =
    		container_of(work, struct spi_master, pump_messages);
    
    	__spi_pump_messages(master, true);
    }
    
    
    static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
    {
    	unsigned long flags;
    	bool was_busy = false;
    	int ret;
    
    	......
    
    	/* 如果spi_master->queue里没有messages */
    	if (list_empty(&master->queue) || !master->running) {
    		
    		......
    
    		//调用spi_master->unprepare_transfer_hardware释放相关硬件资源
    		if (master->unprepare_transfer_hardware &&
    		    master->unprepare_transfer_hardware(master))
    			dev_err(&master->dev,
    				"failed to unprepare transfer hardware\n");
    		
    		......
    
    		return;
    	}
    	
    	//spi_master->queue里有messages,取出第一个messages
    	master->cur_msg =
    		list_first_entry(&master->queue, struct spi_message, queue);
    
    	......
    
    	//调用spi_master->prepare_transfer_hardware准备必要的硬件资源
    	if (!was_busy && master->prepare_transfer_hardware) {
    		ret = master->prepare_transfer_hardware(master);
    	
    		......
    
    
    	}
    
    	trace_spi_message_start(master->cur_msg);
    
    	//调用spi_master->prepare_message对spi_message进行预处理
    	if (master->prepare_message) {
    		ret = master->prepare_message(master, master->cur_msg);
    		
    		......
    
    		master->cur_msg_prepared = true;
    	}
    
    	......
    
    	//最后调用spi_master->transfer_one_message传输一个spi_message
    	ret = master->transfer_one_message(master, master->cur_msg);
    	
    	......
    
    }
    

    前面分析spi_master_initialize_queue函数时,如果驱动未提供transfer_one_message则设置为spi_transfer_one_message,那么就来分析这个函数:

    static int spi_transfer_one_message(struct spi_master *master,
    				    struct spi_message *msg)
    {
    	struct spi_transfer *xfer;
    	bool keep_cs = false;
    	int ret = 0;
    	unsigned long long ms = 1;
    	struct spi_statistics *statm = &master->statistics;
    	struct spi_statistics *stats = &msg->spi->statistics;
    
    	//片选
    	spi_set_cs(msg->spi, true);
    
    	......
    
    	//取出每个spi_transfer,调用spi_master ->transfer_one函数发送
    	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
    		trace_spi_transfer_start(msg, xfer);
    
    		......
    
    
    		if (xfer->tx_buf || xfer->rx_buf) {
    			reinit_completion(&master->xfer_completion);
    			
    			ret = master->transfer_one(master, msg->spi, xfer);
    			
    			......
    
    		} 
    		
    		......
    
    out:
    	if (ret != 0 || !keep_cs)
    		spi_set_cs(msg->spi, false);
    
    	......
    
    	//当前的message传输完成,做相关处理
    	spi_finalize_current_message(master);
    
    	return ret;
    }
    

    spi_finalize_current_message:

    void spi_finalize_current_message(struct spi_master *master)
    {
    	struct spi_message *mesg;
    	unsigned long flags;
    	int ret;
    
    	......
    	//调用master->unprepare_message释放一些资源
    	if (master->cur_msg_prepared && master->unprepare_message) {
    		ret = master->unprepare_message(master, mesg);
    		if (ret) {
    			dev_err(&master->dev,
    				"failed to unprepare message: %d\n", ret);
    		}
    	}
    
    	......
    	
    	//重新放入kworker
    	kthread_queue_work(&master->kworker, &master->pump_messages);
    
    	......
    
    	//处理完message,调用spi_master->complete
    	if (mesg->complete)
    		mesg->complete(mesg->context);
    }
    

    对于设备驱动程序来讲,之后调用spi_sync或spi_async函数即可发起一个message请求,队列化和工作线程被激活,触发一系列的操作,最终完成message的传输操作。

    int spi_async(struct spi_device *spi, struct spi_message *message)
    {
    	struct spi_master *master = spi->master;
    	int ret;
    	unsigned long flags;
    
    	//检查spi_message 
    	ret = __spi_validate(spi, message);
    	if (ret != 0)
    		return ret;
    
    	spin_lock_irqsave(&master->bus_lock_spinlock, flags);
    
    	if (master->bus_lock_flag)
    		ret = -EBUSY;
    	else
    		ret = __spi_async(spi, message); //发起异步传输
    
    	spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
    
    	return ret;
    }
    
    
    
    static int __spi_async(struct spi_device *spi, struct spi_message *message)
    {
    	struct spi_master *master = spi->master;
    
    	message->spi = spi;
    	
    	......
    
    	//调用spi_master->transfer,前面代码分析,采用队列化机制的话,被设置为spi_queued_transfer
    	return master->transfer(spi, message);
    }
    

    spi_queued_transfer:

    static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
    {
    	return __spi_queued_transfer(spi, msg, true);
    }
    
    
    static int __spi_queued_transfer(struct spi_device *spi,
    				 struct spi_message *msg,
    				 bool need_pump)
    {
    	struct spi_master *master = spi->master;
    	unsigned long flags;
    
    	spin_lock_irqsave(&master->queue_lock, flags);
    	
    	......
    
    	
    	//把spi_message放入spi_message->queue链表
    	list_add_tail(&msg->queue, &master->queue);
    	if (!master->busy && need_pump)
    
    		/* 重新把spi_message->pump_messages这个kthread_work放入spi_message->kworker
         * 并唤醒工作线程
         */
    		kthread_queue_work(&master->kworker, &master->pump_messages);
    
    	spin_unlock_irqrestore(&master->queue_lock, flags);
    	return 0;
    }
    

    最后总结一下spi_async函数执行流程图,如下:
    在这里插入图片描述

    通用spi设备驱动

    内核提供了一个通用的SPI外设驱动,驱动文件为driver/spi/spidev.c。
    入口函数:

    static int __init spidev_init(void)
    {
    	int status;
    
    	......
    
    	//注册字符设备,主设备号为153,file_operations为spidev_fops
    	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
    	if (status < 0)
    		return status;
    
    	//创建class
    	spidev_class = class_create(THIS_MODULE, "spidev");
    	if (IS_ERR(spidev_class)) {
    		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
    		return PTR_ERR(spidev_class);
    	}
    
    	//注册spi驱动
    	status = spi_register_driver(&spidev_spi_driver);
    	if (status < 0) {
    		class_destroy(spidev_class);
    		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
    	}
    	return status;
    }
    
    static struct spi_driver spidev_spi_driver = {
    	.driver = {
    		.name =		"spidev",
    		.of_match_table = of_match_ptr(spidev_dt_ids),
    		.acpi_match_table = ACPI_PTR(spidev_acpi_ids),
    	},
    	.probe =	spidev_probe,
    	.remove =	spidev_remove,
    
    };
    
    
    

    驱动与设备匹配,调用spidev_probe:

    struct spidev_data {
    	dev_t			devt;
    	spinlock_t		spi_lock;
    	struct spi_device	*spi;
    
    	//用于插入全局链表device_list
    	struct list_head	device_entry;
    
    	struct mutex		buf_lock;
    	unsigned		users;
    
    	//保存用户空间传入的数据
    	u8			*tx_buffer;
    
    	//用于接收设备的数据
    	u8			*rx_buffer;
    	u32			speed_hz;
    };
    
    
    static int spidev_probe(struct spi_device *spi)
    {
    	struct spidev_data	*spidev;
    	int			status;
    	unsigned long		minor;
    
    	......
    
    	/* 分配一个struct spidev_data,用于保存设备相关信息 */
    	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
    	if (!spidev)
    		return -ENOMEM;
    
    	/* 初始化spidev_data */
    	spidev->spi = spi;
    	spin_lock_init(&spidev->spi_lock);
    	mutex_init(&spidev->buf_lock);
    
    	INIT_LIST_HEAD(&spidev->device_entry);
    
    
    	mutex_lock(&device_list_lock);
    	minor = find_first_zero_bit(minors, N_SPI_MINORS);
    	if (minor < N_SPI_MINORS) {
    		struct device *dev;
    
    		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
    		//创建设备文件
    		dev = device_create(spidev_class, &spi->dev, spidev->devt,
    				    spidev, "spidev%d.%d",
    				    spi->master->bus_num, spi->chip_select);
    		status = PTR_ERR_OR_ZERO(dev);
    	} else {
    		dev_dbg(&spi->dev, "no minor number available!\n");
    		status = -ENODEV;
    	}
    	if (status == 0) {
    		set_bit(minor, minors);
    
    		//把spidev_data插入全局链表device_list
    		list_add(&spidev->device_entry, &device_list);
    	}
    	mutex_unlock(&device_list_lock);
    
    	spidev->speed_hz = spi->max_speed_hz;
    
    	if (status == 0)
    		spi_set_drvdata(spi, spidev);
    	else
    		kfree(spidev);
    
    	return status;
    }
    

    这个驱动程序为用户空间提供了统一的接口:

    static const struct file_operations spidev_fops = {
    	.owner =	THIS_MODULE,
    	.write =	spidev_write,
    	.read =		spidev_read,
    	.unlocked_ioctl = spidev_ioctl,
    	.compat_ioctl = spidev_compat_ioctl,
    	.open =		spidev_open,
    	.release =	spidev_release,
    	.llseek =	no_llseek,
    };
    

    当应用层open时,会调用到spidev_fops的open函数:

    static int spidev_open(struct inode *inode, struct file *filp)
    {
    	struct spidev_data	*spidev;
    	int			status = -ENXIO;
    
    	mutex_lock(&device_list_lock);
    
    	list_for_each_entry(spidev, &device_list, device_entry) {
    		if (spidev->devt == inode->i_rdev) {
    			status = 0;
    			break;
    		}
    	}
    
    	......
    
    	//分配发送buf,大小为4096
    	if (!spidev->tx_buffer) {
    		spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
    		if (!spidev->tx_buffer) {
    			dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
    			status = -ENOMEM;
    			goto err_find_dev;
    		}
    	}
    
    	//分配buf缓存
    	if (!spidev->rx_buffer) {
    		spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
    		if (!spidev->rx_buffer) {
    			dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
    			status = -ENOMEM;
    			goto err_alloc_rx_buf;
    		}
    	}
    
    	......
    
    }
    

    当应用层write时,会调用到spidev_fops的write函数进行发送数据:

    static ssize_t
    spidev_write(struct file *filp, const char __user *buf,
    		size_t count, loff_t *f_pos)
    {
    	struct spidev_data	*spidev;
    	ssize_t			status = 0;
    	unsigned long		missing;
    
    	/* chipselect only toggles at start or end of operation */
    	if (count > bufsiz)
    		return -EMSGSIZE;
    
    	spidev = filp->private_data;
    
    	mutex_lock(&spidev->buf_lock);
    
    	//拷贝数据到发送buf,
    	missing = copy_from_user(spidev->tx_buffer, buf, count);
    	if (missing == 0)
    		//同步发送
    		status = spidev_sync_write(spidev, count);
    	else
    		status = -EFAULT;
    	mutex_unlock(&spidev->buf_lock);
    
    	return status;
    }
    
    
    static inline ssize_t
    spidev_sync_write(struct spidev_data *spidev, size_t len)
    {
    	//构造一个struct spi_message
    	struct spi_transfer	t = {
    			.tx_buf		= spidev->tx_buffer,
    			.len		= len,
    			.speed_hz	= spidev->speed_hz,
    		};
    	struct spi_message	m;
    
    	spi_message_init(&m);
    	spi_message_add_tail(&t, &m);
    	
    	//发送spi_message
    	return spidev_sync(spidev, &m);
    }
    

    进行复杂的数据传输时,通过ioctl命令,如下使用例子:

    
    struct spi_ioc_transfer xfer;
    
    ......
    
    //初始化xfer
    
    //传输数据
    ioctl(fd, SPI_IOC_MESSAGE(2), xfer);
    
    ......
    

    struct spi_ioc_transfer结构体跟struct spi_transfer结构体类似,定义如下:

    struct spi_ioc_transfer {
    	__u64		tx_buf;
    	__u64		rx_buf;
    
    	__u32		len;
    	__u32		speed_hz;
    
    	__u16		delay_usecs;
    	__u8		bits_per_word;
    	__u8		cs_change;
    	__u8		tx_nbits;
    	__u8		rx_nbits;
    	__u16		pad;
    
    };
    

    当应用层ioctl时,会调用到spidev_fops的spidev_ioctl函数,对于通过ioctl命令进行数据传输:

    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;
    
    	......
    
    	switch (cmd) {
    	
    	......
    
    	default:
    		//拷贝用户空间传入的参数到内核空间
    		ioc = spidev_get_ioc_message(cmd,
    				(struct spi_ioc_transfer __user *)arg, &n_ioc);
    
    		......
    
    		//用spi_ioc_transfer构建spi_message,最终调用spi_sync
    		retval = spidev_message(spidev, ioc, n_ioc);
    		kfree(ioc);
    		break;
    	}
    
    	......
    
    }
    
    展开全文
  • Linux spi驱动框架分析(一) spi核心(dervers/spi/spi.c)中提供了一组不依赖于硬件平台的接口函数,理解其中的主要函数非常关键,因为spi_master驱动和spi设备驱动之间赖于i2c核心作为纽带。spi核心中提供的主要...

    系列文章
    Linux spi驱动框架分析(一)
    Linux spi驱动框架分析(二)
    Linux spi驱动框架分析(三)
    Linux spi驱动框架分析(四)

    spi core

    spi核心(dervers/spi/spi.c)中提供了一组不依赖于硬件平台的接口函数,理解其中的主要函数非常关键,因为spi_master驱动和spi设备驱动之间赖于i2c核心作为纽带。spi核心中提供的主要函数如下。

    (1) 为spi_master驱动提供的接口

    //分配spi_master结构体
    struct spi_master *spi_alloc_master(struct device *dev, unsigned size);
    
    //注册spi_master
    int spi_register_master(struct spi_master *master);
    
    //注销spi_master
    void spi_unregister_master(struct spi_master *master);
    

    (2) 为spi设备驱动提供的接口

    //注册spi_driver
    #define spi_register_driver(driver) \
    	__spi_register_driver(THIS_MODULE, driver)
    
    //注销spi_driver
    static inline void spi_unregister_driver(struct spi_driver *sdrv);
    

    (3) 提供了spi设备注册/销毁的接口

    //注册spi_device
    struct spi_device *spi_new_device(struct spi_master *master,
    				  struct spi_board_info *chip);
    //注销spi_device
    void spi_unregister_device(struct spi_device *spi);
    

    (4) 提供了一系列用于操作和维护spi_message和spi_transfer的接口

    //用于初始化spi_message结构
    static inline void spi_message_init(struct spi_message *m);
    
    //把一个spi_transfer加入到一个spi_message中
    static inline void
    spi_message_add_tail(struct spi_transfer *t, struct spi_message *m);
    
    //移除一个spi_transfer
    static inline void spi_transfer_del(struct spi_transfer *t);
    
    //初始化一个spi_message并添加数个spi_transfer
    static inline void
    spi_message_init_with_transfers(struct spi_message *m,
    struct spi_transfer *xfers, unsigned int num_xfers);
    
    //分配一个自带数个spi_transfer结构体的spi_message
    static inline struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags);
    
    //发起一个spi_message,异步版本
    int spi_async(struct spi_device *spi, struct spi_message *message);
    
    //发起一个spi_message,同步版本
    int spi_sync(struct spi_device *spi, struct spi_message *message);
    

    spi核心构建了spi总线,用于驱动与设备的匹配等。

    struct bus_type spi_bus_type = {
    	.name		= "spi",
    	.dev_groups	= spi_dev_groups,
    	.match		= spi_match_device,
    	.uevent		= spi_uevent,
    };
    

    当注册spi_driver或者增添spi_device时,会调用到spi总线的match函数,进行驱动与设备的匹配。

    驱动与设备的匹配

    注册驱动或设备时进行匹配,spi_register_driver:

    #define spi_register_driver(driver) \
    	__spi_register_driver(THIS_MODULE, driver)
    
    
    int __spi_register_driver(struct module *owner, struct spi_driver *sdrv)
    {
    	sdrv->driver.owner = owner;
    
    	//设置总线类型
    	sdrv->driver.bus = &spi_bus_type;
    	if (sdrv->probe)
    		sdrv->driver.probe = spi_drv_probe;
    	if (sdrv->remove)
    		sdrv->driver.remove = spi_drv_remove;
    	if (sdrv->shutdown)
    		sdrv->driver.shutdown = spi_drv_shutdown;
    
    	/* 注册spi_driver
      	   driver_register函数会遍历spi总线的设备链表,调用spi_match_device函数
      	   进行匹配匹配成功的话,会调用驱动的probe函数
    	 */ 
    	return driver_register(&sdrv->driver);
    }
    

    看看具体是怎么匹配的,spi_match_device:

    static int spi_match_device(struct device *dev, struct device_driver *drv)
    {
    	const struct spi_device	*spi = to_spi_device(dev);
    	const struct spi_driver	*sdrv = to_spi_driver(drv);
    
    	/* 设备树匹配*/
    	if (of_driver_match_device(dev, drv))
    		return 1;
    
    	/* Then try ACPI */
    	if (acpi_driver_match_device(dev, drv))
    		return 1;
    
    	//id表匹配
    	if (sdrv->id_table)
    		return !!spi_match_id(sdrv->id_table, spi);
    
    	//前面匹配都不成功,直接比较驱动的name与设备的name是否一致
    	return strcmp(spi->modalias, drv->name) == 0;
    }
    
    

    id表匹配,spi_match_id:

    static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
    						const struct spi_device *sdev)
    {
    	while (id->name[0]) {
    		if (!strcmp(sdev->modalias, id->name))
    			return id;
    		id++;
    	}
    	return NULL;
    }
    

    spi设备与驱动

    Linux spi设备实例化的几种方法

    1. 通过设备树声明spi设备
      在spi控制器的节点里声明子节点,每个子节点代表一个spi设备,如下图spidev节点代表一个spi设备所示:
      在这里插入图片描述
    2. 显式实例化spi设备
      通过spi_new_device()函数,创建并注册spi_device。

    在分析源码之前,先来了解一下spi硬件上的连接,如下图:
    在这里插入图片描述
    如图,一个soc可能含有一个或多个spi控制器,一个spi控制器可以与多个spi设备连接,通过片选引脚选择不同设备。片选引脚可以由控制器来控制,需要配置为专用引脚功能,也可以配置为gpio来控制,主流的还是配置为gpio来控制片选。

    spi_master结构体中的num_chipselect成员,用来描述该控制器支持的片选数目,而spi_device结构体中的chip_select成员,用来描述片选号(从0开始的)。

    spi_master结构体中的cs_gpios成员,指向一个int型数组,保存着所有片选的gpio号,设备的片选号即为这个数组的索引。设备通过片选索引获得自身的片选gpio号,spi_device结构体中的cs_gpio成员保存着这片选gpio号。

    不管是那种方式实例化,最终都是调用spi_new_device()函数进行注册,这个函数传入两个参数,第一个参数表示设备挂接在哪个spi控制器上,第二个参数用来描述这个spi设备的信息(名字、片选引脚等信息)。

    spi_new_device函数的第二个参数是struct spi_board_info *类型的结构体,用于来描述设备的片选索引、name、工作频率等信息,结构体定义如下:

    struct spi_board_info {
    
    	//name,用于匹配驱动
    	char		modalias[SPI_NAME_SIZE];
    
    	const void	*platform_data;
    	void		*controller_data;
    	int		irq;
    
    	//该设备的工作频率
    	u32		max_speed_hz;
    
    
    	//总线号,即该设备连接在哪个控制器上
    	u16		bus_num;
    
    	//片选号
    	u16		chip_select;
    
    	//该设备的工作模式
    	u16		mode;
    
    };
    

    在了解一些信息后,下面就来分析一下spi_new_device函数,该函数的定义如下:

    
    struct spi_device *spi_new_device(struct spi_master *master,
    				  struct spi_board_info *chip)
    {
    	struct spi_device	*proxy;
    	int			status;
    
    	//创建一个struct spi_device,并进行一些初始化
    	proxy = spi_alloc_device(master);
    	if (!proxy)
    		return NULL;
    
    	WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
    
    	//通过传入的struct spi_board_info,初始化新建的struct spi_device
    	proxy->chip_select = chip->chip_select;
    	proxy->max_speed_hz = chip->max_speed_hz;
    	proxy->mode = chip->mode;
    	proxy->irq = chip->irq;
    	strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
    	proxy->dev.platform_data = (void *) chip->platform_data;
    	proxy->controller_data = chip->controller_data;
    	proxy->controller_state = NULL;
    
    	//add设备
    	status = spi_add_device(proxy);
    	if (status < 0) {
    		spi_dev_put(proxy);
    		return NULL;
    	}
    
    	return proxy;
    }
    

    spi_add_device:

    int spi_add_device(struct spi_device *spi)
    {
    	static DEFINE_MUTEX(spi_add_lock);
    	struct spi_master *master = spi->master;
    	struct device *dev = master->dev.parent;
    	int status;
    
    	/* 判断片选索引是否有效 */
    	if (spi->chip_select >= master->num_chipselect) {
    		dev_err(dev, "cs%d >= max %d\n",
    			spi->chip_select,
    			master->num_chipselect);
    		return -EINVAL;
    	}
    
    	/* Set the bus ID string */
    	spi_dev_set_name(spi);
    
    
    	mutex_lock(&spi_add_lock);
    
    	//检查是否重复注册设备(判断spi_device ->master和spi_device ->chip_select是否一致)
    	status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
    	if (status) {
    		dev_err(dev, "chipselect %d already in use\n",
    				spi->chip_select);
    		goto done;
    	}
    
    	//通过片选索引,获得片选gpio号
    	if (master->cs_gpios)
    		spi->cs_gpio = master->cs_gpios[spi->chip_select];
    
    	/* 通过spi_device设置spi控制器,会调用spi_master->setup函数进行设置
       * 之后会调用spi_set_cs函数来选择设备
       */
    	status = spi_setup(spi);
    	if (status < 0) {
    		dev_err(dev, "can't setup %s, status %d\n",
    				dev_name(&spi->dev), status);
    		goto done;
    	}
    
    	/* device_add,函数执行过程中,会进行匹配,前面已讲过匹配相关规则 */
    	status = device_add(&spi->dev);
    	if (status < 0)
    		dev_err(dev, "can't add %s, status %d\n",
    				dev_name(&spi->dev), status);
    	else
    		dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
    
    done:
    	mutex_unlock(&spi_add_lock);
    	return status;
    }
    

    Linux spi设备驱动的模块加载与卸载

    spi设备驱动的模块加载函数通用的方法是通过spi核心的spi_register_driver()函数添加 spi_driver的工作,而在模块卸载函数中需要做相反的工作:通过spi核心的spi_unregister_driver()函数删除 spi_driver。

    展开全文
  • Linux spi驱动框架分析(一) Linux spi驱动框架分析(二) spi_master驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能: 申请必要的硬件资源,例如中断,DMA通道,DMA内存...
  • Linux SPI驱动框架剖析

    千次阅读 2017-09-13 13:45:38
    转载地址:http://blog.csdn.net/kunkliu/article/details/72983590Linux SPI驱动框架剖析 http://www.cnblogs.com/lubiao/p/4806146.html (说明:我的开发平台是TQ210,处理器是cotex-A8架构的s5pv210,Linux内核...
  •   Linux SPI驱动框架(1)和(2)中分别介绍了SPI框架中核心层,和控制器驱动层。其实实际开发过程中,不是IC原厂工程师比较少会接触控制器驱动层,设备驱动层才是接触比较多的。   本文以内核中spidev设备驱动...
  • 概述 linux SPI驱动框架主要分为核心层,控制器驱动层以及设备驱动层。具体结构可参考下图 图中,最下层是硬件空间,SPI总线控制器,总线控制器负责硬件上的数据交互, ...
  • Linux SPI驱动框架分析

    2021-09-18 15:26:42
    声明:本文为本人参考韦东山spi驱动课程,并结合内核代码分析所作 1、枚举过程 (1)设备端(dev) /drivers/spi/spi.c spi_register_board_info /*对于每一个spi_master,调用spi_match_master_to_boardinfo*/ ...
  • linux SPI驱动框架(二) -- 设备驱动

    千次阅读 2017-07-11 21:20:34
    之前文章http://blog.csdn.net/flaoter/article/details/50001133对linux SPI驱动框架的控制器驱动进行了介绍,本节内容对SPI从设备的设备驱动进行讲解。设备驱动关注的结构体主要有两个,struct spi_device描述spi...
  • spi驱动框架主要分为三个部分,spi控制器驱动,spi设备驱动,和spi核心。 内核把spi控制器抽象为struct spi_master结构体,设备驱动抽象为spi_driver,spi设备抽象spi_device结构体。 spi与i2c内核空间的类比: (1...
  • 前言 这篇文章是对linux驱动基础系列--linux spi驱动框架分析的补充,主要是添加了最新的linux内核里设备树相关内容。spi设备树相关信息 如之前的文章里所述,控制器的device和spi device都是通过platform_add_...
  • linux spi驱动框架分析

    2018-09-08 13:27:00
    1.spi驱动框架//核心层函数spi.cdrivers\spi\Spi.c struct bus_type spi_bus_type = { .name = "spi", .dev_attrs = spi_dev_attrs, .match = spi_match_device, .uevent = spi_uevent, .pm =...
  • Linux SPI驱动框架理解

    2020-02-22 17:49:22
    linuxspi、I2C、USB等子系统使用分离设计思想:主机驱动和外设驱动分离,主机只负责产生总线上的输出波形,外设通过标准接口让主机已适当的波形访问自己。 1. 主机驱动 2. 主机与外设的连接 3. 外设驱动 4. 板级...
  • Linux spi驱动框架之执行流程

    千次阅读 2016-04-24 22:45:12
    Linux spi驱动架构由三部分构成:SPI核心层、SPI控制器驱动层、和SPI设备驱动程序。
  • 前言 主要是想对Linuxspi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动、设备模型等也不进行详细说明原理。如果有任何错误地方,请指出,谢谢!spi介绍 SPI接口...
  • SPI控制器驱动层   上节中,讲了SPI核心层的东西,这一部分,以全志平台SPI控制器驱动为例,对SPI控制器驱动进行说明。 SPI控制器驱动,即SPI硬件控制器对应的驱动,核心部分需要实现硬件SPI数据收发部分功能。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,691
精华内容 1,876
关键字:

linuxspi驱动框架

linux 订阅