Linux SPI总线 - SPI控制器驱动层

Posted 四季帆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux SPI总线 - SPI控制器驱动层相关的知识,希望对你有一定的参考价值。

1. 简介

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

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

2. SPI控制器驱动

        以imx6dl 这款SoC为例,其spi控制器对应的驱动代码在drivers/spi/spi-imx.c

static struct platform_driver spi_imx_driver = {
	.driver = {
		   .name = DRIVER_NAME,   /* #define DRIVER_NAME "spi_imx" */
		   .owner = THIS_MODULE,
		   .of_match_table = spi_imx_dt_ids,
		   },
	.id_table = spi_imx_devtype,
	.probe = spi_imx_probe,
	.remove = spi_imx_remove,
};
module_platform_driver(spi_imx_driver);

        spi_imx_driver和对应的spi_imx_device匹配以后就会调用spi_imx_probe函数对驱动程序和设备进行初始化。

static int spi_imx_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	const struct of_device_id *of_id =
			of_match_device(spi_imx_dt_ids, &pdev->dev);
	struct spi_imx_master *mxc_platform_info =
			dev_get_platdata(&pdev->dev);
	struct spi_master *master;
	struct spi_imx_data *spi_imx;
	struct resource *res;
	int i, ret, num_cs;

    /* 获取片选编号 */
	ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
	if (ret < 0) {
		if (mxc_platform_info)
			num_cs = mxc_platform_info->num_chipselect;
		else
			return ret;
	}

    /* 申请spi_master结构体,代表一个spi控制器 */
	master = spi_alloc_master(&pdev->dev,
			sizeof(struct spi_imx_data) + sizeof(int) * num_cs);

	platform_set_drvdata(pdev, master);
	master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
	master->bus_num = pdev->id;
	master->num_chipselect = num_cs;

	spi_imx = spi_master_get_devdata(master);
	spi_imx->bitbang.master = master;

    /* gpio申请 */
	for (i = 0; i < master->num_chipselect; i++) {
		int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
		if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
			cs_gpio = mxc_platform_info->chipselect[i];
		spi_imx->chipselect[i] = cs_gpio;
		ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i], DRIVER_NAME);	
	}

   /* 设置spi_master的回调函数 */
	spi_imx->bitbang.master->setup = spi_imx_setup;
	spi_imx->bitbang.master->cleanup = spi_imx_cleanup;
	spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
	spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
	spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
	
	init_completion(&spi_imx->xfer_done);
	spi_imx->devtype_data = of_id ? of_id->data :
		(struct spi_imx_devtype_data *) pdev->id_entry->driver_data;

    /* 映射io地址 */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
	
   /* 申请中断编号 */
	spi_imx->irq = platform_get_irq(pdev, 0);
	ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0,
			       dev_name(&pdev->dev), spi_imx);

	ret = spi_bitbang_start(&spi_imx->bitbang);
	if (ret) {
		dev_err(&pdev->dev, "bitbang start failed with %d\\n", ret);
		goto out_clk_put;
	}
 
	return ret;
}

int spi_bitbang_start(struct spi_bitbang *bitbang)
{
	struct spi_master *master = bitbang->master;
   /* 设置spi_master的回调函数 */
   master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
	master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
	master->transfer_one_message = spi_bitbang_transfer_one;
	······
	/* driver may get busy before register() returns, especially
	 * if someone registered boardinfo for devices
    * 调用通用层接口注册spi控制器(spi_master)
	 */
	ret = spi_register_master(spi_master_get(master));    /* 注册spi_master结构 */
	if (ret)
		spi_master_put(master);

	return 0;
}

3. spi_master结构的回调函数

        事实上,SPI控制器驱动程序的主要工作,就是要实现spi_master结构中的几个回调函数,其它的工作逻辑,均由通用接口层帮我们完成,通用接口层会在适当的时机调用这几个回调函数。

/* 设置控制器的工作模式或参数。当协议驱动希望修改控制器的工作模式或参数时,会调用通用接口层接口spi_setup,该接口会间接调用这里的setup */
int (*setup)(struct spi_device *spi)

/* 初始化时直接设为NULL即可,通用接口层已经实现了消息队列化,注册spi_master时,通用接口层会提供实现好的通用函数。
 * 现在只有一些老的驱动还在使用该回调方式,新的驱动应该停止使用该回调函数,而是应该使用队列化的transfer_one_message回调。
 * 需要注意的是,我们只能选择其中一种方式,设置了transfer_one_message回调,就不能设置transfer回调,反之亦然。
 */
int (*transfer)(struct spi_device *spi, struct spi_message *mesg)

/* 当一个SPI从设备(spi_device结构)被释放时,该回调函数会被调用,以便释放该从设备所占用的硬件资源 */
void (*cleanup)(struct spi_device *spi)

/* 用于在发起一个数据传送过程前和后,给控制器驱动一个机会,申请或释放某些必要的硬件资源,例如DMA资源和内存资源等等 */
int (*prepare_transfer_hardware)(struct spi_master *master)
int (*unprepare_transfer_hardware)(struct spi_master *master)

/* 用于在发起一个数据传送过程前和后,给控制器驱动一个机会,对message进行必要的预处理或后处理,比如根据message需要交换数据的从设备,设定控制器的正确工作时钟、字长和工作模式等 */
int (*prepare_message)(struct spi_master *master, struct spi_message *message)
int (*unprepare_message)(struct spi_master *master, struct spi_message *message)

/* 当通用接口层发现master的队列中有消息需要传送时,会调用该回调函数,所以该函数是真正完成一个消息传送的工作函数,当传送工作完成时,应该调用spi_finalize_current_message函数,以便通知通用接口层,发起队列中的下一个消息的传送工作 */
int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg)

以上是关于Linux SPI总线 - SPI控制器驱动层的主要内容,如果未能解决你的问题,请参考以下文章

Linux SPI总线 - SPI控制器驱动层

Linux SPI总线和设备驱动架构之三:SPI控制器驱动

Linux SPI总线和设备驱动架构之一:系统概述转

Linux SPI总线 - SPI协议驱动层

i.MX6ULL驱动开发 | 13 - Linux SPI 驱动框架

i.MX6ULL驱动开发 | 13 - Linux SPI 驱动框架