Linux spi驱动框架分析(三)

news/2024/7/6 0:37:54

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

spi_master驱动

spi_master驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能:

  1. 申请必要的硬件资源,例如中断,DMA通道,DMA内存缓冲区等等;
  2. 配置spi控制器的工作模式和参数,使之可以和相应的设备进行正确的数据交换工作;
  3. 向spi核心层提供接口,使得上层的设备驱动可以通过核心层访问控制器驱动;
  4. 配合核心层,完成数据消息队列的排队和处理,直到消息队列变空为止;

控制器的相关信息一般在设备树中描述,驱动程序从设备树中获取硬件资源,用以初始化spi_master,之后调用spi_register_master函数进行注册。
如下图描述了一个spi控制器的设备节点:
在这里插入图片描述
在设备树上描述了spi控制器的各种IO资源,如寄存器地址、中断号,片选引脚的gpio等。
下面就来分析spi_register_master函数,函数执行流程图如下所示:
在这里插入图片描述

源码分析

spi_register_master函数定义如下:

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 (!dev)
		return -ENODEV;

	//从设备树获取信息,用以初始化spi_master里的num_chipselect、cs_gpios成员
	status = of_spi_register_master(master);
	if (status)
		return status;
		

	if (master->num_chipselect == 0)
		return -EINVAL;

	//设置总线号
	if ((master->bus_num < 0) && master->dev.of_node)
		master->bus_num = of_alias_get_id(master->dev.of_node, "spi");
	/* convention:  dynamically assigned bus IDs count down from the max */
	if (master->bus_num < 0) {
		/* FIXME switch to an IDR based scheme, something like
		 * I2C now uses, so we can't run out of "dynamic" IDs
		 */
		master->bus_num = atomic_dec_return(&dyn_bus_id);
		dynamic = 1;
	}

	//初始化各种锁及链表等
	INIT_LIST_HEAD(&master->queue);
	spin_lock_init(&master->queue_lock);
	spin_lock_init(&master->bus_lock_spinlock);
	mutex_init(&master->bus_lock_mutex);
	mutex_init(&master->io_mutex);
	master->bus_lock_flag = 0;
	init_completion(&master->xfer_completion);

	......

	//如果设置了transfer成员,则不采用消息队列机制,不赞成这么做
	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;
		}
	}
	/* add statistics */
	spin_lock_init(&master->statistics.lock);

	mutex_lock(&board_lock);

	//所有的spi_master都会链接在全局链表spi_master_list里
	list_add_tail(&master->list, &spi_master_list);


	/* 对于以前不支持设备树的内核,平台相关的spi设备信息是保存在struct spi_board_info结构体里
   *  然后调用spi_register_board_info函数统一增添到全局链表board_list里,等待注册spi控制器
   *  后,再从board_list链表里取出这些信息,spi_new_device进内核
   */
	list_for_each_entry(bi, &board_list, list)
		spi_match_master_to_boardinfo(master, &bi->board_info);
	mutex_unlock(&board_lock);

	/* 注册在设备树里描述的spi设备 */
	of_register_spi_devices(master);

	......

}

看看在设备树上获取什么信息来初始化spi_master里的num_chipselect、cs_gpios成员,of_spi_register_master:

static int of_spi_register_master(struct spi_master *master)
{
	int nb, i, *cs;
	struct device_node *np = master->dev.of_node;

	if (!np)
		return 0;
	
	//通过cs-gpios属性,获取gpio的个数,即片选数
	nb = of_gpio_named_count(np, "cs-gpios");
	master->num_chipselect = max_t(int, nb, master->num_chipselect);
	/* Return error only for an incorrectly formed cs-gpios property */
	
	......

	//分配int型数组,长度为片选数,用于保存片选gpio号
	cs = devm_kzalloc(&master->dev,
			  sizeof(int) * master->num_chipselect,
			  GFP_KERNEL);

	master->cs_gpios = cs;

	if (!master->cs_gpios)
		return -ENOMEM;

	for (i = 0; i < master->num_chipselect; i++)
		cs[i] = -ENOENT;

	//获取片选gpio号并设置cs_gpios
	for (i = 0; i < nb; i++)
	{
		cs[i] = of_get_named_gpio(np, "cs-gpios", i);
	}
	return 0;
}

前面提到过,实例化spi设备方式之一是在spi控制器的节点下描述设备节点,这些设备节点会在注册spi驱动时,进行解析并注册进内核,调用of_register_spi_devices函数来处理这些设备节点:

static void of_register_spi_devices(struct spi_master *master)
{
	struct spi_device *spi;
	struct device_node *nc;

	if (!master->dev.of_node)
		return;

	//遍历子节点调用of_register_spi_device函数进行处理
	for_each_available_child_of_node(master->dev.of_node, nc) {
		if (of_node_test_and_set_flag(nc, OF_POPULATED))
			continue;
		spi = of_register_spi_device(master, nc);
		if (IS_ERR(spi)) {
			dev_warn(&master->dev, "Failed to create SPI device for %s\n",
				nc->full_name);
			of_node_clear_flag(nc, OF_POPULATED);
		}
	}
}

of_register_spi_device定义如下:

static struct spi_device *
of_register_spi_device(struct spi_master *master, struct device_node *nc)
{
	struct spi_device *spi;
	int rc;
	u32 value;

	/* 创建一个spi_device  */
	spi = spi_alloc_device(master);

	......

	/* 读取节点中"compatible"属性,并作为name */
	rc = of_modalias_node(nc, spi->modalias,
				sizeof(spi->modalias));
	
	......

	/* 读取节点中"reg属性,并作为片选索引 */
	rc = of_property_read_u32(nc, "reg", &value);

	......


	spi->chip_select = value;

	/* 可在设备节点描述"spi-cpha"等相关属性来说明设备的模式 */
	if (of_find_property(nc, "spi-cpha", NULL))
		spi->mode |= SPI_CPHA;
	if (of_find_property(nc, "spi-cpol", NULL))
		spi->mode |= SPI_CPOL;
	if (of_find_property(nc, "spi-cs-high", NULL))
		spi->mode |= SPI_CS_HIGH;
	if (of_find_property(nc, "spi-3wire", NULL))
		spi->mode |= SPI_3WIRE;
	if (of_find_property(nc, "spi-lsb-first", NULL))
		spi->mode |= SPI_LSB_FIRST;

	/* Device DUAL/QUAD mode */
	if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
		switch (value) {
		case 1:
			break;
		case 2:
			spi->mode |= SPI_TX_DUAL;
			break;
		case 4:
			spi->mode |= SPI_TX_QUAD;
			break;
		default:
			dev_warn(&master->dev,
				"spi-tx-bus-width %d not supported\n",
				value);
			break;
		}
	}

	if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
		switch (value) {
		case 1:
			break;
		case 2:
			spi->mode |= SPI_RX_DUAL;
			break;
		case 4:
			spi->mode |= SPI_RX_QUAD;
			break;
		default:
			dev_warn(&master->dev,
				"spi-rx-bus-width %d not supported\n",
				value);
			break;
		}
	}

	/* Device speed */
	rc = of_property_read_u32(nc, "spi-max-frequency", &value);
	if (rc) {
		dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
			nc->full_name, rc);
		goto err_out;
	}
	spi->max_speed_hz = value;

	/* Store a pointer to the node in the device structure */
	of_node_get(nc);
	spi->dev.of_node = nc;

	/* 注册新的设备 */
	rc = spi_add_device(spi);
	if (rc) {
		dev_err(&master->dev, "spi_device register error %s\n",
			nc->full_name);
		goto err_of_node_put;
	}

	return spi;

err_of_node_put:
	of_node_put(nc);
err_out:
	spi_dev_put(spi);
	return ERR_PTR(rc);
}

of_modalias_node有个细节要注意:

int of_modalias_node(struct device_node *node, char *modalias, int len)
{
	const char *compatible, *p;
	int cplen;

	/* 如,对于“compatible"属性为"invensense,icm20608"的设备节点,最终spi_device的modalias成员
   * 为"icm20608"而不是"invensense,icm20608"
   */
	compatible = of_get_property(node, "compatible", &cplen);
	if (!compatible || strlen(compatible) > cplen)
		return -ENODEV;
	p = strchr(compatible, ',');
	strlcpy(modalias, p ? p + 1 : compatible, len);
	return 0;
}

http://www.niftyadmin.cn/n/3657303.html

相关文章

Visual Studio International Pack对汉字的支持

还在重复发明轮子吗&#xff1f;看看微软在汉字方面的新支持&#xff1a;Simplified Chinese Pin-Yin Conversion Library - 支持获取简体中文字符的常用属性比如拼音&#xff0c;多音字&#xff0c;同音字&#xff0c;笔画数。 Traditional Chinese to Simplified Chinese Con…

Hyper-V: 一台服务器变二百台服务器

微软未来的virtualization技术会带来。。。&#xff1f;来自微软的邮件摘录&#xff1a;Q: My customer has a significant investment of 32-bit servers and wants to know why Hyper-V won’t be developed for Windows Server 2008 32-bit editions.A: Developing Hyper-V …

Linux spi驱动框架分析(四)

系列文章&#xff1a; Linux spi驱动框架分析&#xff08;一&#xff09; Linux spi驱动框架分析&#xff08;二&#xff09; Linux spi驱动框架分析&#xff08;三&#xff09; Linux spi驱动框架分析&#xff08;四&#xff09; spi_master的消息队列机制 SPI数据传输可以有…

Linux 输入子系统分析(一)

Linux 输入子系统分析&#xff08;一&#xff09; Linux 输入子系统分析&#xff08;二&#xff09; 分析一个内核提供的input_handler Linux内核输入子系统 输入设备&#xff08;如按键、键盘、触摸屏、鼠标等&#xff09;是典型的字符设备&#xff0c;其一般的工作机理是底…

文件的上传和下载

在Web编程中,我们常需要把一些本地文件上传到Web服务器上,上传后,用户可以通过浏览器方便地浏览这些文件&#xff0c;应用十分广泛。那么使用C#如何实现文件上传的功能呢?首先,在你的Visual C# web project 中增加一个上传用的Web Form,为了要上传文件,需要在ToolBox中选择HTM…

Linux 输入子系统分析(二)

Linux 输入子系统分析&#xff08;一&#xff09; Linux 输入子系统分析&#xff08;二&#xff09; 分析一个内核提供的input_handler input_dev驱动 input_dev驱动程序的工作主要是&#xff1a;申请一些硬件资源&#xff0c;如注册中断等&#xff0c;申请input_dev并设置&a…

分析一个内核提供的input_handler

Linux 输入子系统分析&#xff08;一&#xff09; Linux 输入子系统分析&#xff08;二&#xff09; 分析一个内核提供的input_handler 对应一些常用的输入设备&#xff0c;如键盘、鼠标等&#xff0c;内核提供的对应的input_handler&#xff0c;用来统一处理这类设备的事件。…

Tips - Web UI 资源索引

Basic (CSS,Javascript,AJAX...) - http://www.realsoftwaredevelopment.com/2007/08/ultimate-web-de.htmlAdvanced CSS - http://www.smashingmagazine.com/2007/01/19/53-css-techniques-you-couldnt-live-without/