Linux驱动开发 - spi子系统(6) spi框架

前言

前面的文章介绍了spi控制器驱动、spi设备驱动、spi应用层的读写操作,本文介绍spi的框架。

驱动文件路径

 drivers/spi/spi.c,spi核心初始化、spi控制器驱动的注册、spi设备驱动的注册、spi的读写流程全部在此文件中实现。

spi核心初始化


static int __init spi_init(void)
{
	int	status;

	buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);//申请缓存,在spi_write_then_read中使用
	if (!buf) {
		status = -ENOMEM;
		goto err0;
	}

	status = bus_register(&spi_bus_type);//spi总线注册,后续的spi设备的device和driver都指定spi_bus_type
	if (status < 0)
		goto err1;

	status = class_register(&spi_master_class);//master class 注册
	if (status < 0)
		goto err2;

	if (IS_ENABLED(CONFIG_SPI_SLAVE)) {
		status = class_register(&spi_slave_class);//slave class注册
		if (status < 0)
			goto err3;
	}
......

	return 0;
}

postcore_initcall(spi_init);

 spi核心的初始化完成了如下功能

  • 缓存申请,在spi_write_then_read中使用
  • spi总线注册,后续的spi设备的device add和driver register都需要指定spi_bus_type
  • class 注册,主要在/sys/class/spi目录下创建一些文件

spi控制器驱动注册 

int spi_register_controller(struct spi_controller *ctlr)
{
	struct device		*dev = ctlr->dev.parent;
	struct boardinfo	*bi;
	int			status;
	int			id, first_dynamic;

......

	status = spi_controller_check_ops(ctlr);//在注册SPI控制器之前,确保实现了所有必要的钩子函数。
	if (status)
		return status;

	if (ctlr->bus_num >= 0) {//初始化ctlr->bus_num = -1,不执行此条件语句
		/* devices with a fixed bus num must check-in with the num */
		mutex_lock(&board_lock);
		id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
			ctlr->bus_num + 1, GFP_KERNEL);
		mutex_unlock(&board_lock);
		if (WARN(id < 0, "couldn't get idr"))
			return id == -ENOSPC ? -EBUSY : id;
		ctlr->bus_num = id;
	} else if (ctlr->dev.of_node) {
		/* allocate dynamic bus number using Linux idr */
		id = of_alias_get_id(ctlr->dev.of_node, "spi");//若设备树的别名为spi1,则id=1
		if (id >= 0) {
			ctlr->bus_num = id;
			mutex_lock(&board_lock);
			id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
				       ctlr->bus_num + 1, GFP_KERNEL);
			mutex_unlock(&board_lock);
			if (WARN(id < 0, "couldn't get idr"))
				return id == -ENOSPC ? -EBUSY : id;
		}
	}
	if (ctlr->bus_num < 0) {
......
		ctlr->bus_num = id;//bus_num赋值
	}
......
    //初始化队列、自旋锁、互斥锁、完成量
	INIT_LIST_HEAD(&ctlr->queue);
	spin_lock_init(&ctlr->queue_lock);
	spin_lock_init(&ctlr->bus_lock_spinlock);
	mutex_init(&ctlr->bus_lock_mutex);
	mutex_init(&ctlr->io_mutex);
	ctlr->bus_lock_flag = 0;
	init_completion(&ctlr->xfer_completion);
	if (!ctlr->max_dma_len)
		ctlr->max_dma_len = INT_MAX;

	/* register the device, then userspace will see it.
	 * registration fails if the bus ID is in use.
	 */
	dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);//dev的name赋值
    
    //从设备树中获取CS对应的GPIO,我们的设备树不涉及,不执行该条件语句
	if (!spi_controller_is_slave(ctlr)) {
		if (ctlr->use_gpio_descriptors) {
			status = spi_get_gpio_descs(ctlr);
			if (status)
				goto free_bus_id;
			/*
			 * A controller using GPIO descriptors always
			 * supports SPI_CS_HIGH if need be.
			 */
			ctlr->mode_bits |= SPI_CS_HIGH;
		} else {
			/* Legacy code path for GPIOs from DT */
			status = of_spi_get_gpio_numbers(ctlr);
			if (status)
				goto free_bus_id;
		}
	}

	/*
	 * Even if it's just one always-selected device, there must
	 * be at least one chipselect.
	 */
	if (!ctlr->num_chipselect) { //初始化num_chipselect = 1
		status = -EINVAL;
		goto free_bus_id;
	}

	status = device_add(&ctlr->dev);//spi控制器设备注册
	if (status < 0)
		goto free_bus_id;
	dev_dbg(dev, "registered %s %s\n",
			spi_controller_is_slave(ctlr) ? "slave" : "master",
			dev_name(&ctlr->dev));

	/*
	 * If we're using a queued driver, start the queue. Note that we don't
	 * need the queueing logic if the driver is only supporting high-level
	 * memory operations.
	 */
	if (ctlr->transfer) {
		dev_info(dev, "controller is unqueued, this is deprecated\n");
	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
		status = spi_controller_initialize_queue(ctlr);//初始化队列
		if (status) {
			device_del(&ctlr->dev);
			goto free_bus_id;
		}
	}
	/* add statistics */
	spin_lock_init(&ctlr->statistics.lock);

	mutex_lock(&board_lock);
	list_add_tail(&ctlr->list, &spi_controller_list);
	list_for_each_entry(bi, &board_list, list)
		spi_match_controller_to_boardinfo(ctlr, &bi->board_info);//不使用
	mutex_unlock(&board_lock);

	/* Register devices from the device tree and ACPI */
	of_register_spi_devices(ctlr);//解析设备树,把spi控制器下的设备全部注册到内核
	acpi_register_spi_devices(ctlr);//不使用
	return status;

free_bus_id:
	mutex_lock(&board_lock);
	idr_remove(&spi_master_idr, ctlr->bus_num);
	mutex_unlock(&board_lock);
	return status;
}

status = spi_controller_check_ops(ctlr);//在注册SPI控制器之前,确保实现了所有必要的钩子函数。 Linux驱动开发 - spi子系统(1) spi master控制器驱动 我们实现了钩子函数master->transfer_one = mtk_spi_transfer_one;

由于我们在__spi_alloc_controller函数中初始化ctlr->bus_num = -1,所以执行第23行的条件语句,然后id = of_alias_get_id(ctlr->dev.of_node, "spi")获取id,若设备树的别名为spi1,则id=1,我们的设备树分别为spi0、spi1、spi2,所以对应的id分别为0、1、2;

第57~74行,从设备树中获取CS对应的GPIO,我们的设备树没有“cs”和"cs-gpios",不执行该条件语句;

第80行,初始化num_chipselect = 1,表示只有一个CS脚;

第85行,status = device_add(&ctlr->dev);//spi控制器设备注册,若为spi0,则在/sys/device/platform/../spi0

第100行,status = spi_controller_initialize_queue(ctlr);//初始化队列,会启动一个新的线程,用于后续的spi数据传输。

第116行,of_register_spi_devices(ctlr);//解析设备树,把spi控制器下的设备全部注册到内核.

of_register_spi_devices

static void of_register_spi_devices(struct spi_controller *ctlr)
{
	struct spi_device *spi;
	struct device_node *nc;

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

	for_each_available_child_of_node(ctlr->dev.of_node, nc) { //遍历所有子节点
		if (of_node_test_and_set_flag(nc, OF_POPULATED))//设置标志
			continue;
		spi = of_register_spi_device(ctlr, nc);//注册spi设备
		if (IS_ERR(spi)) {
			dev_warn(&ctlr->dev,
				 "Failed to create SPI device for %pOF\n", nc);
			of_node_clear_flag(nc, OF_POPULATED);
		}
	}
}

of_register_spi_device 


static struct spi_device *
of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
{
	struct spi_device *spi;
	int rc;

	spi = spi_alloc_device(ctlr);	/* 申请spi_device */
......
	rc = of_modalias_node(nc, spi->modalias,	/* 解析设备树的compatible */
				sizeof(spi->modalias));
......

	rc = of_spi_parse_dt(ctlr, spi, nc);/* 解析设备树*/
......

	rc = spi_add_device(spi);	/*注册 spi device */
......

	return spi;
......
}

spi_add_device 

int spi_add_device(struct spi_device *spi)
{
	struct spi_controller *ctlr = spi->controller;
	struct device *dev = ctlr->dev.parent;
	int status;

	/*chip_select是解析设备树中reg得到的, */
	if (spi->chip_select >= ctlr->num_chipselect) {
		dev_err(dev, "cs%d >= max %d\n", spi->chip_select,
			ctlr->num_chipselect);
		return -EINVAL;
	}

	/* 比如spi1.0 */
	spi_dev_set_name(spi);


	mutex_lock(&spi_add_lock);

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

.......

	status = spi_setup(spi);/*最终调用spi-mt65xx.c mtk中的spi_setup*/
......

	/* spi 设备添加 */
	status = device_add(&spi->dev);
......
	return status;
}

 spi设备驱动注册

#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;
	sdrv->driver.probe = spi_drv_probe;
	sdrv->driver.remove = spi_drv_remove;
	if (sdrv->shutdown)
		sdrv->driver.shutdown = spi_drv_shutdown;
	return driver_register(&sdrv->driver);
}

首先设置总线spi_bus_type,probe函数spi_drv_probe,最终调用 driver_register注册设备驱动。

spi数据传输spi_sync

Linux驱动开发 - spi子系统(4) spi 设备驱动介绍了spi的读写过程,最终是调用spi_sync,如下

    char tx_buffer[1024];
    char rx_buffer[1024];
    struct spi_transfer xfer = {
        .tx_buf = tx_buffer,//指定发送buff
        .rx_buf = rx_buffer,//指定接收buff
        .len = len + 1,  //指定数据长度
    };

    spi_message_init(&msg);//初始化msg
    spi_message_add_tail(&xfer, &msg);//add xfer到msg
    
    spi_sync(smi230_gyro_device, &msg);//开始传输

spi_sync调用__spi_sync

int spi_sync(struct spi_device *spi, struct spi_message *message)
{
	int ret;

	mutex_lock(&spi->controller->bus_lock_mutex);
	ret = __spi_sync(spi, message);
	mutex_unlock(&spi->controller->bus_lock_mutex);

	return ret;
}

 __spi_sync


static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
	DECLARE_COMPLETION_ONSTACK(done);//定义一个完成量
	int status;
	struct spi_controller *ctlr = spi->controller;
	unsigned long flags;

	status = __spi_validate(spi, message);//参数有效性检查
	if (status != 0)
		return status;

	message->complete = spi_complete;//完成量回调函数
	message->context = &done;
	message->spi = spi;

	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_sync);
	SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);

	/* If we're not using the legacy transfer method then we will
	 * try to transfer in the calling context so special case.
	 * This code would be less tricky if we could remove the
	 * support for driver implemented message queues.
	 */
	if (ctlr->transfer == spi_queued_transfer) {
		spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);

		trace_spi_message_submit(message);

		status = __spi_queued_transfer(spi, message, false);//false表示不在新线程中执行

		spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);
	} else {
		status = spi_async_locked(spi, message);
	}

	if (status == 0) {
		/* Push out the messages in the calling context if we
		 * can.
		 */
		if (ctlr->transfer == spi_queued_transfer) {
			SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics,
						       spi_sync_immediate);
			SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
						       spi_sync_immediate);
			__spi_pump_messages(ctlr, false); //发送spi数据
		}

		wait_for_completion(&done);//等待完成量
		status = message->status;
	}
	message->context = NULL;
	return status;
}

__spi_pump_messages

static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
{
	struct spi_message *msg;
	bool was_busy = false;
	unsigned long flags;
	int ret;

......

	/* Check if the queue is idle */
	if (list_empty(&ctlr->queue) || !ctlr->running) {//在其他线程中执行
		if (!ctlr->busy) {
			spin_unlock_irqrestore(&ctlr->queue_lock, flags);
			return;
		}

		/* Only do teardown in the thread */
		if (!in_kthread) {
			kthread_queue_work(&ctlr->kworker,
					   &ctlr->pump_messages);
			spin_unlock_irqrestore(&ctlr->queue_lock, flags);
			return;
		}

		ctlr->busy = false;
		ctlr->idling = true;
		spin_unlock_irqrestore(&ctlr->queue_lock, flags);

		kfree(ctlr->dummy_rx);
		ctlr->dummy_rx = NULL;
		kfree(ctlr->dummy_tx);
		ctlr->dummy_tx = NULL;
		if (ctlr->unprepare_transfer_hardware &&
		    ctlr->unprepare_transfer_hardware(ctlr))
			dev_err(&ctlr->dev,
				"failed to unprepare transfer hardware\n");
		if (ctlr->auto_runtime_pm) {
			pm_runtime_mark_last_busy(ctlr->dev.parent);
			pm_runtime_put_autosuspend(ctlr->dev.parent);
		}
		trace_spi_controller_idle(ctlr);

		spin_lock_irqsave(&ctlr->queue_lock, flags);
		ctlr->idling = false;
		spin_unlock_irqrestore(&ctlr->queue_lock, flags);
		return;
	}

	/* Extract head of queue */
	msg = list_first_entry(&ctlr->queue, struct spi_message, queue);
	ctlr->cur_msg = msg;

	list_del_init(&msg->queue);
	if (ctlr->busy)
		was_busy = true;
	else
		ctlr->busy = true;
	spin_unlock_irqrestore(&ctlr->queue_lock, flags);

	mutex_lock(&ctlr->io_mutex);

	if (!was_busy && ctlr->auto_runtime_pm) {
		ret = pm_runtime_get_sync(ctlr->dev.parent);
		if (ret < 0) {
			pm_runtime_put_noidle(ctlr->dev.parent);
			dev_err(&ctlr->dev, "Failed to power device: %d\n",
				ret);
			mutex_unlock(&ctlr->io_mutex);
			return;
		}
	}
......

	if (ctlr->prepare_message) {
		ret = ctlr->prepare_message(ctlr, msg);//调用spi-mt65xx.c的mtk_spi_prepare_message
......
		ctlr->cur_msg_prepared = true;//标志赋值
	}

	ret = spi_map_msg(ctlr, msg);
	if (ret) {
		msg->status = ret;
		spi_finalize_current_message(ctlr);
		goto out;
	}

	ret = ctlr->transfer_one_message(ctlr, msg);//调用spi_transfer_one_message
	if (ret) {
		dev_err(&ctlr->dev,
			"failed to transfer one message from queue\n");
		goto out;
	}

out:
	mutex_unlock(&ctlr->io_mutex);

	/* Prod the scheduler in case transfer_one() was busy waiting */
	if (!ret)
		cond_resched();
}

调用关系为:

__spi_pump_messages 

        transfer_one_message        

                spi_transfer_one_message

                        transfer_one

                                mtk_spi_transfer_one

spi_transfer_one_message中spi_transfer_wait(ctlr, msg, xfer)等待,直到spi_finalize_current_transfer中complete(&ctlr->xfer_completion)退出,完成1次transfer传输

 spi_transfer_one_message最后调用了spi_finalize_current_message,而此函数有mesg->complete(mesg->context),发送完成量,此时__spi_sync函数中的wait_for_completion(&done)结束等待,一次数据传输完成。

总结

        spi.c文件是SPI子系统的核心文件之一,负责实现SPI设备的注册、注销、数据传输等功能,如下。

1. 结构体定义:spi.c文件中定义了一系列结构体,用于表示SPI总线、SPI设备等信息。包括struct spi_device、struct spi_driver等结构体,用于描述SPI设备和SPI驱动程序之间的关系。

2. 函数实现:spi.c文件中包含了一系列函数的实现,用于处理SPI设备的注册、注销、数据传输等操作。例如,spi_register_driver()函数用于注册SPI设备的驱动程序,spi_unregister_driver()函数用于注销SPI设备的驱动程序。

3. 设备操作:spi.c文件中定义了一系列函数,用于实现SPI设备的操作。包括spi_sync()函数用于进行数据传输,spi_setup()函数用于设置SPI设备的参数等。

4. 总线管理:spi.c文件还包含了一些函数,用于管理SPI总线的注册、注销等操作。例如,spi_register_master()函数用于注册SPI总线的主控制器,spi_unregister_master()函数用于注销SPI总线的主控制器。

  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值