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控制器驱动层的主要内容,如果未能解决你的问题,请参考以下文章